使用 C++ 和 OpenCV 将 RGB 转换为 LMS 模型
Converting RGB into LMS model using C++ and OpenCV
我正在尝试将具有 RGB 色彩空间的图像转换为 LMS 模型。我从 this paper. I read this Question : RGB to LMS color space conversion with OpenCV 得到的转换矩阵值,我们参考了同一篇论文,我已经按照答案中的所有说明进行操作。
为了确保我的代码运行良好,我将原始 RGB 转换为 LMS,然后使用第一个矩阵的逆矩阵将 LMS 转换回 RGB,并查看输出是否与原始图像匹配。
但输出与原始图像源不匹配。
这是我的代码:
[更新]
void test(const Mat &original, Mat &pic, int rows, int cols)
{
for (int i = 0; i < original.rows; i++) {
for (int j = 0; j < original.cols; j++) {
//RGB into LMS
pic.at<Vec3b>(i, j)[0] = original.at<Vec3b>(i, j)[0] * 1.4671 +
original.at<Vec3b>(i, j)[1] * 0.1843 +
original.at<Vec3b>(i, j)[2] * 0.003; //B-->S
pic.at<Vec3b>(i, j)[1] = original.at<Vec3b>(i, j)[0] * 3.8671 +
original.at<Vec3b>(i, j)[1] * 27.1554 +
original.at<Vec3b>(i, j)[2] * 3.4557; //G-->M
pic.at<Vec3b>(i, j)[2] = original.at<Vec3b>(i, j)[0] * 4.1194 +
original.at<Vec3b>(i, j)[1] * 43.5161 +
original.at<Vec3b>(i, j)[2] * 17.8824; //R-->L
//LMS back into RGB
pic.at<Vec3b>(i, j)[0] = pic.at<Vec3b>(i, j)[0] * 0.6935 + pic.at<Vec3b>(i, j)[1] * -0.0041 + pic.at<Vec3b>(i, j)[2] * -0.0004; //S-->B
pic.at<Vec3b>(i, j)[1] = pic.at<Vec3b>(i, j)[0] * -0.1136 + pic.at<Vec3b>(i, j)[1] * 0.0540 + pic.at<Vec3b>(i, j)[2] * -0.0102; //M-->G
pic.at<Vec3b>(i, j)[2] = pic.at<Vec3b>(i, j)[0] * 0.1167 + pic.at<Vec3b>(i, j)[1] * -0.1305 + pic.at<Vec3b>(i, j)[2] * 0.0809; //L-->R
}
}
}
主要
int main(int argv, char** argc){
Mat original= imread("original.png", CV_LOAD_IMAGE_COLOR);
int rows = original.rows;
int cols = original.cols;
Mat pic(rows, cols, CV_8UC3);
test(original, pic, rows, cols);
imwrite("pic.png", pic);
}
有什么建议吗?任何帮助将不胜感激。
注意事项:
- OpenCV 按照 BGR 而非 RGB 顺序对通道进行排序。
- 新通道的值是先前通道的所有 3 个通道的值的加权和。
因此,您的转换不正确。考虑以下用于获取通道 S 的代码,
//B-->S
pic.at<Vec3b>(i, j)[0] = original.at<Vec3b>(i, j)[0] * (0.0030, 0.1843, 1.4671);
应该是
pic.at<Vec3b>(i, j)[0] = original.at<Vec3b>(i, j)[0] * 1.4671 +
original.at<Vec3b>(i, j)[1] * 0.1843 +
original.at<Vec3b>(i, j)[2] * 0.03
其他通道(即 L 和 M)的转换也应相应更改。
一般情况下,如果cvtColor
无法进行颜色转换,那么您应该考虑使用矩阵乘法进行。
也就是将转换矩阵存储为Mat
对象,然后将其与图像的Mat
对象相乘得到想要的结果。这将比使用 for 循环执行转换快得多,因为 OpenCV 可能会在幕后使用优化的 BLAS 例程来执行乘法。而且,代码会更加简洁明了。
中的答案应该能让您了解如何实现它。
已更新
根据您的更新,您的错误是当您从 LSM 转换回 BGR 时,pic.at<Vec3b>(i, j)[0]
存储了 B 通道的像素值,因此它不再包含 L 通道的值.然后,在计算 G 和 R 通道的值时继续使用该值。
同样,当你计算R通道的值时,pic.at<Vec3b>(i, j)[0]
和pic.at<Vec3b>(i, j)[1]
不再是S和M通道的值。
请注意,BGR 图像中的通道是所有 3 个 LMS 通道值的混合,反之亦然。
您应该为每个新输出(LMS 图像和最终 BGR 图像)创建一个新矩阵并相应地存储值。那应该可以修复您的错误。
我在 post 中提到了 -0.0003678,因为这是该条目的更精确值。您可以通过反转原始转换矩阵来获得此值。
我正在尝试将具有 RGB 色彩空间的图像转换为 LMS 模型。我从 this paper. I read this Question : RGB to LMS color space conversion with OpenCV 得到的转换矩阵值,我们参考了同一篇论文,我已经按照答案中的所有说明进行操作。
为了确保我的代码运行良好,我将原始 RGB 转换为 LMS,然后使用第一个矩阵的逆矩阵将 LMS 转换回 RGB,并查看输出是否与原始图像匹配。
但输出与原始图像源不匹配。 这是我的代码: [更新]
void test(const Mat &original, Mat &pic, int rows, int cols)
{
for (int i = 0; i < original.rows; i++) {
for (int j = 0; j < original.cols; j++) {
//RGB into LMS
pic.at<Vec3b>(i, j)[0] = original.at<Vec3b>(i, j)[0] * 1.4671 +
original.at<Vec3b>(i, j)[1] * 0.1843 +
original.at<Vec3b>(i, j)[2] * 0.003; //B-->S
pic.at<Vec3b>(i, j)[1] = original.at<Vec3b>(i, j)[0] * 3.8671 +
original.at<Vec3b>(i, j)[1] * 27.1554 +
original.at<Vec3b>(i, j)[2] * 3.4557; //G-->M
pic.at<Vec3b>(i, j)[2] = original.at<Vec3b>(i, j)[0] * 4.1194 +
original.at<Vec3b>(i, j)[1] * 43.5161 +
original.at<Vec3b>(i, j)[2] * 17.8824; //R-->L
//LMS back into RGB
pic.at<Vec3b>(i, j)[0] = pic.at<Vec3b>(i, j)[0] * 0.6935 + pic.at<Vec3b>(i, j)[1] * -0.0041 + pic.at<Vec3b>(i, j)[2] * -0.0004; //S-->B
pic.at<Vec3b>(i, j)[1] = pic.at<Vec3b>(i, j)[0] * -0.1136 + pic.at<Vec3b>(i, j)[1] * 0.0540 + pic.at<Vec3b>(i, j)[2] * -0.0102; //M-->G
pic.at<Vec3b>(i, j)[2] = pic.at<Vec3b>(i, j)[0] * 0.1167 + pic.at<Vec3b>(i, j)[1] * -0.1305 + pic.at<Vec3b>(i, j)[2] * 0.0809; //L-->R
}
}
}
主要
int main(int argv, char** argc){
Mat original= imread("original.png", CV_LOAD_IMAGE_COLOR);
int rows = original.rows;
int cols = original.cols;
Mat pic(rows, cols, CV_8UC3);
test(original, pic, rows, cols);
imwrite("pic.png", pic);
}
有什么建议吗?任何帮助将不胜感激。
注意事项:
- OpenCV 按照 BGR 而非 RGB 顺序对通道进行排序。
- 新通道的值是先前通道的所有 3 个通道的值的加权和。
因此,您的转换不正确。考虑以下用于获取通道 S 的代码,
//B-->S
pic.at<Vec3b>(i, j)[0] = original.at<Vec3b>(i, j)[0] * (0.0030, 0.1843, 1.4671);
应该是
pic.at<Vec3b>(i, j)[0] = original.at<Vec3b>(i, j)[0] * 1.4671 +
original.at<Vec3b>(i, j)[1] * 0.1843 +
original.at<Vec3b>(i, j)[2] * 0.03
其他通道(即 L 和 M)的转换也应相应更改。
一般情况下,如果cvtColor
无法进行颜色转换,那么您应该考虑使用矩阵乘法进行。
也就是将转换矩阵存储为Mat
对象,然后将其与图像的Mat
对象相乘得到想要的结果。这将比使用 for 循环执行转换快得多,因为 OpenCV 可能会在幕后使用优化的 BLAS 例程来执行乘法。而且,代码会更加简洁明了。
已更新
根据您的更新,您的错误是当您从 LSM 转换回 BGR 时,pic.at<Vec3b>(i, j)[0]
存储了 B 通道的像素值,因此它不再包含 L 通道的值.然后,在计算 G 和 R 通道的值时继续使用该值。
同样,当你计算R通道的值时,pic.at<Vec3b>(i, j)[0]
和pic.at<Vec3b>(i, j)[1]
不再是S和M通道的值。
请注意,BGR 图像中的通道是所有 3 个 LMS 通道值的混合,反之亦然。
您应该为每个新输出(LMS 图像和最终 BGR 图像)创建一个新矩阵并相应地存储值。那应该可以修复您的错误。
我在 post 中提到了 -0.0003678,因为这是该条目的更精确值。您可以通过反转原始转换矩阵来获得此值。