RGB renginin parlaklığını belirleme formülü


387

RGB değerleri verilen bir rengin parlaklığını belirlemek için bir tür formül veya algoritma arıyorum. RGB değerlerini bir araya getirmek ve daha yüksek meblağların daha parlak olması kadar basit olamayacağını biliyorum, ama nereden başlayacağım konusunda bir kayıp yaşıyorum.


8
Algılanan parlaklık, aradığım şey olduğunu düşünüyorum, teşekkür ederim.
robmerica

2
Renk uzayları ve bunlar arasındaki teori ve kod (C #) dahil olmak üzere konuşmalar hakkında iyi bir makale ( .NET'te renkleri değiştirme - Bölüm 1 ) vardır. Yanıt için makaledeki Modeller arası dönüşüm konusuna bakın.
alt çizgi

4
Uzun yıllardır üye oldum ve bunu daha önce hiç yapmadım. Yanıtları gözden geçirip hangisini kabul edeceğinizi yeniden düşünmenizi önerebilir miyim?
Jive Dadson

Yanıtlar:


456

Parlaklık mı demek istediniz? Algılanan parlaklık? Parlaklık?

  • Parlaklık (belirli renk uzayları için standart): (0.2126*R + 0.7152*G + 0.0722*B) [1]
  • Parlaklık (algılanan seçenek 1): (0.299*R + 0.587*G + 0.114*B) [2]
  • Parlaklık (algılanan seçenek 2, hesaplanması daha yavaş): 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 )( @MatthewHerbst sayesinde ) [3]

26
Her ikisinin de fizyolojik yönleri vurguladığını unutmayın: insan göz küresi yeşil ışığa en duyarlı, daha az kırmızıya ve en az maviye duyarlıdır.
Bob Cross

16
Bunların hepsinin muhtemelen doğrusal 0-1 RGB için olduğunu ve muhtemelen gama düzeltmeli 0-255 RGB'niz olduğunu unutmayın. Sizin düşündüğünüz gibi dönüştürülmezler.
alex strange

4
Doğru değil. Doğrusal dönüşümü uygulamadan önce, renk alanı için gama fonksiyonunun tersini uygulamak gerekir. Daha sonra doğrusal fonksiyon uygulandıktan sonra gama fonksiyonu uygulanır.
Jive Dadson

6
Son formülde, (0.299 * R) ^ 2 mi yoksa 0.299 * (R ^ 2) mi?
Kaizer Sozay

3
@KaizerSozay Burada yazıldığı gibi anlamına gelir 0.299*(R^2)(çünkü üs çoğaltma
çarpmadan

298

Bence aradığınız RGB -> Luma dönüşüm formülü.

Fotometrik / dijital ITU BT.709 :

Y = 0.2126 R + 0.7152 G + 0.0722 B

Digital ITU BT.601 (R ve B bileşenlerine daha fazla ağırlık verir):

Y = 0.299 R + 0.587 G + 0.114 B

Performans için doğrulukla ticaret yapmak istiyorsanız, bunun için iki yaklaşık formül vardır:

Y = 0.33 R + 0.5 G + 0.16 B

Y = 0.375 R + 0.5 G + 0.125 B

Bunlar şu şekilde hızlı bir şekilde hesaplanabilir:

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

Y = (R+R+R+B+G+G+G+G)>>3

47
Kesin değerleri koymak gibi, ama aynı zamanda hızlı bir "yeterince yakın" türü kısayol dahil. +1.
Beska

3
@Jonathan Dumaine - iki hızlı hesaplama formülünün her ikisi de mavi içerir - birincisi (2 * Kırmızı + Blue+ 3 * Yeşil) / 6, ikincisi (3 * Kırmızı + Blue+ 4 * Yeşil) >> 3'tür. her iki hızlı yaklaşımda da, Mavi en düşük ağırlığa sahiptir, ancak hala oradadır.
Franci Penov


4
Hızlı sürüm iyi çalışıyor. Test edilmiş ve binlerce kullanıcıyla gerçek dünya uygulamasına uygulanmış, her şey iyi görünüyor.
milosmlar

10
Hızlı sürüm olarak yaparsanız daha hızlıdır: Y = (R<<1+R+G<<2+B)>>3(ARM'de sadece 3-4 CPU döngüsü var) ama sanırım iyi bir derleyici sizin için bu optimizasyonu yapacak.
rjmunro

105

Kabul edilen cevaptaki üç algoritmanın karşılaştırmasını yaptım. Her 400. rengin kullanıldığı çevrimde renkler ürettim. Her renk 2x2 piksel ile temsil edilir, renkler en soluktan en soluk (soldan sağa, yukarıdan aşağıya) sıralanır.

1. resim - Parlaklık (göreceli)

0.2126 * R + 0.7152 * G + 0.0722 * B

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

0.299 * R + 0.587 * G + 0.114 * B

3. resim - HSP Renk Modeli

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

4 resim - WİEK 2.0 SC 1.4.3 nispi parlaklık ve kontrast oranı formülü (bkz Synchro en @ cevap burada )

Desen, bir sıradaki renk sayısına bağlı olarak bazen 1. ve 2. resimde görülebilir. 3. veya 4. algoritmadan hiçbir desen görmedim.

Eğer seçmek zorunda olsaydım ben uygulamak çok daha kolay ve 4. yaklaşık% 33 daha hızlı beri 3 numaralı algoritma ile gitmek istiyorum.

Algılanan parlaklık algoritması karşılaştırması


3
Bana göre bu en iyi cevaptır çünkü oyu, aynı parlaklıkla farklı tonlar oluşturulduğunu algılamanıza izin veren bir resim deseni kullanır. Ben ve şimdiki monitör için 3. resim "en iyi görünümlü" çünkü o da o zaman daha hızlı bir artı
CoffeDeveloper

8
Tüm işlevlere doğru girişi sağlamadığınız için karşılaştırma resminiz yanlış. İlk işlev doğrusal RGB girişi gerektirir ; Bantlama etkisini yalnızca doğrusal olmayan (yani gama düzeltmeli) RGB sağlayarak çoğaltabilirim . Bu sorunu düzeltirseniz, hiçbir bant yapaylığı elde edersiniz ve 1. işlev açık kazanır.
Maksimum

1
@ ^2Ve sqrtüçüncü formüle dahil edilenler, lineer RGB'yi lineer olmayan RGB'den yerine lineer RGB'ye yaklaştırmanın daha hızlı bir yoludur ^2.2ve ^(1/2.2)bu daha doğru olacaktır. Doğrusal girişler yerine doğrusal olmayan girdilerin kullanılması maalesef oldukça yaygındır.
Mark Ransom

53

Aşağıda, tarayıcılarda vb. Kullanılan sRGB görüntülerinin gri tonlamaya dönüştürülmesi için tek CORRECT algoritması verilmiştir.

İç ürünü hesaplamadan önce renk boşluğu için gamma fonksiyonunun tersini uygulamak gerekir. Daha sonra gama fonksiyonunu azaltılmış değere uygularsınız. Gama işlevinin birleştirilmemesi,% 20'ye kadar hatalara neden olabilir.

Tipik bilgisayar öğeleri için renk alanı sRGB'dir. SRGB için doğru sayılar yakl. 0.21, 0.72, 0.07. SRGB için gama, on 1 ((2.2)) 'e yaklaşan birleşik bir fonksiyondur. İşte her şey 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)
    );
}

5
Bu, sRGB'nin tanımlandığı yoldur. Bunun nedeni sıfıra yakın bazı sayısal problemlerden kaçınmasıdır. Sayıları 2.2 ve 1 / 2.2'nin güçlerine yükseltmeniz çok fark etmez.
Jive Dadson

8
JMD - görsel algı laboratuarında çalışmanın bir parçası olarak, CRT monitörlerde doğrudan parlaklık ölçümleri yaptım ve değer aralığının altında doğrusal bir parlaklık bölgesi olduğunu doğrulayabilirim.
Jerry Federspiel

2
Bunun çok eski olduğunu biliyorum, ama hala aranacak bir yer. Bunun doğru olabileceğini düşünmüyorum. Gri (255,255,255) = gri (255,0,0) + gri (0,255,0) + gri (0,0,255) olmamalı mı? Öyle değil.
DCBillen

2
@DCBillen: hayır, değerler doğrusal olmayan gama düzeltmeli sRGB alanında olduğundan, bunları ekleyemezsiniz. Bunları eklemek istiyorsanız, gam_sRGB'yi aramadan önce yapmanız gerekir.
rdb

1
@DCBillen Rdb doğru. Bunları eklemenin yolu gam_sRGB'yi "çağıran" int gri (int r, int g, int b) işlevinde gösterilir. Dört yıl sonra doğru cevabın o kadar düşük olduğu beni üzüyor. :-) Pek değil .. Üstesinden geleceğim.
Jive Dadson

45

"Kabul Edildi" Yanıtı Yanlış ve Eksik

Doğru olan tek cevap @ jive-dadson ve @EddingtonsMonkey yanıtlarıdır ve @ nils-pipenbrinck desteğidir . Diğer cevaplar (kabul edilenler dahil) yanlış, alakasız, eski veya bozuk kaynaklara bağlantı vermek veya bunları alıntılamaktır.

Kısaca:

  • sRGB , katsayılar uygulanmadan önce LINEARIZED olmalıdır .
  • Parlaklık (L veya Y) ışık gibi doğrusaldır.
  • Algılanan hafiflik (L *) insan algısı gibi doğrusal değildir.
  • HSV ve HSL algılama açısından uzaktan bile doğru değildir.
  • SRGB için IEC standardı 0.04045 eşik değerini belirtir (0.03928 DEĞİLDİR) (eski bir taslaktan alınmıştır).
  • Yararlı (yani algılamaya göre) , Öklid mesafeleri, CIELAB gibi algısal olarak homojen bir Kartezyen vektör boşluğu gerektirir. sRGB bir değil.

Aşağıdaki doğru ve eksiksiz bir cevaptır:

Bu konu arama motorlarında yüksek göründüğü için, konuyla ilgili çeşitli kavram yanılgılarını netleştirmek için bu yanıtı ekliyorum.

Parlaklık algısal bir özelliktir, doğrudan bir ölçüsü yoktur.

Algılanan hafiflik CIELAB gibi bazı görme modelleri tarafından ölçülür, burada L * (Lstar) algısal hafifliğin bir ölçüsüdür ve insan görüşünün doğrusal olmayan tepki eğrisine yaklaşmak için doğrusal değildir.

Parlaklık , normal görme için spektral olarak ağırlıklandırılmış, ancak doğrusal olmayan hafiflik algısı için ayarlanmamış, ışığın doğrusal bir ölçümüdür.

Luma ( prime), bazı video kodlamalarında kullanılan gama kodlu, ağırlıklı bir sinyaldir. Doğrusal parlaklık ile karıştırılmamalıdır.

Gama veya transfer eğrisi (TRC) genellikle algısal eğriye benzeyen bir eğridir ve algılanan gürültüyü azaltmak ve / veya veri kullanımını (ve ilgili nedenleri) iyileştirmek için genellikle depolama veya yayın amacıyla görüntü verilerine uygulanır.

Algılanan hafifliği belirlemek için önce gama kodlu R´G´B´ görüntü değerlerini doğrusal parlaklığa ( Lveya Y), ardından doğrusal olmayan algılanan hafifliğe ( L*) dönüştürün


Aydınlatmayı Bulmak İçin:

... Görünüşe göre bir yerlerde kayboldu ...

Adım bir:

Tüm sRGB 8 bit tam sayı değerlerini ondalık 0.0-1.0 değerine dönüştürün

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

İkinci adım:

Gama kodlu RGB'yi doğrusal bir değere dönüştürün. Örneğin sRGB (bilgisayar standardı) yaklaşık V ^ 2.2 güç eğrisi gerektirir, ancak "doğru" dönüşüm şöyledir:

sRGB a Doğrusal

Burada V´, sRGB'nin gama kodlu R, G veya B kanalıdır.
pseudocode:

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));
        }
    }

Adım üç:

Luminance (Y) değerini bulmak için sRGB için standart katsayıları uygulayın:

Y = R * 0.2126 + G * 0.7152 + B * 0.0722 katsayılarını uygulayın

Yukarıdaki işlevleri kullanarak sözde kod:

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

ALGILANAN IŞIKLIĞI BULMAK İÇİN:

Adım dört:

Parlaklığı Y yukarıdan alın ve L * 'ye dönüştürün

Y denkleminden L *
pseudocode:

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 *, 0 (siyah) ile 100 (beyaz) arasında bir değerdir; burada 50, algısal "orta gri" dir. L * = 50, Y = 18.4'e veya başka bir deyişle, fotoğrafik pozlamanın ortasını temsil eden% 18'lik bir gri karta eşdeğerdir (Ansel Adams bölgesi V).

Referanslar:

IEC 61966-2-1:1999 Standard
Wikipedia sRGB
Wikipedia CIELAB
Wikipedia CIEXYZ
Charles Poynton'un Gamma SSS


@Rotem teşekkür ederim - Bazı garip ve eksik ifadeler gördüm ve özellikle bu konu hala arama motorlarında yüksek sırada olduğu için çivilenmenin yararlı olacağını hissettim.
Myndex

Birkaç MATLAB komutu kullanarak BT.601 Luma ve CIE 1976 L * Algısal Gri'yi karşılaştıran bir gösteri hazırladım :Luma=rgb2gray(RGB);LAB=rgb2lab(RGB);LAB(:,:,2:3)=0;PerceptualGray=lab2rgb(LAB);
Rotem

@Myndex Formüllerinizi L * elde etmek için kullandım, ancak kullandığım formül ne olursa olsun hala bazı garip sonuçlar alıyorum ... Sizinkiyle, # d05858'in L * değeri, # c51c2a'nın L * 'sinden daha koyu ... doğru yapmanın yolu nedir? Neden hiçbir formül beklendiği gibi çalışmıyor? :(
sjahan

1
@asdfasdfads Evet, L*a*b*bazı psikofiziksel özellikleri dikkate almaz. Helmholtz-Kohlrausch etkisi birdir, ancak başka pek çok şey vardır. CIELAB hiçbir şekilde "tam" bir görüntü değerlendirme modeli değildir. Yazımda çok derin minutialara girmeden temel kavramları mümkün olduğunca tamamen ele almaya çalışıyordum. Hunt modeli, Fairchild'in modelleri ve diğerleri daha eksiksiz bir iş çıkarırlar, ancak aynı zamanda oldukça karmaşıktırlar.
Myndex

1
@Myndex, nevermind, benim uygulamam yorgunluğa dayalıydı ve kötü sonuçlarım bundan kaynaklandı :( Yardımınız ve çok değerli olan yayınınız için çok teşekkür ederim!
sjahan

11

Bir rengin "parlaklığını" hesaplamak için mükemmel bir iş yapan bu kodu (C # ile yazılmış) buldum . Bu senaryoda, kod rengin üzerine beyaz veya siyah metin koyup koymayacağınızı belirlemeye çalışıyor.


1
Tam da ihtiyacım olan buydu. Klasik bir "renk çubukları" demosu yapıyordum ve onları en iyi siyah-beyaz seçeneğiyle etiketlemek istedim!
RufusVS

10

İlginçtir ki, RGB => HSV için bu formülasyon sadece v = MAX3 (r, g, b) kullanır. Başka bir deyişle, HSV'de V olarak maksimum (r, g, b) kullanabilirsiniz.

Ben kontrol ve Hearn & Baker sayfa 575 de bu nasıl "Değer" hesaplamak olduğunu.

Hearn & Baker sayfa 319


Sadece kayıt için bağlantı öldü, arşiv sürümü burada - web.archive.org/web/20150906055359/http://…
Peter

HSV algısal olarak tek tip değildir (ve hatta yakın değildir). Yalnızca rengi ayarlamak için "uygun" bir yol olarak kullanılır, ancak algı ile ilgili değildir ve V, L veya Y'nin gerçek değeriyle (CIE Parlaklığı) ilgili değildir.
Myndex

9

Burada belirtilen rasgele formül seçimi arasında kaybolmak yerine, W3C standartları tarafından önerilen formülü tercih etmenizi öneririm.

İşte WCAG 2.0 SC 1.4.3 göreceli parlaklık ve kontrast oranı formüllerinin basit ama kesin bir PHP uygulaması . Bu sayfada olduğu gibi WCAG uyumluluğu için gerekli oranları değerlendirmek için uygun olan ve bu şekilde herhangi bir web uygulaması için uygun ve uygun değerler üretir . Bu, diğer diller için de önemlidir.

/**
 * 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 <marcus@synchromedia.co.uk>
 */
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 <marcus@synchromedia.co.uk>
 */
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);
}

neden w3c tanımını tercih edersiniz? Şahsen ben hem CCIR 601 hem de tavsiye edilen
w3c'yi uyguladım

1
Çünkü dediğim gibi, hem W3C hem de WCAG tarafından öneriliyor?
Senkro

1
W3C formülü birkaç düzeyde yanlıştır. İnsan algısını hesaba katmaz, doğrusal ve algısal olarak tekdüze olmayan parlaklık kullanarak "basit" kontrast kullanırlar. Diğer şeylerin yanı sıra, bugün geçerli olmayan 1988 (!!!) gibi bazı standartlara dayandıkları görülüyor (bu standartlar yeşil / siyah gibi tek renkli monitörlere dayanıyordu ve toplam kontrastı açıktan kapalıya atıfta bulunuyor) , gri tonlama veya renkler dikkate alınmaz).
Myndex

1
Bu tam bir saçmalık. Luma özellikle algısaldır - bu yüzden kırmızı, yeşil ve mavi için farklı katsayıları vardır. Yaşın bununla hiçbir ilgisi yoktur - mükemmel CIE Laboratuvarı algısal renk alanı 1976'dan kalmadır. W3C alanı o kadar iyi değildir, ancak hesaplanması kolay olan iyi bir pratik yaklaşımdır. Sunacak yapıcı bir şeyiniz varsa, bunu boş eleştiri yerine yayınlayın.
Synchro

3
Sadece eklemek / güncellemek için : şu anda algısal kontrastı daha iyi modelleyen değiştirme algoritmaları araştırıyoruz (Github Sayı 695'te tartışma) . Bununla birlikte, ayrı bir konu FYI olarak, sRGB için eşik 0.04045'tir ve 0.03928 değil, eski bir erken sRGB taslağından bahsedilmiştir. Yetkili IEC std 0,04045 kullanır ve WCAG'daki bu hatayı düzeltmek için bir çekme isteği gelecektir. (ref: IEC 61966-2-1: 1999) Bu Github 360'ta söz konusu olsa da, 8bit'te gerçek bir fark yok - 360 ipliğinin sonuna yakın 8bit'te 0.04045 / 0.03928 dahil hata çizelgeleri var.
Myndex

8

Diğerlerinin söylediklerini eklemek için:

Tüm bu denklemler pratikte iyi çalışır, ancak çok hassas olmanız gerekiyorsa, önce rengi doğrusal renk uzayına dönüştürmeniz gerekir (ters görüntü-gama uygulayın), ana renklerin ağırlık ortalamasını yapın ve - istiyorsanız rengi görüntüleyin - parlaklığı monitör gamasına geri alın.

İngnoring gama ile uygun gama yapmak arasındaki parlaklık farkı koyu grilerde% 20'ye kadardır.


2

Benzer bir görevi bugün javascript'te çözüyordum. getPerceivedLightness(rgb)HEX RGB rengi için bu işleve karar verdim . Işık düzeltmesi için Fairchild ve Perrotta formülü ile Helmholtz-Kohlrausch etkisi ile ilgilenir.

/**
 * 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))))
}

1

HSV renk aralığı hile yapmalı, çalıştığınız dile bağlı olarak wikipedia makalesine bakın, bir kütüphane dönüşümü alabilirsiniz.

H, renk için sayısal bir değer olan renk tonudur (yani kırmızı, yeşil ...)

S rengin doygunluğudur, yani 'yoğun'

V rengin 'parlaklığı'dır.


7
HSV renk uzayıyla ilgili sorun, mavi ve sarı için aynı doygunluğa ve değere, ancak farklı renk tonlarına sahip olabilmenizdir . Sarı maviden çok daha parlaktır. HSL için de aynı şey geçerli.
Ian Boyd

hsv size teknik anlamda bir rengin "parlaklığını" verir. algısal bir parlaklık hsv gerçekten başarısız
user151496

HSV ve HSL algısal olarak doğru değildir (ve hatta yakın değildir). Göreceli rengi ayarlamak için "kontroller" için yararlıdırlar, ancak algısal açıklığın doğru tahmininde kullanılmazlar. Algısal hafiflik için CIELAB'dan L * kullanın.
Myndex

1

RGB Parlaklık değeri = 0,3 R + 0,59 G + 0,11 B

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

Beyazın ne kadar yakın olduğunu arıyorsanız, (255, 255, 255) 'den Öklid Mesafesini kullanabilirsiniz.

RGB renk uzayının L2 öklid mesafesine göre algısal olarak homojen olmadığını düşünüyorum. Düzgün uzaylar arasında CIE LAB ve LUV bulunur.


1

Jive Dadson tarafından ters-gamma formülünün Javascript'te uygulandığında yarım ayarının kaldırılması gerekir, yani gam_sRGB işlevinden geri dönüşün int (v * 255) olması gerekir; int döndürme (v * 255 + .5); Yarım ayar yukarı yuvarlar ve bu, R = G = B'de gri renk triadında çok yüksek bir değere neden olabilir. R = G = B triadındaki gri tonlamalı dönüşüm, R'ye eşit bir değer üretmelidir; formülün geçerli olduğunun bir kanıtıdır. Bkz Greyscale Dokuz Tonlama (yarı ayarlamak olmadan) hareket formül için.


Görünüşe göre eşyalarını biliyorsun, bu yüzden +
0.5'i

Deneyi yaptım. C ++ 'da +0.5 gerekir, bu yüzden geri koymak. Diğer dillere çeviri hakkında bir yorum ekledim.
Jive Dadson

1

Bu rgb katsayılarının nasıl belirlendiğini merak ediyorum. Kendim bir deney yaptım ve aşağıdakilerle sonuçlandım:

Y = 0.267 R + 0.642 G + 0.091 B

Yakın fakat uzun süredir kurulan ITU katsayılarından farklı. Bu katsayıların her gözlemci için farklı olup olmadığını merak ediyorum, çünkü hepimiz gözlerimizdeki retinada farklı miktarda koni ve çubuklara sahip olabiliriz ve özellikle farklı koni türleri arasındaki oran farklı olabilir.

Referans için:

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

Testi parlak kırmızı, parlak yeşil ve parlak mavi bir arka plan üzerinde küçük bir gri çubuğu hızlı bir şekilde hareket ettirerek ve griyi mümkün olduğunca karışana kadar ayarlayarak yaptım. Bu testi diğer tonlarla da tekrarladım. Testi sabit bir gama faktörü 3.0 olan bile farklı ekranlarda tekrarladım, ancak hepsi bana benziyor. Dahası, ITU katsayıları tam anlamıyla gözlerim için yanlış.

Ve evet, muhtemelen normal bir renk vizyonum var.


Deneylerinizde önce gama bileşenini kaldırmak için doğrusallaştırdınız mı? Bunu yapmadıysanız, sonuçlarınızı açıklayabilir. ANCAK AYRICA, katsayılar CIE 1931 deneyleri ile ilgilidir ve bunlar ortalama 17 gözlemcidir, bu yüzden evet sonuçlarda bireysel farklılık vardır.
Myndex

1

İşte algılanan parlaklığı düzgün bir şekilde hesaplaması gereken bir miktar C kodu.

// 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
}

0

Lütfen parlaklığı tanımlayın. Beyazın ne kadar yakın olduğunu arıyorsanız , (255, 255, 255) 'den Öklid Mesafesini kullanabilirsiniz.


1
Hayır, sRGB değerleri arasında öklid mesafesini kullanamazsınız, sRGB algısal olarak tekdüze bir Kartezyen / vektör alanı değildir. Öklid mesafesini renk farkının bir ölçüsü olarak kullanmak istiyorsanız, en azından CIELAB'a dönüştürmeniz veya daha iyisi, CIECAM02 gibi bir CAM kullanmanız gerekir.
Myndex

0

HSV 'V' muhtemelen aradığınız şeydir. MATLAB bir rgb2hsv işlevine sahiptir ve daha önce belirtilen wikipedia makalesi yalancı kodla doludur. Bir RGB2HSV dönüşümü mümkün değilse, görüntünün gri tonlamalı sürümü daha az doğru bir model olacaktır.


0

Bu bağlantı , bu çarpan sabitlerinin neden R, G ve B değerlerinden önce var olduğu da dahil olmak üzere her şeyi derinlemesine açıklar.

Düzenleme: Buradaki cevaplardan birine de bir açıklaması var (0.299 * R + 0.587 * G + 0.114 * B)


0

R ile bir rengin parlaklığını belirlemek için RGB sistem rengini HSV sistem rengine dönüştürüyorum.

Senaryomda, HEX sistem kodunu daha önce başka bir nedenle kullanıyorum, ancak RGB sistem koduyla da başlayabilirsiniz rgb2hsv {grDevices}. Dokümantasyon burada .

İşte benim kod bu kısmı:

 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

0

Açıklık için, kare kök kullanan formüllerin

sqrt(coefficient * (colour_value^2))

değil

sqrt((coefficient * colour_value))^2

Bunun kanıtı, R = G = B triadının gri tonlama R'ye dönüştürülmesinde yatmaktadır. Bu, yalnızca renk değeri kare katsayısı değil, renk değerini kare yaparsanız doğru olacaktır. Dokuz Gri Tonlama Tonuna Bakın


5
parantez mistmatches vardır
log0

kullandığınız katsayı, doğru katsayının karekökü değilse.
RufusVS
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.