Apa cara yang tepat untuk menjepit kebisingan?

9

Ketika mengurangi kedalaman warna dan dithering dengan noise 2 bit (dengan n =] 0,5,1.5 [dan output = lantai (input * (2 ^ bit-1) + n)), ujung rentang nilai (input 0,0 dan 1,0 ) berisik. Akan diinginkan untuk memilikinya menjadi warna solid.

Contoh: https://www.shadertoy.com/view/llsfz4

Gradien kebisingan (di atas adalah tangkapan layar shadertoy, menggambarkan gradien dan kedua ujungnya yang masing-masing harus solid putih dan hitam, tetapi sebaliknya berisik)

Masalahnya tentu saja dapat diselesaikan dengan hanya mengompresi rentang nilai sehingga ujungnya selalu dibulatkan menjadi nilai tunggal. Ini terasa sedikit hack, dan saya bertanya-tanya apakah ada cara untuk menerapkan ini "dengan benar"?

hotmultimedia
sumber
Untuk beberapa alasan shadertoy tidak berjalan di browser saya. Bisakah Anda memposting gambar sederhana untuk menunjukkan apa yang Anda maksud?
Simon F
1
Bukankah lebih seperti n =] - 1, 1 [?
JarkkoL
@JarkkoL Nah rumus untuk mengubah floating point ke integer adalah output = floor (input * intmax + n), di mana n = 0,5 tanpa noise, karena Anda ingin (misalnya)> = 0,5 untuk mengumpulkan, tetapi <0,5 turun. Itu sebabnya suara "terpusat" di 0,5.
hotmultimedia
@SimonF menambahkan gambar shadertoy
hotmultimedia
1
Tampaknya Anda memotong output daripada membulatkannya (seperti GPU lakukan) - sebaliknya, setidaknya Anda mendapatkan putih yang tepat: shadertoy.com/view/MlsfD7 (image: i.stack.imgur.com/kxQWl.png )
Mikkel Gjoel

Jawaban:

8

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:

output - 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).

masukkan deskripsi gambar di sini

Melihat grafik memberikan sedikit lebih banyak wawasan:

masukkan deskripsi gambar di sini (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):

masukkan deskripsi gambar di sini

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 ).

masukkan deskripsi gambar di sini

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:

masukkan deskripsi gambar di sini

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: masukkan deskripsi gambar di sini

Referensi

Mikkel Gjoel
sumber
Sungguh menakjubkan bahwa lerp di tepinya memberikan hasil yang terlihat sempurna. Saya harapkan setidaknya ada sedikit penyimpangan: P
Alan Wolfe
Ya, saya juga sangat terkejut :) Saya percaya ini bekerja karena kita mengurangi magnitudo secara linear, pada laju yang sama sinyalnya menurun. Jadi setidaknya skalanya cocok ... tapi saya setuju bahwa menarik bahwa secara langsung pencampuran distribusi tampaknya tidak memiliki efek samping negatif.
Mikkel Gjoel
@MikkelGjoel Sayangnya, kepercayaan Anda salah karena bug pada kode Anda. Anda menggunakan kembali RNG yang sama untuk keduanya dithertridan dithernormbukannya yang independen. Setelah Anda mengerjakan semua matematika dan membatalkan semua persyaratan, Anda akan menemukan bahwa Anda sama sekali tidak tertipu! Sebaliknya, kode bertindak seperti cutoff keras v < 0.5 / depth || v > 1 - 0.5/depth, langsung beralih ke distribusi seragam di sana. Bukan berarti itu menghilangkan dithering bagus yang Anda miliki, itu hanya rumit. Memperbaiki bug sebenarnya buruk, Anda akan berakhir dengan kehancuran yang lebih buruk. Cukup gunakan cutoff keras.
orlp
Setelah menggali lebih dalam, saya telah menemukan masalah lain di shadertoy Anda di mana Anda tidak melakukan koreksi gamma saat rata-rata sampel (Anda rata-rata di ruang sRGB yang tidak linier). Jika Anda menangani gamma dengan tepat, kami menemukan bahwa sayangnya kami belum selesai. Kita harus membentuk suara kita untuk menangani koreksi gamma. Berikut adalah shadertoy yang menampilkan masalah: shadertoy.com/view/3tf3Dn . Saya sudah mencoba banyak hal dan tidak bisa membuatnya bekerja, jadi saya memposting pertanyaan di sini: computergraphics.stackexchange.com/questions/8793/… .
orlp
3

Saya tidak yakin saya bisa sepenuhnya menjawab pertanyaan Anda, tetapi saya akan menambahkan beberapa pemikiran dan mungkin kita bisa sampai pada jawaban bersama :)

Pertama, dasar dari pertanyaan ini agak tidak jelas bagi saya: Mengapa Anda menganggap perlu untuk membersihkan hitam / putih ketika setiap warna lain memiliki noise? Hasil ideal setelah dithering adalah sinyal asli Anda dengan noise yang sepenuhnya seragam. Jika hitam dan putih berbeda, noise Anda menjadi bergantung pada sinyal (yang mungkin baik-baik saja, karena itu terjadi di mana warna tetap dijepit).

Yang mengatakan, ada beberapa situasi, di mana memiliki kebisingan baik putih atau hitam memang menimbulkan masalah (saya tidak mengetahui penggunaan kata-kata yang mengharuskan hitam dan putih secara bersamaan menjadi "bersih"): Ketika membuat partikel campuran aditif sebagai quad dengan tekstur, Anda tidak ingin noise ditambahkan di seluruh quad, karena itu akan menunjukkan tekstur di luar juga. Salah satu solusinya adalah dengan mengimbangi kebisingan, jadi daripada menambahkan [-0,5; 1,5 [Anda menambahkan [-2,0; 0,0 [(yaitu kurangi 2 bit noise). Ini merupakan solusi empiris, tetapi saya tidak mengetahui pendekatan yang lebih tepat. Berpikir tentang itu, Anda mungkin juga ingin meningkatkan sinyal Anda untuk mengimbangi kehilangan intensitas ...

Agak terkait, Timothy Lottes melakukan pembicaraan GDC pada pembentukan suara ke bagian dari spektrum di mana hal ini sangat diperlukan, mengurangi kebisingan di akhir terang spektrum: http://32ipi028l5q82yhj72224m8j-wpengine.netdna-ssl.com/wp- konten / unggah / 2016/03 / GdcVdrLottes.pdf

Mikkel Gjoel
sumber
(maaf saya menekan enter secara tidak sengaja, dan mengedit batas waktu kadaluwarsa) Penggunaan kata dalam contoh adalah salah satu situasi di mana itu akan menjadi masalah: rendering gambar skala abu-abu mengambang pada perangkat tampilan 3-bit. Di sini intensitasnya sangat berubah dengan hanya mengubah LSB. Saya mencoba memahami jika ada "cara yang benar" untuk memiliki nilai akhir dipetakan ke warna solid, seperti mengompresi rentang nilai dan memiliki nilai akhir jenuh. Dan apa penjelasan matematisnya? Dalam rumus contoh, nilai input 1.0 tidak menghasilkan output yang rata-rata ke 7, dan itulah yang mengganggu saya.
hotmultimedia
1

Saya telah menyederhanakan ide Mikkel Gjoel tentang dithering dengan noise segitiga menjadi fungsi sederhana yang hanya membutuhkan satu panggilan RNG. Saya telah menghapus semua bit yang tidak perlu sehingga harus mudah dibaca dan dimengerti apa yang terjadi:

// Dithers and quantizes color value c in [0, 1] to the given color depth.
// It's expected that rng contains a uniform random variable on [0, 1].
uint dither_quantize(float c, uint depth, float rng) {
    float cmax = float(depth) - 1.0;
    float ci = c * cmax;

    float d;
    if (ci < 0.5 || ci >= cmax - 0.5) {
        // Uniform distribution on [-0.5, 0.5] for edges.
        d = rng - 0.5;
    } else {
        // Symmetric triangular distribution on [-1, 1].
        d = (rng < 0.5) ? sqrt(2.0 * rng) - 1.0 : 1.0 - sqrt(2.0 - 2.0*rng);
    }

    return uint(clamp(ci + d + 0.5, 0.0, cmax));
}

Untuk gagasan dan konteksnya, saya akan merujuk Anda ke jawaban Mikkel Gjoel.

orlp
sumber
0

Saya mengikuti tautan ke pertanyaan luar biasa ini bersama dengan contoh shadertoy.

Saya punya beberapa pertanyaan tentang solusi yang disarankan:

  1. Apa nilai v? Apakah ini sinyal RGB dalam kisaran 0,0 hingga 1,0 (hitam ke putih) yang ingin kami kuantifikasi? Apakah itu pelampung tunggal? (dan jika demikian, bagaimana Anda menghitungnya dari sinyal RGB asli?)
  2. Apa sumber "angka ajaib" 0,5 / 7,0? Saya berasumsi itu setengah bin, tapi saya berharap bahwa ukuran bin diwakili oleh 8 bit menjadi 1,0 / 255,0, jadi saya terkejut melihat 0,5 / 0,7. Apakah Anda keberatan menjelaskan bagaimana Anda memperoleh angka-angka ini. Apa yang saya lewatkan?
  3. Saya mengasumsikan distribusi segitiga berada dalam kisaran [-1,1] dan seragamnya berada di [-0,5,0,5] ("setengah bit" karena kami dekat ke tepi dan kami tidak ingin overshoot - apakah itu logika yang Anda terapkan?)
  4. Variabel acak seragam dan segitiga harus independen. Apakah saya benar?

Kerja bagus! Saya ingin melihat bahwa saya memahami garis pemikiran Anda dengan benar. Terima kasih!

zrizi
sumber