TL; DR: 2 * 1LSB istirahat dithering triangular-pdf dalam edgecases pada 0 dan 1 karena penjepitan. Solusinya adalah lerp ke seragam 1bit di edgecases tersebut.
Saya menambahkan jawaban kedua, mengingat ini ternyata sedikit lebih rumit dari yang saya kira. Tampaknya masalah ini telah menjadi "TODO: perlu dijepit?" dalam kode saya sejak saya beralih dari normalisasi menjadi dithering triangular ... pada 2012. Terasa senang akhirnya melihatnya :) Kode lengkap untuk solusi / gambar yang digunakan di seluruh pos: https://www.shadertoy.com/view/llXfzS
Pertama-tama, di sini adalah masalah yang sedang kita bahas, ketika menghitung sinyal menjadi 3bits dengan 2 * 1LSB dithering triangular-pdf:
- pada dasarnya apa yang ditampilkan hotmultimedia.
Semakin kontras, efek yang dijelaskan dalam pertanyaan menjadi jelas: Outputnya tidak rata-rata menjadi hitam / putih di edgecases (dan benar-benar melampaui 0/1 sebelum melakukannya).
Melihat grafik memberikan sedikit lebih banyak wawasan:
(garis abu-abu menandai 0/1, juga dalam abu-abu adalah sinyal yang kami coba untuk keluaran, garis kuning adalah rata-rata dari keluaran yang terkutuk / dikuantisasi, merah adalah kesalahan (rata-rata sinyal)).
Menariknya, tidak hanya output rata-rata tidak 0/1 pada batasnya, tetapi juga tidak linier (kemungkinan karena pdf segitiga dari kebisingan). Melihat ujung yang lebih rendah, masuk akal secara intuitif mengapa output menyimpang: Ketika sinyal ragu-ragu mulai memasukkan nilai-nilai negatif, clamping-on-output mengubah nilai dari bagian-bagian output yang berkerut rendah (yaitu nilai negatif), dengan demikian meningkatkan nilai rata-rata. Sebuah ilustrasi tampak berurutan (seragam, 2LSB simetris, rata-rata masih berwarna kuning):
Sekarang, jika kita hanya menggunakan 1LSB yang dinormalisasi, tidak ada masalah sama sekali, tapi tentu saja kita kehilangan sifat bagus dari dithering segitiga (lihat misalnya presentasi ini ).
Maka (hack) solusi (pragmatis, empiris), adalah kembali ke [-0,5; 0,5 [dithering seragam untuk edgecase:
float dithertri = (rnd.x + rnd.y - 1.0); //note: symmetric, triangular dither, [-1;1[
float dithernorm = rnd.x - 0.5; //note: symmetric, uniform dither [-0.5;0.5[
float sizt_lo = clamp( v/(0.5/7.0), 0.0, 1.0 );
float sizt_hi = 1.0 - clamp( (v-6.5/7.0)/(1.0-6.5/7.0), 0.0, 1.0 );
dither = lerp( dithernorm, dithertri, min(sizt_lo, sizt_hi) );
Yang memperbaiki edgecases sambil menjaga dithering segitiga tetap utuh untuk rentang yang tersisa:
Jadi untuk tidak menjawab pertanyaan Anda: Saya tidak tahu apakah ada solusi yang lebih kuat secara matematis, dan sama-sama tertarik untuk mengetahui apa yang telah dilakukan Masters of Past :) Sampai saat itu, setidaknya kami memiliki peretasan yang mengerikan ini untuk menjaga kode kami berfungsi.
EDIT
Saya mungkin harus membahas saran-solusi yang diberikan dalam Pertanyaan, pada hanya menekan sinyal. Karena rata-rata tidak linier dalam edgecases, cukup mengompresi sinyal input tidak menghasilkan hasil yang sempurna - meskipun itu memperbaiki titik akhir:
Referensi