जरकी अलाकुइजाला, पीएच॰डी॰, Google LLC, 09-03-2023
खास जानकारी
WebP लॉसलेस, ARGB इमेज को लॉसलेस कंप्रेस करने के लिए इस्तेमाल किया जाने वाला इमेज फ़ॉर्मैट है. कॉन्टेंट बनाने लॉसलेस फ़ॉर्मैट, पिक्सल वैल्यू को ठीक से सेव और पहले जैसा करता है. इनमें, ये वैल्यू भी शामिल हैं पूरी तरह से पारदर्शी पिक्सल के लिए रंग की वैल्यू. ज़्यादा डेटा को कम करने के लिए, क्रम से डेटा को कम करने (LZ77) के लिए यूनिवर्सल एल्गोरिदम, प्रीफ़िक्स कोडिंग, और कलर कैश का इस्तेमाल किया जाता है. PNG की तुलना में तेज़ी से डिकोड करने की स्पीड दिखाया जाता है. साथ ही, इसका इस्तेमाल करके कंप्रेस की जा सकने वाली रेंज 25% ज़्यादा होती है को PNG फ़ॉर्मैट में एक्सपोर्ट किया जा सकता है.
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 नाम
- एआरजीबी
- ऐल्फ़ा, लाल, हरे, और नीले रंग की वैल्यू वाली पिक्सल वैल्यू.
- ARGB इमेज
- दो डाइमेंशन वाला अरे, जिसमें ARGB पिक्सल मौजूद हों.
- कलर कैश
- हाल ही में इस्तेमाल किए गए रंगों को स्टोर करने के लिए, हैश के आधार पर तैयार किया गया छोटा अरे. उन्हें छोटे कोड से याद करते हैं.
- कलर इंडेक्स करने वाली इमेज
- कलर की एक डाइमेंशन वाली इमेज, जिसे छोटे पूर्णांक (WebP लॉसलेस में 256 तक) का इस्तेमाल करके इंडेक्स किया जा सकता है.
- रंग बदलने वाली इमेज
- दो-डाइमेंशन वाली सबरिज़ॉल्यूशन वाली इमेज, जिसमें रंग कॉम्पोनेंट.
- दूरी मैप करना
- पिक्सल के लिए सबसे छोटी वैल्यू रखने के लिए, LZ77 दूरी को बदलता है दो डाइमेंशन में आस-पास की जगह हो.
- एन्ट्रॉपी इमेज
- दो-आयामी सब-रिज़ॉल्यूशन वाली इमेज, जिसमें यह दिखाया गया है कि इमेज के किसी स्क्वेयर में किस एन्ट्रापी कोड का इस्तेमाल किया जाना चाहिए. इसका मतलब है कि हर पिक्सल एक मेटा प्रीफ़िक्स कोड है.
- LZ77
- डिक्शनरी पर आधारित स्लाइडिंग विंडो कंप्रेसन एल्गोरिदम, जो सिंबल को उत्सर्जित करता है या उन्हें पिछले सिंबल के क्रम के तौर पर दिखाता है.
- मेटा प्रीफ़िक्स कोड
- एक छोटा पूर्णांक (ज़्यादा से ज़्यादा 16 बिट), जो मेटा प्रीफ़िक्स टेबल में किसी एलिमेंट को इंडेक्स करता है.
- अनुमान लगाने वाली इमेज
- दो डाइमेंशन वाली सब-रिज़ॉल्यूशन इमेज, जिसमें यह दिखाया गया है कि इमेज के किसी स्क्वेयर के लिए, स्पेसिएल प्रिडिक्टर का इस्तेमाल किस तरह किया जाता है.
- उपसर्ग कोड
- एंट्रॉपी कोडिंग करने का एक क्लासिक तरीका, जिसमें कम बिट का इस्तेमाल किया जाता है अक्सर इस्तेमाल किए जाने वाले कोड के लिए.
- प्रीफ़िक्स कोडिंग
- बड़े पूर्णांकों को एन्ट्रापी कोड करने का एक तरीका, जो एन्ट्रापी कोड का इस्तेमाल करके पूर्णांक के कुछ बिट कोड करता है और बाकी बिट को रॉ कोड करता है. इससे, एंट्रॉपी कोड की जानकारी कम जगह में रहती है. भले ही, सिंबल की रेंज बड़ी हो.
- स्कैन-लाइन ऑर्डर
- पिक्सल का प्रोसेसिंग ऑर्डर (बाएं से दाएं और ऊपर से नीचे), शुरू होता है ऊपर की ओर तीर के निशान से. पंक्ति पूरी होने के बाद, यहां से जारी रखें अगली पंक्ति के बाईं ओर मौजूद कॉलम पर क्लिक करें.
3 RIFF हेडर
हेडर की शुरुआत में RIFF कंटेनर होता है. इसमें यह शामिल है ये 21 बाइट होने चाहिए:
- स्ट्रिंग 'RIFF'.
- चंक की लंबाई की 32-बिट वैल्यू, जो कि RIFF हेडर से कंट्रोल किए जाने वाले चंक का पूरा साइज़ है. आम तौर पर, यह साइज़, पेलोड साइज़ के बराबर होता है. पेलोड साइज़, फ़ाइल साइज़ में से 8 बाइट घटाने पर मिलता है. इनमें 'RIFF' आइडेंटिफ़ायर के लिए 4 बाइट और वैल्यू को स्टोर करने के लिए 4 बाइट होते हैं.
- स्ट्रिंग 'WEBP' (RIFF कंटेनर का नाम).
- स्ट्रिंग 'VP8L' (लॉसलेस-एन्कोडेड इमेज डेटा के लिए चारसीसी).
- लिटिल-एंडियन, 32-बिट की वैल्यू खराब स्ट्रीम.
- एक बाइट का हस्ताक्षर 0x2f.
बिटस्ट्रीम के पहले 28 बिट से, इमेज की चौड़ाई और ऊंचाई का पता चलता है. चौड़ाई और ऊंचाई को 14-बिट के पूर्णांक के तौर पर इस तरह डिकोड किया जाता है:
int image_width = ReadBits(14) + 1;
int image_height = ReadBits(14) + 1;
इमेज की चौड़ाई और ऊंचाई के लिए 14-बिट सटीक डेटा का इस्तेमाल करने पर, WebP की लॉसलेस इमेज का ज़्यादा से ज़्यादा साइज़ 16,384 x 16,384 पिक्सल तक सीमित हो जाता है.
Alphabet_is_used बिट सिर्फ़ एक संकेत है. इससे डिकोड करने पर कोई असर नहीं पड़ना चाहिए. अगर फ़ोटो में सभी ऐल्फ़ा वैल्यू 255 हैं, तो इसे 0 पर सेट किया जाना चाहिए. अगर ऐसा नहीं है, तो इसे 1 पर सेट किया जाना चाहिए.
int alpha_is_used = ReadBits(1);
version_number 3 बिट का कोड है, जिसे 0 पर सेट करना ज़रूरी है. किसी अन्य वैल्यू को गड़बड़ी के रूप में नहीं देखा जाएगा.
int version_number = ReadBits(3);
4 ट्रांसफ़ॉर्म
ट्रांसफ़ॉर्म, इमेज डेटा में किए गए उलटे बदलाव हैं. इन्हें स्पेशल और कलर कोरिलेशन के आधार पर, बची हुई सिंबॉलिक एंट्रॉपी. ये आखिरी कंप्रेस किए गए डेटा को ज़्यादा डेंस बना सकते हैं.
एक इमेज में चार तरह के बदलाव हो सकते हैं. 1 बिट का मतलब बदलाव की मौजूदगी. हर ट्रांसफ़ॉर्म का इस्तेमाल सिर्फ़ एक बार किया जा सकता है. कॉन्टेंट बनाने ट्रांसफ़ॉर्म का इस्तेमाल सिर्फ़ मुख्य-लेवल की एआरजीबी इमेज के लिए किया जाता है; सबरिज़ॉल्यूशन वाली इमेज (रंग बदलने वाली इमेज, एंट्रॉपी इमेज, और अनुमान लगाने वाली इमेज) में कोई ट्रांसफ़ॉर्म नहीं है, यहां तक कि रूपांतरण के अंत को दिखाने वाला 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 अनुमान लगाने वाला बदलाव
एंट्रॉपी को कम करने के लिए, तथ्य का गलत इस्तेमाल करके अनुमान लगाने वाले ट्रांसफ़ॉर्म का इस्तेमाल किया जा सकता है आस-पास के पिक्सल अक्सर एक-दूसरे से जुड़े होते हैं. प्रिडिक्टर ट्रांसफ़ॉर्म में, स्कैन-लाइन के क्रम में पहले से डिकोड किए गए पिक्सल से, मौजूदा पिक्सल वैल्यू का अनुमान लगाया जाता है. साथ ही, सिर्फ़ बाकी बची वैल्यू (असल - अनुमानित) को एन्कोड किया जाता है. द ग्रीन Pixel का कॉम्पोनेंट तय करता है कि पर क्लिक करें. अनुमान मोड से यह तय होता है कि किस तरह का अनुमान इस्तेमाल करना है. हम इमेज को स्क्वेयर में बांटते हैं और स्क्वेयर के सभी पिक्सल, एक ही अनुमान मोड का इस्तेमाल करते हैं.
अनुमानित डेटा के पहले तीन बिट, बिट की संख्या में ब्लॉक की चौड़ाई और ऊंचाई तय करते हैं.
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 में से किस प्रिडिक्टर का इस्तेमाल किया जाए. इस सब-रिज़ॉल्यूशन वाली इमेज को इसका इस्तेमाल करके कोड में बदला गया है
ये वही तकनीक हैं जो चैप्टर 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 का मतलब है सबसे ऊपर, TR का मतलब है सबसे ऊपर दाईं ओर, और L का मतलब है बाईं ओर. P की वैल्यू का अनुमान लगाने के समय, सभी O, TL, T, TR, और L पिक्सल पहले ही प्रोसेस हो चुके होते हैं. साथ ही, P पिक्सल और सभी X पिक्सल की जानकारी नहीं होती.
पिछले आस-पास के पिक्सल को देखते हुए, अनुमान के लिए अलग-अलग मोड हैं परिभाषित किया गया है.
मोड | मौजूदा पिक्सल के हर चैनल की अनुमानित वैल्यू |
---|---|
0 | 0xff000000 (ARGB में सॉलिड ब्लैक कलर दिखाता है) |
1 | L |
2 | T |
3 | में कीमत |
4 | टीम लीडर |
5 | औसत2(औसत2(L, TR), T) |
6 | Average2(L, TL) |
7 | Average2(L, T) |
8 | Average2(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;
}
}
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-pixel हैं और सबसे बाईं ओर के कॉलम के सभी पिक्सल हैं टी-पिक्सल.
सबसे दाएं कॉलम पर मौजूद पिक्सल के लिए, 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 मानों को सजाना है Pixel है. कलर ट्रांसफ़ॉर्म, ग्रीन (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;
}
8-बिट अनसाइन्ड रिप्रज़ेंटेशन (uint8) से 8-बिट साइन किए गए कन्वर्ज़न में कन्वर्ज़न
ColorTransformDelta()
को कॉल करने से पहले, एक (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 लॉसलेस कोडिंग में एक ऐसा कॉन्सेप्ट मौजूद है जो इससे मिलता-जुलता है, लेकिन ज़्यादा डाइनैमिक है: कलर कैश.)
कलर इंडेक्सिंग ट्रांसफ़ॉर्म, इमेज में यूनीक ARGB वैल्यू की संख्या की जांच करता है. अगर वह संख्या, थ्रेशोल्ड (256) से कम है, तो यह उनकी एक कैटगरी बनाता है ARGB वैल्यू का इस्तेमाल किया जाता है. इसके बाद, पिक्सल वैल्यू को संबंधित इंडेक्स: पिक्सल के हरे चैनल को इंडेक्स में, सभी ऐल्फ़ा वैल्यू 255 और लाल और नीले रंग की सभी वैल्यू 0 पर सेट होती हैं.
ट्रांसफ़ॉर्म किए गए डेटा में, कलर टेबल का साइज़ और कलर टेबल में मौजूद एंट्री शामिल होती हैं. डिकोडर, कलर इंडेक्सिंग ट्रांसफ़ॉर्म डेटा को इस तरह पढ़ता है:
// 8-bit value for the color table size
int color_table_size = ReadBits(8) + 1;
कलर टेबल को इमेज के स्टोरेज फ़ॉर्मैट का इस्तेमाल करके सेव किया जाता है. कलर टेबल
को RIFF हेडर, चित्र के आकार, और
1 पिक्सेल की ऊंचाई और color_table_size
की चौड़ाई मानकर बदल जाता है.
इमेज की एन्ट्रॉपी को कम करने के लिए, कलर टेबल में हमेशा घटाव की सुविधा का इस्तेमाल किया जाता है. डेल्टा
पैलेट रंगों में आम तौर पर रंगों की तुलना में बहुत कम एंट्रॉपी होती है
इससे छोटी इमेज के लिए भारी बचत होती है. डिकोड करने के लिए, कलर टेबल में मौजूद हर आखिरी रंग को हासिल किया जा सकता है. इसके लिए, हर ARGB कॉम्पोनेंट के हिसाब से, पिछले कलर कॉम्पोनेंट की वैल्यू को अलग-अलग जोड़ें और नतीजे के कम से कम ज़रूरी 8 बिट को सेव करें.
इमेज का इनवर्स ट्रांसफ़ॉर्म सिर्फ़ पिक्सल वैल्यू को बदल रहा है ( कलर टेबल के इंडेक्स) हैं. इंडेक्स करने की प्रोसेस, ARGB कलर के हरे कॉम्पोनेंट के आधार पर की जाती है.
// Inverse transform
argb = color_table[GREEN(argb)];
अगर इंडेक्स color_table_size
के बराबर या उससे ज़्यादा है, तो argb कलर वैल्यू को 0x00000000 (ट्रांसपेरेंट ब्लैक) पर सेट किया जाना चाहिए.
जब कलर टेबल छोटी होती है (16 के बराबर या उससे कम), तो कई पिक्सल एक पिक्सल में बंडल किए गए हों. पिक्सल बंडलिंग की सुविधा, कई (2, 4 या 8) पिक्सल को एक पिक्सल में पैक करती है. इससे इमेज की चौड़ाई कम हो जाती है. पिक्सल बंडलिंग से, ज़्यादा बेहतर तरीके से जॉइंट डिस्ट्रिब्यूशन की एंट्रॉपी कोडिंग में मदद मिलती है आस-पास के पिक्सल शामिल हैं और इससे अंकगणित की कोडिंग जैसे कुछ फ़ायदे मिलते हैं एंट्रॉपी कोड, जिसका इस्तेमाल सिर्फ़ तब किया जा सकता है, जब यूनीक वैल्यू 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] है. वैल्यू 2 का मतलब है कि चार पिक्सल को आपस में जोड़ा गया है और हर पिक्सल की रेंज [0..3] है. वैल्यू 3 का मतलब है कि आठ पिक्सल को आपस में जोड़ा गया है और हर पिक्सल की रेंज [0..1] है. इसका मतलब है कि यह एक बाइनरी वैल्यू है.
वैल्यू को हरे रंग के कॉम्पोनेंट में इस तरह से पैक किया जाता है:
width_bits
= 1: हर x वैल्यू के लिए, जहां x ≡ 0 (mod 2) है, x पर मौजूद हरे रंग की वैल्यू को x / 2 पर मौजूद हरे रंग की वैल्यू के चार सबसे कम अहम बिट में रखा जाता है. साथ ही, x + 1 पर मौजूद हरे रंग की वैल्यू को x / 2 पर मौजूद हरे रंग की वैल्यू के चार सबसे अहम बिट में रखा जाता है.width_bits
= 2: हर x वैल्यू के लिए, जहां x Permissions 0 (मॉड 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 पर मौजूद हरे रंग की वैल्यू के सबसे अहम बिट के हिसाब से रखा जाता है.
इस ट्रांसफ़ॉर्म को पढ़ने के बाद, image_width
को width_bits
ने सबसैंपल कर दिया है. यह
होने वाले बदलावों के साइज़ पर असर डालता है. नए साइज़ का पता लगाने के लिए,
DIV_ROUND_UP
, जैसा कि पहले बताया गया है.
image_width = DIV_ROUND_UP(image_width, 1 << width_bits);
5 इमेज डेटा
इमेज डेटा, स्कैन-लाइन के क्रम में पिक्सल की वैल्यू का कलेक्शन होता है.
5.1 इमेज डेटा की भूमिकाएं
हम इमेज डेटा का इस्तेमाल पांच अलग-अलग भूमिकाओं में करते हैं:
- ARGB इमेज: इमेज के असल पिक्सल को सेव करती है.
- एन्ट्रापी इमेज: मेटा प्रीफ़िक्स कोड को सेव करती है ("मेटा प्रीफ़िक्स कोड को डिकोड करना" देखें).
- प्रिडिक्टर इमेज: प्रिडिक्टर ट्रांसफ़ॉर्म के लिए मेटाडेटा सेव करता है ("प्रिडिक्टर ट्रांसफ़ॉर्म" देखें).
- रंग बदलने वाली इमेज:
ColorTransformElement
वैल्यू से बनाया गया अलग-अलग ब्लॉक के लिए ("कलर ट्रांसफ़ॉर्म" में बताया गया है) पर क्लिक करें. - कलर इंडेक्स करने वाली इमेज:
color_table_size
के साइज़ का कलेक्शन (256 तक) ARGB मान) जो रंग सूची में बदलाव के लिए मेटाडेटा को स्टोर करता है (देखें "कलर इंडेक्स करने का तरीका बदलना").
5.2 इमेज डेटा को एन्कोड करना
इमेज डेटा की एन्कोडिंग, इसकी भूमिका से अलग होती है.
इमेज को पहले, तय साइज़ के ब्लॉक (आम तौर पर 16x16) में बांटा जाता है ब्लॉक). इनमें से हर ब्लॉक को उसके एन्ट्रापी कोड का इस्तेमाल करके बनाया जाता है. साथ ही, कई ब्लॉक एक ही एन्ट्रापी कोड शेयर कर सकते हैं.
वजह: एंट्रॉपी कोड को सेव करने पर शुल्क लगता है. इस लागत को कम किया जा सकता है, अगर आंकड़ों के हिसाब से मिलते-जुलते ब्लॉक एक एन्ट्रापी कोड शेयर करते हैं. इससे उस कोड को सिर्फ़ एक बार सेव किया जाता है. उदाहरण के लिए, एन्कोडर, मिलते-जुलते ब्लॉक को उनकी आंकड़ों वाली प्रॉपर्टी का इस्तेमाल करके क्लस्टर कर सकता है. इसके अलावा, इमेज को कोड में बदलने के लिए ज़रूरी बिट की कुल संख्या कम करने पर, एन्कोडर, रैंडम तौर पर चुने गए क्लस्टर के पेयर को बार-बार जोड़कर भी मिलते-जुलते ब्लॉक ढूंढ सकता है.
हर पिक्सल को कोड में बदलने के लिए, तीन संभावित तरीकों में से किसी एक का इस्तेमाल किया जाता है:
- प्रीफ़िक्स कोड वाली लिटरल वैल्यू: हर चैनल (हरा, लाल, नीला, और ऐल्फ़ा) अलग से कोड में बदला गया हो.
- LZ77 बैकवर्ड रेफ़रंस: पिक्सल का सीक्वेंस, इसमें कहीं और से कॉपी किया जाता है इमेज.
- कलर कैश कोड: हाल ही में देखे गए रंग के छोटे मल्टीप्लायटिव हैश कोड (कलर कैश इंडेक्स) का इस्तेमाल करना.
नीचे दिए गए सबसेक्शन में, इनके बारे में पूरी जानकारी दी गई है.
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] खास होते हैं और इन्हें क्लोज़िंग मौजूदा Pixel के आस-पास की जगह पर होना चाहिए. इस आस-पड़ोस में 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.1 खास जानकारी
ज़्यादातर डेटा को कैनोनिकल प्रीफ़िक्स कोड का इस्तेमाल करके कोड किया जाता है. इसलिए, प्रीफ़िक्स कोड की लंबाई भेजकर कोड इस तरह से भेजे जाते हैं यह असली प्रीफ़िक्स कोड के उलट है.
खास तौर पर, फ़ॉर्मैट में स्पेशल वैरिएंट प्रीफ़िक्स कोडिंग का इस्तेमाल किया जाता है. अन्य शब्द, इमेज के अलग-अलग ब्लॉक अलग-अलग एन्ट्रॉपी का इस्तेमाल कर सकते हैं कोड.
वजह: हो सकता है कि इमेज के अलग-अलग हिस्सों में अलग-अलग विशेषताएं हों. इसलिए, उन्हें अलग-अलग एंट्रॉपी कोड के इस्तेमाल की अनुमति देने से, उन्हें ज़्यादा विकल्प मिलता है और बेहतर संपीड़न हो सकता है.
6.2 जानकारी
कोड में बदले गए इमेज के डेटा के कई हिस्से होते हैं:
- प्रीफ़िक्स कोड को डिकोड करना और बनाना.
- मेटा प्रीफ़िक्स कोड.
- एंट्रॉपी कोड किया गया इमेज डेटा.
किसी भी पिक्सल (x, y) के लिए, उससे जुड़े पांच प्रीफ़िक्स कोड का एक सेट होता है. ये कोड हैं (बिटस्ट्रीम क्रम में):
- प्रीफ़िक्स कोड #1: इसका इस्तेमाल ग्रीन चैनल, बैकवर्ड-रेफ़रंस की लंबाई, और रंग कैश मेमोरी के लिए किया जाता है.
- प्रीफ़िक्स कोड #2, #3, और #4: लाल, नीले, और ऐल्फ़ा चैनलों के लिए इस्तेमाल किया जाता है, क्रम से.
- प्रीफ़िक्स कोड #5: इसका इस्तेमाल, पिछली दूरी के रेफ़रंस के लिए किया जाता है.
आगे से, हम इस सेट को प्रीफ़िक्स कोड ग्रुप के तौर पर देखते हैं.
6.2.1 प्रीफ़िक्स कोड बनाना और डिकोड करना
इस सेक्शन में, बिटस्ट्रीम से प्रीफ़िक्स कोड की लंबाई पढ़ने का तरीका बताया गया है.
प्रीफ़िक्स कोड की लंबाई को दो तरीकों से कोड किया जा सकता है. इस्तेमाल किया गया तरीका बताया गया है को 1-बिट वैल्यू से गुणा कर सकते हैं.
- अगर यह बिट 1 है, तो यह कोड की लंबाई का आसान कोड है.
- अगर यह बिट 0 है, तो यह सामान्य कोड की लंबाई वाला कोड है.
दोनों ही मामलों में, ऐसे कोड की लंबाई हो सकती है जो अब भी स्ट्रीम का हिस्सा है, लेकिन इस्तेमाल नहीं किया गया है. ऐसा हो सकता है कि ऐसा करना ठीक न हो, लेकिन फ़ॉर्मैट के हिसाब से ऐसा किया जा सकता है. बताया गया ट्री, पूरी तरह से बाइनरी ट्री होना चाहिए. एक लीफ़ नोड को पूरी बाइनरी ट्री माना जाता है. इसे कोड की लंबाई के हिसाब से कोड में बदला जा सकता है. सामान्य कोड की लंबाई वाले कोड का इस्तेमाल करके, एक लीफ़ नोड को कोड करते समय, एक कोड की लंबाई को छोड़कर सभी कोड की लंबाई शून्य होती है. साथ ही, एक लीफ़ नोड की वैल्यू को लंबाई 1 के साथ मार्क किया जाता है. भले ही, उस एक लीफ़ नोड ट्री का इस्तेमाल करते समय कोई बिट खर्च न किया गया हो.
सरल कोड लंबाई का कोड
इस वैरिएंट का इस्तेमाल खास मामलों में किया जाता है. जैसे, जब कोड की लंबाई 1
हो और प्रीफ़िक्स के तौर पर इस्तेमाल होने वाले सिर्फ़ एक या दो सिंबल, [0..255] की सीमा में हों. प्रीफ़िक्स कोड की अन्य सभी लंबाई, डिफ़ॉल्ट रूप से शून्य होती हैं.
पहला बिट सिंबल की संख्या दिखाता है:
int num_symbols = ReadBits(1) + 1;
ये रहे सिंबल की वैल्यू.
इस पहले सिंबल को 1 या 8 बिट का इस्तेमाल करके कोड किया जाता है. यह 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
होता है.
सामान्य कोड की लंबाई का कोड
प्रीफ़िक्स कोड की लंबाई आठ बिट होती है और इसे इस तरह पढ़ा जाता है.
सबसे पहले, 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
) को अंग्रेज़ी के अक्षरों की संख्या पर सेट किया गया है, तो:
- G चैनल: 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 मेटा प्रीफ़िक्स कोड को डिकोड करना
जैसा कि पहले बताया गया है, इस फ़ॉर्मैट में इमेज के अलग-अलग ब्लॉक के लिए, अलग-अलग प्रीफ़िक्स कोड का इस्तेमाल किया जा सकता है. मेटा प्रीफ़िक्स कोड इंडेक्स होते हैं. इनसे यह पता चलता है कि इमेज के अलग-अलग हिस्सों में कौनसे प्रीफ़िक्स कोड इस्तेमाल करने हैं.
मेटा प्रीफ़िक्स कोड का इस्तेमाल सिर्फ़ तब किया जा सकता है, जब इमेज का इस्तेमाल ARGB इमेज की भूमिका.
मेटा प्रीफ़िक्स कोड के लिए दो संभावनाएं होती हैं, जिन्हें एक-बिट वैल्यू से दिखाया जाता है:
- अगर यह बिट शून्य है, तो इमेज में हर जगह सिर्फ़ एक मेटा प्रीफ़िक्स कोड का इस्तेमाल किया जाता है. कोई और डेटा सेव नहीं किया जाता.
- अगर यह बिट एक है, तो इसका मतलब है कि इमेज में एक से ज़्यादा मेटा प्रीफ़िक्स कोड इस्तेमाल किए गए हैं. ये मेटा प्रीफ़िक्स कोड एंट्रॉपी इमेज के तौर पर सेव किए जाते हैं. इस बारे में नीचे बताया गया है.
पिक्सल के लाल और हरे रंग के कॉम्पोनेंट, 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
वाली एन्ट्रापी इमेज होती है.
मेटा प्रीफ़िक्स कोड का मतलब
ARGB इमेज में प्रीफ़िक्स कोड ग्रुप की संख्या एन्ट्रॉपी इमेज से मिला सबसे बड़ा मेटा प्रीफ़िक्स कोड:
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 की वैल्यू के आधार पर, इसका मतलब अलग-अलग होता है:
- अगर S < 256
- हरे रंग के कॉम्पोनेंट के तौर पर S का इस्तेमाल करें.
- प्रीफ़िक्स कोड #2 का इस्तेमाल करके, बिटस्ट्रीम से लाल रंग के टेक्स्ट को पढ़ें.
- प्रीफ़िक्स कोड #3 का इस्तेमाल करके, बिटस्ट्रीम से नीले रंग की जानकारी पढ़ें.
- प्रीफ़िक्स कोड #4 का इस्तेमाल करके, बिटस्ट्रीम से अल्फा पढ़ें.
- If S >= 256 & S < 256 + 24
- लंबाई के प्रीफ़िक्स कोड के तौर पर, S - 256 का इस्तेमाल करें.
- बिटस्ट्रीम से लंबाई के लिए अतिरिक्त बिट पढ़ें.
- लंबाई के प्रीफ़िक्स कोड और पढ़े गए अतिरिक्त बिट से, बैकवर्ड-रेफ़रंस की लंबाई L का पता लगाएं.
- प्रीफ़िक्स कोड #5 का इस्तेमाल करके, बिटस्ट्रीम से दूरी का प्रीफ़िक्स कोड पढ़ें.
- बिटस्ट्रीम से दूरी के लिए अतिरिक्त बिट पढ़ें.
- दूरी उपसर्ग कोड से पीछे-संदर्भ की दूरी D ज्ञात करें और अतिरिक्त बिट पढ़े जा सकते हैं.
- शुरू होने वाले पिक्सेल के क्रम से L पिक्सेल (स्कैन-लाइन के क्रम में) कॉपी करें D पिक्सल को घटाकर मौजूदा पोज़िशन पर.
- If S >= 256 + 24
- कलर कैश में इंडेक्स के तौर पर S - (256 + 24) का इस्तेमाल करें.
- उस इंडेक्स पर कलर कैश मेमोरी से ARGB रंग पाएं.
7 फ़ॉर्मैट का पूरा स्ट्रक्चर
यहां ऑगमेंटेड बैकस-नौर फ़ॉर्म (एबीएनएफ़) का फ़ॉर्मैट दिखाया गया है आरएफ़सी 5234 आरएफ़सी 7405. इसमें सभी जानकारी शामिल नहीं होती. इमेज के आखिर में मौजूद जानकारी (EOI) को सिर्फ़ पिक्सल की संख्या (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