- 8 bit tamsayı renkleri kayan noktalıya dönüştürürken 255’e bölme temelli standart yöntem ile 0.5 bias ekleyip 256’ya bölme temelli alternatif yöntem arasındaki fark
- 255 yöntemi, tamsayı 0’ı 0.0’a, 255’i 1.0’a eşleyerek siyah ve beyazı doğrudan işlemeyi kolaylaştırır ve GPU’nun UNORM-to-float dönüşüm biçimiyle de uyumludur
- 256 yöntemi,
(img + 0.5) / 256.0ile her değeri aralığın merkezine yerleştirir; bu, dithering gibi işlemlerde sınır durumlarını basitleştirebilir, ancak 0 artık 0.0 olmadığı için işleme mantığı 8 bit girdiye bağlı hale gelir - 255 yönteminde uçlardaki aralıklar yarım genişliktedir; bu yüzden tekdüze
[0, 1]rastgele sayılar yeniden 8 bite yuvarlandığında 0 ve 255, diğer değerlerin yarısı sıklıkta ortaya çıkar; yine de gerçek görüntü ileri-geri dönüşümü kayıpsız çalışır - Başkasından gelen bir görseli işleyecekseniz doğru tercih 255 ile normalize etmek; yalnızca kaydetme ve yüklemeyi tamamen kontrol ettiğiniz durumlarda 256 yöntemi düşünülebilir
Problemin kurulumu
- Bir görüntüyü alıp kayan noktalıya dönüştüren, işledikten sonra tekrar 8 bit renk olarak kaydeden bir programda tamsayı-kayan nokta dönüşüm yöntemi tartışma konusudur
- İki yaklaşım vardır
- Standart yöntem (255’e bölme):
pixels = img / 255.0→ işleme →output = np.trunc(result * 255 + 0.5) - Alternatif yöntem (256’ya bölme):
pixels = (img + 0.5) / 256.0→ işleme →output = np.trunc(result * 256) - Her iki durumda da son tür dönüşümünden önce değerler 0~255 aralığına sınırlandırılır:
output.clip(0, 255).astype(np.uint8)
- Standart yöntem (255’e bölme):
- Standart yöntem, tamsayı 0’ı 0.0’a ve 255’i 1.0’a eşler; GPU’nun UNORM-to-float dönüşümü ile aynıdır
- Alternatif yöntem 0.5 bias ekler; böylece tamsayı 0,
0.5/256 = 0.001953125değerine eşlenir- Bunun sonucu olarak bu sabiti bilmiyorsanız siyah pikselleri tespit edemezsiniz
- Kayan noktalı hesap yapsanız bile mantık 8 bit girdiye bağlı kalır
- Standart yöntemde siyahın her zaman 0.0 olduğu varsayılabilir
255.0’a yönelik itirazlar
- Standart yöntem sayı doğrusu üzerinde çizildiğinde biraz tuhaf görünür
-
İki uçta daha küçük kutular (bin) vardır
- Standart formülün iki uçtaki kutuları [0,1] aralığının dışına taşar; aralık gerilmiş gibi görünür
- Kayan noktalıyı tekrar tamsayıya çevirirken uç kutuların genişliği diğer kutuların yalnızca yarısıdır
- Bu da algoritmanın uç değerleri üretmesini "daha zor" hale getirir
- Tekdüze [0,1] gürültüsü üretip standart formülle yuvarlarsanız 0 ve 255 değerleri, diğer tamsayılara göre yarı sıklıkta oluşur
- Bir milyon tekdüze rastgele sayının histogramında 0 ve 255 sütunlarının diğerlerinin yarı yüksekliğinde olduğu görülebilir
- Yine de uç değerlerden kaçınma yanlılığının pratikte sorun olduğu bir durum düşünmek zordur
- Orijinal görüntü yine kayıpsız biçimde ileri-geri dönüştürülebilir (
uint8 → float → uint8) - 0.0 veya 1.0’ın biraz dışına çıkan sonuçlar da doğru kutuya yuvarlanır, böylece çıktı dağılımı dengelenir
- Örneğin işleme sırasında renkten 0.005 çıkarılırsa standart yöntemde siyah 0’ın altına iner, alternatif yöntemde pozitif kalır; ancak iki yöntemde de sonuçta tamsayı 0 üretilir
- Orijinal görüntü yine kayıpsız biçimde ileri-geri dönüştürülebilir (
-
Kesin olmama
- Standart yöntemde kayan noktalı değerler tam değildir; örneğin
128/255.0 ≈ 0.501961iken128/256.0 = 0.5 - Yuvarlama hatası nedeniyle kayan noktalı değerler arasındaki mesafe çok küçük ölçüde değişir, ancak hata son derece küçüktür ve pratikte sorun yaratmaz
- 32 bit kayan nokta 23 bit mantisaya sahiptir ve hata en düşük anlamlı bit düzeyindedir;
2⁻²³’ten küçüktür - %0.00001 düzeyindeki göreli hata, hassas görüntü işlemede bile anlamsızdır; bu kesin olmama teknik değil, estetik bir meseledir
- 32 bit kayan nokta 23 bit mantisaya sahiptir ve hata en düşük anlamlı bit düzeyindedir;
- Standart yöntemde kayan noktalı değerler tam değildir; örneğin
-
Tamsayı aralıklarına denk düşmeyen değerler
- Alternatif yöntem, her kayan noktalı değeri iki tamsayının tam ortasına yerleştirir
- Asıl niceleme değeri bilinmediğinden, art arda gelen iki tamsayının orta noktası iyi bir tahmin olarak görülebilir
- Bunun dithering için daha elverişli olduğu da öne sürülür (Andrew Kesler’ın 2015 tarihli blog yazısı "Converting Color Depth")
- Sınır durumlarıyla uğraşmadan gürültü eklenebilir
- Buna karşılık standart formüldeki tuhaf uç değerler, gürültü dağılımının tutarlı kalması için daha dikkatli işlem gerektirir
- Alternatif yöntem, her kayan noktalı değeri iki tamsayının tam ortasına yerleştirir
İki tür niceleyici
- Bu iki yaklaşım, tekdüze skaler niceleyici (uniform scalar quantizer) türlerinden ikisi olarak görülebilir
- Wikipedia niceleme maddesine göre işaretli giriş verileri için tekdüze niceleyiciler iki türe ayrılır
- mid-tread: 0’ı, yeniden oluşturulan 0 değer seviyesine eşler (basamağın düz kısmı)
- mid-riser: 0’ı, 0 değerinin sınıflandırma eşiğine eşler (basamağın dik yüzü)
- Wikipedia kaynak olarak Allen Gresho’nun 1977 tarihli "Quantization" makalesini gösterir
- Niceleyici formülleri (L çıktı seviyesi sayısıdır; örneğin 256)
- mid-tread basamak niceleyici: kodlama
k = trunc(xL + 0.5), çözmeyₖ = k/L - mid-riser basamak niceleyici: kodlama
k = trunc(xL), çözmeyₖ = (k+0.5)/L
- mid-tread basamak niceleyici: kodlama
- Bu iki yaklaşıma uygulandığında
- Standart formül = mid-tread (L=255)
- Alternatif formül = mid-riser (L=256)
- Standart yöntem, işaretsiz girdide mid-tread kullanırken L=255 kodunu seçen bir kombinasyondur; 8 bit giriş için optimal değildir
- Bunun nedeni 0.0 ve 1.0 uç noktalarını doğrudan eşlemenin programlama açısından pratik olmasıdır
-
Daha yüksek niceleme hatası, ama pratikte değil
- Eğer sisteminiz tekdüze dağılmış
x∈\[0,1\]gerçek sayıları 8 bit tamsayıya kodlayıp tekrar gerçeğe dönüştürüyor olsaydı, standart formül bant genişliği israfı olurdu- Standart yöntemin temsil aralığı
[-0.5/255, 255.5/255]olduğundan, [0,1] girdisi için gerekenden geniştir ve yeniden oluşturma hatasını artırır - StackOverflow kullanıcısı Peter Mudrievskij’in hesabına göre ortalama mutlak hata 255 böleni için
1/1020, 256 böleni için1/1024olur; yani teoride 256’ya bölmek biraz daha hassastır
- Standart yöntemin temsil aralığı
- Ancak pratikte yapılan şey bu değildir
- Varsayım, 8 bit RGB görüntüyü yükleyip işledikten sonra tekrar kaydetmektir; kaydederken niceleme yöntemini kontrol edemeyebilirsiniz ve kaybolan bilgi kalıcı olarak yok olur
- Görüntü standart formülle çarpılıp yuvarlanarak kaydedildiyse, yüklerken 256’ya bölmek bu hassasiyeti geri getirmez
- Daha düşük yeniden oluşturma hatası argümanı yalnızca kaydetme ve yüklemeyi birlikte kontrol ettiğinizde anlamlıdır
- Başkasının görüntüsünü alternatif formülle yüklemek ise daha fazla hata üretebilir
- Çoğu görüntü muhtemelen standart formülle nicelemendiği için yanlış ölçekle çözmek teorik olarak daha hatalıdır
- Pratikte ise renkler mutlak ölçümler olmadığından, bu durum yalnızca biraz daha dar aralıkta küçük bir ofsetle işlem yapmak anlamına gelir
- Bu iki niceleyicinin kodlama ve çözme adımları karıştırılmamalıdır; aksi halde kolayca bozuk kod ortaya çıkar
- Eğer sisteminiz tekdüze dağılmış
Sonuç
- Tanımadığınız birinden gelen bir görüntüyü işleyecekseniz RGB değerlerini 255 ile normalize etmelisiniz
- Kayan noktalı değerlerin tam olmaması ya da soyut yeniden oluşturma hatası kaygıları, alternatifi seçmek için iyi nedenler değildir
- Görüntünün hem kaydını hem yüklenmesini tamamen kontrol ediyor, 0’ı 0’a eşlemeniz gerekmiyor ve işleme kodunun 8 bit dinamik aralığa bağlı olmasını sorun etmiyorsanız 256’ya bölerek çok az daha yüksek hassasiyet elde edebilirsiniz
- Ancak bir ekip arkadaşının görüntüyü standart formülle yükleyip planı bozabileceğini unutmayın
Diğer görüşler
- Jonathan Blow’un 2002 tarihli yazısı, mid-riser ve mid-tread niceleyicileri ad vermeden ele alır; diyagram fikrinin kaynağıdır
- Andrew Kesler’ın 2015 tarihli blog yazısı, alternatif formülü savunur
- Ancak karşılaştırma yuvarlamasız standart formülle yapıldığı için analizin büyük bölümü geçersiz kalır
2 yorum
Hacker News yorumları
Renk değerinin tam olarak ne anlama geldiği, bileşen başına 8 bit kullanımında çoğu zaman pek mesele olmaz. Paydanın 255 mi 256 mı olmasıyla oluşan hata çok küçüktür; farkı görebilmek için hem renk algınızın iyi olması hem de ekrana çok yaklaşmanız gerekir, ayrıca monitörler ve telefon ekranları da genelde kalibre edilmemiştir
Ama mikrodenetleyiciyle VGA sinyali üretip renk çıkış pinleriniz sadece 8 adetse (kırmızı 3, yeşil 3, mavi 2), iş epey can sıkıcı hale gelir. Bu durumda renk değeri, VGA monitörüne gönderilmesi gereken 0V~0.7V gerilim seviyesinin ta kendisidir
Mavi kanal 0→0V, 1→0.23V, 2→0.47V, 3→0.7V olarak eşlenir; kırmızı/yeşil ise 0→0V, 1→0.1V, …, 7→0.7V olur. Uç noktalar dışında mavi gerilimleri kırmızı/yeşil gerilimlerle hiç çakışmadığı için saf gri görmek mümkün olmaz; en yakın renk bile farkın yönüne göre hafif mavi ya da sarımsı görünür
Dahası, maviyi diğer kanallarla karıştıran neredeyse tüm gradyanlar da kaymış görünür. Örneğin saf kırmızıdan saf beyaza giden çizgi üzerindeki en yakın renkler biraz turuncu ya da morumsu görünebilir
Raspberry Pi Pico 2 üzerinde çift tamponlu 320x240 framebuffer ile 8 bit renkli VGA çıkışı üreten kod burada: https://github.com/moefh/pico-vga-8bit-demo
Bu durumda küçük ve büyük değerler arasındaki fark çok daha belirgin hale gelir: 2^2.2 = 4.595, 255^2.2 = 196,964.699
Eğer 30Hz'de değişiyorsa, insanların hafif mavimsi ile hafif sarımsı tonlar arasındaki farkı ayırt etmesi zor olur gibi geliyor
255 lehine bir argüman olarak, siyah-beyaz görüntü gibi uç bir örneğe bakabilirsiniz. Tek bitte 0 siyahtır, 1 beyazdır
0'ın 0.0'a, 1'in de 1.0'a eşlenmesi gerektiği oldukça açıktır. Sonuçta bu siyah-beyazdır; açık gri (0.25) ve koyu gri (0.75) değildir. Yani siyah-beyaz görüntü 2'ye değil 1'e göre normalize edilir
2 bit olsaydı genelde 0=siyah, 1=açık gri, 2=koyu gri, 3=beyaz olurdu; dolayısıyla bunu 0.0, 0.33, 0.66, 1.0'a eşlemek doğaldır. Siyah siyah, beyaz beyaz kalmalı ve aralıklar da eşit olmalıdır; bu yüzden 3'e göre normalize edilir
Bu mantığı 8 bite kadar sürdürünce 255'e göre normalizasyon elde edilir. 8 bitte fark çok küçük olsa bile siyahın 0.0, beyazın 1.0 olması gerekir
8 bitte 256'ya göre normalize etmek gibi alternatif yaklaşımda ise çıkış aralığı bit sayısına göre değişir. 1 bit için [0.25, 0.75], 2 bit için [0.125, 0.875] gibi olur. Genelde istenen şey, bit sayısı arttıkça ton nüanslarının artmasıdır; kontrastın değişmesi değil
Gerçekten düşündürücü bir yazıydı; kişisel olarak benimsediğim bazı varsayımları yeniden sorgulamama neden oldu
Elektrik mühendisliği perspektifinden bakınca, yazıdaki “iki tür niceleyici” sunumuna katılmak zor. Matematiksel olarak titiz olabilir ama gerçek sistemleri temel alan bir açıklama değil
ADC'lerde doğası gereği her zaman ±1/2 LSB niceleme belirsizliği vardır. Aktarım karakteristiği her zaman mid-tread örneklemedir; en azından ben aksini gösteren bir örnek görmedim. Bu, bipolar ADC için de unipolar ADC için de geçerlidir
En düşük kod negatif referans gerilimine, en yüksek kod ise pozitif referans gerilimine karşılık gelir. Aktarım karakteristiği grafiği, yazıda gösterildiği gibi en üst ve en alt aralıkların fiilen 1/2 LSB genişliğinde olduğunu gösterir
Unipolar sistemlerde ara gerilimler tam olarak temsil edilemez; başka bir deyişle gri problemi ortaya çıkar. Bipolar sistemlerde 0V, mid-tread'in N/2 değerine denk gelir ama bu, “256 aralık” olduğu anlamına gelmez
Bu yüzden ben yine de (VREF+ - VREF-) * k / (2^N - 1) kullanırım. Yani 255 normalizasyonuna katılıyorum. Sonuçta bu bir çit direği hatası gibi: değer sayısı N ama aralık sayısı N-1. Değerlerden daha az aralık varsa, bir aralığı iki değer arasında paylaşmak gerekir; bu yüzden uçlarda 1/2 LSB'lik aralık oluşur
126'dan 127'ye geçiş, pozitif tam ölçeğin 1.5 LSB altında gerçekleşir. 1 LSB farkı 2/255=0.00784V değil, 1/128=0.00781V fark anlamına gelir
Ama pratikte gerilim ve belirsizlik önemliyse, bu farkların çoğu zaman pek önemi yoktur. Referans geriliminde bias vardır ve doğrusal olmama hataları da bulunur. 1 LSB ne tam olarak 1/128'e ne de 2/255'e denk gelir; kalibrasyon için ek parametreler gerekir
Bu, bilimsel hesaplamada sözü edilen düğüm merkezli örnekleme ile hücre merkezli örnekleme farkının 1 boyutlu bir versiyonu gibi. Değerin aralığın ortasında mı (ya da üçgen/tetrahedron merkezinde mi), yoksa aralık sınırında mı (ya da üçgen/tetrahedron köşesinde mi) bulunduğuna karar vermek gerekir
Bilimsel hesaplamada, değerleri nasıl yorumlamak gerektiğini bilmeden veri işlemeye başlamak anlamsızdır. Ses sinyali işlemede de, elinizde sadece bir tamsayı akışı varsa, orijinal sinyal üzerinde hesap yapabilmek için bu tamsayıların hangi temsil niyetiyle verildiğini — örneğin mu-law kodlaması mı yoksa lineer mi olduğunu — bilmeniz gerekir. Buna dair cevabı, değere eşlik eden metadata'nın vermesi beklenir
Ama 8 bit piksel değerlerinde, temsil niyetini aktaracak düzgün bir dosya formatı metadata'sı yoksa ortada kalırsınız ve tek bir doğru cevap da olmaz. Yazarın dediği gibi kendi kullanımında daha iyi sonuç veren yaklaşımı seçti diye onu eleştirmek doğru olmaz; ama bağlamdan kopuk bitlerin anlamı bozduğunu söylemek mümkündür
Yaklaşık olarak şöyle: Digital Number DN=0, “NO_DATA” değeri olarak ayrılır; DN [1; 1;215-1] aralığındayken L2A SR yansıtırlık değeri L2A_SRi = (L2A_DNi + BOA_ADD_OFFSETi) / QUANTIFICATION_VALUE olur
https://sentiwiki.copernicus.eu/web/s2-products
Burada 0'dan 255'e kadar 256 seviye olduğunu varsayan bir hata var. Aslında 8 bit ile ifade edilebilen 256 değer vardır ve 0 (siyah) ile 255 (saf beyaz) arasında 255 aralık bulunur
Bu yüzden 255'e bölmek sorun değildir. Elbette 128 tam olarak orta gri değildir ve 0~255 aralığındaki nicemlenmiş 8 bit değerler neredeyse her zaman doğrusal algısal uzayda değil sRGB'dedir
Modern API'lerde örnekleme konumları ele alınırken de benzer bir karışıklık ortaya çıkar. Çünkü konumlar piksel merkezi olarak değil koordinat olarak belirtilir
Cebirsel olarak bakıldığında cevap açıkça f(x) -> [0, 255]'tir
f(n * 0) == n * f(0) sağlanmıyorsa tuhaf şeyler olur. Örneğin f(x) -> [0, 255] ise f(0) + f(0) + f(0) = 0 + 0 + 0 = 0 = f(0) olur
Buna karşılık f(x) -> [0.5/8, 7.5/8] ise f(0) + f(0) + f(0) = 0.5/8 + 0.5/8 + 0.5/8 = 1.5/8 != f(0) olur
İkincisini seçerseniz, x tarafında yapılan bir hesapla f(x) tarafında yapılan bir hesabın birbiriyle tutarlı olmasını bekleyemezsiniz. Yani cebirsel karşılık bozulur
+0.5 çözümünü desteklemek istiyorum. Birincisi, kenarlardaki yarım boyutlu aralıkları sevmiyorum; ikincisi, 255 tabanlı gösterim genelde HDR değil, SDR görüntü içindir
RGB değerleri, belirli bir adaptasyon durumuna göre parlaklığı temsil eder; gündüz sahnesindeki “0”, “parlaklık 0” değildir. Yalnızca en parlak noktanın yaklaşık 0.001 katıdır ve foton sayısı yüz binlerce, hatta milyonlarca olduğu için 0'dan çok büyüktür
Bir bakıma göz, kontrastı kayan bir ölçek üzerinde deneyimler ve sistem içinde mutlak bir 0 yoktur. Örneğin yayın sistemleri tarihsel olarak SDR parlaklık aralığı için 16~235 kullandı. “Mutlaka 0 olmalı” mantığının yanlılık ürettiğini düşünüyorum ve çoğu durumda 0'a gerek olmadığını savunuyorum
Ayrıca görüntü işleme ve compositing iş akışlarının önemli bir kısmı, doğru ya da yanlış, 0'ın 0 anlamına geldiğini varsayar. Bu yüzden 8 bit'te 0u'nun 0.0f'ye, 255'in de 1.0f'ye eşlendiği kabul edilir. Maske ya da alfa değerinde 0 değeri 0.0'ı az da olsa aşarsa, bir yerlerdeki kod 0.0 sabit eşiğiyle başka işlemleri maskeleyip artefaktlar üretir. Tersine, alfada 255 artık 1.0f değilse, premultiply sonrasında nesne çok hafif şeffaflaşır
Aynı şey, +0.5 yüzünden maskelemede 254'ün 1.0f olması durumunda da yaşanabilir
Kilit nokta 0 fotonu temsil edip etmemek değil, 1 bayta sığan bilgiyi en üst düzeye çıkarmaktır. İdeal olarak bayt değeri 0 daha az kullanılmamalı ve 0. kovaya düşmesi gereken verilere bias eklenmemelidir. Parlak ile çok parlak arasında giden bir renk uzayı olsa bile, tüm baytlar parlaklık aralığının eşit büyüklükte parçalarını temsil etmelidir
Cetvel 12 inçe kadar gidiyorsa, üzerindeki nokta sayısı olan 13'e değil, uzunluk L'ye göre normalize etmelisiniz
>> 8çok daha hızlıUzun zamandır düşünmediğim bir konuyu ele aldığı için okuması keyifli bir yazıydı. Oyun geliştirmede oyun mantığı kayan noktalı matematik kullanırken, pixel art'ı tamsayı koordinatlarına çizmek zorunda kaldığımız anları hatırlattı
Birkaç yerde +0.5'e benzer yöntemler kullanarak işleri daha az tuhaf göstermeye çalışmıştım. Özellikle hareketli kamera varken böyleydi; kamerayı da kırpmak gerekiyordu
Aşağıda bağlantısı verilen Jonathan Blow'un 2002 tarihli yazısı [1] da ilginçti. İlk yazıdaki görselleştirme, konuya daha derin indikçe çok yardımcı oluyor
[1] https://web.archive.org/web/20240706043551/https://number-no...
Lobste.rs görüşleri
Sezgisel gelmiyorsa bunu 2 bitlik dejenere bir örnek üzerinden düşünebilirsiniz. Olası tamsayı değerleri yalnızca 0, 1, 2, 3 olduğunda tamsayı→kayan nokta dönüşümünü tamamen hesaplarsanız, siyah/beyazın gerçekten siyah/beyaz olmaması ya da aralıkların bariz biçimde eşit dağılmaması gibi tuhaf davranışlardan kaçınmak için sonuç 0.0, 0.33..., 0.66..., 1.0 olur
Dolayısıyla ters dönüşüm de 4(2^2) ile değil, 3 ile çarpma şeklinde olur
Ters dönüşümde kuantalama (yuvarlama) gerekir ve simetriyi bozan kritik nokta tam da budur
0..=1 aralığında eşit bir gerçek sayı gradyanı oluşturup bunu 0, 1, 2, 3'e kuantalarsanız, 3 ile çarpmanın eşit sonuç vermediğini görürsünüz. ×3 sonrası
round()1 ve 2'yi aşırı temsil eder; ×3 sonrasıfloorya daceilise 0 veya 3'ü tekillik gibi içeri katlayarak gradyanın 4 renkten yalnızca 3'ünü kullanıyormuş gibi görünmesine yol açar/3ve×3mantığı, tam sayıları gidiş-dönüş çevirmek için kulağa doğru geliyor olabilir; ama ara değerler yuvarlama seçimine büyük ölçüde bağlıdır ve veri işlemeye başladığınız anda bu önemli hâle gelirTamsayı oranlarının eşit olması ancak (4-ε) ile çarpıp aşağı yuvarladığınızda gerçekleşir; bu da ×4,
floor(),clamp()ile aynıdır. Garip bir 1 farkı ya da ε farkı hatası gibi hissettirse de sezgisel olarak en iyi görünen çözüm budurBenim için cevap her zaman “elbette” [0.0..255.0] olmuştu, ama görünüşe göre bu herkes için o kadar da bariz değil
Yazıda “uç” aralıkların diğer aralıkların yalnızca yarısı kadar kapasiteye sahip olduğu söyleniyor; bence bu çerçeveleme de doğru değil
Eğer [0..1] dışında değer yoksa, bunun daha dar bir aralık gibi görünmesi render etmenin bir yan ürünü. Kovaları, aralığın dışında değer olmadığını bilerek kestiğiniz için sadece daha dar render ediliyor
Tersine, eğer [0..1] dışında değerler varsa bu aralık sonsuzdur. Yazı ikincisini kabul ediyor ama birincisini etmiyor
Birincisini kabul ettiğiniz anda doğru davranış oldukça açık görünüyor; ama böyle bir yazının ortaya çıkmış olması bile bunun nesnel olarak o kadar “açık” bir mesele olmadığını gösteriyor :D
0..<1 tamsayı 0'a gidiyor, 254>..255.0 da tamsayı 255'e gidiyor derseniz 128 arada kaybolur. Muhtemelen 127.5..128.5 aralığının 128'e gitmesini istersiniz; peki o zaman bu yarımlar nereye gitmeli?
128'i doğru yere koymak için tüm aralığı biraz kaydırırsanız, 0..0.99609375 tamsayı 0'a eşlenmiş olur
round()çağırmasından doğmuş gibi görünüyorİnsanlara bu yöntem oldukça doğal geldiği için, sadeliği yüzünden standart hâline gelmiş gibi
pngcrushile sıkıştırmıştım. Yoksa görsel içeriğinde bir sorun mu olduğunu söylüyorsunuz?