Was ist falsch an diesem RGB-zu-XYZ-Farbraumkonvertierungsalgorithmus?

Lesezeit: 10 Minuten

Benutzer-Avatar
Geheimdienst – nicht wirklich

Mein Ziel ist es, ein RGB-Pixel für einige spezielle Berechnungen, die nur in CIELab möglich sind, in den CIELab-Farbraum zu konvertieren. Dazu muss ich zuerst RGB in XYZ konvertieren, was der wirklich schwierige Teil ist.

Ich habe versucht, diesen Algorithmus in Objective-C zu implementieren (meistens jedoch mit einfachem C), aber die Ergebnisse sind falsch.

Mein Code basiert auf der von bereitgestellten Pseudo-Implementierung easyrgb.com. Sie haben einen Online-Farbkonverter, der großartig funktioniert. Sie sagen, dass ihr Pseudocode derselbe ist, der in ihrem Konverter verwendet wird.

Dies ist ihr Pseudo-Code:

var_R = ( R / 255 )        //R from 0 to 255
var_G = ( G / 255 )        //G from 0 to 255
var_B = ( B / 255 )        //B from 0 to 255

if ( var_R > 0.04045 ) var_R = ( ( var_R + 0.055 ) / 1.055 ) ^ 2.4
else                   var_R = var_R / 12.92
if ( var_G > 0.04045 ) var_G = ( ( var_G + 0.055 ) / 1.055 ) ^ 2.4
else                   var_G = var_G / 12.92
if ( var_B > 0.04045 ) var_B = ( ( var_B + 0.055 ) / 1.055 ) ^ 2.4
else                   var_B = var_B / 12.92

var_R = var_R * 100
var_G = var_G * 100
var_B = var_B * 100

//Observer. = 2°, Illuminant = D65
X = var_R * 0.4124 + var_G * 0.3576 + var_B * 0.1805
Y = var_R * 0.2126 + var_G * 0.7152 + var_B * 0.0722
Z = var_R * 0.0193 + var_G * 0.1192 + var_B * 0.9505

Dies ist mein Versuch, es in Objective-C / C zu implementieren:

void convertRGBtoXYZ(NSInteger * inR, NSInteger * inG, NSInteger * inB, CGFloat * outX, CGFloat * outY, CGFloat * outZ) {
    // http://www.easyrgb.com/index.php?X=MATH&H=02#text2

    CGFloat var_R = (*inR / 255); //R from 0 to 255
    CGFloat var_G = (*inG / 255); //G from 0 to 255
    CGFloat var_B = (*inB / 255); //B from 0 to 255

    if (var_R > 0.04045f) {
        var_R = powf(( (var_R + 0.055f) / 1.055f), 2.4f);
    } else {
        var_R = var_R / 12.92f;
    }

    if (var_G > 0.04045) {
        var_G = powf(( (var_G + 0.055f) / 1.055f), 2.4f);
    } else {
        var_G = var_G / 12.92f;
    }

    if (var_B > 0.04045f) {
        var_B = powf(( (var_B + 0.055f) / 1.055f), 2.4f);
    } else {
        var_B = var_B / 12.92f;
    }

    var_R = var_R * 100;
    var_G = var_G * 100;
    var_B = var_B * 100;

    //Observer. = 2°, Illuminant = D65
    *outX = var_R * 0.4124f + var_G * 0.3576f + var_B * 0.1805f;
    *outY = var_R * 0.2126f + var_G * 0.7152f + var_B * 0.0722f;
    *outZ = var_R * 0.0193f + var_G * 0.1192f + var_B * 0.9505f;
}

Ich erhalte jedoch nicht die gleichen Ergebnisse wie ihr Tool (mit derselben Observer- und Illuminant-Einstellung).

In meinem Test habe ich diese Werte in ihr Tool eingegeben und dieses Ergebnis für XYZ erhalten, das weit von dem entfernt ist, was meine Implementierung für diesen RGB-Wert erzeugt. Siehe Screenshot:


Bildschirmfoto


Die resultierenden Lab-Farbwerte entsprechen ziemlich genau den Angaben von Photoshop, sodass der Konverter hervorragend funktioniert.

Der obige C-Code gibt mir jedoch folgende Ergebnisse:

X = 35.76... // should be 42.282
Y = 71.52... // should be 74.129
Z = 11.92... // should be 46.262

Irgendeine Idee, was die Ursache für diesen Fehler ist? Habe ich einen Fehler in meiner Implementierung gemacht oder brauche ich andere Konstanten?

Wenn Sie einige getestete RGB-zu-XYZ-, XYZ-zu-CIELab- oder RGB-zu-CIELab-, XYZ-zu-Lab- oder RGB-zu-Lab-Implementierungen kennen, zögern Sie bitte nicht, sie hier zu posten.

Im Grunde möchte ich nur die Abweichung zwischen zwei Farben berechnen, auch bekannt als Delta-E. Deshalb muss ich von RGB in XYZ in Lab (oder CIELab) konvertieren …

  • Ist es Ihnen gelungen, Photoshop-Werte zu replizieren? Danke.

    – Royi

    2. März 2017 um 9:06 Uhr

Ich glaube, hier ist Ihr Problem, das wird auf eine ganze Zahl gekürzt:

CGFloat var_R = (*inR / 255); //R from 0 to 255
CGFloat var_G = (*inG / 255); //G from 0 to 255
CGFloat var_B = (*inB / 255); //B from 0 to 255

Versuche dies:

CGFloat var_R = (*inR / 255.0f); //R from 0 to 255
CGFloat var_G = (*inG / 255.0f); //G from 0 to 255
CGFloat var_B = (*inB / 255.0f); //B from 0 to 255

Ich habe den Rest des Codes nicht auf andere Probleme überprüft.

  • Wie konnte ich das übersehen?! Bekomme jetzt genau die gleichen Ergebnisse. Vielen Dank!!

    – SecretService – nicht wirklich

    8. Juli 2011 um 20:38 Uhr


  • Für Python: var_R = ( sR / 255.0 ) var_G = ( sG / 255.0 ) var_B = ( sB / 255.0 )

    Benutzer6867490

    17. September 2017 um 19:50 Uhr

*inR/255 ist eine ganzzahlige Division. 1/255 ist null. Schreiben Sie stattdessen *inR/255.0.

  • Vielen Dank! Das war die Lösung! Ich habe die Antwort von Hexa jedoch der Vollständigkeit halber akzeptiert. Es war eine schwere Entscheidung, sich zwischen ihnen zu entscheiden.

    – SecretService – nicht wirklich

    8. Juli 2011 um 20:41 Uhr

Benutzer-Avatar
George Garyfallou

#include <stdio.h>
#include <math.h>

float ref_X = 95.047;
float ref_Y = 100.0;
float ref_Z = 108.883;


void convertRGBtoXYZ(int inR, int inG, int inB, float * outX, float * outY, float * outZ) {


    float var_R = (inR / 255.0f); //R from 0 to 255
    float var_G = (inG / 255.0f); //G from 0 to 255
    float var_B = (inB / 255.0f); //B from 0 to 255

    if (var_R > 0.04045f)
        var_R = powf(( (var_R + 0.055f) / 1.055f), 2.4f);
    else 
        var_R = var_R / 12.92f;

    if (var_G > 0.04045)
        var_G = powf(( (var_G + 0.055f) / 1.055f), 2.4f);
    else
        var_G = var_G / 12.92f;

    if (var_B > 0.04045f)
        var_B = powf(( (var_B + 0.055f) / 1.055f), 2.4f);
    else
        var_B = var_B / 12.92f;

    var_R = var_R * 100;
    var_G = var_G * 100;
    var_B = var_B * 100;

    //Observer. = 2°, Illuminant = D65
    *outX = var_R * 0.4124f + var_G * 0.3576f + var_B * 0.1805f;
    *outY = var_R * 0.2126f + var_G * 0.7152f + var_B * 0.0722f;
    *outZ = var_R * 0.0193f + var_G * 0.1192f + var_B * 0.9505f;
}

void convertXYZtoLab(float inX, float inY, float inZ, float * outL, float * outa, float * outb) {

    float var_X = (inX / ref_X); //ref_X = 95.047
    float var_Y = (inY / ref_Y); //ref_Y = 100.0
    float var_Z = (inZ / ref_Z); //ref_Z = 108.883

    if ( var_X > 0.008856 ) 
        var_X = powf(var_X , ( 1.0f/3 )); 
    else 
        var_X = ( 7.787 * var_X ) + ( 16.0f/116 );

    if ( var_Y > 0.008856 )
        var_Y = powf(var_Y , ( 1.0f/3 )); 
    else
        var_Y = ( 7.787 * var_Y ) + ( 16.0f/116 );

    if ( var_Z > 0.008856 )
        var_Z = powf(var_Z , ( 1.0f/3 )); 
    else 
        var_Z = ( 7.787 * var_Z ) + ( 16.0f/116 );

    *outL = ( 116 * var_Y ) - 16;
    *outa = 500 * ( var_X - var_Y );
    *outb = 200 * ( var_Y - var_Z );
}

void convertLabtoXYZ( float inL, float ina, float  inb, float * outX, float * outY, float * outZ) {

    float var_Y = ( inL + 16 ) / 116;
    float var_X = (ina/500) + var_Y;
    float var_Z = var_Y - (inb/200);

    if ( powf(var_Y,3.f) > 0.008856 ) 
        var_Y = powf(var_Y,3.f);
    else
        var_Y = ( var_Y - (16/116) ) / 7.787;

    if ( powf(var_X,3.f) > 0.008856 ) 
        var_X = powf(var_X,3.f);
    else 
        var_X = ( var_X - (16/116) ) / 7.787;

    if ( powf(var_Z,3.f) > 0.008856 )
        var_Z = powf(var_Z,3.f);
    else
        var_Z = ( var_Z - (16/116) ) / 7.787;

    *outX = ref_X * var_X;     //ref_X =  95.047     Observer= 2°, Illuminant= D65
    *outY = ref_Y * var_Y;     //ref_Y = 100.000
    *outZ = ref_Z * var_Z;     //ref_Z = 108.883
}

void convertXYZtoRGB(float inX, float inY, float inZ, int * outR, int * outG, int * outB) {


    float var_X = inX/100;
    float var_Y = inY/100;
    float var_Z = inZ/100;

    float var_R = var_X *  3.2406 + (var_Y * -1.5372) + var_Z * (-0.4986);
    float var_G = var_X * (-0.9689) + var_Y *  1.8758 + var_Z *  0.0415;
    float var_B = var_X *  0.0557 + var_Y * (-0.2040) + var_Z *  1.0570;

    if ( var_R > 0.0031308 )
        var_R = 1.055 * powf(var_R, ( 1.0f / 2.4 ) )  - 0.055;
    else 
        var_R = 12.92 * var_R;

    if ( var_G > 0.0031308 ) 
        var_G = 1.055 * powf(var_G, ( 1.0f / 2.4 ) ) - 0.055;
    else 
        var_G = 12.92 * var_G;

    if ( var_B > 0.0031308 )
        var_B = 1.055 * powf(var_B, ( 1.0f / 2.4 ) ) - 0.055;
    else
        var_B = 12.92 * var_B;

    *outR = (int)(var_R * 255);
    *outG = (int)(var_G * 255);
    *outB = (int)(var_B * 255);


}

float Lab_color_difference( float inL1, float ina1, float  inb1, float inL2, float ina2, float  inb2){
    return( sqrt( powf(inL1 - inL2, 2.f) + powf(ina1 - ina2, 2.f) + powf(inb1 - inb2, 2.f) ) );
}

float RGB_color_Lab_difference( int R1, int G1, int B1, int R2, int G2, int B2){
    float x1=0,y1=0,z1=0;
    float x2=0,y2=0,z2=0;
    float l1=0,a1=0,b1=0;
    float l2=0,a2=0,b2=0;

    convertRGBtoXYZ(R1, G1, B1, &x1, &x1, &z1);
    convertRGBtoXYZ(R2, G2, B2, &x2, &x2, &z2);

    convertXYZtoLab(x1, y1, z1, &l1, &a1, &b1);
    convertXYZtoLab(x2, y2, z2, &l2, &a2, &b2); 

    return( Lab_color_difference(l1 ,a1 ,b1 ,l2 ,a2 ,b2) );
}


void main(int argc, char const *argv[])
{
    int R1,G1,B1,R2,G2,B2;
    float x=0.f,y=0.f,z=0.f;
    float l=0.f,a=0.f,b=0.f;

    R1 = 200;
    G1 = 2;
    B1 = 50; 

    R2 = 200;
    G2 = 2;
    B2 = 70; 

    printf("LAB DISTANCE = %lf \n", RGB_color_Lab_difference(R1,G1,B1,R2,G2,B2) );

    /*convertRGBtoXYZ(R, G, B, &x, &y, &z);
    printf("R =%d,G =%d,B =%d,x =%lf,y =%lf,z =%lf\n",R,G,B,x,y,z );
    convertXYZtoLab(x, y, z, &l, &a, &b);
    printf("x =%lf,y =%lf,z =%lf, l =%lf,a =%lf,b =%lf\n",x,y,z, l,a,b );
    convertLabtoXYZ( l, a, b ,&x, &y, &z);
    printf("x =%lf,y =%lf,z =%lf, l =%lf,a =%lf,b =%lf\n",x,y,z, l,a,b );
    convertXYZtoRGB( x, y, z,&R, &G, &B);
    printf("R =%d,G =%d,B =%d,x =%lf,y =%lf,z =%lf\n",R,G,B,x,y,z );*/

}

Farbkonvertierungen und Unterschiede in C https://github.com/gi0rikas/Color-conversions Laborabstand ~ = 2,3 entspricht JND (gerade wahrnehmbarer Unterschied)

Benutzer-Avatar
Säbel

Die richtigen Werte dieser Matrix unterscheiden sich geringfügig von den genauen Werten von “RGB / XYZ-Matrizen”. http://www.brucelindbloom.com

sX = sRed * 0.4124564 + sGreen * 0.3575761 + sBlue * 0.1804375
sY = sRed * 0.2126729 + sGreen * 0.7151522 + sBlue * 0.072175
sZ = sRed * 0.0193339 + sGreen * 0.119192 + sBlue * 0.9503041

Ich verwende nur Ihren Code, um von RGB (in XYZ) in La * b * zu konvertieren, und ich habe gerade festgestellt, dass die XYZ-Werte zwischen 0 und 1 liegen sollten, bevor Sie versuchen, sie in La * b * zu konvertieren

var_R = var_R * 100;
var_G = var_G * 100;
var_B = var_B * 100;

Daher muss der vorherige Code gelöscht werden, um korrekte La*b*-Werte zu erhalten.

  • Aus Ihrer Antwort geht nicht hervor, wo der Code gelöscht werden sollte und warum. Sie sollten Ihren Code (es ist ein wesentlicher Teil) für die Konvertierung von RGB nach La besser postenb.

    – Artemix

    15. Juli 2013 um 15:14 Uhr

Benutzer-Avatar
Omotto

Wie sie sagten:

var_R = ( R / 255 )  ->  var_R = ( R / 255.0 ) or var_R = ( R * 0.003922 ) 
var_G = ( G / 255 )  ->  var_G = ( G / 255.0 ) or var_G = ( G * 0.003922 )
var_B = ( B / 255 )  ->  var_B = ( B / 255.0 ) or var_B = ( B * 0.003922 )

Dies liegt an der impliziten Konvertierung. Trotz der Tatsache, dass die Variablen var_R, var_G und var_B Float-Typen sind, sieht der /-Operator zwei ganze Zahlen R, G, B und 255. Er muss dividieren und gibt eine ganze Zahl zurück.

Um einen Float-Wert zu erhalten, können Sie einen CAST durchführen oder mindestens eine der Variablen in einen Float-Typ umwandeln, indem Sie wie folgt einen Dezimalpunkt hinzufügen:

var_B = ( B / 255.0f)

Ein weiteres Beispiel für die RGB2LAB- und LAB2RGB-Konvertierung (denken Sie daran, dass dies CIE La istb D65):

void RGB2LAB(uint8_t R, uint8_t G, uint8_t B, float *l, float *a, float *b) {
    float RGB[3], XYZ[3];

    RGB[0] = R * 0.003922;
    RGB[1] = G * 0.003922;
    RGB[2] = B * 0.003922;

    RGB[0] = (RGB[0] > 0.04045) ? pow(((RGB[0] + 0.055)/1.055), 2.4) : RGB[0] / 12.92;
    RGB[1] = (RGB[1] > 0.04045) ? pow(((RGB[1] + 0.055)/1.055), 2.4) : RGB[1] / 12.92;
    RGB[2] = (RGB[2] > 0.04045) ? pow(((RGB[2] + 0.055)/1.055), 2.4) : RGB[2] / 12.92;

    XYZ[0] = 0.412424  * RGB[0] + 0.357579 * RGB[1] + 0.180464  * RGB[2];
    XYZ[1] = 0.212656  * RGB[0] + 0.715158 * RGB[1] + 0.0721856 * RGB[2];
    XYZ[2] = 0.0193324 * RGB[0] + 0.119193 * RGB[1] + 0.950444  * RGB[2];

    *l = 116 * ( ( XYZ[1] / 1.000000) > 0.008856 ? pow(XYZ[1] / 1.000000, 0.333333) : 7.787 * XYZ[1] / 1.000000 + 0.137931) - 16;
    *a = 500 * ( ((XYZ[0] / 0.950467) > 0.008856 ? pow(XYZ[0] / 0.950467, 0.333333) : 7.787 * XYZ[0] / 0.950467 + 0.137931) - ((XYZ[1] / 1.000000) > 0.008856 ? pow(XYZ[1] / 1.000000, 0.333333) : 7.787 * XYZ[1] / 1.000000 + 0.137931) );
    *b = 200 * ( ((XYZ[1] / 1.000000) > 0.008856 ? pow(XYZ[1] / 1.000000, 0.333333) : 7.787 * XYZ[1] / 1.000000 + 0.137931) - ((XYZ[2] / 1.088969) > 0.008856 ? pow(XYZ[2] / 1.088969, 0.333333) : 7.787 * XYZ[2] / 1.088969 + 0.137931) );
}

void LAB2RGB(float L, float A, float B, uint8_t *r, uint8_t *g, uint8_t *b) {
    float XYZ[3], RGB[3];

    XYZ[1] = (L + 16 ) / 116;
    XYZ[0] = A / 500 + XYZ[1];
    XYZ[2] = XYZ[1] - B / 200;

    XYZ[1] = (XYZ[1]*XYZ[1]*XYZ[1] > 0.008856) ? XYZ[1]*XYZ[1]*XYZ[1] : (XYZ[1] - (16 / 116)) / 7.787;
    XYZ[0] = (XYZ[0]*XYZ[0]*XYZ[0] > 0.008856) ? XYZ[0]*XYZ[0]*XYZ[0] : (XYZ[0] - (16 / 116)) / 7.787;
    XYZ[2] = (XYZ[2]*XYZ[2]*XYZ[2] > 0.008856) ? XYZ[2]*XYZ[2]*XYZ[2] : (XYZ[2] - (16 / 116)) / 7.787;

    RGB[0] = 0.950467 * XYZ[0] *  3.2406 + 1.000000 * XYZ[1] * -1.5372 + 1.088969 * XYZ[2] * -0.4986;
    RGB[1] = 0.950467 * XYZ[0] * -0.9689 + 1.000000 * XYZ[1] *  1.8758 + 1.088969 * XYZ[2] *  0.0415;
    RGB[2] = 0.950467 * XYZ[0] *  0.0557 + 1.000000 * XYZ[1] * -0.2040 + 1.088969 * XYZ[2] *  1.0570;

    *r = (255 * ( (RGB[0] > 0.0031308) ? 1.055 * (pow(RGB[0], (1/2.4)) - 0.055) : RGB[0] * 12.92 ));
    *g = (255 * ( (RGB[1] > 0.0031308) ? 1.055 * (pow(RGB[1], (1/2.4)) - 0.055) : RGB[1] * 12.92 ));
    *b = (255 * ( (RGB[2] > 0.0031308) ? 1.055 * (pow(RGB[2], (1/2.4)) - 0.055) : RGB[2] * 12.92 ));
}

  • Aus Ihrer Antwort geht nicht hervor, wo der Code gelöscht werden sollte und warum. Sie sollten Ihren Code (es ist ein wesentlicher Teil) für die Konvertierung von RGB nach La besser postenb.

    – Artemix

    15. Juli 2013 um 15:14 Uhr

1204980cookie-checkWas ist falsch an diesem RGB-zu-XYZ-Farbraumkonvertierungsalgorithmus?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy