这个 RGB 到 XYZ 色彩空间转换算法有什么问题?
我的目标是将 RGB 像素转换为 CIELab 颜色空间,以进行一些只能在 CIELab 中进行的特殊计算。为此,我必须首先将 RGB 转换为 XYZ,这是非常困难的部分。
我尝试在 Objective-C 中实现这个算法(虽然主要使用纯 C),但结果是错误的。
我的代码基于 easyrgb.com< 提供的伪实现/a>.他们有一个在线颜色转换器,效果很好。他们说他们的伪代码与转换器中使用的伪代码相同。
这是他们的伪代码:
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)...
My goal is to convert an RGB pixel into CIELab color space for some special calculations only possible in CIELab. For this, I must convert RGB to XYZ first, which is the really hard part.
I tried to implement this algorithm in Objective-C (mostly using plain C though), but the results are wrong.
My code is based on the pseudo-implementation provided by easyrgb.com. They have an online-color-converter which works great. They say that their pseudo-code is the same one used in their converter.
This is their 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
This is my attempt to implement it in 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;
}
However, I don't get the same results as their tool (with same Observer and Illuminant setting).
In my test, I entered these values into their tool and got this result for XYZ which is far off from what my implementation produces for that RGB value. Please see screenshot:
The resulting Lab color values are pretty close to what Photoshop tells me, so the converter works great.
The C-code above gives me this results though:
X = 35.76... // should be 42.282
Y = 71.52... // should be 74.129
Z = 11.92... // should be 46.262
Any idea what's the cause for this failure? Did I do a mistake in my implementation, or do I need other constants?
If you know some tested RGB to XYZ, XYZ to CIELab or RGB to CIELab, XYZ to Lab or RGB to Lab implementations, please don't hesitate to post them here.
Basically, all I want to do is calculate the deviation between two colors, also known as Delta-E. That's why I need to convert from RGB to XYZ to Lab (or CIELab)...
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
我相信这是您的问题,这是截断为整数:
试试这个:
我还没有检查其余代码是否存在其他问题。
I believe here is your problem, this is truncating to an integer:
Try this:
I haven't checked the rest of the code for other problems.
*inR/255 是整数除法。 1/255 为零。改为写入 *inR/255.0。
*inR/255 is an integer division. 1/255 is zero. Write *inR/255.0 instead.
C 语言中的颜色转换和差异 https://github.com/gi0rikas/Color-conversions Lab 距离~= 2.3 对应于 JND(只是明显的差异)
Color conversions and differences in C https://github.com/gi0rikas/Color-conversions Lab distance ~= 2.3 corresponds to JND(Just noticeable difference)
该矩阵的右值略有不同,准确的值来自 http://www.brucelindbloom 中的“RGB/XYZ Matrices” .com
The right values of this matrix is different slightly,the accurate one from "RGB/XYZ Matrices" in http://www.brucelindbloom.com
我只是使用您的代码从 RGB(到 XYZ)转换为 La*b*,并且我刚刚发现 XYZ 值应该在 0 到 1 之间,然后再尝试将它们转换为 La*b*
所以前面的代码必须是擦除以获得正确的 La*b* 值。
I just use your code to convert from RGB (to XYZ) to La*b*, and I just found that the XYZ values should go between 0 a 1 before you try to convert them to La*b*
So the previous code must be erased in order to get correct La*b* values.
正如他们所说:
这是因为隐式转换。尽管 var_R、var_G 和 var_B 变量是浮点类型,但 / 运算符看到两个整数 R、G、B 和 255。它必须进行除法并返回一个整数。
为了获得 float 类型值,您可以执行 CAST 或将至少一个变量转换为添加小数点的 float 类型,如下所示:
RGB2LAB 和 LAB2RGB 转换的另一个示例(请记住,这是 CIE Lab D65):
As they said:
This is because of implicit conversion. Despite the fact that var_R, var_G and var_B variables are float type, the / operator sees two integers R, G, B and 255. it has to divide and returns an integer.
In order to get a float type value you can do a CAST or convert at least one of the variables into float type adding of a decimal point as follows:
Another example of RGB2LAB and LAB2RGB conversion (bear in mind that is CIE Lab D65):