- 8-bit integer रंगों को floating-point में बदलते समय 255 से भाग देने वाली standard method और 0.5 bias जोड़कर 256 से भाग देने वाली alternative method के बीच का अंतर
- 255 method integer 0 को 0.0 और 255 को 1.0 पर map करती है, इसलिए काले और सफेद को सीधे संभालना आसान होता है, और यह GPU की UNORM-to-float conversion method से भी मेल खाती है
- 256 method में
(img + 0.5) / 256.0के जरिए हर मान को interval के बीच में रखा जाता है, जिससे dithering जैसे कामों में boundary handling सरल हो सकती है, लेकिन 0 अब 0.0 नहीं रहता, इसलिए processing logic 8-bit input से बंध जाती है - 255 method में दोनों सिरों के interval आधी चौड़ाई के होते हैं, इसलिए uniform
[0, 1]random numbers को फिर 8-bit में round करने पर 0 और 255 बाकी मानों की तुलना में आधी frequency से आते हैं, लेकिन वास्तविक image round-trip conversion lossless रहती है - अगर आप किसी और की image process कर रहे हैं, तो 255 से normalize करना सही है; 256 method पर सिर्फ तभी विचार करें जब storage और loading दोनों आपके control में हों
समस्या की रूपरेखा
- ऐसी program में जो image लेकर उसे floating-point में बदलती है, process करती है, और फिर 8-bit रंगों में save करती है, integer-floating-point conversion method मुख्य मुद्दा है
- दो approaches मौजूद हैं
- standard method (255 से भाग):
pixels = img / 255.0→ process →output = np.trunc(result * 255 + 0.5) - alternative method (256 से भाग):
pixels = (img + 0.5) / 256.0→ process →output = np.trunc(result * 256) - दोनों ही मामलों में final type conversion से पहले value को 0~255 तक limit किया जाता है:
output.clip(0, 255).astype(np.uint8)
- standard method (255 से भाग):
- standard method integer 0 को 0.0 और 255 को 1.0 पर map करती है, और GPU की UNORM-to-float conversion method जैसी है
- alternative method में 0.5 bias जोड़ा जाता है, जिससे integer 0
0.5/256 = 0.001953125पर map होता है- इसके कारण इस constant को जाने बिना black pixel detect नहीं किए जा सकते
- floating-point गणना करने पर भी logic 8-bit input से बंधी रहती है
- standard method में black को हमेशा 0.0 मानना संभव है
255.0 के खिलाफ तर्क
- standard method को number line पर दिखाने पर यह थोड़ा अजीब लगती है
-
दोनों सिरों पर छोटे bin मौजूद हैं
- standard formula के दोनों edge bin [0,1] range के बाहर निकलते हैं, यानी range खींची हुई (stretched) लगती है
- floating-point को integer में वापस बदलते समय edge bin की चौड़ाई बाकी bin की आधी होती है
- algorithm के लिए extreme values output करना "ज़्यादा कठिन" हो जाता है
- uniform [0,1] noise generate करके standard formula से round करने पर 0 और 255 के मान बाकी integers की तुलना में आधी frequency से आते हैं
- 10 लाख uniform random numbers के histogram में देखा जा सकता है कि 0 और 255 bin बाकी bin की आधी ऊंचाई के होते हैं
- हालांकि extreme values को avoid करने वाला यह bias वास्तव में समस्या कब बनता है, यह सोचना कठिन है
- original image अब भी lossless round-trip conversion (
uint8 → float → uint8) करती है - 0.0 या 1.0 से थोड़ा बाहर गए result भी सही bin में round होकर output distribution को फिर संतुलित कर देते हैं
- उदाहरण: processing के दौरान रंग से 0.005 घटाने पर standard method में black 0 से नीचे चला जाता है, जबकि alternative method में positive रहता है, लेकिन दोनों methods अंततः integer 0 ही output करती हैं
- original image अब भी lossless round-trip conversion (
-
अशुद्धता
- standard method के floating-point values exact नहीं होते, उदाहरण के लिए
128/255.0 ≈ 0.501961जबकि128/256.0 = 0.5 - rounding error के कारण floating-point values के बीच की दूरी थोड़ा बदलती है, लेकिन यह error इतनी छोटी है कि व्यावहारिक समस्या नहीं बनती
- 32-bit floating point में 23-bit mantissa होती है, और error least significant bit स्तर पर
2⁻²³से कम होती है - 0.00001% relative error बहुत advanced image processing में भी मायने नहीं रखती; यह अशुद्धता तकनीकी नहीं, सौंदर्य संबंधी समस्या है
- 32-bit floating point में 23-bit mantissa होती है, और error least significant bit स्तर पर
- standard method के floating-point values exact नहीं होते, उदाहरण के लिए
-
integer range में न आने वाले मान
- alternative method हर floating-point value को दो integers के ठीक बीच में रखती है
- क्योंकि मूल quantized value ज्ञात नहीं होती, इसलिए लगातार दो integers के midpoint को एक अच्छी estimate मानना एक समझौता है
- दावा किया गया है कि dithering अधिक सुविधाजनक हो जाती है (Andrew Kesler की 2015 की blog post "Converting Color Depth")
- edge cases की चिंता किए बिना noise जोड़ा जा सकता है
- इसके उलट standard formula के awkward extreme values में noise distribution की consistency बनाए रखने के लिए सावधानी चाहिए
- alternative method हर floating-point value को दो integers के ठीक बीच में रखती है
दो प्रकार के quantizer
- दोनों approaches को uniform scalar quantizer के दो प्रकार के रूप में देखा जा सकता है
- Wikipedia के quantization लेख के अनुसार signed input data के uniform quantizer दो प्रकार के होते हैं
- mid-tread: 0 को 0 value reconstruction level पर map करता है
- mid-riser: 0 को 0 value classification threshold पर map करता है
- Wikipedia इसके source के रूप में 1977 के paper (Allen Gresho, "Quantization") का हवाला देता है
- quantizer formula (L output levels की संख्या है, जैसे 256)
- mid-tread staircase quantizer: encoding
k = trunc(xL + 0.5), decodingyₖ = k/L - mid-riser staircase quantizer: encoding
k = trunc(xL), decodingyₖ = (k+0.5)/L
- mid-tread staircase quantizer: encoding
- इन्हें यहाँ लागू करने पर
- standard formula = mid-tread (L=255)
- alternative formula = mid-riser (L=256)
- standard method unsigned input पर mid-tread का उपयोग करते हुए L=255 code चुनती है, जो 8-bit input के लिए optimal नहीं है
- यह 0.0 और 1.0 पर mapping की programming convenience के लिए चुना गया है
-
अधिक quantization error, लेकिन वास्तव में नहीं
- यदि सिस्टम uniform distribution वाले real numbers x∈[0,1] को 8-bit integer में encode करके फिर real numbers में reconstruct करता है, तो standard formula bandwidth waste करती है
- standard method की representable range
[-0.5/255, 255.5/255]है, जो [0,1] input के लिए जरूरत से ज्यादा चौड़ी है, इसलिए reconstruction error बढ़ती है - StackOverflow user Peter Mudrievskij की गणना के अनुसार average absolute error 255 divisor पर
1/1020और 256 divisor पर1/1024होती है, इसलिए सिद्धांततः 256 से भाग देना थोड़ा अधिक सटीक है
- standard method की representable range
- लेकिन व्यवहार में वास्तव में ऐसा reconstruction नहीं किया जा रहा
- मान लिया गया है कि 8-bit RGB image load की जाती है, process की जाती है, और फिर save की जाती है; save करते समय quantization method पर आपका control नहीं होता, और खोई हुई जानकारी स्थायी रूप से खो चुकी होती है
- अगर image standard formula से multiply और round करके save की गई थी, तो load करते समय 256 से भाग देने से precision वापस नहीं लाई जा सकती
- lower reconstruction error वाला तर्क तभी मायने रखता है जब save और load दोनों आपके control में हों
- किसी और की image को alternative formula से load करने पर उल्टा और ज्यादा error पैदा हो सकती है
- अधिक संभावना है कि वह standard formula से quantize की गई हो, इसलिए गलत scale से decode करना सिद्धांततः inaccurate है
- व्यवहार में रंग कोई absolute measurement नहीं होते, इसलिए यह बस थोड़ा छोटे range और छोटे offset के साथ processing करने जैसा है
- दोनों quantizer के encoding और decoding steps को mix नहीं करना चाहिए; ऐसा broken code आम गलती है
- यदि सिस्टम uniform distribution वाले real numbers x∈[0,1] को 8-bit integer में encode करके फिर real numbers में reconstruct करता है, तो standard formula bandwidth waste करती है
निष्कर्ष
- अगर आप किसी और द्वारा दी गई image process कर रहे हैं, तो RGB values को 255 से normalize करना चाहिए
- inaccurate floating-point values या abstract reconstruction error की चिंता alternative चुनने का अच्छा कारण नहीं है
- अगर image saving और loading दोनों आपके control में हैं, 0 को 0 पर map करना जरूरी नहीं है, और processing code को 8-bit dynamic range से बांधना स्वीकार्य है, तो 256 से भाग देकर थोड़ी अधिक precision पाई जा सकती है
- लेकिन ध्यान रहे कि कोई सहकर्मी standard formula से image load करके पूरी योजना बिगाड़ सकता है
अन्य दृष्टिकोण
- Jonathan Blow की 2002 की post mid-riser और mid-tread quantizer को बिना नाम दिए समझाती है; diagram idea का स्रोत यही है
- Andrew Kesler की 2015 की blog post alternative formula का समर्थन करती है
- हालांकि उसकी तुलना बिना rounding वाली standard formula से की गई है, इसलिए उसका अधिकांश analysis अमान्य हो जाता है
2 टिप्पणियां
Hacker News की राय
रंग मान वास्तव में किस चीज़ को दर्शाता है, यह प्रति घटक 8-बिट के मामले में आम तौर पर कोई बड़ी बात नहीं होती। हर के रूप में 255 या 256 लेने से जो त्रुटि आती है वह बहुत छोटी होती है, और अंतर देखने के लिए या तो रंगों की बहुत अच्छी समझ चाहिए या स्क्रीन के बहुत पास जाना पड़ता है, ऊपर से मॉनिटर और फ़ोन स्क्रीन भी आम तौर पर ठीक से कैलिब्रेटेड नहीं होते
लेकिन अगर microcontroller से VGA signal बनाया जा रहा हो और रंग आउटपुट के लिए सिर्फ 8 pins हों (लाल 3, हरा 3, नीला 2), तो यह काफ़ी पेचीदा हो जाता है। तब रंग मान वही 0V~0.7V voltage levels होते हैं जिन्हें VGA monitor को भेजना है
नीला channel 0→0V, 1→0.23V, 2→0.47V, 3→0.7V पर map होता है, और लाल/हरा 0→0V, 1→0.1V, …, 7→0.7V पर map होते हैं। सिरों को छोड़ दें तो नीले के voltage levels लाल/हरे से बिल्कुल मेल नहीं खाते, इसलिए शुद्ध gray देखना संभव नहीं होता, और सबसे नज़दीकी रंग भी अंतर की दिशा के हिसाब से हल्का नीला या हल्का पीला झुकाव लिए होता है
इसके अलावा, नीले को दूसरे channels के साथ मिलाकर बनने वाले लगभग सभी gradients भी असंतुलित लगते हैं। उदाहरण के लिए, शुद्ध लाल से शुद्ध सफ़ेद तक जाने वाली रेखा पर सबसे नज़दीकी रंग थोड़े नारंगी या बैंगनी जैसे दिखते हैं
Raspberry Pi Pico 2 पर double-buffered 320x240 framebuffer के साथ 8-bit color VGA output का code यहाँ है: https://github.com/moefh/pico-vga-8bit-demo
इससे छोटे और बड़े मानों का फ़र्क़ और ज़्यादा उभरकर आता है: 2^2.2 = 4.595, 255^2.2 = 196,964.699
अगर 30Hz पर बदलाव हो, तो इंसान के लिए हल्के नीले और हल्के पीले के बीच का अंतर पहचानना शायद मुश्किल होगा
255 के पक्ष में तर्क के लिए, black-and-white image जैसा चरम उदाहरण देखना काफ़ी है। एक single bit में 0 काला और 1 सफ़ेद होता है
0 का 0.0 पर और 1 का 1.0 पर map होना काफ़ी स्पष्ट है। आखिर यह black-and-white है, हल्का gray (0.25) और गहरा gray (0.75) नहीं। यानी black-and-white image को 2 नहीं बल्कि 1 से normalize किया जाता है
अगर 2-bit हो, तो आम तौर पर 0=काला, 1=हल्का gray, 2=गहरा gray, 3=सफ़ेद होगा, इसलिए 0.0, 0.33, 0.66, 1.0 पर map करना स्वाभाविक है। काला काला होना चाहिए, सफ़ेद सफ़ेद, और अंतर भी समान होना चाहिए, इसलिए 3 से normalize किया जाता है
यही तर्क 8-bit तक ले जाएँ, तो 255 से normalization मिलता है। 8-bit में अंतर बहुत छोटा हो जाने पर भी काला 0.0 और सफ़ेद 1.0 होना चाहिए
दूसरा तरीका, यानी 8-bit में 256 normalization, आउटपुट range को bit depth के हिसाब से बदल देता है। 1-bit में यह [0.25, 0.75], 2-bit में [0.125, 0.875] जैसा हो जाएगा। आम तौर पर चाहत यह होती है कि bit depth बढ़ने पर सूक्ष्मता बढ़े, न कि contrast बदल जाए
यह सचमुच सोचने पर मजबूर करने वाला लेख था, और इसने मेरी कुछ निजी धारणाओं को फिर से परखने पर मजबूर किया
electrical engineering के नज़रिए से देखें तो लेख में दिखाए गए “दो तरह के quantizer” से सहमत होना मुश्किल है। गणितीय रूप से यह सख़्त हो सकता है, लेकिन यह वास्तविक systems पर आधारित व्याख्या नहीं है
ADC में हमेशा मूलतः ±1/2 LSB quantization uncertainty होती है। transfer characteristic हमेशा mid-tread sampling होती है, कम से कम मैंने इसका कोई प्रतिवाद नहीं देखा। यह bipolar ADC हो या unipolar ADC, दोनों पर लागू होता है
सबसे निचला code negative voltage reference होता है और सबसे ऊँचा code positive voltage reference। transfer characteristic graph यही दिखाता है कि लेख में दिखाए अनुसार सबसे ऊपर/नीचे के हिस्से वास्तव में 1/2 LSB चौड़ाई के होते हैं
unipolar systems में मध्य voltage को ठीक-ठीक व्यक्त नहीं किया जा सकता, यानी gray वाली समस्या पैदा होती है। bipolar systems में 0V mid-tread का N/2 मान होता है, लेकिन इसका मतलब यह नहीं कि “256 intervals” मौजूद हैं
इसलिए मैं (VREF+ - VREF-) * k / (2^N - 1) का ही उपयोग करता रहूँगा। यानी मैं 255 normalization से सहमत हूँ। अंततः यह fencepost error जैसा ही है: मान N हैं, लेकिन intervals N-1। अगर values से intervals कम हों, तो एक interval को दो values के बीच बाँटना पड़ता है, और इसलिए सिरों पर 1/2 LSB interval बनता है
126 से 127 पर जाने वाला transition positive full range के 1.5 LSB नीचे होता है। 1 LSB का अंतर 2/255=0.00784V नहीं बल्कि 1/128=0.00781V के अंतर को दर्शाता है
लेकिन अगर वास्तव में voltage और uncertainty महत्वपूर्ण हों, तो ऐसे अंतर ज़्यादातर मायने नहीं रखते। reference voltage में bias होता है और linearity error भी। 1 LSB न तो 1/128 से और न ही 2/255 से बिल्कुल सटीक बैठता है, और calibration के लिए अतिरिक्त parameters चाहिए होते हैं
यह कुछ वैसा है जैसे वैज्ञानिक गणना में node-centered samples और cell-centered samples का फ़र्क़, बस 1D में। यह तय करना पड़ता है कि मान interval के बीच में है (या त्रिभुज/चतुष्फलक के केंद्र में), या interval की सीमा पर है (या त्रिभुज/चतुष्फलक के vertices पर)
scientific computing में यह जाने बिना कि values की व्याख्या कैसे करनी है, data processing शुरू कर देना समझदारी नहीं है। audio signal processing में भी अगर केवल integer stream मिले, तो मूल signal पर गणना करने के लिए यह जानना ज़रूरी है कि उन integers का अभिप्रेत representation क्या है, जैसे mu-law encoding है या linear। उम्मीद यही रहती है कि values के साथ जुड़ा metadata इसका जवाब दे
लेकिन 8-bit pixel values के मामले में, अगर representation intent बताने वाला ठीक-ठाक file-format metadata न हो, तो हम भटक जाते हैं और कोई एक सही जवाब नहीं होता। जैसा लेखक कहता है, अपनी ज़रूरत के हिसाब से बेहतर नतीजा देने वाला विकल्प चुनने पर किसी को दोष नहीं दिया जा सकता, लेकिन यह बात ज़रूर बताई जा सकती है कि context के बिना bits अपना अर्थ खो देते हैं
लगभग ऐसा है: Digital Number DN=0 को “NO_DATA” value के रूप में छोड़ा जाता है, और जब DN [1; 1;215-1] range में हो, तब L2A SR reflectance value L2A_SRi = (L2A_DNi + BOA_ADD_OFFSETi) / QUANTIFICATION_VALUE होती है
https://sentiwiki.copernicus.eu/web/s2-products
यहाँ यह मान लेने की गलती है कि 0 से 255 तक 256 स्टेप्स हैं। असल में 8-bit में व्यक्त किए जा सकने वाले मान 256 हैं, और 0 (काला) से 255 (शुद्ध सफेद) तक के बीच 255 अंतराल हैं
इसलिए 255 से divide करना कोई समस्या नहीं है। बेशक 128 बिल्कुल आधा gray नहीं है, और 0~255 के quantized 8-bit मान लगभग हमेशा linear perceptual space में नहीं बल्कि sRGB में होते हैं
आधुनिक API में sampling location को संभालते समय भी ऐसा ही भ्रम पैदा होता है। क्योंकि position pixel center नहीं बल्कि coordinate के रूप में दी जाती है
बीजगणितीय रूप से देखें तो उत्तर स्पष्ट है: f(x) -> [0, 255]
अगर f(n * 0) == n * f(0) सत्य नहीं है, तो अजीब चीजें होने लगती हैं। उदाहरण के लिए, अगर f(x) -> [0, 255] है, तो f(0) + f(0) + f(0) = 0 + 0 + 0 = 0 = f(0)
दूसरी ओर, अगर f(x) -> [0.5/8, 7.5/8] है, तो f(0) + f(0) + f(0) = 0.5/8 + 0.5/8 + 0.5/8 = 1.5/8 != f(0)
अगर आप दूसरा विकल्प चुनते हैं, तो यह उम्मीद नहीं की जा सकती कि x पक्ष पर की गई गणना और f(x) पक्ष पर की गई गणना एक-दूसरे से मेल खाएँगी। यानी बीजगणितीय correspondence टूट जाता है
मैं +0.5 समाधान का समर्थन करना चाहूँगा। पहली बात, किनारों पर आधे आकार के interval मुझे पसंद नहीं हैं, और दूसरी बात, 255-आधारित representation आम तौर पर HDR नहीं बल्कि SDR image होता है
RGB मान किसी adaptation state के सापेक्ष luminance को दर्शाते हैं, और day scene का “0” “luminance 0” नहीं होता। वह बस सबसे चमकीले बिंदु का लगभग 0.001 गुना होता है, और photons की संख्या लाखों में होती है, यानी 0 से बहुत अधिक
एक अर्थ में, आँख contrast को एक sliding scale की तरह अनुभव करती है, और system के भीतर कोई absolute 0 नहीं है। उदाहरण के लिए broadcast system ऐतिहासिक रूप से SDR luminance range के लिए 16~235 का उपयोग करते थे। “ज़रूर 0 होना चाहिए” वाला तर्क मुझे biased लगता है, और मेरा मानना है कि अधिकतर मामलों में 0 की आवश्यकता नहीं होती
इसके अलावा, कई image processing और compositing workflows, सही हों या गलत, यह मानते हैं कि 0 का मतलब 0 होता है। इसलिए 8-bit में 0u को 0.0f और 255 को 1.0f से map किया गया माना जाता है। अगर mask या alpha में 0 मान 0.0 से थोड़ा भी ऊपर हो जाए, तो कहीं न कहीं का code 0.0 hard threshold के साथ दूसरी operations को mask करते समय artifact पैदा करेगा। उलटे, अगर alpha में 255 अब 1.0f नहीं रहा, तो premultiplication के बाद object बहुत हल्का-सा transparent हो जाएगा
+0.5 की वजह से masking में 254 के 1.0f बन जाने पर भी यही बात हो सकती है
असली बात 0 photons को व्यक्त करना नहीं, बल्कि 1 byte में stored information को अधिकतम करना है। आदर्श रूप से byte value 0 का उपयोग कम नहीं होना चाहिए, और न ही उस data में bias जोड़ना चाहिए जो 0वें bucket में जाना चाहिए था। चाहे color space bright से very bright तक का ही क्यों न हो, सभी bytes को brightness range के समान आकार के हिस्सों का प्रतिनिधित्व करना चाहिए
अगर ruler 12 inch तक जाता है, तो आपको ruler पर मौजूद 13 points नहीं बल्कि length L से normalize करना चाहिए
>> 8कहीं तेज़ हैयह पढ़कर अच्छा लगा कि लेख ने ऐसे विषय को छुआ, जिसके बारे में मैंने काफ़ी समय से नहीं सोचा था। इससे game development के वे पल याद आ गए जब game logic floating-point math का उपयोग करता था लेकिन pixel art को integer coordinates पर draw करना पड़ता था
कुछ जगहों पर +0.5 जैसी approach इस्तेमाल की थी ताकि चीजें कम अजीब दिखें। खासकर जब moving camera होता था, और camera को भी quantize करना पड़ता था
नीचे linked Jonathan Blow का 2002 का लेख [1] भी रोचक था। पहले लेख का visualization इस विषय में और गहराई से जाते समय बहुत मदद करता है
[1] https://web.archive.org/web/20240706043551/https://number-no...
Lobste.rs की राय
अगर यह सहज न लगे, तो इसे 2-बिट के साधारण उदाहरण से देख सकते हैं। जब संभव integer values सिर्फ 0, 1, 2, 3 हों, तो integer→floating-point रूपांतरण के सभी मामलों को गिनने पर पता चलता है कि काला/सफेद को सचमुच काला/सफेद बनाए रखने और intervals को बराबर रखने के लिए मान 0.0, 0.33..., 0.66..., 1.0 होने चाहिए
इसलिए reverse conversion में 4(2^2) नहीं, बल्कि 3 से गुणा करना चाहिए
reverse conversion में quantization (rounding) चाहिए, और symmetry यहीं टूटती है
अगर 0..=1 रेंज में एक uniform real gradient बनाकर उसे 0, 1, 2, 3 में quantize करें, तो दिखेगा कि 3 से गुणा करने पर नतीजा uniform नहीं रहता। ×3 के बाद
round()करने पर 1 और 2 ज़्यादा represent होते हैं, और ×3 के बादfloorयाceilकरने पर 0 या 3 ऐसे fold हो जाते हैं कि gradient मानो 4 रंगों में से सिर्फ 3 ही इस्तेमाल कर रहा हो/3और×3वाली logic exact numbers के round-trip conversion के लिए ठीक लग सकती है, लेकिन बीच के मान rounding के चुनाव से बहुत प्रभावित होते हैं, और डेटा processing शुरू करते ही यही महत्वपूर्ण हो जाता हैintegers का uniform proportion सिर्फ (4-ε) से गुणा करके floor लेने पर मिलता है, जो ×4,
floor(),clamp()के बराबर है। यह 1 के अजीब अंतर या ε-error जैसा लग सकता है, लेकिन सहज रूप से यही सबसे अच्छा दिखने वाला हल हैमेरे लिए जवाब हमेशा “बिलकुल” [0.0..255.0] रहा है, लेकिन शायद यह सबके लिए उतना स्पष्ट नहीं है
लेख में कहा गया है कि “extreme” intervals की क्षमता बाकी intervals की आधी है, लेकिन मुझे यह framing भी सही नहीं लगती
अगर [0..1] के बाहर कोई values हैं ही नहीं, तो उनका संकरा interval दिखना rendering का प्रभाव है। आपने यह जानते हुए कि range के बाहर कोई value नहीं है, buckets को काट दिया है, इसलिए वे संकरे render हुए हैं
उलटे, अगर [0..1] के बाहर values मौजूद हैं, तो वह range अनंत है। लेख बाद वाली बात मानता है, लेकिन पहली नहीं
जैसे ही पहली बात मान लें, सही behavior काफ़ी स्पष्ट लगता है, लेकिन इस तरह का लेख आया ही है, यह बताता है कि यह वस्तुनिष्ठ रूप से इतना “स्पष्ट” सवाल नहीं है :D
अगर 0..<1 integer 0 पर जाए, और 254>..255.0 integer 255 पर, तो 128 बीच में गायब हो जाता है। शायद आप चाहेंगे कि 127.5..128.5, 128 पर जाए, लेकिन फिर ये आधे हिस्से कहाँ जाएंगे?
अगर 128 को सही बैठाने के लिए पूरी range को थोड़ा shift करें, तो 0..0.99609375 integer 0 पर map होता है
round()बुला देते हैंलोगों को वह तरीका काफ़ी natural लगता है, इसलिए शायद सादगी की वजह से वही standard बन गया
pngcrushसे compress किया था। या आपका मतलब है कि image की सामग्री में कुछ गड़बड़ है?