WebP लॉसलेस बिटस्ट्रीम के लिए खास जानकारी

जेर्की अलकुइजाला, पीएचडी, Google LLC, 2023-03-09

खास जानकारी

WebP लॉसलेस, ऐसा इमेज फ़ॉर्मैट है जिसे एआरजीबी इमेज को लॉसलेस कंप्रेस करने के लिए इस्तेमाल किया जाता है. लॉसलेस फ़ॉर्मैट, पिक्सल वैल्यू को पूरी तरह से स्टोर करता है और पहले जैसा करता है. इनमें, पूरी तरह पारदर्शी पिक्सल के लिए कलर की वैल्यू भी शामिल हैं. बल्क डेटा को कंप्रेस करने के लिए, क्रम में चलने वाले डेटा कंप्रेशन (LZ77), प्रीफ़िक्स कोडिंग, और कलर कैश के लिए एक यूनिवर्सल एल्गोरिदम का इस्तेमाल किया जाता है. PNG के मुकाबले, तेज़ी से डिकोड करने की स्पीड को दिखाया गया है. साथ ही, मौजूदा PNG फ़ॉर्मैट के मुकाबले, इसकी तुलना में 25% ज़्यादा डेंसिटी वाला कंप्रेशन हासिल किया जा सकता है.

1 परिचय

इस दस्तावेज़ में, WebP लॉसलेस इमेज के कंप्रेस किए गए डेटा के बारे में बताया गया है. इसका इस्तेमाल, WebP लॉसलेस एन्कोडर और डिकोडर को लागू करने के बारे में ज़्यादा जानकारी देने के लिए बनाया गया है.

इस दस्तावेज़ में, बिटस्ट्रीम के बारे में बताने और बिट को पढ़ने के लिए फ़ंक्शन मौजूद होने का अनुमान लगाने के लिए, हम सी प्रोग्रामिंग लैंग्वेज सिंटैक्स का भरपूर इस्तेमाल करते हैं ReadBits(n). बाइट को उनकी स्ट्रीम के सामान्य क्रम में पढ़ा जाता है और हर बाइट के बिट, कम से कम ज़रूरी बिट-फ़र्स्ट क्रम में पढ़े जाते हैं. जब एक से ज़्यादा बिट को एक ही समय पर पढ़ा जाता है, तो पूर्णांक को ओरिजनल डेटा से ओरिजनल ऑर्डर में बनाया जाता है. लौटाए गए पूर्णांक के सबसे अहम बिट, मूल डेटा के सबसे अहम बिट भी होते हैं. इसलिए, स्टेटमेंट

b = ReadBits(2);

नीचे दिए गए दो स्टेटमेंट के बराबर है:

b = ReadBits(1);
b |= ReadBits(1) << 1;

हम मानते हैं कि हर कलर कॉम्पोनेंट, जैसे कि ऐल्फ़ा, लाल, नीला, और हरा, को 8-बिट बाइट का इस्तेमाल करके दिखाया गया है. संबंधित टाइप को हम uint8 के तौर पर बताते हैं. पूरे ARGB पिक्सल को uint32 नाम से दिखाया जाता है. यह बिना साइन वाला पूर्णांक होता है, जिसमें 32 बिट होते हैं. ट्रांसफ़ॉर्म के व्यवहार को दिखाने वाले कोड में, इन वैल्यू को इन बिट में कोड में बदला जाता है: बिट में ऐल्फ़ा 31..24, बिट 23..16 में लाल, 15..8 में हरा, और बिट 7..0 में नीला; हालांकि, फ़ॉर्मैट को लागू करने के लिए, अंदरूनी तौर पर दूसरे रिप्रज़ेंटेशन का इस्तेमाल किया जा सकता है.

मोटे तौर पर, WebP लॉसलेस इमेज में हेडर डेटा, जानकारी में बदलाव करने वाला, और असल इमेज डेटा होता है. हेडर में इमेज की चौड़ाई और ऊंचाई शामिल होती है. एक WebP लॉसलेस इमेज के लिए, एंट्रॉपी को कोड में बदलने से पहले चार तरह के बदलाव किए जा सकते हैं. बिटस्ट्रीम की ट्रांसफ़ॉर्म जानकारी में वह डेटा होता है जो संबंधित इनवर्स ट्रांसफ़ॉर्म को लागू करने के लिए ज़रूरी होता है.

2 नाम

एआरजीबी
Pixel की वैल्यू जिसमें ऐल्फ़ा, लाल, हरे, और नीले रंग की वैल्यू शामिल हैं.
ARGB इमेज
दो डाइमेंशन वाला कलेक्शन, जिसमें एआरजीबी पिक्सल हैं.
कलर कैश मेमोरी
हाल ही में इस्तेमाल किए गए रंगों को सेव करने के लिए, हैश के तौर पर इस्तेमाल किया जाने वाला एक छोटा अरे. इसकी मदद से, रंगों को छोटे कोड से याद किया जा सकता है.
रंग को इंडेक्स करने से जुड़ी इमेज
यह रंगों की एक डाइमेंशन वाली इमेज होती है. इसे एक छोटे पूर्णांक का इस्तेमाल करके इंडेक्स किया जा सकता है. WebP लॉसलेस में 256 तक.
इमेज का रंग बदलें
दो डाइमेंशन वाली सब-रिज़ॉल्यूशन इमेज, जिसमें रंग के कॉम्पोनेंट से जुड़ाव के बारे में डेटा शामिल होता है.
दूरी की मैपिंग
LZ77 की दूरी को बदलकर, टू-डाइमेंशन प्रॉक्सिमिटी में पिक्सल के लिए सबसे छोटी वैल्यू तय करता है.
एंट्रॉपी इमेज
दो डाइमेंशन वाली सब-रिज़ॉल्यूशन इमेज, जिससे यह पता चलता है कि इमेज में मौजूद स्क्वेयर में कौनसी एंट्रॉपी कोडिंग का इस्तेमाल किया जाना चाहिए. इसका मतलब है कि हर पिक्सल एक मेटा प्रीफ़िक्स कोड है.
LZ77
डिक्शनरी पर आधारित, स्लाइडिंग विंडो कंप्रेस करने वाला एल्गोरिदम, जो या तो किसी सिंबल का उत्सर्जन करता है या उसे पिछले सिंबल के क्रम के तौर पर बताता है.
मेटा प्रीफ़िक्स कोड
एक छोटा पूर्णांक (ज़्यादा से ज़्यादा 16 बिट) जो मेटा प्रीफ़िक्स टेबल में किसी एलिमेंट को इंडेक्स करता है.
अनुमान लगाने के लिए इमेज
दो डाइमेंशन वाली सब-रिज़ॉल्यूशन इमेज, जिससे यह पता चलता है कि इमेज में किसी खास स्क्वेयर के लिए, किस जगह का अनुमान लगाने वाला टूल इस्तेमाल किया गया है.
प्रीफ़िक्स कोड
यह एंट्रॉपी कोडिंग करने का एक क्लासिक तरीका है, जिसमें कम संख्या में बिट का इस्तेमाल ज़्यादा बार इस्तेमाल किए जाने वाले कोड के लिए किया जाता है.
प्रीफ़िक्स कोडिंग
बड़े पूर्णांकों को एंट्रॉपी कोड में डालने का एक तरीका. इसमें एंट्रॉपी कोड का इस्तेमाल करके, पूर्णांक के कुछ बिट को कोड किया जाता है और बाकी बिट को रॉ फ़ॉर्मैट में बदल दिया जाता है. इससे एंट्रॉपी कोड के ब्यौरों की रेंज बड़ी होने पर भी छोटे बने रहते हैं.
स्कैन लाइन क्रम
पिक्सल के प्रोसेसिंग क्रम (बाएं से दाएं और ऊपर से नीचे), जो बाईं ओर के सबसे ऊपर वाले पिक्सल से शुरू होता है. एक पंक्ति पूरी हो जाने के बाद, अगली पंक्ति के बाएं कॉलम से जारी रखें.

तीन RIFF हेडर

हेडर की शुरुआत में RIFF कंटेनर होता है. इसमें नीचे दी गई 21 बाइट होती हैं:

  1. स्ट्रिंग 'RIFF'.
  2. हिस्से की लंबाई का 32-बिट वाला छोटा-एंडियन मान, जो डेटा वाले हिस्से का पूरा साइज़ है, जिसे RIFF हेडर से कंट्रोल किया जाता है. आम तौर पर, यह पेलोड का साइज़ (फ़ाइल का साइज़ माइनस 8 बाइट: 'RIFF' आइडेंटिफ़ायर के लिए चार बाइट और वैल्यू को स्टोर करने के लिए चार बाइट) के बराबर होता है.
  3. स्ट्रिंग 'WEBP' (RIFF कंटेनर का नाम).
  4. स्ट्रिंग 'VP8L' (लॉसलेस कोड में बदले गए इमेज डेटा के लिए FourCC).
  5. लॉसलेस स्ट्रीम में बाइट की संख्या का छोटा-एंडियन, 32-बिट.
  6. 1-बाइट सिग्नेचर 0x2f.

बिटस्ट्रीम के पहले 28 बिट, इमेज की चौड़ाई और ऊंचाई की जानकारी देते हैं. चौड़ाई और ऊंचाई को 14-बिट पूर्णांक के तौर पर, इस तरह डिकोड किया जाता है:

int image_width = ReadBits(14) + 1;
int image_height = ReadBits(14) + 1;

इमेज की चौड़ाई और ऊंचाई के लिए 14-बिट के सटीक होने की वजह से, WebP लॉसलेस इमेज का साइज़ 16384 गुने 16384 पिक्सल तक सीमित हो जाता है.

safety_is_used बिट सिर्फ़ एक संकेत है. इसे डिकोड करने पर असर नहीं पड़ता है. अगर तस्वीर में सभी अल्फ़ा वैल्यू 255 हैं, तो इसे 0 पर और नहीं तो 1 पर सेट किया जाना चाहिए.

int alpha_is_used = ReadBits(1);

version_number तीन बिट का कोड होता है, जिसे 0 पर सेट किया जाना ज़रूरी है. किसी भी दूसरी वैल्यू को गड़बड़ी माना जाएगा.

int version_number = ReadBits(3);

4 ट्रांसफ़ॉर्म

ये ट्रांसफ़ॉर्मेशन, इमेज के डेटा में किए गए बदलाव को उलटा किया जा सकता है. इससे जगह और रंग के बीच संबंध जोड़कर, बाकी बचे हुए सिंबॉलिक एंट्रॉपी को कम किया जा सकता है. वे फ़ाइनल कंप्रेशन को ज़्यादा घना बना सकते हैं.

किसी इमेज में चार तरह के बदलाव हो सकते हैं. 1 बिट ट्रांसफ़ॉर्म की मौजूदगी दिखाता है. हर ट्रांसफ़ॉर्म को सिर्फ़ एक बार इस्तेमाल किया जा सकता है. ट्रांसफ़ॉर्म का इस्तेमाल सिर्फ़ मुख्य लेवल की ARGB इमेज के लिए किया जाता है. सब-रिज़ॉल्यूशन वाली इमेज (रंग बदलने वाली इमेज, एंट्रॉपी इमेज, और अनुमान लगाने वाली इमेज) में कोई ट्रांसफ़ॉर्म नहीं होता है. यहां तक कि 0 बिट भी, ट्रांसफ़ॉर्म के खत्म होने के बारे में नहीं बताता है.

आम तौर पर, एन्कोडर इस तरह के ट्रांसफ़ॉर्म का इस्तेमाल, रेज़िड्यूअल इमेज में शैनन एंट्रॉपी को कम करने के लिए करता है. साथ ही, एंट्रॉपी मिनिमाइज़ेशन के आधार पर ट्रांसफ़ॉर्म डेटा का फ़ैसला लिया जा सकता है.

while (ReadBits(1)) {  // Transform present.
  // Decode transform type.
  enum TransformType transform_type = ReadBits(2);
  // Decode transform data.
  ...
}

// Decode actual image data (Section 5).

अगर कोई ट्रांसफ़ॉर्म मौजूद है, तो अगले दो बिट ट्रांसफ़ॉर्म टाइप तय करते हैं. बदलाव चार तरह के होते हैं.

enum TransformType {
  PREDICTOR_TRANSFORM             = 0,
  COLOR_TRANSFORM                 = 1,
  SUBTRACT_GREEN_TRANSFORM        = 2,
  COLOR_INDEXING_TRANSFORM        = 3,
};

डेटा बदलने के टाइप के बाद, डेटा ट्रांसफ़ॉर्म करें. ट्रांसफ़ॉर्म डेटा में वह जानकारी होती है जो इनवर्स ट्रांसफ़ॉर्म को लागू करने के लिए ज़रूरी होती है. यह डेटा ट्रांसफ़ॉर्म के टाइप पर निर्भर करता है. इनवर्स ट्रांसफ़ॉर्म को उसी क्रम में लागू किया जाता है जिस क्रम में उन्हें बिटस्ट्रीम से पढ़ा जाता है. इसका मतलब है कि पहले आखिरी ट्रांसफ़ॉर्मेशन को बिटस्ट्रीम से पढ़ा जाता है.

इसके बाद, हम अलग-अलग टाइप के लिए डेटा बदलें के बारे में बताते हैं.

4.1 अनुमानित बदलाव

अनुमान लगाने वाले ट्रांसफ़ॉर्म का इस्तेमाल, एंट्रॉपी को कम करने के लिए किया जा सकता है. ऐसा करने के लिए, आस-पास के पिक्सल अक्सर एक-दूसरे से जुड़े होते हैं. अनुमान लगाने वाले ट्रांसफ़ॉर्म में, मौजूदा पिक्सल वैल्यू का अनुमान पहले से डिकोड किए गए पिक्सल (स्कैन लाइन क्रम में) से लगाया जाता है. साथ ही, सिर्फ़ बचा हुआ वैल्यू (असल - अनुमानित) को कोड में बदला जाता है. पिक्सल का हरा कॉम्पोनेंट यह बताता है कि ARGB इमेज के किसी खास ब्लॉक में 14 अनुमान लगाने वाले किन चीज़ों का इस्तेमाल किया जाएगा. पूर्वानुमान मोड यह तय करता है कि अनुमान किस तरह का है. हम इमेज को स्क्वेयर में बांटते हैं और स्क्वेयर में मौजूद सभी पिक्सल, एक ही अनुमान मोड का इस्तेमाल करते हैं.

अनुमानित डेटा के पहले तीन बिट, ब्लॉक की चौड़ाई और ऊंचाई को बिट की संख्या में तय करते हैं.

int size_bits = ReadBits(3) + 2;
int block_width = (1 << size_bits);
int block_height = (1 << size_bits);
#define DIV_ROUND_UP(num, den) (((num) + (den) - 1) / (den))
int transform_width = DIV_ROUND_UP(image_width, 1 << size_bits);

ट्रांसफ़ॉर्म डेटा में इमेज के हर ब्लॉक के लिए अनुमान मोड होता है. यह एक सब-रिज़ॉल्यूशन वाली इमेज है. पिक्सल का हरा कॉम्पोनेंट यह बताता है कि ARGB इमेज के किसी खास ब्लॉक में, सभी block_width * block_height पिक्सल के लिए 14 में से किन 14 अनुमान लगाने वालों का इस्तेमाल किया जाएगा. इस सब-रिज़ॉल्यूशन इमेज को चैप्टर 5 में बताई गई तकनीकों का इस्तेमाल करके एन्कोड किया गया है.

ब्लॉक कॉलम, transform_width का इस्तेमाल दो-डाइमेंशन वाले इंडेक्स में किया जाता है. पिक्सल (x, y) के लिए, फ़िल्टर ब्लॉक के पते का पता लगाने के लिए, यहां दिया गया तरीका अपनाएं:

int block_index = (y >> size_bits) * transform_width +
                  (x >> size_bits);

अनुमान लगाने के लिए, अलग-अलग 14 मोड हैं. हर अनुमान मोड में, मौजूदा पिक्सल वैल्यू का अनुमान आस-पास के एक या उससे ज़्यादा पिक्सल से लगाया जाता है, जिनकी वैल्यू पहले से मालूम होती हैं.

हमने मौजूदा पिक्सल (P) के पास के पिक्सल (TL, T, TR, और L) इस तरह चुने हैं:

O    O    O    O    O    O    O    O    O    O    O
O    O    O    O    O    O    O    O    O    O    O
O    O    O    O    TL   T    TR   O    O    O    O
O    O    O    O    L    P    X    X    X    X    X
X    X    X    X    X    X    X    X    X    X    X
X    X    X    X    X    X    X    X    X    X    X

जहां TL का मतलब है सबसे ऊपर बाईं ओर, T का मतलब सबसे ऊपर, और L का मतलब है सबसे ऊपर वाला दाईं ओर. P के लिए वैल्यू का अनुमान लगाते समय, सभी O, TL, T, TR और L पिक्सल पहले ही प्रोसेस किए जा चुके होते हैं. साथ ही, P पिक्सल और सभी X पिक्सल की जानकारी नहीं होती है.

पास वाले पिछले पिक्सल को देखते हुए, अलग-अलग अनुमान मोड इस तरह तय किए गए हैं.

मोड मौजूदा पिक्सल के हर चैनल की अनुमानित वैल्यू
0 0xff000000 (ARGB में ठोस काले रंग का प्रतिनिधित्व करता है)
1 L
2 T
3 में कीमत
4 TL
5 औसत2(औसत2(L, TR), T)
6 औसत2(L, TL)
7 औसत2(L, T)
8 औसत2(TL, T)
9 औसत2(T, TR)
10 औसत2(औसत2(L, TL), औसत2(T, TR))
11 चुनें(L, T, TL)
12 ClampAddSubtractFull(L, T, TL)
13 ClampAddSubtractHalf(Average2(L, T), TL)

हर ARGB कॉम्पोनेंट के लिए, Average2 की जानकारी इस तरह दी गई है:

uint8 Average2(uint8 a, uint8 b) {
  return (a + b) / 2;
}

'चीज़ें चुनें' पैरामीटर इस तरह परिभाषित किया गया है:

uint32 Select(uint32 L, uint32 T, uint32 TL) {
  // L = left pixel, T = top pixel, TL = top-left pixel.

  // ARGB component estimates for prediction.
  int pAlpha = ALPHA(L) + ALPHA(T) - ALPHA(TL);
  int pRed = RED(L) + RED(T) - RED(TL);
  int pGreen = GREEN(L) + GREEN(T) - GREEN(TL);
  int pBlue = BLUE(L) + BLUE(T) - BLUE(TL);

  // Manhattan distances to estimates for left and top pixels.
  int pL = abs(pAlpha - ALPHA(L)) + abs(pRed - RED(L)) +
           abs(pGreen - GREEN(L)) + abs(pBlue - BLUE(L));
  int pT = abs(pAlpha - ALPHA(T)) + abs(pRed - RED(T)) +
           abs(pGreen - GREEN(T)) + abs(pBlue - BLUE(T));

  // Return either left or top, the one closer to the prediction.
  if (pL < pT) {
    return L;
  } else {
    return T;
  }
}

हर ARGB कॉम्पोनेंट के लिए ClampAddSubtractFull और ClampAddSubtractHalf फ़ंक्शन इस तरह से किए जाते हैं:

// Clamp the input value between 0 and 255.
int Clamp(int a) {
  return (a < 0) ? 0 : (a > 255) ? 255 : a;
}
int ClampAddSubtractFull(int a, int b, int c) {
  return Clamp(a + b - c);
}
int ClampAddSubtractHalf(int a, int b) {
  return Clamp(a + (a - b) / 2);
}

कुछ बॉर्डर पिक्सल के लिए खास हैंडलिंग नियम होते हैं. अगर इन पिक्सल के लिए मोड [0..13] चाहे कुछ भी हो, तो इमेज के सबसे ऊपर बाईं ओर दिए गए पिक्सल की अनुमानित वैल्यू 0xff000000 होगी, ऊपर की पंक्ति के सभी पिक्सल L-पिक्सल, और सबसे बाईं ओर के कॉलम के सभी पिक्सल T-पिक्सल होंगे.

सबसे दाईं ओर वाले कॉलम में, TR-पिक्सल में पिक्सल का जवाब देना बेहतरीन है. सबसे दाईं ओर दिए गए कॉलम के पिक्सल का अनुमान लगाने के लिए, मोड [0..13] का इस्तेमाल किया जाता है. यह मोड, बॉर्डर पर मौजूद पिक्सल की तरह ही होता है. इसमें सबसे बाईं ओर मौजूद पिक्सल का इस्तेमाल, उसी पंक्ति में मौजूद पिक्सल के बजाय TR-पिक्सल के तौर पर किया जाता है जिसकी लाइन में मौजूदा पिक्सल है.

आखिरी पिक्सल की वैल्यू पाने के लिए, अनुमानित वैल्यू के हर चैनल को कोड में बदली गई वैल्यू में जोड़ा जाता है.

void PredictorTransformOutput(uint32 residual, uint32 pred,
                              uint8* alpha, uint8* red,
                              uint8* green, uint8* blue) {
  *alpha = ALPHA(residual) + ALPHA(pred);
  *red = RED(residual) + RED(pred);
  *green = GREEN(residual) + GREEN(pred);
  *blue = BLUE(residual) + BLUE(pred);
}

4.2 रंग बदलना

कलर ट्रांसफ़ॉर्म का मकसद, हर पिक्सल की R, G, और B वैल्यू को सजद करना है. कलर ट्रांसफ़ॉर्म करने से, हरे (G) की वैल्यू में कोई बदलाव नहीं होता है. हरे रंग की वैल्यू के आधार पर लाल (R) वैल्यू को बदला जाता है. साथ ही, हरे रंग की वैल्यू के आधार पर नीले (B) वैल्यू को लाल और फिर लाल वैल्यू के आधार पर बदला जाता है.

अनुमान लगाने वाले ट्रांसफ़ॉर्म की तरह, पहले इमेज को ब्लॉक में बांटा जाता है. साथ ही, किसी ब्लॉक में मौजूद सभी पिक्सल के लिए भी बदलने वाले मोड का इस्तेमाल किया जाता है. हर ब्लॉक के लिए, रंग बदलने वाले एलिमेंट तीन तरह के होते हैं.

typedef struct {
  uint8 green_to_red;
  uint8 green_to_blue;
  uint8 red_to_blue;
} ColorTransformElement;

वास्तविक रंग रूपांतरण एक रंग रूपांतरण डेल्टा को परिभाषित करके किया जाता है. रंग बदलने का डेल्टा ColorTransformElement पर निर्भर करता है, जो किसी खास ब्लॉक में सभी पिक्सल के लिए एक जैसा ही है. रंग बदलने के दौरान डेल्टा को घटाया जाता है. इसके बाद, उलटा रंग बदलने की वजह से वे डेल्टा को और बढ़ा देते हैं.

रंग बदलने का फ़ंक्शन इस तरह परिभाषित किया गया है:

void ColorTransform(uint8 red, uint8 blue, uint8 green,
                    ColorTransformElement *trans,
                    uint8 *new_red, uint8 *new_blue) {
  // Transformed values of red and blue components
  int tmp_red = red;
  int tmp_blue = blue;

  // Applying the transform is just subtracting the transform deltas
  tmp_red  -= ColorTransformDelta(trans->green_to_red,  green);
  tmp_blue -= ColorTransformDelta(trans->green_to_blue, green);
  tmp_blue -= ColorTransformDelta(trans->red_to_blue, red);

  *new_red = tmp_red & 0xff;
  *new_blue = tmp_blue & 0xff;
}

ColorTransformDelta की गणना करने के लिए, एक साइन किए गए 8-बिट पूर्णांक का इस्तेमाल किया जाता है. यह 3.5-फ़िक्स-पॉइंट नंबर और साइन किए गए 8-बिट आरजीबी कलर चैनल (c) [-128..127] को दिखाता है. इसे इस तरह परिभाषित किया गया है:

int8 ColorTransformDelta(int8 t, int8 c) {
  return (t * c) >> 5;
}

ColorTransformDelta() को कॉल करने से पहले, 8-बिट अनसाइन्ड रिप्रज़ेंटेशन (uint8) से 8-बिट साइन किए गए (int8) में कन्वर्ज़न की ज़रूरत है. साइन की गई वैल्यू को 8-बिट दो की कॉम्प्लिमेंट नंबर के तौर पर समझा जाना चाहिए. इसका मतलब है कि uint8 रेंज [128..255] को इसकी बदली गई int8 वैल्यू की [-128..-1] रेंज से मैप किया गया है.

गुणा ज़्यादा सटीक (कम से कम 16-बिट सटीक) का इस्तेमाल करके किया जाना है. यहां शिफ़्ट ऑपरेशन का साइन एक्सटेंशन प्रॉपर्टी मायने नहीं रखती; नतीजे में सबसे कम 8 बिट का ही इस्तेमाल किया जाता है. साथ ही, साइन एक्सटेंशन शिफ़्टिंग और बिना साइन वाले शिफ़्टिंग एक-दूसरे से मेल खाते हैं.

अब हम कलर ट्रांसफ़ॉर्म डेटा के कॉन्टेंट के बारे में बताते हैं, ताकि डीकोडिंग से इनवर्स कलर ट्रांसफ़ॉर्मेशन को लागू किया जा सके. साथ ही, लाल और नीले रंग की मूल वैल्यू को भी वापस पाया जा सके. कलर ट्रांसफ़ॉर्म डेटा के पहले तीन बिट में इमेज ब्लॉक की चौड़ाई और ऊंचाई बिट की संख्या में होती है, जैसा कि अनुमान लगाने वाला ट्रांसफ़ॉर्म करता है:

int size_bits = ReadBits(3) + 2;
int block_width = 1 << size_bits;
int block_height = 1 << size_bits;

कलर ट्रांसफ़ॉर्म डेटा के बाकी हिस्से में, इमेज के हर ब्लॉक से जुड़े ColorTransformElement इंस्टेंस होते हैं. हर ColorTransformElement 'cte' को सब-रिज़ॉल्यूशन इमेज में पिक्सल के तौर पर माना जाता है जिसका ऐल्फ़ा कॉम्पोनेंट 255 होता है, लाल रंग का कॉम्पोनेंट cte.red_to_blue होता है, हरे कॉम्पोनेंट को cte.green_to_blue, और नीले कॉम्पोनेंट को cte.green_to_red होता है.

डिकोड करने के दौरान, ब्लॉक के ColorTransformElement इंस्टेंस डिकोड किए जाते हैं. साथ ही, इनवर्स कलर ट्रांसफ़ॉर्म को पिक्सल के ARGB मानों पर लागू किया जाता है. जैसा कि पहले बताया गया है, इनवर्स कलर ट्रांसफ़ॉर्मेशन की वजह से, लाल और नीले चैनल में ColorTransformElement वैल्यू बस जोड़ी जा रही हैं. ऐल्फ़ा और हरे चैनल में कोई बदलाव नहीं हुआ है.

void InverseTransform(uint8 red, uint8 green, uint8 blue,
                      ColorTransformElement *trans,
                      uint8 *new_red, uint8 *new_blue) {
  // Transformed values of red and blue components
  int tmp_red = red;
  int tmp_blue = blue;

  // Applying the inverse transform is just adding the
  // color transform deltas
  tmp_red  += ColorTransformDelta(trans->green_to_red, green);
  tmp_blue += ColorTransformDelta(trans->green_to_blue, green);
  tmp_blue +=
      ColorTransformDelta(trans->red_to_blue, tmp_red & 0xff);

  *new_red = tmp_red & 0xff;
  *new_blue = tmp_blue & 0xff;
}

4.3 ग्रीन ट्रांसफ़ॉर्म को घटाना

घटाए गए हरे रंग के ट्रांसफ़ॉर्म की मदद से, हर पिक्सल की लाल और नीले रंग की वैल्यू में से हरे रंग की वैल्यू को घटाया जाता है. यह ट्रांसफ़ॉर्म होने पर, डिकोडर को लाल और नीले, दोनों की वैल्यू में हरा वैल्यू जोड़नी होती है. इस ट्रांसफ़ॉर्मेशन से जुड़ा कोई डेटा नहीं है. डिकोडर, इन्वर्स ट्रांसफ़ॉर्म को इस तरह लागू करता है:

void AddGreenToBlueAndRed(uint8 green, uint8 *red, uint8 *blue) {
  *red  = (*red  + green) & 0xff;
  *blue = (*blue + green) & 0xff;
}

यह ट्रांसफ़ॉर्म करना निरर्थक है, क्योंकि इसे कलर ट्रांसफ़ॉर्म का इस्तेमाल करके मॉडल किया जा सकता है, लेकिन यहां कोई अतिरिक्त डेटा नहीं है. इसलिए, घटाए गए ग्रीन ट्रांसफ़ॉर्म को फ़ुल-ब्लेंड कलर ट्रांसफ़ॉर्म की तुलना में कम बिट का इस्तेमाल करके कोड किया जा सकता है.

4.4 रंग इंडेक्स करने के तरीके में बदलाव

अगर कई यूनीक पिक्सल वैल्यू नहीं हैं, तो कलर इंडेक्स का कलेक्शन बनाना और पिक्सल वैल्यू को ऐरे के इंडेक्स से बदलना ज़्यादा बेहतर हो सकता है. इसके लिए, कलर इंडेक्स ट्रांसफ़ॉर्मेशन का इस्तेमाल किया जाता है. (WebP लॉसलेस के संदर्भ में, हम इसे खास तौर पर पैलेट ट्रांसफ़ॉर्म का नाम नहीं देते, क्योंकि WebP लॉसलेस एन्कोडिंग में एक जैसा ही डाइनैमिक कॉन्सेप्ट मौजूद होता है. WebP लॉसलेस एन्कोडिंग: कलर कैश.)

रंग को इंडेक्स करने से, इमेज में यूनीक एआरजीबी वैल्यू की संख्या की जांच करता है. अगर यह संख्या थ्रेशोल्ड (256) से कम है, तो यह उन ARGB वैल्यू की एक अरे बनाती है जिसका इस्तेमाल पिक्सल वैल्यू को उससे जुड़े इंडेक्स से बदलने के लिए किया जाता है: पिक्सल के हरे चैनल को इंडेक्स से बदल दिया जाता है, सभी ऐल्फ़ा वैल्यू 255 पर और सभी लाल और नीले रंग की वैल्यू 0 पर सेट कर दी जाती हैं.

ट्रांसफ़ॉर्म डेटा में कलर टेबल का साइज़ और कलर टेबल की एंट्री होती हैं. डिकोडर, कलर इंडेक्स में बदलाव किए गए डेटा को इस तरह पढ़ता है:

// 8-bit value for the color table size
int color_table_size = ReadBits(8) + 1;

कलर टेबल को इमेज स्टोरेज फ़ॉर्मैट का इस्तेमाल करके ही स्टोर किया जाता है. आरआईएफ़एफ़ हेडर, इमेज के साइज़, और ट्रांसफ़ॉर्म के बिना, कलर टेबल को किसी इमेज को पढ़कर पाया जा सकता है. ऐसा करने के लिए, 1 पिक्सल की ऊंचाई और color_table_size की चौड़ाई को शामिल किया जा सकता है. इमेज की एंट्रॉपी कम करने के लिए कलर टेबल में हमेशा घटाव-कोड किया जाता है. पैलेट रंगों के डेल्टा में आम तौर पर, रंगों की तुलना में कम एंट्रॉपी होती है, जिससे छोटी इमेज की बचत होती है. डिकोड करने में, कलर टेबल का हर फ़ाइनल कलर हासिल किया जा सकता है. इसके लिए, हर ARGB कॉम्पोनेंट के लिए पिछले कलर कॉम्पोनेंट की वैल्यू को अलग-अलग जोड़कर और नतीजे के कम से कम ज़रूरी आठ बिट को स्टोर किया जा सकता है.

इमेज के लिए इन्वर्स ट्रांसफ़ॉर्म, पिक्सल वैल्यू (जो कलर टेबल के इंडेक्स हैं) को कलर टेबल की असल वैल्यू से बदल देता है. इंडेक्स करने की प्रक्रिया, एआरजीबी के हरे कॉम्पोनेंट के आधार पर की जाती है.

// Inverse transform
argb = color_table[GREEN(argb)];

अगर इंडेक्स color_table_size के बराबर या उससे बड़ा है, तो आरजीबी के रंग की वैल्यू 0x00000000 (पारदर्शी काला) पर सेट होनी चाहिए.

जब कलर टेबल छोटी होती है (16 रंगों के बराबर या उससे कम), तो कई पिक्सल एक पिक्सल में बंडल हो जाते हैं. पिक्सल बंडलिंग कई (2, 4 या 8) पिक्सल को एक पिक्सल में पैक करती है, जिससे इमेज की चौड़ाई घट जाती है. Pixel बंडल करने से, पास वाले पिक्सल की एंट्रॉपी कोडिंग का ज़्यादा बेहतर तरीके से किया जा सकता है. साथ ही, इससे एंट्रॉपी कोड को अंकगणितीय कोडिंग जैसे कुछ फ़ायदे मिलते हैं. हालांकि, इसका इस्तेमाल सिर्फ़ तब किया जा सकता है, जब यूनीक वैल्यू की संख्या 16 या इससे कम हो.

color_table_size बताता है कि कितने पिक्सल जोड़े गए हैं:

int width_bits;
if (color_table_size <= 2) {
  width_bits = 3;
} else if (color_table_size <= 4) {
  width_bits = 2;
} else if (color_table_size <= 16) {
  width_bits = 1;
} else {
  width_bits = 0;
}

width_bits की वैल्यू 0, 1, 2 या 3 होती है. 0 वैल्यू यह बताती है कि इमेज के लिए कोई पिक्सल बंडल नहीं करना है. वैल्यू 1 से पता चलता है कि दो पिक्सल जोड़े गए हैं और हर पिक्सल की रेंज [0..15] है. दो वैल्यू से पता चलता है कि चार पिक्सल जोड़े गए हैं और हर पिक्सल की रेंज [0..3] है. तीन की वैल्यू बताती है कि आठ पिक्सल जोड़े जाते हैं और हर पिक्सल की रेंज [0..1] होती है, जो कि एक बाइनरी वैल्यू होती है.

वैल्यू को हरे रंग के कॉम्पोनेंट में इस तरह पैक किया जाता है:

  • width_bits = 1: हर x वैल्यू के लिए, जहां x ≡ 0 (mod 2) है, वहां x पर मौजूद हरे रंग की वैल्यू, x / 2 पर मौजूद हरी वैल्यू के चार सबसे अहम बिट में रखी जाती है. साथ ही, x / 2 पर हरी वैल्यू के चार सबसे अहम बिट में x + 1 रखा जाता है.
  • width_bits = 2: हर x वैल्यू के लिए, जहां x ≡ 0 (mod 4) है, वहां x पर मौजूद हरे रंग की वैल्यू, x / 4 पर मौजूद हरी वैल्यू के दो सबसे कम अहम बिट में रखी जाती है. साथ ही, x + 1 से x + 3 पर हरी वैल्यू, x / 4 पर हरे वैल्यू के ज़्यादा अहम बिट के क्रम में रखी जाती है.
  • width_bits = 3: हर x वैल्यू के लिए, जहां x ≡ 0 (mod 8) है, वहां x पर मौजूद हरे रंग की वैल्यू, x / 8 पर मौजूद हरे रंग की वैल्यू के सबसे कम अहम बिट में रखी जाती है. साथ ही, x + 1 से x + 7 की हरी वैल्यू, x / 8 पर हरे वैल्यू के ज़्यादा अहम बिट के क्रम में रखी जाती है.

इस ट्रांसफ़ॉर्म को पढ़ने के बाद, width_bits के ज़रिए image_width का सैंपल तैयार किया जाता है. इससे बाद के ट्रांसफ़ॉर्म के साइज़ पर असर पड़ता है. नए साइज़ का हिसाब DIV_ROUND_UP का इस्तेमाल करके लगाया जा सकता है, जैसा कि पहले बताया गया है.

image_width = DIV_ROUND_UP(image_width, 1 << width_bits);

5 इमेज डेटा

इमेज डेटा, स्कैन लाइन के क्रम में पिक्सल की वैल्यू का कलेक्शन होता है.

5.1 इमेज डेटा की भूमिकाएं

हम इमेज डेटा का इस्तेमाल पांच अलग-अलग भूमिकाओं में करते हैं:

  1. ARGB इमेज: इमेज के असल पिक्सल स्टोर करता है.
  2. एंट्रॉपी इमेज: मेटा प्रीफ़िक्स कोड सेव करता है ("मेटा प्रीफ़िक्स कोड का डिकोड करना" देखें).
  3. अनुमान लगाने वाली इमेज: अनुमान लगाने वाले के ट्रांसफ़ॉर्मेशन के लिए मेटाडेटा सेव करता है ("अनुमानित बदलाव" देखें).
  4. इमेज का कलर ट्रांसफ़ॉर्म करें: इमेज के अलग-अलग ब्लॉक के लिए, ColorTransformElement वैल्यू ("कलर ट्रांसफ़ॉर्म" में तय की गई) के हिसाब से बनाया जाता है.
  5. कलर इंडेक्स करने वाली इमेज: color_table_size साइज़ (ज़्यादा से ज़्यादा 256 एआरजीबी वैल्यू) की एक कैटगरी, जिसमें कलर इंडेक्स ट्रांसफ़ॉर्म के लिए मेटाडेटा को स्टोर किया जाता है. "कलर इंडेक्स ट्रांसफ़ॉर्म" देखें.

5.2 इमेज डेटा को कोड में बदलने का तरीका

इमेज डेटा को कोड में बदलने का तरीका, उसकी भूमिका पर निर्भर नहीं करता है.

इमेज को पहले तय साइज़ के ब्लॉक (आम तौर पर 16x16 ब्लॉक) में बांटा जाता है. इनमें से हर ब्लॉक को अपने-अपने एंट्रॉपी कोड का इस्तेमाल करके मॉडल किया गया है. साथ ही, कई ब्लॉक एक ही एंट्रॉपी कोड को शेयर कर सकते हैं.

वजह: एंट्रॉपी कोड को स्टोर करने के लिए शुल्क देना होता है. अगर आंकड़ों के हिसाब से एक जैसे ब्लॉक एक एंट्रॉपी कोड शेयर करते हैं, तो इस लागत को कम किया जा सकता है. ऐसा होने पर उस कोड को सिर्फ़ एक बार सेव किया जा सकता है. उदाहरण के लिए, एन्कोडर, आंकड़ों की प्रॉपर्टी का इस्तेमाल करके, मिलते-जुलते ब्लॉक ढूंढ सकता है. इसके अलावा, वह बिना किसी क्रम के चुने गए क्लस्टर के जोड़े को बार-बार जोड़कर भी ऐसे ब्लॉक ढूंढ सकता है. ऐसा तब होता है, जब इमेज को कोड में बदलने के लिए ज़रूरी बिट की संख्या कम हो जाती है.

हर पिक्सल को इन तीन संभावित तरीकों में से किसी एक का इस्तेमाल करके एन्कोड किया जाता है:

  1. प्रीफ़िक्स कोड की लिटरल वैल्यू: हर चैनल (हरा, लाल, नीला, और ऐल्फ़ा) को अलग-अलग एंट्रॉपी के लिए कोड किया जाता है.
  2. LZ77 बैकवर्ड रेफ़रंस: पिक्सल के क्रम को इमेज में किसी दूसरी जगह से कॉपी किया जाता है.
  3. कलर कैश कोड: हाल ही में देखे गए कलर का शॉर्ट मल्टीप्लिकेटिव हैश कोड (कलर कैश इंडेक्स) का इस्तेमाल करना.

ये सब-सेक्शन, इनमें से हर एक के बारे में विस्तार से बताते हैं.

5.2.1 प्रीफ़िक्स-कोडेड लिटरल

पिक्सल को हरे, लाल, नीले, और अल्फ़ा (इसी क्रम में) के प्रीफ़िक्स कोड की वैल्यू के तौर पर स्टोर किया जाता है. जानकारी के लिए सेक्शन 6.2.3 देखें.

5.2.2 LZ77 पुराने वर्शन का रेफ़रंस

पीछे के रेफ़रंस, लंबाई और दूरी के कोड के टूल होते हैं:

  • लंबाई से पता चलता है कि स्कैन लाइन के क्रम में कितने पिक्सल कॉपी करने हैं.
  • दूरी का कोड, पहले देखे गए पिक्सल की जगह को दिखाने वाली संख्या है. इससे पिक्सल को कॉपी किया जाता है. सटीक मैपिंग के बारे में नीचे बताया गया है.

लंबाई और दूरी की वैल्यू को LZ77 प्रीफ़िक्स कोडिंग का इस्तेमाल करके सेव किया जाता है.

LZ77 प्रीफ़िक्स कोडिंग में बड़े पूर्णांक की वैल्यू को दो हिस्सों में बांटा जाता है: प्रीफ़िक्स कोड और अतिरिक्त बिट. प्रीफ़िक्स कोड को एंट्रॉपी कोड का इस्तेमाल करके स्टोर किया जाता है, जबकि अतिरिक्त बिट को वैसे ही सेव किया जाता है (बिना एंट्रॉपी कोड के).

वजह: इस तरीके से, एंट्रॉपी कोड के लिए स्टोरेज की ज़रूरत कम हो जाती है. साथ ही, आम तौर पर बड़ी वैल्यू बहुत कम होती हैं. इसलिए, इमेज में बहुत कम वैल्यू के लिए अतिरिक्त बिट का इस्तेमाल किया जाएगा. इस तरह, इस तरीके का इस्तेमाल करने से

नीचे दी गई टेबल, प्रीफ़िक्स कोड और वैल्यू की अलग-अलग रेंज को स्टोर करने के लिए इस्तेमाल किए जाने वाले अतिरिक्त बिट के बारे में बताती है.

वैल्यू की रेंज प्रीफ़िक्स कोड अतिरिक्त बिट
1 0 0
2 1 0
3 2 0
4 3 0
5..6 4 1
7..8 5 1
9.12 6 2
13..16 7 2
... ... ...
3072..4096 23 10
... ... ...
524289..786432 38 18
786433..1048576 39 18

प्रीफ़िक्स कोड से (लंबाई या दूरी) की वैल्यू पाने के लिए स्यूडोकोड इस तरह होता है:

if (prefix_code < 4) {
  return prefix_code + 1;
}
int extra_bits = (prefix_code - 2) >> 1;
int offset = (2 + (prefix_code & 1)) << extra_bits;
return offset + ReadBits(extra_bits) + 1;
दूरी की मैपिंग

जैसा कि पहले बताया गया है, दूरी का कोड वह संख्या है जो पहले देखे गए पिक्सल की उस जगह को दिखाती है जिससे पिक्सल को कॉपी किया जाता है. यह सब-सेक्शन, दूरी के कोड और पिछले पिक्सल की पोज़िशन के बीच मैपिंग के बारे में बताता है.

120 से बड़े दूरी कोड स्कैन-लाइन के क्रम में पिक्सल की दूरी दिखाते हैं, जिसे ऑफ़सेट 120 से तय किया जाता है.

सबसे छोटे दूरी के कोड [1..120] खास होते हैं और ये मौजूदा पिक्सल के नज़दीकी आस-पड़ोस के लिए रिज़र्व होते हैं. इस पड़ोसी में 120 पिक्सल हैं:

  • ऐसे पिक्सल जो मौजूदा पिक्सल से एक से सात लाइन ऊपर हैं और बाईं ओर आठ कॉलम तक या मौजूदा पिक्सल के दाईं ओर ज़्यादा से ज़्यादा सात कॉलम तक हैं. [कुल ऐसे पिक्सल = 7 * (8 + 1 + 7) = 112].
  • ऐसे पिक्सल जो मौजूदा पिक्सल वाली लाइन में ही हों और जिनमें मौजूदा पिक्सल के बाईं ओर ज़्यादा से ज़्यादा आठ कॉलम हों. [8 ऐसे पिक्सल].

दूरी कोड distance_code और पास के पिक्सल ऑफ़सेट (xi, yi) के बीच मैपिंग इस तरह है:

(0, 1),  (1, 0),  (1, 1),  (-1, 1), (0, 2),  (2, 0),  (1, 2),
(-1, 2), (2, 1),  (-2, 1), (2, 2),  (-2, 2), (0, 3),  (3, 0),
(1, 3),  (-1, 3), (3, 1),  (-3, 1), (2, 3),  (-2, 3), (3, 2),
(-3, 2), (0, 4),  (4, 0),  (1, 4),  (-1, 4), (4, 1),  (-4, 1),
(3, 3),  (-3, 3), (2, 4),  (-2, 4), (4, 2),  (-4, 2), (0, 5),
(3, 4),  (-3, 4), (4, 3),  (-4, 3), (5, 0),  (1, 5),  (-1, 5),
(5, 1),  (-5, 1), (2, 5),  (-2, 5), (5, 2),  (-5, 2), (4, 4),
(-4, 4), (3, 5),  (-3, 5), (5, 3),  (-5, 3), (0, 6),  (6, 0),
(1, 6),  (-1, 6), (6, 1),  (-6, 1), (2, 6),  (-2, 6), (6, 2),
(-6, 2), (4, 5),  (-4, 5), (5, 4),  (-5, 4), (3, 6),  (-3, 6),
(6, 3),  (-6, 3), (0, 7),  (7, 0),  (1, 7),  (-1, 7), (5, 5),
(-5, 5), (7, 1),  (-7, 1), (4, 6),  (-4, 6), (6, 4),  (-6, 4),
(2, 7),  (-2, 7), (7, 2),  (-7, 2), (3, 7),  (-3, 7), (7, 3),
(-7, 3), (5, 6),  (-5, 6), (6, 5),  (-6, 5), (8, 0),  (4, 7),
(-4, 7), (7, 4),  (-7, 4), (8, 1),  (8, 2),  (6, 6),  (-6, 6),
(8, 3),  (5, 7),  (-5, 7), (7, 5),  (-7, 5), (8, 4),  (6, 7),
(-6, 7), (7, 6),  (-7, 6), (8, 5),  (7, 7),  (-7, 7), (8, 6),
(8, 7)

उदाहरण के लिए, दूरी का कोड 1, पास के पिक्सल के लिए (0, 1) का ऑफ़सेट दिखाता है. इसका मतलब है कि मौजूदा पिक्सल के ऊपर पिक्सल (X दिशा में 0 पिक्सल का अंतर और Y दिशा में 1 पिक्सल का अंतर). इसी तरह, दूरी का कोड 3, ऊपर बाएं पिक्सल को दिखाता है.

डिकोडर, दूरी के कोड distance_code को स्कैन-लाइन ऑर्डर के लिए तय की गई दूरी dist में इस तरह बदल सकता है:

(xi, yi) = distance_map[distance_code - 1]
dist = xi + yi * image_width
if (dist < 1) {
  dist = 1
}

जहां distance_map ऊपर बताई गई मैपिंग है और image_width, पिक्सल में इमेज की चौड़ाई है.

5.2.3 कलर कैश कोडिंग

कलर कैश मेमोरी में, इमेज में हाल ही में इस्तेमाल किए गए रंगों के सेट को सेव किया जाता है.

वजह: इस तरह, कभी-कभी हाल ही में इस्तेमाल किए गए रंगों को उत्सर्जित करने के बजाय, दूसरे दो तरीकों (5.2.1 और 5.2.2 में बताया गया है) का इस्तेमाल करके, ज़्यादा असरदार बताया जा सकता है.

कलर कैश कोड इस तरह सेव किए जाते हैं. सबसे पहले, एक 1-बिट वैल्यू होती है, जो बताती है कि कलर कैश मेमोरी का इस्तेमाल किया गया है या नहीं. अगर यह बिट 0 है, तो कोई कलर कैश कोड मौजूद नहीं होगा. साथ ही, इन्हें हरे निशान और लंबाई वाले प्रीफ़िक्स कोड को डिकोड करने वाले प्रीफ़िक्स कोड में नहीं भेजा जाता. हालांकि, अगर यह बिट 1 है, तो कलर कैश का साइज़ आगे पढ़ा जाएगा:

int color_cache_code_bits = ReadBits(4);
int color_cache_size = 1 << color_cache_code_bits;

color_cache_code_bits कलर कैश का साइज़ (1 << color_cache_code_bits) बताता है. color_cache_code_bits के लिए वैल्यू की सीमा [1..11] है. पालन करने वाले डिकोडर को, अन्य वैल्यू के लिए खराब बिटस्ट्रीम के बारे में बताना चाहिए.

कलर कैश मेमोरी, color_cache_size साइज़ का कलेक्शन है. हर एंट्री में एक ARGB रंग स्टोर होता है. रंगों को (0x1e35a7bd * color) >> (32 - color_cache_code_bits) की मदद से इंडेक्स करके देखा जाता है. कलर कैश में सिर्फ़ एक लुकअप प्रोसेस होता है. किसी भी समस्या का समाधान नहीं होता है.

किसी इमेज को डीकोड करने या कोड में बदलने की शुरुआत में, सभी कलर कैश मेमोरी में मौजूद सभी वैल्यू को शून्य पर सेट कर दिया जाता है. डिकोड करने के समय, कलर कैश कोड इस रंग में बदल जाता है. कलर कैश की स्थिति को बनाए रखने के लिए, हर पिक्सल को कैश मेमोरी में उसी क्रम में बनाया जाता है जिस क्रम में वह स्ट्रीम में दिखता है. भले ही, वह बैकवर्ड रेफ़रंस या लिटरल वैल्यू के तौर पर बनाया गया हो.

6 एंट्रॉपी कोड

6.1 खास जानकारी

ज़्यादातर डेटा को कैननिकल प्रीफ़िक्स कोड का इस्तेमाल करके कोड किया जाता है. इसलिए, कोड असल प्रीफ़िक्स कोड के उलट, प्रीफ़िक्स कोड की लंबाई को भेजकर ट्रांसमिट किए जाते हैं.

खास तौर पर, फ़ॉर्मैट में जगह के हिसाब से वैरिएंट प्रीफ़िक्स कोडिंग का इस्तेमाल किया जाता है. दूसरे शब्दों में, इमेज के अलग-अलग ब्लॉक अलग-अलग एंट्रॉपी कोड का इस्तेमाल कर सकते हैं.

वजह: इमेज के अलग-अलग हिस्सों की विशेषताएं अलग-अलग हो सकती हैं. इसलिए, उन्हें अलग-अलग एंट्रॉपी कोड का इस्तेमाल करने की अनुमति देने से ज़्यादा सुविधाजनक और संभावित रूप से बेहतर कंप्रेशन मिलता है.

6.2 जानकारी

कोड में बदले गए इमेज डेटा के कई हिस्से होते हैं:

  1. प्रीफ़िक्स कोड बनाना और उन्हें डिकोड करना.
  2. मेटा प्रीफ़िक्स कोड.
  3. एंट्रॉपी-कोड किया गया इमेज डेटा.

किसी भी दिए गए पिक्सल (x, y) के लिए, पांच प्रीफ़िक्स कोड का एक सेट होता है. ये कोड हैं (बिटस्ट्रीम क्रम में):

  • प्रीफ़िक्स कोड #1: इसका इस्तेमाल हरे चैनल, बैकवर्ड रेफ़रंस लंबाई, और कलर कैश मेमोरी के लिए किया जाता है.
  • प्रीफ़िक्स कोड #2, #3, और #4: लाल, नीले, और ऐल्फ़ा चैनलों के लिए इस्तेमाल किया जाता है.
  • प्रीफ़िक्स कोड #5: इसका इस्तेमाल, पीछे के संदर्भ की दूरी के लिए किया जाता है.

अब से, हम इस सेट को प्रीफ़िक्स कोड ग्रुप के तौर पर कहेंगे.

6.2.1 प्रीफ़िक्स कोड बनाना और उन्हें डिकोड करना

इस सेक्शन में, बिटस्ट्रीम से प्रीफ़िक्स कोड की लंबाई को पढ़ने का तरीका बताया गया है.

प्रीफ़िक्स कोड की लंबाई को दो तरीकों से कोड किया जा सकता है. इस्तेमाल किया गया तरीका, 1-बिट वैल्यू से बताया जाता है.

  • अगर यह बिट 1 है, तो यह आसान कोड लंबाई कोड है.
  • अगर यह बिट 0 है, तो यह सामान्य कोड लंबाई वाला कोड होता है.

दोनों मामलों में, हो सकता है कि कोड की ऐसी लंबाई हो जिसका इस्तेमाल न किया गया हो. शायद यह तरीका कारगर न हो, लेकिन फ़ॉर्मैट के हिसाब से इसकी अनुमति है. बताया गया ट्री एक पूरा बाइनरी ट्री होना चाहिए. सिंगल लीफ़ नोड को पूरा बाइनरी ट्री माना जाता है. इसे सामान्य कोड वाले कोड या सामान्य कोड लंबाई के कोड का इस्तेमाल करके एन्कोड किया जा सकता है. सामान्य कोड लंबाई के कोड का इस्तेमाल करके एक लीफ़ नोड को कोड करते समय, एक कोड की लंबाई शून्य को छोड़कर बाकी सभी कोड की लंबाई शून्य होती है. साथ ही, सिंगल लीफ़ नोड की वैल्यू 1 से मार्क की जाती है -- भले ही, उस एक लीफ़ ट्री का इस्तेमाल करते समय किसी बिट का इस्तेमाल नहीं किया जाता.

सरल कोड लंबाई कोड

इस वैरिएंट का इस्तेमाल खास मामले में तब किया जाता है, जब सिर्फ़ एक या दो प्रीफ़िक्स सिंबल [0..255] की रेंज में होते हैं और कोड की लंबाई 1 होती है. बाकी सभी प्रीफ़िक्स कोड की लंबाई शून्य होती है.

पहला बिट, सिंबल की संख्या बताता है:

int num_symbols = ReadBits(1) + 1;

यहां सिंबल की वैल्यू दी गई हैं.

पहले सिंबल को is_first_8bits की वैल्यू के आधार पर, एक या आठ बिट का इस्तेमाल करके कोड किया जाता है. क्रमश: [0..1] या [0..255] की रेंज है. अगर दूसरा चिह्न मौजूद है, तो उसे हमेशा [0..255] की रेंज में माना जाता है और उसे 8 बिट का इस्तेमाल करके कोड किया जाता है.

int is_first_8bits = ReadBits(1);
symbol0 = ReadBits(1 + 7 * is_first_8bits);
code_lengths[symbol0] = 1;
if (num_symbols == 2) {
  symbol1 = ReadBits(8);
  code_lengths[symbol1] = 1;
}

दोनों चिह्न अलग-अलग होने चाहिए. डुप्लीकेट चिह्नों की अनुमति है, लेकिन ऐसे चिह्नों का इस्तेमाल नहीं किया जा सकता.

ध्यान दें: एक और खास मामला यह है कि जब सभी प्रीफ़िक्स कोड की लंबाई शून्य (खाली प्रीफ़िक्स कोड) होती है. उदाहरण के लिए, अगर पीछे की ओर कोई रेफ़रंस नहीं है, तो दूरी के लिए प्रीफ़िक्स कोड खाली हो सकता है. इसी तरह, अगर एक ही मेटा प्रीफ़िक्स कोड में मौजूद सभी पिक्सल कलर कैश मेमोरी का इस्तेमाल करके बनाए गए हैं, तो ऐल्फ़ा, लाल, और नीले रंग के लिए प्रीफ़िक्स कोड खाली हो सकते हैं. हालांकि, इस मामले में खास हैंडलिंग की ज़रूरत नहीं है, क्योंकि खाली प्रीफ़िक्स कोड को उन प्रीफ़िक्स कोड के तौर पर कोड किया जा सकता है जिनमें सिंगल सिंबल 0 होता है.

सामान्य कोड लंबाई कोड

प्रीफ़िक्स कोड की कोड लंबाई 8 बिट में फ़िट होती है और इस तरह से पढ़ी जाती है. सबसे पहले, num_code_lengths कोड की लंबाई बताता है.

int num_code_lengths = 4 + ReadBits(4);

कोड की लंबाई को प्रीफ़िक्स कोड का इस्तेमाल करके एन्कोड किया जाता है. कम लेवल वाले कोड code_length_code_lengths को पढ़ने से पहले इन्हें पढ़ना पड़ता है. बाकी के code_length_code_lengths (kCodeLengthCodeOrder में दिए ऑर्डर के मुताबिक) शून्य हैं.

int kCodeLengthCodes = 19;
int kCodeLengthCodeOrder[kCodeLengthCodes] = {
  17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
};
int code_length_code_lengths[kCodeLengthCodes] = { 0 };  // All zeros
for (i = 0; i < num_code_lengths; ++i) {
  code_length_code_lengths[kCodeLengthCodeOrder[i]] = ReadBits(3);
}

इसके बाद, अगर ReadBits(1) == 0 है, तो हर सिंबल टाइप (A, R, G, B, और दूरी) के लिए, पढ़े जाने वाले अलग-अलग सिंबल (max_symbol) की ज़्यादा से ज़्यादा संख्या, उसके वर्णमाला के साइज़ पर सेट होगी:

  • जी चैनल: 256 + 24 + color_cache_size
  • अन्य लिटरल (A, R, और B): 256
  • दूरी कोड: 40

अगर ऐसा नहीं है, तो इसे इस तरह परिभाषित किया जाता है:

int length_nbits = 2 + 2 * ReadBits(3);
int max_symbol = 2 + ReadBits(length_nbits);

अगर max_symbol, सिंबल टाइप के लिए वर्णमाला के साइज़ से बड़ा है, तो बिटस्ट्रीम अमान्य है.

इसके बाद, code_length_code_lengths से एक प्रीफ़िक्स टेबल बनाई जाती है और max_symbol कोड की लंबाई को पढ़ने के लिए उसका इस्तेमाल किया जाता है.

  • कोड [0..15], लिटरल कोड की लंबाई दिखाता है.
    • वैल्यू 0 का मतलब है कि किसी भी सिंबल को कोड नहीं किया गया है.
    • वैल्यू [1..15], संबंधित कोड की बिट की लंबाई के बारे में बताती है.
  • कोड 16, पिछली नॉन-ज़ीरो वैल्यू को [3..6] बार यानी 3 + ReadBits(2) बार दोहराता है. अगर कोड 16 का इस्तेमाल किसी नॉन-ज़ीरो वैल्यू के उत्सर्जित होने से पहले किया जाता है, तो 8 की वैल्यू दोहराई जाती है.
  • कोड 17, लंबाई के शून्यों की एक स्ट्रीक बनाता है [3..10], यानी कि 3 + ReadBits(3) बार.
  • कोड 18 से, [11..138] की लंबाई के शून्यों वाला एक स्ट्रीक मिलता है. इसका मतलब है कि यह 11 + ReadBits(7) बार है.

कोड की लंबाई पढ़ने के बाद, हर तरह के प्रतीक (A, R, G, B, और दूरी) के लिए एक प्रीफ़िक्स कोड बनाया जाता है. इसके लिए, उनके वर्णमाला के साइज़ का इस्तेमाल किया जाता है.

सामान्य कोड की लंबाई के कोड को पूरे डिसिज़न ट्री के कोड में होना चाहिए. इसका मतलब है कि सभी नॉन-ज़ीरो कोड के लिए, 2 ^ (-length) का योग ठीक एक होना चाहिए. हालांकि, इस नियम का एक अपवाद है, सिंगल लीफ़ नोड ट्री, जहां लीफ़ नोड की वैल्यू 1 और अन्य वैल्यू 0 से मार्क की जाती है.

6.2.2 मेटा प्रीफ़िक्स कोड को डिकोड करना

जैसा कि पहले बताया गया है, इस फ़ॉर्मैट में इमेज के अलग-अलग ब्लॉक के लिए अलग-अलग प्रीफ़िक्स कोड का इस्तेमाल किया जा सकता है. मेटा प्रीफ़िक्स कोड ऐसे इंडेक्स होते हैं जिनसे पता चलता है कि इमेज के अलग-अलग हिस्सों में कौनसे प्रीफ़िक्स कोड इस्तेमाल करने हैं.

मेटा प्रीफ़िक्स कोड का इस्तेमाल सिर्फ़ तब किया जा सकता है, जब इमेज का इस्तेमाल किसी एआरजीबी इमेज की भूमिका के लिए किया जा रहा हो.

मेटा प्रीफ़िक्स कोड की दो संभावनाएं होती हैं. इन्हें 1-बिट वैल्यू से दिखाया जाता है:

  • अगर यह बिट शून्य है, तो इमेज में हर जगह सिर्फ़ एक मेटा प्रीफ़िक्स कोड का इस्तेमाल किया जाता है. इसके बाद, कोई डेटा सेव नहीं किया जाता.
  • अगर यह बिट एक है, तो इमेज में एक से ज़्यादा मेटा प्रीफ़िक्स कोड का इस्तेमाल किया गया है. ये मेटा प्रीफ़िक्स कोड, एंट्रॉपी इमेज के तौर पर स्टोर किए जाते हैं. इसके बारे में नीचे बताया गया है.

पिक्सल के लाल और हरे रंग के कॉम्पोनेंट 16-बिट मेटा प्रीफ़िक्स कोड के बारे में बताते हैं, जिसका इस्तेमाल ARGB इमेज के एक खास ब्लॉक में किया जाता है.

एंट्रॉपी इमेज

एंट्रॉपी इमेज से पता चलता है कि इमेज के अलग-अलग हिस्सों में कौनसे प्रीफ़िक्स कोड इस्तेमाल किए गए हैं.

पहले तीन बिट में prefix_bits वैल्यू होती है. एंट्रॉपी इमेज के डाइमेंशन, prefix_bits से लिए जाते हैं:

int prefix_bits = ReadBits(3) + 2;
int prefix_image_width =
    DIV_ROUND_UP(image_width, 1 << prefix_bits);
int prefix_image_height =
    DIV_ROUND_UP(image_height, 1 << prefix_bits);

जहां DIV_ROUND_UP, पहले के तौर पर बताया गया है.

अगले बिट में चौड़ाई prefix_image_width और ऊंचाई prefix_image_height की एंट्रॉपी इमेज होती है.

मेटा प्रीफ़िक्स कोड की व्याख्या

एआरजीबी इमेज में प्रीफ़िक्स कोड ग्रुप की संख्या जानने के लिए, एंट्रॉपी इमेज से सबसे बड़े मेटा प्रीफ़िक्स कोड का पता लगाया जा सकता है:

int num_prefix_groups = max(entropy image) + 1;

यहां max(entropy image), एंट्रॉपी इमेज में स्टोर किए गए सबसे बड़े प्रीफ़िक्स कोड के बारे में बताता है.

हर प्रीफ़िक्स कोड ग्रुप में पांच प्रीफ़िक्स कोड होते हैं. इसलिए, प्रीफ़िक्स कोड की कुल संख्या इस तरह है:

int num_prefix_codes = 5 * num_prefix_groups;

ARGB इमेज में पिक्सल (x, y) दिए जाने पर, हम संबंधित प्रीफ़िक्स कोड पा सकते हैं, ताकि इनका इस्तेमाल इस तरह किया जा सके:

int position =
    (y >> prefix_bits) * prefix_image_width + (x >> prefix_bits);
int meta_prefix_code = (entropy_image[position] >> 8) & 0xffff;
PrefixCodeGroup prefix_group = prefix_code_groups[meta_prefix_code];

जहां हमने PrefixCodeGroup स्ट्रक्चर के मौजूद होने का अनुमान लगाया है, जो पांच प्रीफ़िक्स कोड का सेट है. साथ ही, prefix_code_groups, PrefixCodeGroup (num_prefix_groups साइज़ का) की एक कैटगरी है.

इसके बाद, डिकोडर, पिक्सल (x, y) को डिकोड करने के लिए, प्रीफ़िक्स कोड ग्रुप prefix_group का इस्तेमाल करता है. इसके बारे में, "डिकोडिंग एंट्रॉपी-कोड इमेज डेटा" में बताया गया है.

6.2.3 एंट्रॉपी-कोडेड इमेज डेटा डिकोड करना

इमेज में मौजूदा पोज़िशन (x, y) के लिए, डीकोडर सबसे पहले उससे जुड़े प्रीफ़िक्स कोड ग्रुप की पहचान करता है (जैसा कि आखिरी सेक्शन में बताया गया है). प्रीफ़िक्स कोड ग्रुप के हिसाब से, पिक्सल को इस तरह से पढ़ा और डिकोड किया जाता है.

इसके बाद, प्रीफ़िक्स कोड #1 का इस्तेमाल करके, बिटस्ट्रीम से S सिंबल को पढ़ें. ध्यान दें कि S 0 से (256 + 24 + color_cache_size- 1) की रेंज में कोई भी पूर्णांक है.

S का मतलब उसकी वैल्यू के हिसाब से तय होता है:

  1. अगर S < 256
    1. हरे रंग के कॉम्पोनेंट के तौर पर, S का इस्तेमाल करें.
    2. प्रीफ़िक्स कोड #2 का इस्तेमाल करके, बिटस्ट्रीम से लाल रंग को पढ़ें.
    3. प्रीफ़िक्स कोड #3 का इस्तेमाल करके, बिटस्ट्रीम से नीले रंग को पढ़ें.
    4. प्रीफ़िक्स कोड #4 का इस्तेमाल करके, बिटस्ट्रीम से ऐल्फ़ा पढ़ें.
  2. अगर S >= 256 & S < 256 + 24
    1. लंबाई वाले प्रीफ़िक्स कोड के तौर पर, S - 256 का इस्तेमाल करें.
    2. बिटस्ट्रीम से लंबाई के लिए अतिरिक्त बिट पढ़ें.
    3. लंबाई प्रीफ़िक्स कोड और पढ़े गए अतिरिक्त बिट से बैकवर्ड रेफ़रंस लंबाई L तय करें.
    4. प्रीफ़िक्स कोड #5 का इस्तेमाल करके, बिटस्ट्रीम से दूरी का प्रीफ़िक्स कोड पढ़ें.
    5. बिटस्ट्रीम से दूरी के लिए अतिरिक्त बिट पढ़ें.
    6. दूरी प्रीफ़िक्स कोड और रीड की गई अतिरिक्त बिट से बैकवर्ड-रेफ़रंस दूरी D तय करें.
    7. मौजूदा जगह से D पिक्सल घटाकर, पिक्सल के क्रम से L पिक्सल (स्कैन लाइन क्रम में) कॉपी करें.
  3. अगर S >= 256 + 24 है
    1. कलर कैश में इंडेक्स के तौर पर, S - (256 + 24) का इस्तेमाल करें.
    2. उस इंडेक्स में कलर कैश मेमोरी से ARGB का रंग पाएं.

7 फ़ॉर्मैट का पूरा स्ट्रक्चर

यहां ऑगमेंटेड बैकस-नौर फ़ॉर्म (एबीएनएफ़) के फ़ॉर्मैट की जानकारी दी गई है आरएफ़सी 5234 आरएफ़सी 7405. इसमें पूरी जानकारी नहीं दी गई है. इमेज के आखिर में (ईओआई) को सीधे तौर पर पिक्सल की संख्या (image_width * image_height) में कोड किया जाता है.

ध्यान दें कि *element का मतलब है कि element को 0 या इससे ज़्यादा बार दोहराया जा सकता है. 5element का मतलब है कि element को ठीक पांच बार दोहराया गया है. %b एक बाइनरी वैल्यू है.

7.1 बुनियादी संरचना

format        = RIFF-header image-header image-stream
RIFF-header   = %s"RIFF" 4OCTET %s"WEBPVP8L" 4OCTET
image-header  = %x2F image-size alpha-is-used version
image-size    = 14BIT 14BIT ; width - 1, height - 1
alpha-is-used = 1BIT
version       = 3BIT ; 0
image-stream  = optional-transform spatially-coded-image

7.2 बदलावों की संरचना

optional-transform   =  (%b1 transform optional-transform) / %b0
transform            =  predictor-tx / color-tx / subtract-green-tx
transform            =/ color-indexing-tx

predictor-tx         =  %b00 predictor-image
predictor-image      =  3BIT ; sub-pixel code
                        entropy-coded-image

color-tx             =  %b01 color-image
color-image          =  3BIT ; sub-pixel code
                        entropy-coded-image

subtract-green-tx    =  %b10

color-indexing-tx    =  %b11 color-indexing-image
color-indexing-image =  8BIT ; color count
                        entropy-coded-image

7.3 इमेज के डेटा का स्ट्रक्चर

spatially-coded-image =  color-cache-info meta-prefix data
entropy-coded-image   =  color-cache-info data

color-cache-info      =  %b0
color-cache-info      =/ (%b1 4BIT) ; 1 followed by color cache size

meta-prefix           =  %b0 / (%b1 entropy-image)

data                  =  prefix-codes lz77-coded-image
entropy-image         =  3BIT ; subsample value
                         entropy-coded-image

prefix-codes          =  prefix-code-group *prefix-codes
prefix-code-group     =
    5prefix-code ; See "Interpretation of Meta Prefix Codes" to
                 ; understand what each of these five prefix
                 ; codes are for.

prefix-code           =  simple-prefix-code / normal-prefix-code
simple-prefix-code    =  ; see "Simple Code Length Code" for details
normal-prefix-code    =  ; see "Normal Code Length Code" for details

lz77-coded-image      =
    *((argb-pixel / lz77-copy / color-cache-code) lz77-coded-image)

नीचे क्रम का उदाहरण दिया गया है:

RIFF-header image-size %b1 subtract-green-tx
%b1 predictor-tx %b0 color-cache-info
%b0 prefix-codes lz77-coded-image