Apa perbedaan praktis saat bekerja dengan warna dalam ruang RGB linier vs. non-linier?

90

Apa sifat dasar dari ruang RGB linier dan apa sifat dasar ruang RGB non-linier? Ketika berbicara tentang nilai di dalam setiap saluran dalam 8 (atau lebih) bit tersebut, apa yang berubah?

Di OpenGL, warna adalah nilai 3 + 1, dan dengan ini maksud saya RGB + alpha, dengan 8 bit dicadangkan untuk setiap saluran, dan ini adalah bagian yang saya dapatkan dengan jelas.

Tetapi ketika datang ke koreksi gamma, saya tidak mengerti apa efek bekerja di ruang RGB non-linier.

Karena saya tahu cara menggunakan kurva dalam perangkat lunak grafis untuk pengeditan foto, penjelasan saya adalah bahwa dalam ruang RGB linier Anda mengambil nilai apa adanya, tanpa manipulasi dan tidak ada fungsi matematika yang terpasang, alih-alih ketika itu non-linier masing-masing saluran biasanya berkembang mengikuti perilaku fungsi daya klasik.

Kalaupun saya menganggap penjelasan ini sebagai yang asli, saya masih belum mengerti apa itu ruang linier nyata, karena setelah perhitungan semua ruang RGB non-linier menjadi linier dan yang terpenting dari semua saya tidak mendapatkan bagian di mana non -Ruang warna linier lebih cocok untuk mata manusia karena pada akhirnya semua ruang RGB linier untuk apa yang saya pahami.

Ken
sumber
Pada catatan praktis, Anda dapat menentukan RGB linier atau standar sebagai ruang warna Anda di SVG dan saya tidak tahu apa efeknya, terlepas dari fakta bahwa ini tampaknya penting :-)
Michael Mullany
@MichaelMullany, hal linier ini lebih terlihat seperti petunjuk bagi pengguna daripada kualitas khas yang nyata.
Ken
Sesuatu yang membantu saya: Saya pikir itu adalah guru matematika yang mengatakan kepada saya "pikirkan baris ketika Anda mendengar linier". Tidak yakin apakah ini membantu, tetapi bagi saya, itu adalah "Oh ya!" momen
Joe Plante
Bacaan terkait tentang Gamma: Apakah saya perlu gamma mengoreksi keluaran warna akhir?
legends2k

Jawaban:

235

Katakanlah Anda bekerja dengan warna RGB: setiap warna diwakili dengan tiga intensitas atau kecerahan. Anda harus memilih antara "RGB linier" dan "sRGB". Untuk saat ini, kami akan menyederhanakan berbagai hal dengan mengabaikan tiga intensitas berbeda, dan menganggap Anda hanya memiliki satu intensitas: yaitu, Anda hanya berurusan dengan bayangan abu-abu.

Dalam ruang warna linier, hubungan antara angka yang Anda simpan dan intensitas yang direpresentasikannya adalah linier. Secara praktis, ini berarti bahwa jika Anda menggandakan angkanya, Anda menggandakan intensitas (kecerahan abu-abu). Jika Anda ingin menambahkan dua intensitas bersamaan (karena Anda menghitung intensitas berdasarkan kontribusi dua sumber cahaya, atau karena Anda menambahkan objek transparan di atas objek buram), Anda dapat melakukannya hanya dengan menambahkan dua angka bersama. Jika Anda melakukan pencampuran 2D atau bayangan 3D, atau hampir semua pemrosesan gambar, Anda ingin intensitas Anda dalam ruang warna linier., jadi Anda bisa menambah, mengurangi, mengalikan, dan membagi angka untuk mendapatkan efek yang sama pada intensitas. Sebagian besar algoritme pemrosesan dan rendering warna hanya memberikan hasil yang benar dengan RGB linier, kecuali Anda menambahkan bobot ekstra pada semuanya.

Kedengarannya sangat mudah, tapi ada masalah. Kepekaan mata manusia terhadap cahaya lebih baik pada intensitas rendah daripada intensitas tinggi. Artinya, jika Anda membuat daftar semua intensitas yang dapat Anda bedakan, ada lebih banyak intensitas gelap daripada yang terang. Dengan kata lain, Anda dapat membedakan warna abu-abu gelap lebih baik daripada warna abu-abu terang. Secara khusus, jika Anda menggunakan 8 bit untuk mewakili intensitas Anda, dan Anda melakukan ini dalam ruang warna linier, Anda akan mendapatkan terlalu banyak bayangan terang, dan tidak cukup bayangan gelap. Anda mendapatkan garis melintang di area gelap Anda, sementara di area terang Anda, Anda membuang-buang bit pada berbagai nuansa hampir putih yang tidak dapat dibedakan oleh pengguna.

Untuk menghindari masalah ini, dan memanfaatkan 8 bit tersebut dengan sebaik-baiknya, kami cenderung menggunakan sRGB . Standar sRGB memberi tahu Anda kurva yang akan digunakan, untuk membuat warna Anda non-linier. Kurva lebih dangkal di bagian bawah, sehingga Anda dapat memiliki lebih banyak abu-abu gelap, dan lebih curam di bagian atas, sehingga Anda memiliki lebih sedikit abu-abu terang. Jika Anda menggandakan angkanya, Anda akan menggandakan intensitasnya. Ini berarti bahwa jika Anda menambahkan warna sRGB bersamaan, Anda akan mendapatkan hasil yang lebih terang dari yang seharusnya. Saat ini, sebagian besar monitor menafsirkan warna input mereka sebagai sRGB. Jadi, saat Anda meletakkan warna di layar, atau menyimpannya dalam tekstur 8-bit-per-saluran, simpan sebagai sRGB , sehingga Anda dapat memanfaatkan 8 bit tersebut dengan sebaik-baiknya.

Anda akan melihat kami sekarang memiliki masalah: kami ingin warna kami diproses dalam ruang linier, tetapi disimpan dalam sRGB. Ini berarti Anda akhirnya melakukan konversi sRGB-ke-linier saat dibaca, dan konversi linier-ke-sRGB saat tulis. Seperti yang telah kita katakan bahwa intensitas 8-bit linier tidak memiliki cukup gelap, ini akan menimbulkan masalah, jadi ada satu aturan praktis lagi: jangan gunakan warna linier 8-bit jika Anda dapat menghindarinya. Menjadi konvensional untuk mengikuti aturan bahwa warna 8-bit selalu sRGB, jadi Anda melakukan konversi sRGB-ke-linier pada saat yang sama dengan memperluas intensitas Anda dari 8 menjadi 16 bit, atau dari integer ke floating-point; demikian pula, setelah Anda menyelesaikan pemrosesan floating-point, Anda mempersempit menjadi 8 bit pada saat yang sama dengan mengonversi ke sRGB. Jika Anda mengikuti aturan ini,

Saat Anda membaca gambar sRGB, dan Anda menginginkan intensitas linier, terapkan rumus ini ke setiap intensitas:

float s = read_channel();
float linear;
if (s <= 0.04045) linear = s / 12.92;
else linear = pow((s + 0.055) / 1.055, 2.4);

Sebaliknya, saat Anda ingin menulis gambar sebagai sRGB, terapkan rumus ini ke setiap intensitas linier:

float linear = do_processing();
float s;
if (linear <= 0.0031308) s = linear * 12.92;
else s = 1.055 * pow(linear, 1.0/2.4) - 0.055; ( Edited: The previous version is -0.55 )

Dalam kedua kasus, nilai floating-point berkisar dari 0 hingga 1, jadi jika Anda membaca bilangan bulat 8-bit, Anda ingin membagi dengan 255 terlebih dahulu, dan jika Anda menulis bilangan bulat 8-bit, Anda ingin mengalikan dengan 255 terakhir, dengan cara yang sama seperti biasanya. Itu saja yang perlu Anda ketahui untuk bekerja dengan sRGB.

Sampai sekarang, saya hanya berurusan dengan satu intensitas, tetapi ada hal-hal cerdas yang berkaitan dengan warna. Mata manusia dapat membedakan kecerahan yang berbeda dengan lebih baik daripada warna yang berbeda (secara teknis, ini memiliki resolusi luminansi yang lebih baik daripada chrominance), sehingga Anda dapat menggunakan 24 bit Anda dengan lebih baik dengan menyimpan kecerahan secara terpisah dari warnanya. Inilah yang coba dilakukan oleh representasi YUV, YCrCb, dll. Saluran Y adalah kecerahan warna keseluruhan, dan menggunakan lebih banyak bit (atau memiliki resolusi spasial lebih banyak) daripada dua saluran lainnya. Dengan cara ini, Anda tidak (selalu) perlu menerapkan kurva seperti yang Anda lakukan dengan intensitas RGB. YUV adalah ruang warna linier, jadi jika Anda menggandakan angka di saluran Y, Anda menggandakan kecerahan warna, tetapi Anda tidak dapat menambahkan atau mengalikan warna YUV bersama-sama seperti yang Anda bisa dengan warna RGB, jadi '

Saya pikir itu menjawab pertanyaan Anda, jadi saya akan mengakhiri dengan catatan sejarah singkat. Sebelum sRGB, CRT lama biasanya memiliki non-linearitas yang tertanam di dalamnya. Jika Anda menggandakan tegangan untuk satu piksel, Anda akan melipatgandakan intensitas. Berapa banyak lagi yang berbeda untuk setiap monitor, dan parameter ini disebut gamma . Perilaku ini berguna karena itu berarti Anda bisa mendapatkan lebih banyak kegelapan daripada cahaya, tetapi itu juga berarti Anda tidak dapat mengetahui seberapa terang warna Anda pada CRT pengguna, kecuali Anda mengkalibrasi terlebih dahulu. Koreksi gammaberarti mengubah warna yang Anda mulai (mungkin linier) dan mengubahnya untuk gamma CRT pengguna. OpenGL berasal dari era ini, itulah sebabnya perilaku sRGB-nya terkadang sedikit membingungkan. Tetapi vendor GPU sekarang cenderung bekerja dengan konvensi yang saya jelaskan di atas: bahwa ketika Anda menyimpan intensitas 8-bit dalam tekstur atau framebuffer, itu adalah sRGB, dan saat Anda memproses warna, itu linier. Misalnya, OpenGL ES 3.0, setiap framebuffer dan tekstur memiliki "tanda sRGB" yang dapat Anda aktifkan untuk mengaktifkan konversi otomatis saat membaca dan menulis. Anda tidak perlu secara eksplisit melakukan konversi sRGB atau koreksi gamma sama sekali.

Dan Hulme
sumber
6
jawaban yang luar biasa, terima kasih, selalu luar biasa melihat hal-hal dijelaskan, saya hanya akan meminta buku atau sumber daya yang menurut Anda akan cukup baik untuk topik ini, ruang warna, dan rumus apa yang digunakan untuk melakukan konversi sRGB <-> linier atau apa fungsi yang dapat mendekati perilaku ini.
Ken
Saya khawatir saya tidak tahu buku atau sumber yang bagus. The halaman Wikipedia komprehensif, dan mencakup semua hal tentang putih-titik dan seberapa kecil gamut adalah (yang belum saya sebutkan, karena kebanyakan orang tidak perlu tahu tentang hal itu), tapi yang membuatnya menjadi tak tertembus sedikit .
Dan Hulme
1

Saya bukan "ahli pendeteksi warna manusia", tetapi saya telah menemukan hal serupa pada konversi YUV-> RGB. Ada bobot yang berbeda untuk saluran R / G / B, jadi jika Anda mengubah warna sumber dengan x, nilai RGB mengubah kuantitas yang berbeda.

Seperti yang dikatakan, saya bukan ahli, bagaimanapun, saya pikir, jika Anda ingin melakukan transformasi warna-benar, Anda harus melakukannya di ruang YUV, lalu mengubahnya menjadi RGB (atau melakukan operasi yang setara secara matematis pada RGB, waspadalah kehilangan data). Selain itu, saya tidak yakin YUV adalah representasi warna asli terbaik, tetapi kamera video menyediakan format itu, di situlah saya menemukan masalah.

Berikut adalah rumus ajaib YUV-> RGB dengan nomor rahasia yang disertakan: http://www.fourcc.org/fccyvrgb.php

ern0
sumber
1
Hati-hati dengan konversi RGB <-> YUV dan sebaliknya. Saya tidak yakin apakah ini terjadi di setiap kasus, tetapi ruang warna YUV terkadang diubah ke kisaran 16-235 alih-alih 0-255 dalam RGB 24-bit. Jadi, Anda bisa kehilangan data setiap kali Anda melakukan konversi ruang warna. Kebanyakan orang cenderung mengatakan untuk tetap berada dalam ruang warna yang sama jika Anda bisa membantu.
Joe Plante