在没有 OpenCV 的情况下将 BGR 转换为 Lab
convert BGR to Lab without OpenCV
由于对颜色 spaces 的经验很少,我使用以下代码将 BGR 图像(每个通道范围从 0 到 255 的无符号字符数组)转换为实验室颜色 space:
double F(double input) // function f(...), which is used for defining L, a and b changes within [4/29,1]
{
if (input > 0.008856)
return (pow(input, 0.333333333)); // maximum 1
else
return ((841/108)*input + 4/29); //841/108 = 29*29/36*16
}
// RGB to XYZ
void RGBtoXYZ(uchar R, uchar G, uchar B, double &X, double &Y, double &Z)
{
// RGB Working Space: sRGB
// Reference White: D65
X = 0.412453*R + 0.357580*G + 0.189423*B; // maximum value = 0.959456 * 255 = 244.66128
Y = 0.212671*R + 0.715160*G + 0.072169*B; // maximum value = 1 * 255 = 255
Z = 0.019334*R + 0.119193*G + 0.950227*B; // maximum value = 1.088754 * 255 = 277.63227
}
// XYZ to CIELab
void XYZtoLab(double X, double Y, double Z, double &L, double &a, double &b)
{
const double Xo = 244.66128; // reference white
const double Yo = 255.0;
const double Zo = 277.63227;
L = 116 * F(Y / Yo) - 16; // maximum L = 100
a = 500 * (F(X / Xo) - F(Y / Yo)); // maximum
b = 200 * (F(Y / Yo) - F(Z / Zo));
}
// RGB to CIELab
void RGBtoLab(double R, double G, double B, double &L, double &a, double &b)
{
double X, Y, Z;
RGBtoXYZ(R, G, B, X, Y, Z);
XYZtoLab(X, Y, Z, L, a, b);
}
我已经将生成的实验室图像重新转换为 BGR(使用 cvtcolor)以使用以下代码使用 OpenCV 显示它(我不想使用 OpenCV 进行转换,我只是用它来显示结果。基本上我想了解颜色 space 转换的工作原理):
// Lchannel, Achannel, Bchannel are arrays of type double
cv::Mat temp64bitL(height, width, CV_64FC1, Lchannel);
cv::Mat temp32bitL;
temp64bitL.convertTo(temp32bitL, CV_32F);
cv::Mat temp64bitA(height, width, CV_64FC1, Achannel);
cv::Mat temp32bitA;
temp64bitA.convertTo(temp32bitA, CV_32F);
cv::Mat temp64bitB(height, width, CV_64FC1, Bchannel);
cv::Mat temp32bitB;
temp64bitB.convertTo(temp32bitB, CV_32F);
cv::Mat chan[3] = {
temp32bitL, temp32bitA, temp32bitB
};
cv::Mat merged;
cv::merge(chan, 3, merged);
cv::Mat BGRImage;
cv::cvtColor(merged, BGRImage, CV_Lab2BGR, 3);
然而,the computed image is different from the original image。是因为代码有问题吗?
您的代码在 double F(double input)
中有错误。由于您有整数除法,它无法按预期工作。您可能愿意更改函数以阅读如下内容。请注意 double
铸件,使除法在 floating-point 域中工作,并使用 cbrt
而不是 pow
.
#include <cmath>
double F(double input) // function f(...), which is used for defining L, a and b
// changes within [4/29,1]
{
if (input > 0.008856)
return std::cbrt(input); // maximum 1 --- prefer cbrt to pow for cubic root
else
return ((double(841) / 108) * input +
double(4) / 29); // 841/108 = 29*29/36*16
}
那么,另一个问题可能是您为 XYZ 使用的参考值 space。我们有以下参考值,来自 D65 / CIE-1931:
double Xo = 95.047;
double Yo = 100;
double Zo = 108.883;
然后,我们的 RGBtoXYZ
转换是这样进行的:
template <class float_t> struct Convert<XYZ<float_t>> {
template <class real_t> static XYZ<float_t> from(const RGB<real_t> &rhs) {
// Assume RGB has the type invariance satisfied, i.e., channels \in [0,255]
float_t var_R = float_t(rhs.comp1()) / 255;
float_t var_G = float_t(rhs.comp2()) / 255;
float_t var_B = float_t(rhs.comp3()) / 255;
var_R = (var_R > 0.04045) ? std::pow((var_R + 0.055) / 1.055, 2.4)
: var_R / 12.92;
var_G = (var_G > 0.04045) ? std::pow((var_G + 0.055) / 1.055, 2.4)
: var_G / 12.92;
var_B = (var_B > 0.04045) ? std::pow((var_B + 0.055) / 1.055, 2.4)
: var_B / 12.92;
var_R *= 100;
var_G *= 100;
var_B *= 100;
return XYZ<float_t>{var_R * float_t(0.4124) + var_G * float_t(0.3576) +
var_B * float_t(0.1805),
var_R * float_t(0.2126) + var_G * float_t(0.7152) +
var_B * float_t(0.0722),
var_R * float_t(0.0193) + var_G * float_t(0.1192) +
var_B * float_t(0.9505)};
}
};
其中 RGB
的频道被假定为在有效范围内,如评论中所述。然后,我们得到的XYZtoLAB
函数除了cbrt
和参考值改变之外是一样的。
编辑。 以上数字来自EasyRGB's Math 页面。您可以在页面上找到从 sRGB 到 XYZ 和 XYZ 到 Lab 的转换,具有 table 个 XYZ 参考值。我们使用的是 "Daylight, sRGB, Adobe RGB."
的集合
由于对颜色 spaces 的经验很少,我使用以下代码将 BGR 图像(每个通道范围从 0 到 255 的无符号字符数组)转换为实验室颜色 space:
double F(double input) // function f(...), which is used for defining L, a and b changes within [4/29,1]
{
if (input > 0.008856)
return (pow(input, 0.333333333)); // maximum 1
else
return ((841/108)*input + 4/29); //841/108 = 29*29/36*16
}
// RGB to XYZ
void RGBtoXYZ(uchar R, uchar G, uchar B, double &X, double &Y, double &Z)
{
// RGB Working Space: sRGB
// Reference White: D65
X = 0.412453*R + 0.357580*G + 0.189423*B; // maximum value = 0.959456 * 255 = 244.66128
Y = 0.212671*R + 0.715160*G + 0.072169*B; // maximum value = 1 * 255 = 255
Z = 0.019334*R + 0.119193*G + 0.950227*B; // maximum value = 1.088754 * 255 = 277.63227
}
// XYZ to CIELab
void XYZtoLab(double X, double Y, double Z, double &L, double &a, double &b)
{
const double Xo = 244.66128; // reference white
const double Yo = 255.0;
const double Zo = 277.63227;
L = 116 * F(Y / Yo) - 16; // maximum L = 100
a = 500 * (F(X / Xo) - F(Y / Yo)); // maximum
b = 200 * (F(Y / Yo) - F(Z / Zo));
}
// RGB to CIELab
void RGBtoLab(double R, double G, double B, double &L, double &a, double &b)
{
double X, Y, Z;
RGBtoXYZ(R, G, B, X, Y, Z);
XYZtoLab(X, Y, Z, L, a, b);
}
我已经将生成的实验室图像重新转换为 BGR(使用 cvtcolor)以使用以下代码使用 OpenCV 显示它(我不想使用 OpenCV 进行转换,我只是用它来显示结果。基本上我想了解颜色 space 转换的工作原理):
// Lchannel, Achannel, Bchannel are arrays of type double
cv::Mat temp64bitL(height, width, CV_64FC1, Lchannel);
cv::Mat temp32bitL;
temp64bitL.convertTo(temp32bitL, CV_32F);
cv::Mat temp64bitA(height, width, CV_64FC1, Achannel);
cv::Mat temp32bitA;
temp64bitA.convertTo(temp32bitA, CV_32F);
cv::Mat temp64bitB(height, width, CV_64FC1, Bchannel);
cv::Mat temp32bitB;
temp64bitB.convertTo(temp32bitB, CV_32F);
cv::Mat chan[3] = {
temp32bitL, temp32bitA, temp32bitB
};
cv::Mat merged;
cv::merge(chan, 3, merged);
cv::Mat BGRImage;
cv::cvtColor(merged, BGRImage, CV_Lab2BGR, 3);
然而,the computed image is different from the original image。是因为代码有问题吗?
您的代码在 double F(double input)
中有错误。由于您有整数除法,它无法按预期工作。您可能愿意更改函数以阅读如下内容。请注意 double
铸件,使除法在 floating-point 域中工作,并使用 cbrt
而不是 pow
.
#include <cmath>
double F(double input) // function f(...), which is used for defining L, a and b
// changes within [4/29,1]
{
if (input > 0.008856)
return std::cbrt(input); // maximum 1 --- prefer cbrt to pow for cubic root
else
return ((double(841) / 108) * input +
double(4) / 29); // 841/108 = 29*29/36*16
}
那么,另一个问题可能是您为 XYZ 使用的参考值 space。我们有以下参考值,来自 D65 / CIE-1931:
double Xo = 95.047;
double Yo = 100;
double Zo = 108.883;
然后,我们的 RGBtoXYZ
转换是这样进行的:
template <class float_t> struct Convert<XYZ<float_t>> {
template <class real_t> static XYZ<float_t> from(const RGB<real_t> &rhs) {
// Assume RGB has the type invariance satisfied, i.e., channels \in [0,255]
float_t var_R = float_t(rhs.comp1()) / 255;
float_t var_G = float_t(rhs.comp2()) / 255;
float_t var_B = float_t(rhs.comp3()) / 255;
var_R = (var_R > 0.04045) ? std::pow((var_R + 0.055) / 1.055, 2.4)
: var_R / 12.92;
var_G = (var_G > 0.04045) ? std::pow((var_G + 0.055) / 1.055, 2.4)
: var_G / 12.92;
var_B = (var_B > 0.04045) ? std::pow((var_B + 0.055) / 1.055, 2.4)
: var_B / 12.92;
var_R *= 100;
var_G *= 100;
var_B *= 100;
return XYZ<float_t>{var_R * float_t(0.4124) + var_G * float_t(0.3576) +
var_B * float_t(0.1805),
var_R * float_t(0.2126) + var_G * float_t(0.7152) +
var_B * float_t(0.0722),
var_R * float_t(0.0193) + var_G * float_t(0.1192) +
var_B * float_t(0.9505)};
}
};
其中 RGB
的频道被假定为在有效范围内,如评论中所述。然后,我们得到的XYZtoLAB
函数除了cbrt
和参考值改变之外是一样的。
编辑。 以上数字来自EasyRGB's Math 页面。您可以在页面上找到从 sRGB 到 XYZ 和 XYZ 到 Lab 的转换,具有 table 个 XYZ 参考值。我们使用的是 "Daylight, sRGB, Adobe RGB."
的集合