8 पॉइंट द्वारा GN⁺ 2026-06-02 | 2 टिप्पणियां | WhatsApp पर शेयर करें
  • 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 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 करती हैं
  • अशुद्धता

    • 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 में भी मायने नहीं रखती; यह अशुद्धता तकनीकी नहीं, सौंदर्य संबंधी समस्या है
  • 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 बनाए रखने के लिए सावधानी चाहिए

दो प्रकार के 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), decoding yₖ = k/L
    • mid-riser staircase quantizer: encoding k = trunc(xL), decoding yₖ = (k+0.5)/L
  • इन्हें यहाँ लागू करने पर
    • 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 से भाग देना थोड़ा अधिक सटीक है
    • लेकिन व्यवहार में वास्तव में ऐसा 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 आम गलती है

निष्कर्ष

  • अगर आप किसी और द्वारा दी गई 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 टिप्पणियां

 
GN⁺ 2026-06-03
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

    • बचपन में शोर वाले CRT स्क्रीन को देखते हुए किनारों पर हल्की नीली और पीली रेखाएँ दिखती थीं, यह याद है। हमेशा सोचता था कि वही दो रंग क्यों दिखते हैं; अगर वजह यही है, तो अब जाकर समझ आया
    • इसमें gamma correction छूट गया है। 0~255 range के मानों को voltage में बदलने से पहले PC आम तौर पर उन मानों को 2.2 की घात तक उठाता है
      इससे छोटे और बड़े मानों का फ़र्क़ और ज़्यादा उभरकर आता है: 2^2.2 = 4.595, 255^2.2 = 196,964.699
    • इस समस्या के लिए temporal dithering सबसे अच्छा लगता है। प्रति-पिक्सेल delta-sigma modulation काफ़ी आसानी से किया जा सकता है
      अगर 30Hz पर बदलाव हो, तो इंसान के लिए हल्के नीले और हल्के पीले के बीच का अंतर पहचानना शायद मुश्किल होगा
    • शायद इसी वजह से 80 के दशक में RGBI colors इतने आम थे
  • 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 बनता है

    • मैंने जितने ADC documents देखे हैं, वे सभी कहते हैं कि positive full-scale को represent नहीं किया जा सकता। उदाहरण के लिए 8-bit ±1V ADC में -128 का मतलब -1V है, और +127 का मतलब 127/128=0.99219V
      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 अपना अर्थ खो देते हैं

    • इससे ESA की Sentinel-2 level-2 satellite image quantization में इस्तेमाल होने वाला normalization value याद आता है
      लगभग ऐसा है: 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 के रूप में दी जाती है

    • BeOS API pixel center आधारित है। हालाँकि अब इसकी परवाह शायद ही कोई करता हो
  • बीजगणितीय रूप से देखें तो उत्तर स्पष्ट है: 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 की आवश्यकता नहीं होती

    • VFX image processing और rendering का बहुत काम कर चुके व्यक्ति के रूप में, लगता है कि आप यह भूल रहे हैं कि इसके बाद color space conversion होता है। पुराने SDR में sRGB के linear Rec.709 में, और आधुनिक formats में इससे चौड़े gamut में conversion किया जाता है। इसलिए dynamic range का compression load होने के बाद होता है
      इसके अलावा, कई 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 बन जाने पर भी यही बात हो सकती है
    • लेख RGB पर केंद्रित है, लेकिन यही quantization problem हर तरह के signal में मौजूद है जहाँ discrete representation और continuous representation के बीच mapping होती है
      असली बात 0 photons को व्यक्त करना नहीं, बल्कि 1 byte में stored information को अधिकतम करना है। आदर्श रूप से byte value 0 का उपयोग कम नहीं होना चाहिए, और न ही उस data में bias जोड़ना चाहिए जो 0वें bucket में जाना चाहिए था। चाहे color space bright से very bright तक का ही क्यों न हो, सभी bytes को brightness range के समान आकार के हिस्सों का प्रतिनिधित्व करना चाहिए
    • ऐतिहासिक रूप से broadcast systems का SDR luminance range के लिए 16~235 का उपयोग करना ही समस्या है। दुर्भाग्य से “आधुनिक” HDMI भी अब तक इस अजीब प्रथा से जूझ रहा है, इसलिए अगर display और source सहमत न हों, तो स्क्रीन धुली-धुली दिखती है या black crush हो जाता है
    • दोनों solutions में 0.5 जोड़ा जाता है। फर्क सिर्फ इतना है कि यह प्रक्रिया के किस चरण में होता है
    • दिलचस्प विचार है, लेकिन इससे लगता है जैसे दुनिया हिल गई हो। processing program के नज़रिए से पहले वाला black (0.0) और white (1.0) अब बहुत गहरा gray और बहुत चमकीला gray बन जाते हैं
  • अगर ruler 12 inch तक जाता है, तो आपको ruler पर मौजूद 13 points नहीं बल्कि length L से normalize करना चाहिए

    • यह analogy उलझाऊ है। समझ नहीं आता कि “ruler” 0~255 से चिह्नित 256 points वाला 255-inch ruler है, या 1-inch के 256 intervals वाला 256-inch ruler, जहाँ L = 256×1 है
    • अगर आप वास्तव में fence posts गिनना चाहते हैं, तो fencepost error गलती नहीं है
    • सही है, लेकिन >> 8 कहीं तेज़ है
    • किसने तय किया कि numbers points को दर्शाते हैं? वे points के बीच के intervals को भी दर्शा सकते हैं
    • क्या मैं बेवकूफ हूँ? 0 क्या starting point से शुरू नहीं होता?
  • यह पढ़कर अच्छा लगा कि लेख ने ऐसे विषय को छुआ, जिसके बारे में मैंने काफ़ी समय से नहीं सोचा था। इससे 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...

 
GN⁺ 2026-06-02
Lobste.rs की राय
  • देखने में बेतरतीब लगता है, लेकिन सही जवाब 255 ही है
    अगर यह सहज न लगे, तो इसे 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 में 3 से गुणा होना चाहिए, 4 नहीं” यह निष्कर्ष नहीं निकलता
      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..1 [0..255.0] से मेल खाता है, या [0.5..255.5] से?”
    मेरे लिए जवाब हमेशा “बिलकुल” [0.0..255.0] रहा है, लेकिन शायद यह सबके लिए उतना स्पष्ट नहीं है
    लेख में कहा गया है कि “extreme” intervals की क्षमता बाकी intervals की आधी है, लेकिन मुझे यह framing भी सही नहीं लगती
    अगर [0..1] के बाहर कोई values हैं ही नहीं, तो उनका संकरा interval दिखना rendering का प्रभाव है। आपने यह जानते हुए कि range के बाहर कोई value नहीं है, buckets को काट दिया है, इसलिए वे संकरे render हुए हैं
    उलटे, अगर [0..1] के बाहर values मौजूद हैं, तो वह range अनंत है। लेख बाद वाली बात मानता है, लेकिन पहली नहीं
    जैसे ही पहली बात मान लें, सही behavior काफ़ी स्पष्ट लगता है, लेकिन इस तरह का लेख आया ही है, यह बताता है कि यह वस्तुनिष्ठ रूप से इतना “स्पष्ट” सवाल नहीं है :D
    • अगर सचमुच 0…255.0 ही स्पष्ट जवाब है, तो कौन-सी floating-point value range integer 0 पर वापस जानी चाहिए, और कौन-सी integer 255 पर?
      अगर 0..<1 integer 0 पर जाए, और 254>..255.0 integer 255 पर, तो 128 बीच में गायब हो जाता है। शायद आप चाहेंगे कि 127.5..128.5, 128 पर जाए, लेकिन फिर ये आधे हिस्से कहाँ जाएंगे?
      अगर 128 को सही बैठाने के लिए पूरी range को थोड़ा shift करें, तो 0..0.99609375 integer 0 पर map होता है
  • standard approach भी शायद इसलिए बनी क्योंकि लोग सहज रूप से round() बुला देते हैं
    लोगों को वह तरीका काफ़ी natural लगता है, इसलिए शायद सादगी की वजह से वही standard बन गया
  • सोच रहा हूँ कि 256 वाले लक्ष्य का उल्टा तरीका भी उपयोगी हो सकता है या नहीं। यानी 0.0 को 0 पर, 1.0 को 255 पर भेजें, और बाकी floating-point values को 1 से 254 के बीच map करें
    uint8_t output = 0.0f >= result  
                     ? 0  
                     : 1.0f <= result  
                     ? 255  
                     : 1 + 253*result;  
    
    अच्छा होगा अगर processing के दौरान भी black, black ही रहे और white, white ही रहे
    • ऐसा करने पर 0 और 255 को unit interval में बाकी संख्याओं की तुलना में बड़ा हिस्सा मिलता है। लगभग 0.8%, यानी 255/253 के बराबर
  • पहली image मेरे environment में टूटी हुई दिखती है
    • मैं लेख का लेखक हूँ। क्या आपका मतलब image file के corrupt होने से है? मैंने इसे pngcrush से compress किया था। या आपका मतलब है कि image की सामग्री में कुछ गड़बड़ है?