Formula untuk menentukan kecerahan warna RGB

387

Saya mencari beberapa jenis rumus atau algoritma untuk menentukan kecerahan warna yang diberikan nilai RGB. Saya tahu ini tidak sesederhana menambahkan nilai-nilai RGB bersama-sama dan memiliki jumlah yang lebih tinggi menjadi lebih cerah, tapi saya agak bingung harus mulai dari mana.

Robmerica
sumber
8
Kecerahan yang dirasakan adalah apa yang saya pikir saya cari, terima kasih.
robmerica
2
Ada artikel yang bagus ( Memanipulasi warna dalam .NET - Bagian 1 ) tentang ruang warna dan percakapan di antara keduanya termasuk teori dan kode (C #). Untuk jawabannya lihat Konversi antara model topik dalam artikel.
garis bawah
4
Saya telah menjadi anggota selama bertahun-tahun, dan saya belum pernah melakukan ini sebelumnya. Bolehkah saya menyarankan Anda meninjau jawaban dan memikirkan kembali jawaban mana yang akan diterima?
Jive Dadson

Jawaban:

456

Apakah maksud Anda kecerahan? Kecerahan yang dirasakan? Luminance?

  • Luminance (standar untuk ruang warna tertentu): (0.2126*R + 0.7152*G + 0.0722*B) [1]
  • Luminance (opsi yang dirasakan 1): (0.299*R + 0.587*G + 0.114*B) [2]
  • Luminance (opsi yang dirasakan 2, lebih lambat untuk menghitung): sqrt( 0.241*R^2 + 0.691*G^2 + 0.068*B^2 )sqrt( 0.299*R^2 + 0.587*G^2 + 0.114*B^2 )(terima kasih kepada @MatthewHerbst ) [3]
Anonim
sumber
26
Perhatikan bahwa kedua hal ini menekankan aspek fisiologis: bola mata manusia paling sensitif terhadap cahaya hijau, lebih sedikit ke merah dan paling sedikit ke biru.
Bob Cross
16
Perhatikan juga bahwa semua ini mungkin untuk 0-1 RGB linier, dan Anda mungkin memiliki 0-255 RGB yang dikoreksi gamma. Mereka tidak bertobat seperti yang Anda pikirkan.
alex aneh
4
Tidak benar. Sebelum menerapkan transformasi linier, pertama-tama seseorang harus menerapkan kebalikan dari fungsi gamma untuk ruang warna. Kemudian setelah menerapkan fungsi linier, fungsi gamma diterapkan.
Jive Dadson
6
Dalam rumus terakhir, apakah itu (0,299 * R) ^ 2 atau apakah itu 0,299 * (R ^ 2)?
Kaizer Sozay
3
@ KaizerSozay Seperti yang tertulis di sini artinya 0.299*(R^2)(karena eksponensial berjalan sebelum multiplikasi)
Dantevg
298

Saya pikir yang Anda cari adalah formula konversi RGB -> Luma .

Fotometrik / digital ITU BT.709 :

Y = 0.2126 R + 0.7152 G + 0.0722 B

Digital ITU BT.601 (memberi bobot lebih besar pada komponen R dan B):

Y = 0.299 R + 0.587 G + 0.114 B

Jika Anda ingin memperdagangkan akurasi untuk kinerja, ada dua rumus perkiraan untuk yang satu ini:

Y = 0.33 R + 0.5 G + 0.16 B

Y = 0.375 R + 0.5 G + 0.125 B

Ini dapat dihitung dengan cepat sebagai

Y = (R+R+B+G+G+G)/6

Y = (R+R+R+B+G+G+G+G)>>3
Franci Penov
sumber
47
Saya suka bahwa Anda memasukkan nilai yang tepat, tetapi juga termasuk pintas jenis "cukup dekat" cepat. +1.
Beska
3
@ Jonathan Dumaine - dua rumus perhitungan cepat keduanya termasuk biru - 1 adalah (2 * Merah + Blue+ 3 * Hijau) / 6, yang kedua adalah (3 * Merah + Blue+ 4 * Hijau) >> 3. Memang, dalam kedua pendekatan cepat, Biru memiliki bobot terendah, tetapi masih ada di sana.
Franci Penov
84
@ JonathanDumaine Itu karena mata manusia paling tidak peka terhadap Biru ;-)
Christopher Oezbek
4
Versi cepat berfungsi dengan baik. Diuji dan diterapkan ke aplikasi dunia nyata dengan ribuan pengguna, semuanya terlihat baik-baik saja.
milosmns
10
Versi cepat bahkan lebih cepat jika Anda melakukannya sebagai: Y = (R<<1+R+G<<2+B)>>3(itu hanya 3-4 siklus CPU pada ARM) tapi saya kira kompiler yang baik akan melakukan optimasi untuk Anda.
rjmunro
105

Saya telah membuat perbandingan ketiga algoritma dalam jawaban yang diterima. Saya menghasilkan warna dalam siklus di mana hanya sekitar setiap warna ke-400 digunakan. Setiap warna diwakili oleh 2x2 piksel, warna diurutkan dari yang paling gelap ke paling terang (kiri ke kanan, atas ke bawah).

Gambar 1 - Luminance (relatif)

0.2126 * R + 0.7152 * G + 0.0722 * B

Gambar 2 - http://www.w3.org/TR/AERT#color-contrast

0.299 * R + 0.587 * G + 0.114 * B

Gambar 3 - Model Warna HSP

sqrt(0.299 * R^2 + 0.587 * G^2 + 0.114 * B^2)

Gambar ke 4 - WCAG 2.0 SC 1.4.3 rumus luminans dan rasio perbandingan relatif (lihat jawaban @ Synchro di sini )

Pola kadang-kadang dapat terlihat pada gambar 1 dan 2 tergantung pada jumlah warna dalam satu baris. Saya tidak pernah melihat pola pada gambar dari algoritma ke-3 atau ke-4.

Jika saya harus memilih saya akan pergi dengan algoritma nomor 3 karena jauh lebih mudah diimplementasikan dan sekitar 33% lebih cepat daripada yang ke-4.

Perbandingan algoritma kecerahan yang dirasakan

Petr Hurtak
sumber
3
Bagi saya ini adalah jawaban terbaik karena oyu menggunakan pola gambar yang memungkinkan Anda memahami jika warna berbeda diberikan dengan pencahayaan yang sama. Bagi saya dan monitor saya saat ini, gambar ke-3 adalah yang "paling
cantik
8
Gambar perbandingan Anda salah karena Anda tidak memberikan input yang benar ke semua fungsi. Fungsi pertama membutuhkan input RGB linier ; Saya hanya dapat mereproduksi efek pita dengan menyediakan RGB nonlinier (yaitu yang dikoreksi gamma). Memperbaiki masalah ini, Anda tidak mendapatkan artefak banding dan fungsi pertama adalah pemenang yang jelas.
Maks
1
@ Max ^2dan yang sqrttermasuk dalam formula ketiga adalah cara yang lebih cepat untuk mendekati RGB linier dari bukan-linear RGB daripada ^2.2dan ^(1/2.2)itu akan lebih benar. Sayangnya, menggunakan input nonlinier dan bukannya linear.
Mark Ransom
53

Di bawah ini adalah satu-satunya algoritma CORRECT untuk mengonversi gambar sRGB, seperti yang digunakan dalam peramban dll., Ke skala abu-abu.

Penting untuk menerapkan kebalikan dari fungsi gamma untuk ruang warna sebelum menghitung produk dalam. Kemudian Anda menerapkan fungsi gamma ke nilai yang dikurangi. Kegagalan untuk menggabungkan fungsi gamma dapat menyebabkan kesalahan hingga 20%.

Untuk barang-barang komputer biasa, ruang warnanya adalah sRGB. Angka yang tepat untuk sRGB adalah sekitar. 0,21, 0,72, 0,07. Gamma untuk sRGB adalah fungsi gabungan yang mendekati eksponensial dengan 1 / (2.2). Inilah semuanya dalam C ++.

// sRGB luminance(Y) values
const double rY = 0.212655;
const double gY = 0.715158;
const double bY = 0.072187;

// Inverse of sRGB "gamma" function. (approx 2.2)
double inv_gam_sRGB(int ic) {
    double c = ic/255.0;
    if ( c <= 0.04045 )
        return c/12.92;
    else 
        return pow(((c+0.055)/(1.055)),2.4);
}

// sRGB "gamma" function (approx 2.2)
int gam_sRGB(double v) {
    if(v<=0.0031308)
        v *= 12.92;
    else 
        v = 1.055*pow(v,1.0/2.4)-0.055;
    return int(v*255+0.5); // This is correct in C++. Other languages may not
                           // require +0.5
}

// GRAY VALUE ("brightness")
int gray(int r, int g, int b) {
    return gam_sRGB(
            rY*inv_gam_sRGB(r) +
            gY*inv_gam_sRGB(g) +
            bY*inv_gam_sRGB(b)
    );
}
Jive Dadson
sumber
5
Itulah cara sRGB didefinisikan. Saya pikir alasannya adalah bahwa ia menghindari beberapa masalah numerik mendekati nol. Tidak akan ada banyak perbedaan jika Anda hanya menaikkan angka ke kekuatan 2,2 dan 1 / 2,2.
Jive Dadson
8
JMD - sebagai bagian dari pekerjaan di laboratorium persepsi visual, saya telah melakukan pengukuran pencahayaan langsung pada monitor CRT dan dapat mengkonfirmasi bahwa ada wilayah linear pencahayaan di bagian bawah kisaran nilai.
Jerry Federspiel
2
Saya tahu ini sangat tua, tetapi masih di luar sana untuk dicari. Saya pikir itu tidak benar. Tidakkah abu-abu (255.255.255) = abu-abu (255,0,0) + abu-abu (0,255,0) + abu-abu (0,0255)? Tidak.
DCBillen
2
@ DCBillen: tidak, karena nilainya berada dalam ruang sRGB non-linear yang dikoreksi, Anda tidak bisa menambahkannya begitu saja. Jika Anda ingin menambahkannya, Anda harus melakukannya sebelum memanggil gam_sRGB.
rdb
1
@ DCBillen Rdb benar. Cara untuk menambahkannya ditampilkan dalam fungsi int grey (int r, int g, int b), yang "menghilangkan" gam_sRGB. Sangat menyakitkan saya bahwa setelah empat tahun, jawaban yang benar dinilai sangat rendah. :-) Tidak juga .. saya akan mengatasinya.
Jive Dadson
45

Jawaban "Diterima" Tidak Benar dan Tidak Lengkap

Satu-satunya jawaban yang akurat adalah jawaban @ jive-dadson dan @EddingtonsMonkey , dan dalam dukungan @ nils-pipenbrinck . Jawaban lain (termasuk yang diterima) adalah menautkan atau mengutip sumber yang salah, tidak relevan, usang, atau rusak.

Secara singkat:

  • sRGB harus di- LINEARIZED sebelum menerapkan koefisien.
  • Luminance (L atau Y) linier seperti cahaya.
  • Perceived lightness (L *) tidak linier seperti halnya persepsi manusia.
  • HSV dan HSL bahkan tidak akurat dalam hal persepsi.
  • Standar IEC untuk sRGB menentukan ambang batas 0,04045 itu BUKAN 0,03928 (yang berasal dari konsep awal yang usang).
  • Yang berguna (yaitu relatif terhadap persepsi) , jarak Euclidian membutuhkan ruang vektor Cartesian yang perseptual seragam seperti CIELAB. sRGB bukan satu.

Berikut ini adalah jawaban yang benar dan lengkap:

Karena utas ini sangat muncul di mesin pencari, saya menambahkan jawaban ini untuk mengklarifikasi berbagai kesalahpahaman pada subjek.

Kecerahan adalah atribut persepsi, tidak memiliki ukuran langsung.

Perceived lightness diukur oleh beberapa model penglihatan seperti CIELAB, di sini L * (Lstar) adalah ukuran dari lightness perseptual , dan non-linear untuk memperkirakan kurva respon non-linear penglihatan manusia.

Luminance adalah ukuran cahaya linier, berbobot spektral untuk penglihatan normal tetapi tidak disesuaikan untuk persepsi non-linear terhadap cahaya.

Luma ( prime) adalah sinyal yang dikodekan gamma, tertimbang yang digunakan dalam beberapa pengkodean video. Tidak perlu bingung dengan pencahayaan linier.

Gamma atau transfer curve (TRC) adalah kurva yang sering serupa dengan kurva persepsi, dan biasanya diterapkan pada data gambar untuk penyimpanan atau siaran untuk mengurangi noise yang dirasakan dan / atau meningkatkan pemanfaatan data (dan alasan terkait).

Untuk menentukan penerangan yang dirasakan , pertama-tama konversikan gamma yang dikodekan nilai-nilai gambar R´G´B menjadi luminance linier ( Latau Y) dan kemudian ke penerangan yang dirasakan non-linear ( L*)


UNTUK MENCARI LUMINANCE:

... Karena ternyata itu hilang di suatu tempat ...

Langkah pertama:

Konversikan semua nilai integer sRGB 8 bit ke desimal 0,0-1.0

  vR = sR / 255;
  vG = sG / 255;
  vB = sB / 255;

Langkah Dua:

Konversi gamma yang dikodekan RGB ke nilai linier. sRGB (standar komputer) misalnya membutuhkan kurva daya sekitar V ^ 2.2, meskipun transformasi "akurat" adalah:

sRGB ke Linear

Di mana V´ adalah saluran R, G, atau B yang disandikan gamma dari sRGB.
Kodesemu:

function sRGBtoLin(colorChannel) {
        // Send this function a decimal sRGB gamma encoded color value
        // between 0.0 and 1.0, and it returns a linearized value.

    if ( colorChannel <= 0.04045 ) {
            return colorChannel / 12.92;
        } else {
            return pow((( colorChannel + 0.055)/1.055),2.4));
        }
    }

Langkah ketiga:

Untuk menemukan Luminance (Y) terapkan koefisien standar untuk sRGB:

Terapkan koefisien Y = R * 0,2126 + G * 0,7152 + B * 0,0722

Kodesemu menggunakan fungsi di atas:

Y = (0.2126 * sRGBtoLin(vR) + 0.7152 * sRGBtoLin(vG) + 0.0722 * sRGBtoLin(vB))

UNTUK MENCARI LIGHTNESS YANG DAPAT DIBERI:

Langkah Empat:

Ambil luminance Y dari atas, dan ubah ke L *

L * dari persamaan Y
Kodesemu:

function YtoLstar(Y) {
        // Send this function a luminance value between 0.0 and 1.0,
        // and it returns L* which is "perceptual lightness"

    if ( Y <= (216/24389) {       // The CIE standard states 0.008856 but 216/24389 is the intent for 0.008856451679036
            return Y * (24389/27);  // The CIE standard states 903.3, but 24389/27 is the intent, making 903.296296296296296
        } else {
            return pow(Y,(1/3)) * 116 - 16;
        }
    }

L * adalah nilai dari 0 (hitam) hingga 100 (putih) di mana 50 adalah "abu-abu tengah" perseptual. L * = 50 adalah setara dengan Y = 18,4, atau dengan kata lain kartu abu-abu 18%, mewakili bagian tengah dari eksposur fotografi (Ansel Adams zona V).

Referensi:

IEC 61966-2-1:1999 Standard
Wikipedia sRGB
Wikipedia CIELAB
Wikipedia CIEXYZ
FAQ Gamma Charles Poynton

Myndex
sumber
@Rotem terima kasih - Saya melihat beberapa pernyataan aneh dan tidak lengkap dan merasa akan sangat membantu untuk memakukannya, terutama karena utas ini masih berperingkat tinggi di mesin pencari.
Myndex
Saya membuat demonstrasi membandingkan BT.601 Luma dan CIE 1976 L * Perceptual Grey , menggunakan beberapa perintah MATLAB:Luma=rgb2gray(RGB);LAB=rgb2lab(RGB);LAB(:,:,2:3)=0;PerceptualGray=lab2rgb(LAB);
Rotem
@Myndex Saya menggunakan rumus Anda untuk sampai ke L *, tapi saya masih mendapatkan beberapa hasil aneh, apa pun rumus yang saya gunakan ... Dengan rumus Anda, L * dari # d05858 lebih gelap daripada L * dari # c51c2a ... Apakah ada cara untuk memperbaiki ini? Mengapa formula tidak berfungsi seperti yang diharapkan? :(
sjahan
1
@asdfasdfads Ya, L*a*b*tidak memperhitungkan sejumlah atribut psikofisik. Efek Helmholtz-Kohlrausch adalah satu, tetapi ada banyak lainnya. CIELAB bukanlah model penilaian gambar "penuh" dengan cara apa pun. Dalam posting saya, saya mencoba untuk menutupi konsep dasar selengkap mungkin tanpa menjelajah ke dalam hal-hal kecil yang sangat dalam. Model Hunt, model Fairchild, dan lainnya melakukan pekerjaan yang lebih lengkap, tetapi juga jauh lebih kompleks.
Myndex
1
@Myndex, tidak apa-apa, implementasi saya berbasis kelelahan dan hasil buruk saya berasal dari itu :( Terima kasih banyak atas bantuan dan pos Anda yang bernilai tinggi!
sjahan
11

Saya menemukan kode ini (ditulis dalam C #) yang melakukan pekerjaan yang sangat baik dalam menghitung "kecerahan" warna. Dalam skenario ini, kode mencoba menentukan apakah akan menempatkan teks putih atau hitam di atas warna.

sitesbyjoe
sumber
1
Itulah yang saya butuhkan. Saya sedang melakukan demo "bilah warna" klasik, dan ingin memberi label pada mereka di atas warna dengan pilihan hitam-putih terbaik!
RufusVS
10

Menariknya, formulasi ini untuk RGB => HSV hanya menggunakan v = MAX3 (r, g, b). Dengan kata lain, Anda dapat menggunakan maksimum (r, g, b) sebagai V di HSV.

Saya memeriksa dan pada halaman 575 Hearn & Baker inilah cara mereka menghitung "Nilai" juga.

Dari Hearn & Baker hal 319

bobobobo
sumber
Sebagai catatan, tautannya sudah mati, versi arsipkan di sini - web.archive.org/web/20150906055359/http://…
Peter
HSV tidak seragam secara seragam (dan bahkan tidak dekat). Ini digunakan hanya sebagai cara "nyaman" untuk menyesuaikan warna, tetapi tidak relevan dengan persepsi, dan V tidak berhubungan dengan nilai sebenarnya dari L atau Y (CIE Luminance).
Myndex
9

Daripada tersesat di antara pemilihan formula acak yang disebutkan di sini, saya sarankan Anda menggunakan formula yang direkomendasikan oleh standar W3C.

Berikut ini adalah implementasi PHP yang sederhana namun tepat untuk rumus relatif dan rasio kontras WCAG 2.0 SC 1.4.3 . Ini menghasilkan nilai-nilai yang sesuai untuk mengevaluasi rasio yang diperlukan untuk kepatuhan WCAG, seperti pada halaman ini , dan karena itu cocok dan sesuai untuk aplikasi web apa pun. Ini sepele untuk port ke bahasa lain.

/**
 * Calculate relative luminance in sRGB colour space for use in WCAG 2.0 compliance
 * @link http://www.w3.org/TR/WCAG20/#relativeluminancedef
 * @param string $col A 3 or 6-digit hex colour string
 * @return float
 * @author Marcus Bointon <[email protected]>
 */
function relativeluminance($col) {
    //Remove any leading #
    $col = trim($col, '#');
    //Convert 3-digit to 6-digit
    if (strlen($col) == 3) {
        $col = $col[0] . $col[0] . $col[1] . $col[1] . $col[2] . $col[2];
    }
    //Convert hex to 0-1 scale
    $components = array(
        'r' => hexdec(substr($col, 0, 2)) / 255,
        'g' => hexdec(substr($col, 2, 2)) / 255,
        'b' => hexdec(substr($col, 4, 2)) / 255
    );
    //Correct for sRGB
    foreach($components as $c => $v) {
        if ($v <= 0.04045) {
            $components[$c] = $v / 12.92;
        } else {
            $components[$c] = pow((($v + 0.055) / 1.055), 2.4);
        }
    }
    //Calculate relative luminance using ITU-R BT. 709 coefficients
    return ($components['r'] * 0.2126) + ($components['g'] * 0.7152) + ($components['b'] * 0.0722);
}

/**
 * Calculate contrast ratio acording to WCAG 2.0 formula
 * Will return a value between 1 (no contrast) and 21 (max contrast)
 * @link http://www.w3.org/TR/WCAG20/#contrast-ratiodef
 * @param string $c1 A 3 or 6-digit hex colour string
 * @param string $c2 A 3 or 6-digit hex colour string
 * @return float
 * @author Marcus Bointon <[email protected]>
 */
function contrastratio($c1, $c2) {
    $y1 = relativeluminance($c1);
    $y2 = relativeluminance($c2);
    //Arrange so $y1 is lightest
    if ($y1 < $y2) {
        $y3 = $y1;
        $y1 = $y2;
        $y2 = $y3;
    }
    return ($y1 + 0.05) / ($y2 + 0.05);
}
Sinkron
sumber
mengapa Anda lebih suka definisi w3c? secara pribadi saya telah mengimplementasikan CCIR 601 dan w3c yang direkomendasikan dan saya jauh lebih puas dengan hasil CCIR 601
user151496
1
Karena, seperti yang saya katakan, direkomendasikan oleh W3C dan WCAG?
Sinkronisasi
1
Formula W3C salah pada beberapa level. Ini tidak memperhitungkan persepsi manusia, mereka menggunakan kontras "sederhana" menggunakan luminance yang linier dan sama sekali tidak seragam secara perseptual. Di antara hal-hal lain, tampaknya mereka mendasarkannya pada beberapa standar setua 1988 (!!!) yang tidak relevan saat ini (standar-standar itu didasarkan pada monitor monokrom seperti hijau / hitam, dan merujuk pada kontras total dari hidup ke mati). , tidak mempertimbangkan skala abu-abu atau warna).
Myndex
1
Itu benar-benar sampah. Luma secara khusus persepsi - itu sebabnya ia memiliki koefisien berbeda untuk merah, hijau, dan biru. Usia tidak ada hubungannya dengan itu - ruang warna perseptual CIE Lab yang sangat baik berasal dari tahun 1976. Ruang W3C tidak sebagus itu, namun itu adalah perkiraan praktis yang baik yang mudah untuk dihitung. Jika Anda memiliki sesuatu yang konstruktif untuk ditawarkan, posting itu daripada kritik kosong.
Sinkronisasi
3
Sekadar menambahkan / memperbarui : saat ini kami sedang meneliti algoritme pengganti yang memodelkan kontras persepsi yang lebih baik (diskusi dalam Github Issue 695) . Namun, sebagai masalah terpisah FYI ambang untuk sRGB adalah 0,04045 , dan bukan 0,03928 yang dirujuk dari konsep awal sRGB yang sudah usang. IEC std otoritatif menggunakan 0,04045 dan permintaan tarik akan datang untuk memperbaiki kesalahan ini di WCAG. (ref: IEC 61966-2-1: 1999) Ini ada dalam edisi Github 360, meskipun untuk menyebutkan, dalam 8bit tidak ada perbedaan aktual - dekat ujung utas 360 Saya memiliki grafik kesalahan termasuk 0,04045 / 0,03928 dalam 8bit.
Myndex
8

Untuk menambahkan apa yang dikatakan semua orang:

Semua persamaan ini bekerja cukup baik dalam praktiknya, tetapi jika Anda harus sangat tepat, Anda harus terlebih dahulu mengonversi warna ke ruang warna linier (menerapkan invers gambar-gamma), lakukan rata-rata bobot warna primer dan - jika Anda ingin tampilkan warnanya - bawa luminance kembali ke gamma monitor.

Perbedaan luminance antara gamma ingnoring dan melakukan gamma yang tepat hingga 20% dalam abu-abu gelap.

Nils Pipenbrinck
sumber
2

Saya sedang menyelesaikan tugas yang sama hari ini di javascript. Saya telah menetapkan getPerceivedLightness(rgb)fungsi ini untuk warna HEX RGB. Ini berkaitan dengan efek Helmholtz-Kohlrausch melalui rumus Fairchild dan Perrotta untuk koreksi pencahayaan.

/**
 * Converts RGB color to CIE 1931 XYZ color space.
 * https://www.image-engineering.de/library/technotes/958-how-to-convert-between-srgb-and-ciexyz
 * @param  {string} hex
 * @return {number[]}
 */
export function rgbToXyz(hex) {
    const [r, g, b] = hexToRgb(hex).map(_ => _ / 255).map(sRGBtoLinearRGB)
    const X =  0.4124 * r + 0.3576 * g + 0.1805 * b
    const Y =  0.2126 * r + 0.7152 * g + 0.0722 * b
    const Z =  0.0193 * r + 0.1192 * g + 0.9505 * b
    // For some reason, X, Y and Z are multiplied by 100.
    return [X, Y, Z].map(_ => _ * 100)
}

/**
 * Undoes gamma-correction from an RGB-encoded color.
 * https://en.wikipedia.org/wiki/SRGB#Specification_of_the_transformation
 * /programming/596216/formula-to-determine-brightness-of-rgb-color
 * @param  {number}
 * @return {number}
 */
function sRGBtoLinearRGB(color) {
    // Send this function a decimal sRGB gamma encoded color value
    // between 0.0 and 1.0, and it returns a linearized value.
    if (color <= 0.04045) {
        return color / 12.92
    } else {
        return Math.pow((color + 0.055) / 1.055, 2.4)
    }
}

/**
 * Converts hex color to RGB.
 * /programming/5623838/rgb-to-hex-and-hex-to-rgb
 * @param  {string} hex
 * @return {number[]} [rgb]
 */
function hexToRgb(hex) {
    const match = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
    if (match) {
        match.shift()
        return match.map(_ => parseInt(_, 16))
    }
}

/**
 * Converts CIE 1931 XYZ colors to CIE L*a*b*.
 * The conversion formula comes from <http://www.easyrgb.com/en/math.php>.
 * https://github.com/cangoektas/xyz-to-lab/blob/master/src/index.js
 * @param   {number[]} color The CIE 1931 XYZ color to convert which refers to
 *                           the D65/2° standard illuminant.
 * @returns {number[]}       The color in the CIE L*a*b* color space.
 */
// X, Y, Z of a "D65" light source.
// "D65" is a standard 6500K Daylight light source.
// https://en.wikipedia.org/wiki/Illuminant_D65
const D65 = [95.047, 100, 108.883]
export function xyzToLab([x, y, z]) {
  [x, y, z] = [x, y, z].map((v, i) => {
    v = v / D65[i]
    return v > 0.008856 ? Math.pow(v, 1 / 3) : v * 7.787 + 16 / 116
  })
  const l = 116 * y - 16
  const a = 500 * (x - y)
  const b = 200 * (y - z)
  return [l, a, b]
}

/**
 * Converts Lab color space to Luminance-Chroma-Hue color space.
 * http://www.brucelindbloom.com/index.html?Eqn_Lab_to_LCH.html
 * @param  {number[]}
 * @return {number[]}
 */
export function labToLch([l, a, b]) {
    const c = Math.sqrt(a * a + b * b)
    const h = abToHue(a, b)
    return [l, c, h]
}

/**
 * Converts a and b of Lab color space to Hue of LCH color space.
 * /programming/53733379/conversion-of-cielab-to-cielchab-not-yielding-correct-result
 * @param  {number} a
 * @param  {number} b
 * @return {number}
 */
function abToHue(a, b) {
    if (a >= 0 && b === 0) {
        return 0
    }
    if (a < 0 && b === 0) {
        return 180
    }
    if (a === 0 && b > 0) {
        return 90
    }
    if (a === 0 && b < 0) {
        return 270
    }
    let xBias
    if (a > 0 && b > 0) {
        xBias = 0
    } else if (a < 0) {
        xBias = 180
    } else if (a > 0 && b < 0) {
        xBias = 360
    }
    return radiansToDegrees(Math.atan(b / a)) + xBias
}

function radiansToDegrees(radians) {
    return radians * (180 / Math.PI)
}

function degreesToRadians(degrees) {
    return degrees * Math.PI / 180
}

/**
 * Saturated colors appear brighter to human eye.
 * That's called Helmholtz-Kohlrausch effect.
 * Fairchild and Pirrotta came up with a formula to
 * calculate a correction for that effect.
 * "Color Quality of Semiconductor and Conventional Light Sources":
 * https://books.google.ru/books?id=ptDJDQAAQBAJ&pg=PA45&lpg=PA45&dq=fairchild+pirrotta+correction&source=bl&ots=7gXR2MGJs7&sig=ACfU3U3uIHo0ZUdZB_Cz9F9NldKzBix0oQ&hl=ru&sa=X&ved=2ahUKEwi47LGivOvmAhUHEpoKHU_ICkIQ6AEwAXoECAkQAQ#v=onepage&q=fairchild%20pirrotta%20correction&f=false
 * @return {number}
 */
function getLightnessUsingFairchildPirrottaCorrection([l, c, h]) {
    const l_ = 2.5 - 0.025 * l
    const g = 0.116 * Math.abs(Math.sin(degreesToRadians((h - 90) / 2))) + 0.085
    return l + l_ * g * c
}

export function getPerceivedLightness(hex) {
    return getLightnessUsingFairchildPirrottaCorrection(labToLch(xyzToLab(rgbToXyz(hex))))
}
catamphetamine
sumber
1

Ruang warna HSV harus melakukan triknya, lihat artikel wikipedia tergantung pada bahasa tempat Anda bekerja, Anda mungkin mendapatkan konversi perpustakaan.

H adalah rona yang merupakan nilai numerik untuk warna (yaitu merah, hijau ...)

S adalah saturasi warna, yaitu seberapa 'intens' warna itu

V adalah 'kecerahan' warna.

Ian Hopkinson
sumber
7
Masalah dengan ruang warna HSV adalah bahwa Anda dapat memiliki saturasi dan nilai yang sama, tetapi rona yang berbeda, untuk biru dan kuning . Kuning jauh lebih terang daripada biru. Sama berlaku untuk HSL.
Ian Boyd
hsv memberi Anda "kecerahan" warna dalam arti teknis. dalam kecerahan persepsi hsv benar-benar gagal
user151496
HSV dan HSL tidak akurat secara perseptual (dan bahkan tidak mendekati). Mereka berguna untuk "kontrol" untuk menyesuaikan warna relatif, tetapi tidak untuk prediksi akurat dari persepsi cahaya. Gunakan L * dari CIELAB untuk cahaya persepsi.
Myndex
1

Nilai Luminance RGB = 0,3 R + 0,59 G + 0,11 B

http://www.scantips.com/lumin.html

Jika Anda mencari seberapa dekat dengan putih warnanya, Anda dapat menggunakan Euclidean Distance from (255, 255, 255)

Saya pikir ruang warna RGB jelas tidak seragam sehubungan dengan jarak euclidian L2. Ruang seragam termasuk CIE LAB dan LUV.


sumber
1

Rumus invers-gamma oleh Jive Dadson harus memiliki setengah penyesuaian dihapus ketika diimplementasikan dalam Javascript, yaitu kembali dari fungsi gam_sRGB perlu kembali int (v * 255); tidak mengembalikan int (v * 255 + .5); Setengah-penyesuaian dibulatkan ke atas, dan ini dapat menyebabkan nilai terlalu tinggi pada R = G = B yaitu triad warna abu-abu. Konversi skala abu-abu pada triad R = G = B harus menghasilkan nilai yang sama dengan R; itu salah satu bukti bahwa formula itu valid. Lihat Nine Shades of Greyscale untuk formula yang sedang beraksi (tanpa setengah menyesuaikan).

Dave Collier
sumber
Sepertinya Anda tahu barang-barang Anda, jadi saya menghapus +0.5
Jive Dadson
Saya melakukan percobaan. Dalam C ++ perlu +0.5, jadi saya masukkan kembali. Saya menambahkan komentar tentang menerjemahkan ke bahasa lain.
Jive Dadson
1

Saya bertanya-tanya bagaimana koefisien rgb ditentukan. Saya melakukan percobaan sendiri dan berakhir dengan yang berikut:

Y = 0.267 R + 0.642 G + 0.091 B

Tutup tetapi jelas berbeda dari koefisien ITU yang telah lama terbentuk. Saya bertanya-tanya apakah koefisien-koefisien itu bisa berbeda untuk masing-masing dan setiap pengamat, karena kita semua mungkin memiliki jumlah kerucut dan batang yang berbeda pada retina di mata kita, dan terutama perbandingan antara berbagai jenis kerucut yang mungkin berbeda.

Sebagai referensi:

ITU BT.709:

Y = 0.2126 R + 0.7152 G + 0.0722 B

ITU BT.601:

Y = 0.299 R + 0.587 G + 0.114 B

Saya melakukan tes dengan cepat memindahkan bilah abu-abu kecil pada latar belakang merah, hijau terang dan biru terang, dan menyesuaikan abu-abu sampai tercampur sebanyak mungkin. Saya juga mengulangi tes itu dengan nuansa lain. Saya mengulangi tes pada layar yang berbeda, bahkan satu dengan faktor gamma tetap 3.0, tetapi semuanya terlihat sama bagi saya. Terlebih lagi, koefisien ITU benar-benar salah untuk mata saya.

Dan ya, saya mungkin memiliki penglihatan warna normal.

pusaran
sumber
Dalam percobaan Anda, apakah Anda linearisasi untuk menghapus komponen gamma terlebih dahulu? Jika tidak, itu bisa menjelaskan hasil Anda. TETAPI JUGA, koefisien terkait dengan percobaan CIE 1931 dan itu adalah rata-rata 17 pengamat, jadi ya ada variasi individual dalam hasil.
Myndex
1

Berikut adalah sedikit kode C yang seharusnya menghitung luminance yang dirasakan dengan benar.

// reverses the rgb gamma
#define inverseGamma(t) (((t) <= 0.0404482362771076) ? ((t)/12.92) : pow(((t) + 0.055)/1.055, 2.4))

//CIE L*a*b* f function (used to convert XYZ to L*a*b*)  http://en.wikipedia.org/wiki/Lab_color_space
#define LABF(t) ((t >= 8.85645167903563082e-3) ? powf(t,0.333333333333333) : (841.0/108.0)*(t) + (4.0/29.0))


float
rgbToCIEL(PIXEL p)
{
   float y;
   float r=p.r/255.0;
   float g=p.g/255.0;
   float b=p.b/255.0;

   r=inverseGamma(r);
   g=inverseGamma(g);
   b=inverseGamma(b);

   //Observer = 2°, Illuminant = D65 
   y = 0.2125862307855955516*r + 0.7151703037034108499*g + 0.07220049864333622685*b;

   // At this point we've done RGBtoXYZ now do XYZ to Lab

   // y /= WHITEPOINT_Y; The white point for y in D65 is 1.0

    y = LABF(y);

   /* This is the "normal conversion which produces values scaled to 100
    Lab.L = 116.0*y - 16.0;
   */
   return(1.16*y - 0.16); // return values for 0.0 >=L <=1.0
}
EddingtonsMonkey
sumber
0

Silakan tentukan kecerahan. Jika Anda mencari seberapa dekat dengan putih warnanya, Anda dapat menggunakan Euclidean Distance from (255, 255, 255)

Ben S
sumber
1
Tidak, Anda tidak dapat menggunakan jarak euclidian antara nilai sRGB, sRGB bukan ruang Cartesian / vektor yang seragam secara persepsi. Jika Anda ingin menggunakan jarak Euclidian sebagai ukuran perbedaan warna, Anda harus setidaknya mengonversi ke CIELAB, atau lebih baik lagi, gunakan CAM seperti CIECAM02.
Myndex
0

'V' HSV mungkin adalah yang Anda cari. MATLAB memiliki fungsi rgb2hsv dan artikel wikipedia yang sebelumnya dikutip penuh dengan pseudocode. Jika konversi RGB2HSV tidak layak, model yang kurang akurat adalah versi skala abu-abu dari gambar.

Yakub
sumber
0

Tautan ini menjelaskan semuanya secara mendalam, termasuk mengapa konstanta pengali itu ada sebelum nilai R, G, dan B.

Sunting: Ini memiliki penjelasan untuk salah satu jawaban di sini juga (0,299 * R + 0,587 * G + 0,114 * B)

dsignr
sumber
0

Untuk menentukan kecerahan warna dengan R, saya mengonversi warna sistem RGB ke warna sistem HSV.

Dalam skrip saya, saya menggunakan kode sistem HEX sebelumnya karena alasan lain, tetapi Anda dapat mulai juga dengan kode sistem RGB rgb2hsv {grDevices}. Dokumentasinya ada di sini .

Ini bagian dari kode saya:

 sample <- c("#010101", "#303030", "#A6A4A4", "#020202", "#010100")
 hsvc <-rgb2hsv(col2rgb(sample)) # convert HEX to HSV
 value <- as.data.frame(hsvc) # create data.frame
 value <- value[3,] # extract the information of brightness
 order(value) # ordrer the color by brightness
Pierre-louis Stenger
sumber
0

Untuk kejelasan, rumus yang menggunakan akar kuadrat perlu

sqrt(coefficient * (colour_value^2))

tidak

sqrt((coefficient * colour_value))^2

Buktinya terletak pada konversi triad R = G = B menjadi skala abu-abu R. Itu hanya akan benar jika Anda menyamakan nilai warna, bukan koefisien nilai nilai kali. Lihat Sembilan Nuansa Skala Abu-Abu

Dave Collier
sumber
5
ada kesalahan tanda kurung
log0
kecuali koefisien yang Anda gunakan adalah akar kuadrat dari koefisien yang benar.
RufusVS