Qu'est-ce qui ne va pas avec cet algorithme de conversion D'espace de couleur RGB en XYZ?
mon but est de convertir un pixel RGB en espace de couleur CIELab pour quelques calculs spéciaux seulement possible dans CIELab. Pour cela, je dois d'abord convertir RGB en XYZ, ce qui est la partie vraiment difficile.
j'ai essayé d'implémenter cet algorithme dans L'Objectif-C (en utilisant principalement le C simple cependant), mais les résultats sont erronés.
mon code est basé sur la pseudo-implémentation fournie par easyrgb.com. Ils ont un convertisseur de couleur en ligne qui fonctionne très bien. Ils disent que leur pseudo-code est le même que celui utilisé dans leur convertisseur.
voici leur 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
C'est ma tentative de le mettre en œuvre en Objective-C / C:
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;
}
cependant, je n'obtiens pas les mêmes résultats que leur outil (avec le même observateur et le même réglage D'éclairage).
dans mon test, j'ai entré ces valeurs dans leur outil et j'ai obtenu ce résultat pour XYZ qui est loin de ce que mon implémentation produit pour cette valeur RGB. Veuillez voir capture d'écran:
les valeurs de couleur de laboratoire résultantes sont assez proches de ce que Photoshop me dit, donc le convertisseur fonctionne très bien.
Le code C ci-dessus me donne ce résultat:
X = 35.76... // should be 42.282
Y = 71.52... // should be 74.129
Z = 11.92... // should be 46.262
une idée de la cause de cet échec? Ai-je fait une erreur dans mon implémentation, ou Ai-je besoin d'autres constantes?
si vous connaissez certains RGB testés à XYZ, XYZ à CIELab ou rgb à CIELab, XYZ to Lab ou RGB to Lab implémentations, n'hésitez pas à les poster ici.
fondamentalement, tout ce que je veux faire est de calculer l'écart entre deux couleurs, également connu sous le nom de Delta-E. C'est pourquoi je dois convertir de RGB en XYZ En Lab (ou CIELab)...
6 réponses
je crois que là est votre problème, c'est tronquer un nombre entier:
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
essaye ceci:
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
je n'ai pas vérifié le reste du code pour d'autres problèmes.
*inR / 255 est une division entière. 1/255 est égale à zéro. Écrire *inR / 255.0 à la place.
#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 );*/
}
les conversions de couleurs et les différences de C https://github.com/gi0rikas/Color-conversions Distance de laboratoire ~ = 2,3 correspond à JND (juste différence notable)
j'utilise juste votre code pour convertir de RGB (en XYZ) En La*b*, et je viens de trouver que les valeurs XYZ devraient aller entre 0 a 1 avant que vous essayiez de les convertir en La*b*
var_R = var_R * 100;
var_G = var_G * 100;
var_B = var_B * 100;
donc le code précédent doit être effacé pour obtenir les valeurs correctes de La*b*.
Comme ils ont dit:
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 )
ceci est dû à la conversion implicite. Malgré le fait que les variables var_R, var_G et var_B sont de type float, l'opérateur / voit deux entiers R, G, B et 255. il doit diviser et retourner un entier.
pour obtenir une valeur de type float vous pouvez faire un CAST ou convertir au moins une des variables en type float en ajoutant un point décimal comme suit:
b D65):var_B = (B / 255.0 f)
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 ));
}
les valeurs correctes de cette matrice sont légèrement différentes, celle exacte de "RGB / XYZ Matrices" dans 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