565 Stimmen

Formel zur Bestimmung der wahrgenommenen Helligkeit der RGB-Farbe

Ich suche nach einer Art Formel oder Algorithmus, um die Helligkeit einer Farbe anhand der RGB-Werte zu bestimmen. Ich weiß, dass es nicht so einfach ist, die RGB-Werte zusammenzuzählen und höhere Summen heller zu machen, aber ich bin irgendwie ratlos, wo ich anfangen soll.

10 Stimmen

Die wahrgenommene Helligkeit ist das, wonach ich suche, danke.

0 Stimmen

Ich habe [diesen Code][1] (geschrieben in C#) gefunden, der hervorragende Arbeit bei der Berechnung der "Helligkeit" einer Farbe leistet. In diesem Szenario versucht der Code festzustellen, ob weißer oder schwarzer Text über der Farbe platziert werden soll. [1]:nbdtech.com/Blog/archive/2008/04/27/…

2 Stimmen

Es gibt einen guten Artikel (Farbmanipulation in .NET - Teil 1) über Farbräume und deren Konvertierungen, einschließlich sowohl Theorie als auch Code (C#). Für die Antwort schauen Sie sich das Thema Konvertierung zwischen Modellen im Artikel an.

8voto

catamphetamine Punkte 3817

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 handelt sich um den Helmholtz-Kohlrausch-Effekt über die Fairchild- und Perrotta-Formel zur Helligkeitskorrektur.

/**
 * Konvertiert RGB-Farbe in den CIE 1931 XYZ-Farbraum.
 * 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
    // Aus irgendeinem Grund werden X, Y und Z mit 100 multipliziert.
    return [X, Y, Z].map(_ => _ * 100)
}

/**
 * Hebt die Gamma-Korrektur einer RGB-codierten Farbe auf.
 * https://en.wikipedia.org/wiki/SRGB#Specification_of_the_transformation
 * https://stackoverflow.com/questions/596216/formula-to-determine-brightness-of-rgb-color
 * @param {number}
 * @return {number}
 */
function sRGBtoLinearRGB(color) {
    // Übergeben Sie dieser Funktion einen dezimalen sRGB-gamma-codierten Farbwert
    // zwischen 0,0 und 1,0, und sie gibt einen linearisierten Wert zurück.
    if (color <= 0,04045) {
        return color / 12,92
    } else {
        return Math.pow((color + 0,055) / 1,055, 2,4)
    }
}

/**
 * Konvertiert Hex-Farbe in RGB.
 * https://stackoverflow.com/questions/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))
    }
}

/**
 * Konvertiert CIE 1931 XYZ-Farben in CIE L*a*b*.
 * Die Konversionsformel stammt von .
 * https://github.com/cangoektas/xyz-to-lab/blob/master/src/index.js
 * @param {number[]} color Die zu konvertierende CIE 1931 XYZ-Farbe, die sich auf
 * das D65/2°-Standardbeleuchtung bezieht.
 * @returns {number[]} Die Farbe im CIE L*a*b*-Farbraum.
 */
// X, Y, Z einer "D65" Lichtquelle.
// "D65" ist eine Standard-Tageslichtlichtquelle von 6500K.
// 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]
}

/**
 * Konvertiert Lab-Farbraum in Luminance-Chroma-Hue-Farbraum.
 * 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]
}

/**
 * Konvertiert a und b des Lab-Farbraums in Hue des LCH-Farbraums.
 * https://stackoverflow.com/questions/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
}

/**
 * Sättigte Farben erscheinen dem menschlichen Auge heller.
 * Das nennt man den Helmholtz-Kohlrausch-Effekt.
 * Fairchild und Pirrotta haben eine Formel entwickelt, um
 * einen Korrekturfaktor für diesen Effekt zu berechnen.
 * "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))))
}

5voto

EddingtonsMonkey Punkte 102

Hier ist ein Stück C-Code, das die wahrgenommene Helligkeit korrekt berechnen soll.

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

   // An diesem Punkt haben wir RGBtoXYZ durchgeführt, jetzt XYZ zu Lab

   // y /= WHITEPOINT_Y; Der weiße Punkt für y in D65 ist 1.0

    y = LABF(y);

   /* Dies ist die "normale Konvertierung, die Werte im Bereich von 100 skaliert
    Lab.L = 116,0*y - 16,0;
   */
   return(1,16*y - 0,16); // Rückgabewerte für 0,0 >=L <=1,0
}

2voto

joe Punkte 1581

Wie von @Nils Pipenbrinck erwähnt:

All diese Gleichungen funktionieren in der Praxis ziemlich gut, aber wenn Sie sehr genau sein müssen, müssen Sie [einige zusätzliche Gamma-Sachen machen]. Der Leuchtdichtunterschied zwischen dem Ignorieren von Gamma und dem korrekten Umgang mit Gamma beträgt bis zu 20% bei dunklen Grautönen.

Hier ist eine vollständig eigenständige JavaScript-Funktion, die die "zusätzlichen" Schritte ausführt, um diese zusätzliche Genauigkeit zu erzielen. Sie basiert auf Jive Dadsons C++-Antwort auf dieselbe Frage.

// Gibt die Grauskala-"Helligkeit" (0-1) der gegebenen 0-255 RGB-Werte zurück
// Basierend auf dieser C++-Implementierung: https://stackoverflow.com/a/13558570/11950764
function rgbBrightness(r, g, b) {
  let v = 0;
  v += 0.212655 * ((r/255) <= 0.04045 ? (r/255)/12.92 : Math.pow(((r/255)+0.055)/1.055, 2.4));
  v += 0.715158 * ((g/255) <= 0.04045 ? (g/255)/12.92 : Math.pow(((g/255)+0.055)/1.055, 2.4));
  v += 0.072187 * ((b/255) <= 0.04045 ? (b/255)/12.92 : Math.pow(((b/255)+0.055)/1.055, 2.4));
  return v <= 0.0031308 ? v*12.92 : 1.055 * Math.pow(v,1.0/2.4) - 0.055;
}

Siehe Myndex's Antwort für eine genauere Berechnung.

1voto

Jan Punkte 11

Die Antwort von Myindex in Java kodiert:

public static double calculateRelativeLuminance(Color color)
{
    double red = color.getRed() / 255.0;
    double green = color.getGreen() / 255.0;
    double blue = color.getBlue() / 255.0;

    double r = (red <= 0.04045) ? red / 12.92 : Math.pow((red + 0.055) / 1.055, 2.4);
    double g = (green <= 0.04045) ? green / 12.92 : Math.pow((green + 0.055) / 1.055, 2.4);
    double b = (blue <= 0.04045) ? blue / 12.92 : Math.pow((blue + 0.055) / 1.055, 2.4);

    return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}

Ich habe es verwendet, um das Kontrastverhältnis der Hintergrundfarbe zu berechnen und zu bestimmen, ob die Textfarbe hell ist oder nicht. Vollständiges Beispiel:

public static boolean isBright(Color backgroundColor)
{
    double backgroundLuminance = calculateRelativeLuminance(backgroundColor);
    double whiteContrastRatio = calculateContrastRatio(backgroundLuminance, 1.0);
    double blackContrastRatio = calculateContrastRatio(backgroundLuminance, 0.0);
    return whiteContrastRatio > blackContrastRatio;
}

public static double calculateRelativeLuminance(Color color)
{
    double red = color.getRed() / 255.0;
    double green = color.getGreen() / 255.0;
    double blue = color.getBlue() / 255.0;

    double r = (red <= 0.04045) ? red / 12.92 : Math.pow((red + 0.055) / 1.055, 2.4);
    double g = (green <= 0.04045) ? green / 12.92 : Math.pow((green + 0.055) / 1.055, 2.4);
    double b = (blue <= 0.04045) ? blue / 12.92 : Math.pow((blue + 0.055) / 1.055, 2.4);

    return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}

public static double calculateContrastRatio(double backgroundLuminance, double textLuminance)
{
    var brightest = Math.max(backgroundLuminance, textLuminance);
    var darkest = Math.min(backgroundLuminance, textLuminance);
    return (brightest + 0.05) / (darkest + 0.05);
}

1voto

vortex Punkte 83

Ich frage mich, wie diese RGB-Koeffizienten bestimmt wurden. Ich habe selbst ein Experiment gemacht und bin zu folgendem Ergebnis gekommen:

Y = 0,267 R + 0,642 G + 0,091 B

Nahe dran, aber offensichtlich anders als die etablierten ITU-Koeffizienten. Ich frage mich, ob diese Koeffizienten für jeden Beobachter unterschiedlich sein könnten, da wir alle eine unterschiedliche Anzahl von Zapfen und Stäbchen auf der Netzhaut unserer Augen haben können und insbesondere das Verhältnis zwischen den verschiedenen Arten von Zapfen variieren kann.

Zur 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 gemacht, indem ich eine kleine graue Leiste schnell auf einem hellroten, hellgrünen und hellblauen Hintergrund bewegt habe und das Grau angepasst habe, bis es so gut wie möglich verschmolz. Ich habe den Test auch mit anderen Schattierungen wiederholt. Ich habe den Test auf verschiedenen Bildschirmen 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 buchstäblich falsch für meine Augen.

Und ja, ich habe vermutlich eine normale Farbwahrnehmung.

0 Stimmen

In deinen Experimenten hast du lineare Regression durchgeführt, um zuerst die Gammakomponente zu entfernen? Wenn nicht, könnte das deine Ergebnisse erklären. ABER AUCH die Koeffizienten sind mit den CIE-1931-Experimenten verbunden und diese sind ein Durchschnitt von 17 Beobachtern, also ja, es gibt individuelle Varianz in den Ergebnissen.

0 Stimmen

Und zusätzlich: Die CIE-Werte von 1931 basieren auf einem 2°-Beobachter, und es gibt bekannte Fehler im blauen Bereich. Die Werte des 10°-Beobachters sind signifikant unterschiedlich, da die S-Konen nicht im zentralen Sehbereich der Fovea liegen. In beiden Fällen wurde darauf geachtet, Stabeintritte zu vermeiden, indem die Helligkeitswerte im fotopischen Bereich gehalten wurden. Wenn Messungen im mesopischen Bereich durchgeführt werden, werden auch Stabeintritte die Ergebnisse verfälschen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X