Formel zur Bestimmung der Helligkeit der RGB-Farbe

387

Ich suche nach einer Formel oder einem Algorithmus, um die Helligkeit einer Farbe anhand der RGB-Werte zu bestimmen. Ich weiß, dass es nicht so einfach sein kann, die RGB-Werte zu addieren und höhere Summen zu erzielen, aber ich weiß nicht, wo ich anfangen soll.

robmerica
quelle
8
Wahrgenommene Helligkeit ist das, wonach ich suche, danke.
Robmerica
2
Es gibt einen guten Artikel ( Manipulieren von Farben in .NET - Teil 1 ) über Farbräume und Konversationen zwischen ihnen, einschließlich Theorie und Code (C #). Die Antwort finden Sie im Thema Konvertierung zwischen Modellen im Artikel.
unterstreichen
4
Ich bin seit vielen Jahren Mitglied und habe dies noch nie zuvor getan. Darf ich vorschlagen, dass Sie die Antworten überprüfen und überlegen, welche Sie akzeptieren möchten?
Jive Dadson

Antworten:

456

Meinst du Helligkeit? Wahrgenommene Helligkeit? Luminanz?

  • Luminanz (Standard für bestimmte Farbräume): (0.2126*R + 0.7152*G + 0.0722*B) [1]
  • Luminanz (wahrgenommene Option 1): (0.299*R + 0.587*G + 0.114*B) [2]
  • Luminanz (wahrgenommene Option 2, langsamer zu berechnen): 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 )(dank @MatthewHerbst ) [3]
Anonym
quelle
26
Beachten Sie, dass beide die physiologischen Aspekte betonen: Der menschliche Augapfel reagiert am empfindlichsten auf grünes Licht, weniger auf rot und am wenigsten auf blau.
Bob Cross
16
Beachten Sie auch, dass all dies wahrscheinlich für lineares 0-1 RGB gilt und Sie wahrscheinlich gammakorrigiertes 0-255 RGB haben. Sie werden nicht so konvertiert, wie Sie denken.
Alex seltsam
4
Nicht richtig. Vor dem Anwenden der linearen Transformation muss zunächst die Umkehrung der Gammafunktion für den Farbraum angewendet werden. Nach dem Anwenden der linearen Funktion wird dann die Gammafunktion angewendet.
Jive Dadson
6
Ist es in der letzten Formel (0,299 * R) ^ 2 oder ist es 0,299 * (R ^ 2)?
Kaizer Sozay
3
@ KaizerSozay Wie es hier geschrieben steht, würde es bedeuten 0.299*(R^2)(weil Potenzierung vor Multiplikation geht)
Dantevg
298

Ich denke, was Sie suchen, ist die RGB -> Luma- Umrechnungsformel.

Photometrische / digitale ITU BT.709 :

Y = 0.2126 R + 0.7152 G + 0.0722 B

Digital ITU BT.601 (verleiht den R- und B-Komponenten mehr Gewicht):

Y = 0.299 R + 0.587 G + 0.114 B

Wenn Sie bereit sind, Genauigkeit gegen Leistung zu tauschen, gibt es zwei Näherungsformeln für diese:

Y = 0.33 R + 0.5 G + 0.16 B

Y = 0.375 R + 0.5 G + 0.125 B

Diese können schnell berechnet werden als

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

Y = (R+R+R+B+G+G+G+G)>>3
Franci Penov
quelle
47
Ich finde es gut, dass Sie präzise Werte eingeben, aber auch eine schnelle Verknüpfung vom Typ "nah genug" eingefügt haben. +1.
Beska
3
@ Jonathan Dumaine - die beiden Schnellberechnungsformeln enthalten beide Blau - die erste ist (2 * Rot + Blue+ 3 * Grün) / 6, die zweite ist (3 * Rot + Blue+ 4 * Grün) >> 3. Zugegeben, in beiden schnellen Näherungen hat Blau das niedrigste Gewicht, aber es ist immer noch da.
Franci Penov
84
@ JonathanDumaine Das liegt daran, dass das menschliche Auge Blau am wenigsten
wahrnimmt ;-)
4
Die Schnellversion funktioniert gut. Getestet und auf eine reale App mit Tausenden von Benutzern angewendet, sieht alles gut aus.
Milosmns
10
Die schnelle Version ist sogar noch schneller, wenn Sie Y = (R<<1+R+G<<2+B)>>3Folgendes tun: (das sind nur 3-4 CPU-Zyklen auf ARM), aber ich denke, ein guter Compiler wird diese Optimierung für Sie durchführen.
rjmunro
105

Ich habe die drei Algorithmen in der akzeptierten Antwort verglichen. Ich habe Farben im Zyklus generiert, in dem nur etwa jede 400. Farbe verwendet wurde. Jede Farbe wird durch 2x2 Pixel dargestellt, die Farben werden von dunkel bis hell sortiert (von links nach rechts, von oben nach unten).

1. Bild - Luminanz (relativ)

0.2126 * R + 0.7152 * G + 0.0722 * B

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

0.299 * R + 0.587 * G + 0.114 * B

3. Bild - HSP-Farbmodell

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

4. Bild - WCAG 2.0 SC 1.4.3 Formel für relative Luminanz und Kontrastverhältnis (siehe Antwort von @ Synchro hier )

Abhängig von der Anzahl der Farben in einer Reihe kann das Muster manchmal auf dem 1. und 2. Bild erkannt werden. Ich habe nie ein Muster auf dem Bild vom 3. oder 4. Algorithmus entdeckt.

Wenn ich mich entscheiden müsste, würde ich mich für Algorithmus Nummer 3 entscheiden, da er viel einfacher zu implementieren ist und ungefähr 33% schneller als der vierte.

Vergleich des wahrgenommenen Helligkeitsalgorithmus

Petr Hurtak
quelle
3
Für mich ist dies die beste Antwort, da Sie ein Bildmuster verwenden, mit dem Sie erkennen können, ob unterschiedliche Farbtöne mit derselben Luminanz gerendert werden. Für mich und meinen aktuellen Monitor ist das 3. Bild das "am besten aussehende", da es auch schneller als das 4. ist, was ein Plus ist
CoffeDeveloper
8
Ihr Vergleichsbild ist falsch, weil Sie nicht alle Funktionen korrekt eingegeben haben. Die erste Funktion erfordert einen linearen RGB-Eingang. Ich kann den Streifenbildungseffekt nur reproduzieren, indem ich nichtlineares (dh gammakorrigiertes) RGB bereitstelle . Wenn Sie dieses Problem beheben, erhalten Sie keine Streifenartefakte und die erste Funktion ist der klare Gewinner.
Max
1
@Max the ^2und sqrtin der dritten Formel enthalten sind eine schnellere Möglichkeit, lineares RGB von nichtlinearem RGB anstelle von ^2.2und zu approximieren, und ^(1/2.2)das wäre korrekter. Die Verwendung nichtlinearer Eingaben anstelle linearer Eingaben ist leider äußerst häufig.
Mark Ransom
53

Im Folgenden finden Sie den einzigen RICHTIGEN Algorithmus zum Konvertieren von sRGB-Bildern, wie sie in Browsern usw. verwendet werden, in Graustufen.

Vor der Berechnung des inneren Produkts muss eine Umkehrung der Gammafunktion für den Farbraum angewendet werden. Dann wenden Sie die Gammafunktion auf den reduzierten Wert an. Wenn die Gammafunktion nicht integriert wird, können Fehler von bis zu 20% auftreten.

Für typische Computer ist der Farbraum sRGB. Die richtigen Zahlen für sRGB sind ca. 0,21, 0,72, 0,07. Gamma für sRGB ist eine zusammengesetzte Funktion, die die Exponentiation um 1 / (2.2) approximiert. Hier ist das Ganze in 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
quelle
5
So wird sRGB definiert. Ich denke, der Grund dafür ist, dass einige numerische Probleme nahe Null vermieden werden. Es würde keinen großen Unterschied machen, wenn Sie nur die Zahlen auf die Potenzen 2.2 und 1 / 2.2 erhöhen würden.
Jive Dadson
8
JMD - Im Rahmen der Arbeit in einem visuellen Wahrnehmungslabor habe ich direkte Luminanzmessungen an CRT-Monitoren durchgeführt und kann bestätigen, dass sich am unteren Rand des Wertebereichs ein linearer Luminanzbereich befindet.
Jerry Federspiel
2
Ich weiß, dass dies sehr alt ist, aber es ist immer noch da draußen, um durchsucht zu werden. Ich denke nicht, dass es richtig sein kann. Sollte nicht grau (255,255,255) = grau (255,0,0) + grau (0,255,0) + grau (0,0,255) sein? Das tut es nicht.
DCBillen
2
@DCBillen: Nein, da sich die Werte im nichtlinearen gammakorrigierten sRGB-Bereich befinden, können Sie sie nicht einfach addieren. Wenn Sie sie addieren möchten, sollten Sie dies tun, bevor Sie gam_sRGB aufrufen.
Rdb
1
@DCBillen Rdb ist korrekt. Die Art und Weise, sie zu addieren, wird in der Funktion int grey (int r, int g, int b) gezeigt, die gam_sRGB "aufruft". Es schmerzt mich, dass nach vier Jahren die richtige Antwort so niedrig bewertet wird. :-) Nicht wirklich .. Ich werde darüber hinwegkommen.
Jive Dadson
45

Die Antwort "Akzeptiert" ist falsch und unvollständig

Die einzigen Antworten, die korrekt sind, sind die Antworten @ jive-dadson und @EddingtonsMonkey sowie zur Unterstützung von @ nils-pipenbrinck . Die anderen Antworten (einschließlich der akzeptierten) verweisen auf oder zitieren Quellen, die entweder falsch, irrelevant, veraltet oder fehlerhaft sind.

Kurz:

  • sRGB muss LINEARISIERT sein, bevor die Koeffizienten angewendet werden.
  • Die Luminanz (L oder Y) ist linear, ebenso wie das Licht.
  • Die wahrgenommene Leichtigkeit (L *) ist nichtlinear, ebenso wie die menschliche Wahrnehmung.
  • HSV und HSL sind in Bezug auf die Wahrnehmung nicht einmal annähernd genau.
  • Der IEC-Standard für sRGB legt einen Schwellenwert von 0,04045 fest, der NICHT 0,03928 beträgt (der aus einem veralteten frühen Entwurf stammt).
  • Die nützlichen (dh relativ zur Wahrnehmung) euklidischen Abstände erfordern einen wahrnehmungsmäßig einheitlichen kartesischen Vektorraum wie CIELAB. sRGB ist keiner.

Was folgt, ist eine korrekte und vollständige Antwort:

Da dieser Thread in Suchmaschinen häufig vorkommt, füge ich diese Antwort hinzu, um die verschiedenen Missverständnisse zu diesem Thema zu klären.

Helligkeit ist ein Wahrnehmungsattribut, es hat kein direktes Maß.

Die wahrgenommene Helligkeit wird von einigen Sehmodellen wie CIELAB gemessen. Hier ist L * (Lstar) ein Maß für die Wahrnehmungshelligkeit und nicht linear, um die nichtlineare Antwortkurve des menschlichen Sehens zu approximieren.

Die Luminanz ist ein lineares Maß für Licht, das für normales Sehen spektral gewichtet, aber für nichtlineare Wahrnehmung von Helligkeit nicht angepasst ist.

Luma ( prime) ist ein Gamma-codiertes, gewichtetes Signal, das in einigen Videocodierungen verwendet wird. Es ist nicht mit linearer Luminanz zu verwechseln.

Gamma oder Übertragungskurve (TRC) ist eine Kurve, die häufig der Wahrnehmungskurve ähnlich ist und üblicherweise auf Bilddaten zur Speicherung oder Ausstrahlung angewendet wird, um das wahrgenommene Rauschen zu reduzieren und / oder die Datennutzung (und verwandte Gründe) zu verbessern.

Um die wahrgenommene Helligkeit zu bestimmen , konvertieren Sie zuerst Gamma-codierte R´G´B´-Bildwerte in lineare Luminanz ( Loder Y) und dann in nichtlineare wahrgenommene Helligkeit ( L*)


LUMINANZ FINDEN:

... weil es anscheinend irgendwo verloren gegangen ist ...

Schritt eins:

Konvertieren Sie alle sRGB 8-Bit-Ganzzahlwerte in eine Dezimalzahl von 0,0 bis 1,0

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

Schritt zwei:

Konvertieren Sie ein Gamma-codiertes RGB in einen linearen Wert. sRGB (Computer Standard) erfordert zum Beispiel eine Leistungskurve von ungefähr V ^ 2.2, obwohl die "genaue" Transformation lautet:

sRGB auf linear

Wobei V´ der gamma-codierte R-, G- oder B-Kanal von sRGB ist.
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));
        }
    }

Schritt drei:

Um die Luminanz (Y) zu ermitteln, wenden Sie die Standardkoeffizienten für sRGB an:

Wenden Sie die Koeffizienten Y = R * 0,2126 + G * 0,7152 + B * 0,0722 an

Pseudocode mit den oben genannten Funktionen:

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

Wahrgenommene Leichtigkeit zu finden:

Schritt vier:

Nehmen Sie die Luminanz Y von oben und transformieren Sie sie in L *

L * aus der Y-Gleichung
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 * ist ein Wert von 0 (schwarz) bis 100 (weiß), wobei 50 das wahrgenommene "mittlere Grau" ist. L * = 50 ist das Äquivalent von Y = 18,4 oder mit anderen Worten eine 18% ige Graukarte, die die Mitte einer fotografischen Belichtung darstellt (Ansel Adams Zone V).

Verweise:

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

Myndex
quelle
@Rotem danke - ich habe einige merkwürdige und unvollständige Aussagen gesehen und fand es hilfreich, sie festzunageln, zumal dieser Thread bei Suchmaschinen immer noch einen hohen Stellenwert hat.
Myndex
Ich habe eine Demonstration erstellt , in der BT.601 Luma und CIE 1976 L * Perceptual Grey mit wenigen MATLAB-Befehlen verglichen wurden :Luma=rgb2gray(RGB);LAB=rgb2lab(RGB);LAB(:,:,2:3)=0;PerceptualGray=lab2rgb(LAB);
Rotem
@Myndex Ich habe Ihre Formeln verwendet, um zu L * zu gelangen, aber ich erhalte immer noch einige seltsame Ergebnisse, unabhängig von der Formel, die ich verwende ... Bei Ihnen ist L * von # d05858 dunkler als L * von # c51c2a ... Gibt es welche? Weg, um das richtig zu machen? Warum funktioniert keine Formel wie erwartet? :(
sjahan
1
@asdfasdfads Ja, L*a*b*berücksichtigt eine Reihe von psychophysischen Attributen nicht. Der Helmholtz-Kohlrausch-Effekt ist einer, aber es gibt viele andere. CIELAB ist keineswegs ein "vollständiges" Bildbewertungsmodell. In meinem Beitrag habe ich versucht, die Grundkonzepte so vollständig wie möglich zu behandeln, ohne mich in die sehr tiefen Minutien zu wagen. Das Hunt-Modell, Fairchilds Modelle und andere machen einen vollständigeren Job, sind aber auch wesentlich komplexer.
Myndex
1
@Myndex, egal, meine Implementierung basierte auf Müdigkeit und meine schlechten Ergebnisse kamen daraus :( Vielen Dank für Ihre Hilfe und Ihren Beitrag, der von großem Wert ist!
sjahan
11

Ich habe diesen Code (in C # geschrieben) gefunden, der die "Helligkeit" einer Farbe hervorragend berechnet. In diesem Szenario versucht der Code zu bestimmen, ob weißer oder schwarzer Text über die Farbe eingefügt werden soll.

sitesbyjoe
quelle
1
Genau das brauchte ich. Ich habe eine klassische "Farbbalken" -Demo gemacht und wollte sie mit der besten Schwarz-Weiß-Auswahl über der Farbe beschriften!
RufusVS
10

Interessanterweise verwendet diese Formulierung für RGB => HSV nur v = MAX3 (r, g, b). Mit anderen Worten, Sie können das Maximum von (r, g, b) als V im HSV verwenden.

Ich habe nachgesehen und auf Seite 575 von Hearn & Baker wird auf diese Weise auch "Wert" berechnet.

Von Hearn & Baker S. 319

Bobobobo
quelle
Nur für die Aufzeichnung ist der Link tot, Archivversion hier - web.archive.org/web/20150906055359/http://…
Peter
HSV ist nicht wahrnehmungsmäßig einheitlich (und es ist nicht einmal nah). Es wird nur als "bequeme" Methode zum Einstellen der Farbe verwendet, ist jedoch für die Wahrnehmung nicht relevant, und das V bezieht sich nicht auf den wahren Wert von L oder Y (CIE-Luminanz).
Myndex
9

Anstatt sich in der hier erwähnten zufälligen Auswahl von Formeln zu verlieren, empfehle ich Ihnen, die von W3C-Standards empfohlene Formel zu wählen.

Hier ist eine einfache, aber genaue PHP-Implementierung der Formeln für relative Luminanz und Kontrastverhältnis von WCAG 2.0 SC 1.4.3 . Es werden Werte erstellt, die für die Bewertung der für die WCAG-Konformität erforderlichen Verhältnisse wie auf dieser Seite geeignet sind und als solche für jede Web-App geeignet und geeignet sind. Dies ist trivial, um in andere Sprachen zu portieren.

/**
 * 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);
}
Synchro
quelle
Warum bevorzugen Sie die w3c-Definition? persönlich habe ich sowohl CCIR 601 als auch das von w3c empfohlene implementiert und war mit den Ergebnissen von CCIR 601 viel zufriedener
user151496
1
Weil es, wie gesagt, sowohl vom W3C als auch von der WCAG empfohlen wird?
Synchro
1
Die W3C-Formel ist auf mehreren Ebenen falsch. Es berücksichtigt nicht die menschliche Wahrnehmung, sie verwenden einen "einfachen" Kontrast unter Verwendung einer Luminanz, die linear und überhaupt nicht wahrnehmungsgleich ist. Unter anderem scheinen sie es auf einigen Standards von 1988 (!!!) zu basieren, die heute nicht relevant sind (diese Standards basierten auf monochromen Monitoren wie Grün / Schwarz und bezogen sich auf den Gesamtkontrast von Ein zu Aus ohne Berücksichtigung von Graustufen oder Farben).
Myndex
1
Das ist völliger Müll. Luma ist spezifisch wahrnehmbar - deshalb hat es unterschiedliche Koeffizienten für Rot, Grün und Blau. Das Alter hat nichts damit zu tun - der ausgezeichnete Wahrnehmungsfarbraum von CIE Lab stammt aus dem Jahr 1976. Der W3C-Raum ist nicht so gut, aber eine gute praktische Annäherung, die leicht zu berechnen ist. Wenn Sie etwas Konstruktives zu bieten haben, posten Sie dies anstelle leerer Kritik.
Synchro
3
Nur zum Hinzufügen / Aktualisieren : Wir untersuchen derzeit Ersatzalgorithmen, die den Wahrnehmungskontrast besser modellieren (Diskussion in Github, Ausgabe 695) . Als separate Ausgabe zu Ihrer Information beträgt der Schwellenwert für sRGB jedoch 0,04045 und nicht 0,03928, auf den aus einem veralteten frühen sRGB-Entwurf verwiesen wurde. Der maßgebliche IEC-Standard verwendet 0,04045, und es wird eine Pull-Anforderung gesendet, um diesen Fehler in der WCAG zu korrigieren. (Ref: IEC 61966-2-1: 1999) Dies ist in der Github-Ausgabe 360, obwohl zu erwähnen ist, dass es in 8 Bit keinen tatsächlichen Unterschied gibt - am Ende des Threads 360 habe ich Fehlerdiagramme, einschließlich 0,04045 / 0,03928 in 8 Bit.
Myndex
8

Um hinzuzufügen, was alle anderen gesagt haben:

Alle diese Gleichungen funktionieren in der Praxis ein bisschen gut, aber wenn Sie sehr genau sein müssen, müssen Sie zuerst die Farbe in einen linearen Farbraum konvertieren (inverses Bild-Gamma anwenden), den Gewichtsdurchschnitt der Primärfarben berechnen und - wenn Sie möchten Anzeige der Farbe - Nehmen Sie die Luminanz zurück in das Monitor-Gamma.

Der Luminanzunterschied zwischen Ingnoring-Gamma und richtigem Gamma beträgt in den dunklen Grautönen bis zu 20%.

Nils Pipenbrinck
quelle
2

Ich habe heute eine ähnliche Aufgabe in Javascript gelöst. Ich habe mich für diese getPerceivedLightness(rgb)Funktion für eine HEX RGB-Farbe entschieden. Es behandelt den Helmholtz-Kohlrausch-Effekt über die Fairchild- und Perrotta-Formel zur Luminanzkorrektur.

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

Der HSV-Farbraum sollte den Trick machen. Siehe Wikipedia-Artikel. Abhängig von der Sprache, in der Sie arbeiten, erhalten Sie möglicherweise eine Bibliothekskonvertierung.

H ist der Farbton, der ein numerischer Wert für die Farbe ist (dh rot, grün ...)

S ist die Sättigung der Farbe, dh wie 'intensiv' sie ist

V ist die 'Helligkeit' der Farbe.

Ian Hopkinson
quelle
7
Das Problem mit dem HSV-Farbraum ist, dass Sie für Blau und Gelb die gleiche Sättigung und den gleichen Wert, aber unterschiedliche Farbtöne haben können . Gelb ist viel heller als Blau. Gleiches gilt für HSL.
Ian Boyd
hsv gibt Ihnen die "Helligkeit" einer Farbe im technischen Sinne. in einer Wahrnehmungshelligkeit versagt hsv wirklich
user151496
HSV und HSL sind nicht wahrnehmungsgenau (und es ist nicht einmal nah). Sie sind nützlich für "Steuerelemente" zum Einstellen der relativen Farbe, jedoch nicht zur genauen Vorhersage der Wahrnehmungshelligkeit. Verwenden Sie L * von CIELAB für die Wahrnehmungshelligkeit.
Myndex
1

RGB-Luminanzwert = 0,3 R + 0,59 G + 0,11 B.

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

Wenn Sie suchen, wie nah die Farbe an Weiß ist, können Sie den euklidischen Abstand von (255, 255, 255) verwenden.

Ich denke, der RGB-Farbraum ist in Bezug auf den euklidischen L2-Abstand wahrnehmbar ungleichmäßig. Einheitliche Räume umfassen CIE LAB und LUV.


quelle
1

Bei der Inverse-Gamma-Formel von Jive Dadson muss die halbe Anpassung entfernt werden, wenn sie in Javascript implementiert wird, dh die Rückgabe von der Funktion gam_sRGB muss int (v * 255) sein. nicht int zurückgeben (v * 255 + .5); Eine halbe Anpassung rundet auf, und dies kann dazu führen, dass bei einer R = G = B, dh einer grauen Farbtriade, ein Wert zu hoch ist. Die Graustufenumwandlung auf einer R = G = B-Triade sollte einen Wert gleich R ergeben. Es ist ein Beweis dafür, dass die Formel gültig ist. Siehe Nine Shades of Greyscale für die Formel in Aktion (ohne die halbe Anpassung).

Dave Collier
quelle
Es hört sich so an, als ob Sie sich auskennen, also habe ich die +0.5
Jive Dadson
Ich habe das Experiment gemacht. In C ++ benötigt es +0.5, also habe ich es wieder eingefügt. Ich habe einen Kommentar zum Übersetzen in andere Sprachen hinzugefügt.
Jive Dadson
1

Ich frage mich, wie diese RGB-Koeffizienten bestimmt wurden. Ich habe selbst ein Experiment durchgeführt und am Ende Folgendes festgestellt:

Y = 0.267 R + 0.642 G + 0.091 B

Nah, aber offensichtlich anders als die seit langem etablierten ITU-Koeffizienten. Ich frage mich, ob diese Koeffizienten für jeden Beobachter unterschiedlich sein könnten, da wir alle möglicherweise eine unterschiedliche Anzahl von Zapfen und Stäbchen auf der Netzhaut in unseren Augen haben und insbesondere das Verhältnis zwischen den verschiedenen Zapfentypen unterschiedlich sein kann.

Als Referenz:

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

Ich habe den Test durchgeführt, indem ich schnell einen kleinen grauen Balken auf einem leuchtend roten, hellgrünen und hellblauen Hintergrund verschoben und das Grau angepasst habe, bis es sich so gut wie möglich eingemischt hat. Ich habe diesen Test auch mit anderen Farben wiederholt. Ich habe den Test auf verschiedenen Displays wiederholt, sogar auf einem mit einem festen Gammafaktor von 3,0, aber für mich sieht alles gleich aus. Darüber hinaus sind die ITU-Koeffizienten für meine Augen buchstäblich falsch.

Und ja, ich habe vermutlich ein normales Farbsehen.

Wirbel
quelle
Haben Sie in Ihren Experimenten linearisiert, um zuerst die Gammakomponente zu entfernen? Wenn Sie dies nicht tun, könnte dies Ihre Ergebnisse erklären. ABER AUCH die Koeffizienten beziehen sich auf die CIE 1931-Experimente und das sind durchschnittlich 17 Beobachter, also gibt es ja individuelle Unterschiede in den Ergebnissen.
Myndex
1

Hier ist ein bisschen C-Code, der die wahrgenommene Luminanz richtig berechnen sollte.

// 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
quelle
0

Bitte definieren Sie die Helligkeit. Wenn Sie suchen, wie nah die Farbe an Weiß ist, können Sie den euklidischen Abstand von (255, 255, 255) verwenden.

Ben S.
quelle
1
Nein, Sie können den euklidischen Abstand zwischen sRGB-Werten nicht verwenden. SRGB ist kein wahrnehmungseinheitlicher kartesischer / Vektorraum. Wenn Sie den euklidischen Abstand als Maß für den Farbunterschied verwenden möchten, müssen Sie mindestens in CIELAB konvertieren oder noch besser eine CAM wie CIECAM02 verwenden.
Myndex
0

Das 'V' des HSV ist wahrscheinlich das, wonach Sie suchen. MATLAB hat eine rgb2hsv-Funktion und der zuvor zitierte Wikipedia-Artikel ist voller Pseudocode. Wenn eine RGB2HSV-Konvertierung nicht möglich ist, ist die Graustufenversion des Bildes ein weniger genaues Modell.

Jacob
quelle
0

Dieser Link erklärt alles ausführlich, einschließlich der Gründe, warum diese Multiplikator-Konstanten vor den R-, G- und B-Werten existieren.

Bearbeiten: Es gibt auch hier eine Erklärung zu einer der Antworten (0,299 * R + 0,587 * G + 0,114 * B)

dsignr
quelle
0

Um die Helligkeit einer Farbe mit R zu bestimmen, konvertiere ich die RGB-Systemfarbe in die HSV-Systemfarbe.

In meinem Skript verwende ich den HEX-Systemcode zuvor aus einem anderen Grund, aber Sie können auch mit RGB-Systemcode mit beginnen rgb2hsv {grDevices}. Die Dokumentation finden Sie hier .

Hier ist dieser Teil meines Codes:

 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
quelle
0

Aus Gründen der Klarheit müssen die Formeln, die eine Quadratwurzel verwenden, sein

sqrt(coefficient * (colour_value^2))

nicht

sqrt((coefficient * colour_value))^2

Der Beweis dafür liegt in der Umwandlung einer R = G = B-Triade in Graustufen R. Dies gilt nur, wenn Sie den Farbwert quadrieren, nicht den Farbwert mal den Koeffizienten. Siehe Neun Graustufentöne

Dave Collier
quelle
5
Es gibt Klammern Mistmatches
log0
es sei denn, der von Ihnen verwendete Koeffizient ist die Quadratwurzel des richtigen Koeffizienten.
RufusVS