从 RGB 颜色转换为 L*ab 颜色时值不准确
Inaccurate values when converting from RGB to L*ab colours
我正在尝试将 sRGB 值(在 Photoshop 中)转换为 L*ab 值(也可在 Photoshop 中使用)我在大概的范围内,但没有得到与 EasyRGB 相同的结果我从哪里得到转换公式。我预计会有舍入误差(不是在最后,我四舍五入到三个地方)。只是我看不见。
- 输入:sRGB 255、0、255
- 输出:L*ab:60.32、98.254、-60.843
- 预期输出 L*ab:60.324 98.234 -60.825
为了方便起见,我删除了所有 Photoshop 代码,所以它只是 JavaScript
var msg = "";
// RGB
// var Red = foregroundColor.rgb.red;
// var Green = foregroundColor.rgb.green;
// var Blue = foregroundColor.rgb.blue;
var Red = 255;
var Green = 0;
var Blue = 255;
msg = "RGB: " + Red + ", " + Green + ", " + Blue + "<br>";
printy(msg);
// user colour converted to XYZ space
var myColXYZ = RGB_to_XYZ(Red, Green, Blue)
var colX = myColXYZ[0];
var colY = myColXYZ[1];
var colZ = myColXYZ[2];
// using CIE L-ab* colour space
var myLab = XYZ_to_LAB(colX, colY, colZ)
//msg = "L*ab: " + myLab[0] + ", " + myLab[1] + ", " + myLab[2] + "<br>";
// b4 rounding: 60.319933664076004, 98.25421868616108, -60.84298422386232
// round to three places
for (var i = 0; i < 3; i++) {
myLab[i] = round_nicely(myLab[i], 3);
}
msg = "L*ab: " + myLab[0] + ", " + myLab[1] + ", " + myLab[2] + "<br>";
printy(msg);
// results
// RGB: 255, 0, 255
// L*ab: 60.32, 98.254, -60.843
// should be
// CIE-L*ab = 60.324 98.234 -60.825
// function RGB TO XYZ (R, G, B)
// --------------------------------------------------------
// http://www.easyrgb.com/index.php?X=MATH&H=02#text2
function RGB_to_XYZ(r, g, b) {
var_R = parseFloat(r / 255); //r from 0 to 255
var_G = parseFloat(g / 255); //g from 0 to 255
var_B = parseFloat(b / 255); //b from 0 to 255
if (var_R > 0.04045) var_R = Math.pow(((var_R + 0.055) / 1.055), 2.4)
else var_R = var_R / 12.92
if (var_G > 0.04045) var_G = Math.pow(((var_G + 0.055) / 1.055), 2.4)
else var_G = var_G / 12.92
if (var_B > 0.04045) var_B = Math.pow(((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;
return [X, Y, Z]
}
// function XYZ TO LAB (x, y, z)
// --------------------------------------------------------
// http://www.easyrgb.com/index.php?X=MATH&H=16#text16
function XYZ_to_LAB(x, y, z) {
var ref_X = 95.047;
var ref_Y = 100.000;
var ref_Z = 108.883;
var_X = x / ref_X; //ref_X = 95.047 Observer= 2°, Illuminant= D65
var_Y = y / ref_Y; //ref_Y = 100.000
var_Z = z / ref_Z; //ref_Z = 108.883
if (var_X > 0.008856) var_X = Math.pow(var_X, (1 / 3))
else var_X = (7.787 * var_X) + (16 / 116)
if (var_Y > 0.008856) var_Y = Math.pow(var_Y, (1 / 3))
else var_Y = (7.787 * var_Y) + (16 / 116)
if (var_Z > 0.008856) var_Z = Math.pow(var_Z, (1 / 3))
else var_Z = (7.787 * var_Z) + (16 / 116)
CIE_L = (116 * var_Y) - 16;
CIE_a = 500 * (var_X - var_Y);
CIE_b = 200 * (var_Y - var_Z);
return [CIE_L, CIE_a, CIE_b]
}
// function ROUND NICELY (num number, number of decimal places)
// --------------------------------------------------------
function round_nicely(num, places) {
// if(! places) places = 3;
var p = Math.pow(10, places)
return Math.round(num * p) / p
}
// function PRINTY (str)
// --------------------------------------------------------
function printy(message) {
var outputDiv = document.getElementById('myPage');
outputDiv.innerHTML += message;
}
<!-- page content -->
<p id="myPage"></p>
显然,EasyRBG RGB
到 XYZ
的转换存在缺陷。它不遵守自己的规则。
让我们一步一步来:
输入数据为
R: 255 G: 0 B: 255
第一步是通过除以 255 将它们映射到从零到一的范围。产生这个
var_R: 1 var_G: 0 var_B: 1
下一步是一些操作,似乎是一些色域调整。但是这些操作在极端处有固定点,这意味着输入值 1
或 0
在此操作中不会改变。所以我们还在
var_R: 1 var_G: 0 var_B: 1
下一步是将所有内容缩放 100
var_R: 100 var_G: 0 var_B: 100
然后是向量(R,G,B)
到(X,Y,Z)
的矩阵乘法是RGB向量
的加权线性组合
X = 42.24 + 18.05 = 60.29
Y = 21.26 + 7.22 = 28.48
Y = 1.93 + 95.05 = 96.98
EasyRGB 对于从 RGB
到 XYZ
的转换有一些不同的结果
X: 59.289, Y: 28.485, Z: 96.964
考虑使用 Photoshop 的 ExtendScript/JS API 而不是 EasyRGB 进行准确的颜色转换。
下面要点中的自定义 rgbToLab
函数演示了如何利用 SolidColor
class 首先创建新的 RGB 颜色(使用给定的颜色分量;R、G ,b)以及后续如何得到对应的Lab值
#target photoshop
/**
* Converts RGB color values to the Lab color model.
* @param {Number} red - The red component value (0-255).
* @param {Number} green - The green component value (0-255).
* @param {Number} blue - The blue component value (0-255).
* @returns {Array} The Lab values.
*/
function rgbToLab(red, green, blue) {
var color = new SolidColor;
color.rgb.red = red;
color.rgb.green = green;
color.rgb.blue = blue;
return [
color.lab.l,
color.lab.a,
color.lab.b
]
}
var labValues = rgbToLab(255, 0, 255);
$.writeln('L*ab: '+ labValues) // --> L*ab: 60.16845703125,92.6736755371094,-60.763671875
出现这种差异是因为 Photoshop 使用 D50 光源进行 Lab 转换(source) while the EasyRGB Color Converter uses D65 when converting from sRGB (notice the mention of "D65/2°" in the sRGB and XYZ rows of the conversion results, also the D65 illuminant is hardcoded in their pseudocode 用于此转换)。据我所知,在从 sRGB 开始时,没有办法让 EasyRGB 使用不同的光源。
至于所提供的 JavaScript 代码,我可以看出您正在使用与 D65 光源对应的值,因此您的代码将生成与 EasyRGB 给出的值相匹配的 Lab 值,而不是与Photoshop.
如果要编写同时支持 D50 和 D65 光源的代码,则需要使用不同的值。以下值来自 this source。在上面的 JavaScript 代码中,这些值的“D65 光源,RGB 到 XYZ”集合被硬编码到 RGB_to_XYZ
函数中。
D65 光源,sRGB 工作 space,RGB 到 XYZ:
0.4124564 0.3575761 0.1804375
0.2126729 0.7151522 0.0721750
0.0193339 0.1191920 0.9503041
D65 光源,sRGB 工作 space,XYZ 到 RGB:
3.2404542 -1.5371385 -0.4985314
-0.9692660 1.8760108 0.0415560
0.0556434 -0.2040259 1.0572252
D50 光源,sRGB (Bradford-adapted) 工作 space,RGB 到 XYZ:
0.4360747 0.3850649 0.1430804
0.2225045 0.7168786 0.0606169
0.0139322 0.0971045 0.7141733
D50 光源,sRGB (Bradford-adapted) 工作 space,XYZ 到 RGB:
3.1338561 -1.6168667 -0.4906146
-0.9787684 1.9161415 0.0334540
0.0719453 -0.2289914 1.4052427
在 XYZ 到 Lab 和 Lab 到 XYZ 的转换过程中,也有不同的白点值要使用,具体取决于您的光源。在上面的 JavaScript 代码中,这些变量被命名为 ref_X
、ref_Y
和 ref_Z
.
D65 光源,用于 XYZ 到 Lab 和 Lab 到 XYZ 的转换 (source)
X: 95.047
Y: 100.0
Z: 108.883
D50 光源,用于 XYZ 到 Lab 和 Lab 到 XYZ 的转换 (source 1, source 2)
X: 96.42
Y: 100.0
Z: 82.49
我自己用这些值测试了转换,它们确实解决了您注意到的 EasyRGB 转换和 Photoshop 转换之间的差异。使用任何一组值,您都可以成功地从 RGB -> XYZ -> Lab -> XYZ -> RGB 转到(原始 RGB 值在转换后恢复)。如果光源为 D65,则 XYZ 和 Lab 值将与 EasyRGB 给出的值匹配。如果光源为 D50,则 Lab 值将与 Photoshop 给出的值匹配。
最后,关于我所引用的转换算法的一些上下文:
- 从 sRGB 转换为 Lab 颜色时,必须使用 XYZ 作为中间格式。没有直接转换。
- pseudocode examples of conversion formulas on the EasyRGB site, but there's almost no annotation. I found the formulas here 更容易阅读。他们在 C# 中。
- 我包括了从 Lab -> XYZ -> sRGB 转换时所需的值集,尽管原始问题中的代码仅来自 sRGB -> XYZ -> Lab。我认为最好能够双向转换以确认您的代码是否正常工作。
- 需要注意的一件事是 Photoshop 的拾色器工具不显示 Lab 值的小数位。因此,如果您首先输入 RGB 值,记下拾色器工具中的 Lab 值,然后再次输入这些相同的 Lab 值,您可能会得到一组与开始时略有不同的 RGB 值。
我正在尝试将 sRGB 值(在 Photoshop 中)转换为 L*ab 值(也可在 Photoshop 中使用)我在大概的范围内,但没有得到与 EasyRGB 相同的结果我从哪里得到转换公式。我预计会有舍入误差(不是在最后,我四舍五入到三个地方)。只是我看不见。
- 输入:sRGB 255、0、255
- 输出:L*ab:60.32、98.254、-60.843
- 预期输出 L*ab:60.324 98.234 -60.825
为了方便起见,我删除了所有 Photoshop 代码,所以它只是 JavaScript
var msg = "";
// RGB
// var Red = foregroundColor.rgb.red;
// var Green = foregroundColor.rgb.green;
// var Blue = foregroundColor.rgb.blue;
var Red = 255;
var Green = 0;
var Blue = 255;
msg = "RGB: " + Red + ", " + Green + ", " + Blue + "<br>";
printy(msg);
// user colour converted to XYZ space
var myColXYZ = RGB_to_XYZ(Red, Green, Blue)
var colX = myColXYZ[0];
var colY = myColXYZ[1];
var colZ = myColXYZ[2];
// using CIE L-ab* colour space
var myLab = XYZ_to_LAB(colX, colY, colZ)
//msg = "L*ab: " + myLab[0] + ", " + myLab[1] + ", " + myLab[2] + "<br>";
// b4 rounding: 60.319933664076004, 98.25421868616108, -60.84298422386232
// round to three places
for (var i = 0; i < 3; i++) {
myLab[i] = round_nicely(myLab[i], 3);
}
msg = "L*ab: " + myLab[0] + ", " + myLab[1] + ", " + myLab[2] + "<br>";
printy(msg);
// results
// RGB: 255, 0, 255
// L*ab: 60.32, 98.254, -60.843
// should be
// CIE-L*ab = 60.324 98.234 -60.825
// function RGB TO XYZ (R, G, B)
// --------------------------------------------------------
// http://www.easyrgb.com/index.php?X=MATH&H=02#text2
function RGB_to_XYZ(r, g, b) {
var_R = parseFloat(r / 255); //r from 0 to 255
var_G = parseFloat(g / 255); //g from 0 to 255
var_B = parseFloat(b / 255); //b from 0 to 255
if (var_R > 0.04045) var_R = Math.pow(((var_R + 0.055) / 1.055), 2.4)
else var_R = var_R / 12.92
if (var_G > 0.04045) var_G = Math.pow(((var_G + 0.055) / 1.055), 2.4)
else var_G = var_G / 12.92
if (var_B > 0.04045) var_B = Math.pow(((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;
return [X, Y, Z]
}
// function XYZ TO LAB (x, y, z)
// --------------------------------------------------------
// http://www.easyrgb.com/index.php?X=MATH&H=16#text16
function XYZ_to_LAB(x, y, z) {
var ref_X = 95.047;
var ref_Y = 100.000;
var ref_Z = 108.883;
var_X = x / ref_X; //ref_X = 95.047 Observer= 2°, Illuminant= D65
var_Y = y / ref_Y; //ref_Y = 100.000
var_Z = z / ref_Z; //ref_Z = 108.883
if (var_X > 0.008856) var_X = Math.pow(var_X, (1 / 3))
else var_X = (7.787 * var_X) + (16 / 116)
if (var_Y > 0.008856) var_Y = Math.pow(var_Y, (1 / 3))
else var_Y = (7.787 * var_Y) + (16 / 116)
if (var_Z > 0.008856) var_Z = Math.pow(var_Z, (1 / 3))
else var_Z = (7.787 * var_Z) + (16 / 116)
CIE_L = (116 * var_Y) - 16;
CIE_a = 500 * (var_X - var_Y);
CIE_b = 200 * (var_Y - var_Z);
return [CIE_L, CIE_a, CIE_b]
}
// function ROUND NICELY (num number, number of decimal places)
// --------------------------------------------------------
function round_nicely(num, places) {
// if(! places) places = 3;
var p = Math.pow(10, places)
return Math.round(num * p) / p
}
// function PRINTY (str)
// --------------------------------------------------------
function printy(message) {
var outputDiv = document.getElementById('myPage');
outputDiv.innerHTML += message;
}
<!-- page content -->
<p id="myPage"></p>
显然,EasyRBG RGB
到 XYZ
的转换存在缺陷。它不遵守自己的规则。
让我们一步一步来:
输入数据为
R: 255 G: 0 B: 255
第一步是通过除以 255 将它们映射到从零到一的范围。产生这个
var_R: 1 var_G: 0 var_B: 1
下一步是一些操作,似乎是一些色域调整。但是这些操作在极端处有固定点,这意味着输入值 1
或 0
在此操作中不会改变。所以我们还在
var_R: 1 var_G: 0 var_B: 1
下一步是将所有内容缩放 100
var_R: 100 var_G: 0 var_B: 100
然后是向量(R,G,B)
到(X,Y,Z)
的矩阵乘法是RGB向量
X = 42.24 + 18.05 = 60.29
Y = 21.26 + 7.22 = 28.48
Y = 1.93 + 95.05 = 96.98
EasyRGB 对于从 RGB
到 XYZ
X: 59.289, Y: 28.485, Z: 96.964
考虑使用 Photoshop 的 ExtendScript/JS API 而不是 EasyRGB 进行准确的颜色转换。
下面要点中的自定义 rgbToLab
函数演示了如何利用 SolidColor
class 首先创建新的 RGB 颜色(使用给定的颜色分量;R、G ,b)以及后续如何得到对应的Lab值
#target photoshop
/**
* Converts RGB color values to the Lab color model.
* @param {Number} red - The red component value (0-255).
* @param {Number} green - The green component value (0-255).
* @param {Number} blue - The blue component value (0-255).
* @returns {Array} The Lab values.
*/
function rgbToLab(red, green, blue) {
var color = new SolidColor;
color.rgb.red = red;
color.rgb.green = green;
color.rgb.blue = blue;
return [
color.lab.l,
color.lab.a,
color.lab.b
]
}
var labValues = rgbToLab(255, 0, 255);
$.writeln('L*ab: '+ labValues) // --> L*ab: 60.16845703125,92.6736755371094,-60.763671875
出现这种差异是因为 Photoshop 使用 D50 光源进行 Lab 转换(source) while the EasyRGB Color Converter uses D65 when converting from sRGB (notice the mention of "D65/2°" in the sRGB and XYZ rows of the conversion results, also the D65 illuminant is hardcoded in their pseudocode 用于此转换)。据我所知,在从 sRGB 开始时,没有办法让 EasyRGB 使用不同的光源。
至于所提供的 JavaScript 代码,我可以看出您正在使用与 D65 光源对应的值,因此您的代码将生成与 EasyRGB 给出的值相匹配的 Lab 值,而不是与Photoshop.
如果要编写同时支持 D50 和 D65 光源的代码,则需要使用不同的值。以下值来自 this source。在上面的 JavaScript 代码中,这些值的“D65 光源,RGB 到 XYZ”集合被硬编码到 RGB_to_XYZ
函数中。
D65 光源,sRGB 工作 space,RGB 到 XYZ:
0.4124564 0.3575761 0.1804375
0.2126729 0.7151522 0.0721750
0.0193339 0.1191920 0.9503041
D65 光源,sRGB 工作 space,XYZ 到 RGB:
3.2404542 -1.5371385 -0.4985314
-0.9692660 1.8760108 0.0415560
0.0556434 -0.2040259 1.0572252
D50 光源,sRGB (Bradford-adapted) 工作 space,RGB 到 XYZ:
0.4360747 0.3850649 0.1430804
0.2225045 0.7168786 0.0606169
0.0139322 0.0971045 0.7141733
D50 光源,sRGB (Bradford-adapted) 工作 space,XYZ 到 RGB:
3.1338561 -1.6168667 -0.4906146
-0.9787684 1.9161415 0.0334540
0.0719453 -0.2289914 1.4052427
在 XYZ 到 Lab 和 Lab 到 XYZ 的转换过程中,也有不同的白点值要使用,具体取决于您的光源。在上面的 JavaScript 代码中,这些变量被命名为 ref_X
、ref_Y
和 ref_Z
.
D65 光源,用于 XYZ 到 Lab 和 Lab 到 XYZ 的转换 (source)
X: 95.047
Y: 100.0
Z: 108.883
D50 光源,用于 XYZ 到 Lab 和 Lab 到 XYZ 的转换 (source 1, source 2)
X: 96.42
Y: 100.0
Z: 82.49
我自己用这些值测试了转换,它们确实解决了您注意到的 EasyRGB 转换和 Photoshop 转换之间的差异。使用任何一组值,您都可以成功地从 RGB -> XYZ -> Lab -> XYZ -> RGB 转到(原始 RGB 值在转换后恢复)。如果光源为 D65,则 XYZ 和 Lab 值将与 EasyRGB 给出的值匹配。如果光源为 D50,则 Lab 值将与 Photoshop 给出的值匹配。
最后,关于我所引用的转换算法的一些上下文:
- 从 sRGB 转换为 Lab 颜色时,必须使用 XYZ 作为中间格式。没有直接转换。
- pseudocode examples of conversion formulas on the EasyRGB site, but there's almost no annotation. I found the formulas here 更容易阅读。他们在 C# 中。
- 我包括了从 Lab -> XYZ -> sRGB 转换时所需的值集,尽管原始问题中的代码仅来自 sRGB -> XYZ -> Lab。我认为最好能够双向转换以确认您的代码是否正常工作。
- 需要注意的一件事是 Photoshop 的拾色器工具不显示 Lab 值的小数位。因此,如果您首先输入 RGB 值,记下拾色器工具中的 Lab 值,然后再次输入这些相同的 Lab 值,您可能会得到一组与开始时略有不同的 RGB 值。