19

我的目标是将 RGB 像素转换为 CIELab 色彩空间,以便进行一些只有在 CIELab 中才能进行的特殊计算。为此,我必须先将 RGB 转换为 XYZ,这是真正困难的部分。

我试图在 Objective-C 中实现这个算法(虽然主要使用纯 C),但结果是错误的。

我的代码基于easyrgb.com提供的伪实现。他们有一个在线颜色转换器,效果很好。他们说他们的伪代码与转换器中使用的伪代码相同。

这是他们的伪代码:

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

这是我在 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;
}

但是,我没有得到与他们的工具相同的结果(具有相同的观察者和光源设置)。

在我的测试中,我将这些值输入到他们的工具中,并得到了 XYZ 的结果,这与我的实现为该 RGB 值产生的结果相去甚远。请看截图:


截屏


生成的 Lab 颜色值与 Photoshop 告诉我的非常接近,因此转换器效果很好。

上面的 C 代码给了我这个结果:

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

知道这次失败的原因是什么吗?我在实现中是否犯了错误,还是需要其他常量?

如果您知道一些经过测试的 RGB 到 XYZ、XYZ 到 CIELab 或 RGB 到 CIELab、XYZ 到 Lab 或 RGB 到 Lab 实现,请不要犹豫,在此处发布它们。

基本上,我要做的就是计算两种颜色之间的偏差,也称为 Delta-E。这就是为什么我需要从 RGB 转换为 XYZ 到 Lab(或 CIELab)...

4

6 回答 6

17

我相信这是您的问题,这是截断为整数:

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

尝试这个:

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

我还没有检查其余代码是否存在其他问题。

于 2011-07-08T20:07:39.027 回答
8

*inR/255 是整数除法。1/255 为零。改为 *inR/255.0。

于 2011-07-08T20:04:34.557 回答
2

该矩阵的正确值略有不同,准确值来自http://www.brucelindbloom.com中的“RGB/XYZ 矩阵”

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
于 2017-05-02T06:57:14.747 回答
2
#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 );*/

}

C中的颜色转换和差异https://github.com/gi0rikas/Color-conversions Lab距离~= 2.3对应于JND(只是明显的差异)

于 2016-07-15T09:52:55.633 回答
0

我只是使用您的代码将 RGB(到 XYZ)转换为 La*b*,我只是发现 XYZ 值在您尝试将它们转换为 La*b* 之前应该介于 0 到 1 之间

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

所以必须删除前面的代码才能得到正确的 La*b* 值。

于 2013-07-15T14:52:28.983 回答
0

正如他们所说:

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 )

这是因为隐式转换。尽管 var_R、var_G 和 var_B 变量是浮点类型,但 / 运算符看到两个整数 R、G、B 和 255。它必须相除并返回一个整数。

为了获得浮点类型值,您可以执行 CAST 或将至少一个变量转换为浮点类型并添加小数点,如下所示:

var_B = ( B / 255.0f)

RGB2LAB 和 LAB2RGB 转换的另一个例子(记住是 CIE La b 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 ));
}
于 2017-01-27T09:11:30.973 回答