在 OpenCV 中合并两个 Mat 对象
Merge two Mat-Objects in OpenCV
我写了一个小函数,以便能够 "stick" 一个图像的像素在另一个图像之上,但不知何故它不起作用:虽然我的 "sticked" 的 "shapes" ] 图像是正确的,颜色不是。
示例花是第一张图片,第二张图片是黑色梯形 png。如您所见,存在多个问题:
1. 颜色显示怪异。实际上没有颜色,只有灰度,还有一些奇怪的条纹作为叠加。
2. 不尊重阿尔法值。叠加图像的白色部分在png中是透明的。
这是我的代码:
void mergeMats(Mat mat1, Mat mat2, int x, int y){
//unsigned char * pixelPtr = (unsigned char *)mat2.data;
//unsigned char * pixelPtr2 = (unsigned char *)mat1.data;
//int cn = mat2.channels();
//int cn2 = mat2.channels();
//Scalar_<unsigned char> bgrPixel;
for (int i = 0; i < mat2.cols; i++){
for (int j = 0; j < mat2.rows; j++){
if (x + i < mat1.cols && y + j < mat1.rows){
Vec3b &intensity = mat1.at<Vec3b>(j+y, i+x);
Vec3b intensity2 = mat2.at<Vec3b>(j, i);
for (int k = 0; k < mat1.channels(); k++) {
intensity.val[k] = saturate_cast<uchar>(intensity2.val[k]);
}
//pixelPtr2[(i + x)*mat1.cols*cn2 + (j + y)*cn2 + 0] = pixelPtr[(i + x)*mat2.cols*cn + (j + y)*cn + 0];
//pixelPtr2[(i + x)*mat1.cols*cn2 + (j + y)*cn2 + 1] = pixelPtr[(i + x)*mat2.cols*cn + (j + y)*cn + 1];
//pixelPtr2[(i + x)*mat1.cols*cn2 + (j + y)*cn2 + 2] = pixelPtr[(i + x)*mat2.cols*cn + (j + y)*cn + 2];
}
}
}
}
评论的代码是另一种方法,但结果相同。
所以,这是我的问题:
1. 我如何解决 2 个问题(1. 颜色...,2. alpha ...)
2. 任何 Mat 对象的像素阵列实际上是如何组织的?我想如果我知道其中的内容,我将更容易操作这些数组。
因为您正在使用错误的类型迭代 mat2
。将 Vec3b intensity2 = mat2.at<Vec3b>(j, i);
更改为:
Vec4b intensity2 = mat2.at<Vec4b>(j, i);
并且消除了奇怪的条纹。并使用intensity2[3]
处理alpha通道。
假设您正在使用-1
标志读取黑色梯形png文件:
auto trapezoidImg = cv::imread("trapezoid.png", -1);
其中 -1
flag 指定读取 alpha 通道。然后trapezoidImg
按以下格式组织:
[B, G, R, A, B, G, R, A, ......;
B, G, R, A, B, G, R, A, ......;
......
B, G, R, A, B, G, R, A, ......]
您可以打印出 trapezoidImg
,例如使用 std::cout
,以找出这种格式。
如果你用at<Vec3b>
读取trapezoidImg
,你得到的实际上是(B, G, R), (A, B, G), (R, A, B), ......, 这就是怪异条纹的由来。因此,使用at<Vec4b>
正确读取(R,G,B,A)强度。
接下来,您应该定义如何处理 Alpha 通道。您可以混合两个 Mat 或覆盖另一个,无论如何。一种简单的方法是仅当 mat2
中的 alpha 通道足够大时才覆盖 mat1
:
cv::Vec3b &intensity = mat1.at<cv::Vec3b>(j + y, i + x);
cv::Vec4b intensity2 = mat2.at<cv::Vec4b>(j, i);
for (int k = 0; k < mat1.channels(); k++) {
if (intensity2.val[3] > 250){ //3 for alpha channel
intensity.val[k] = cv::saturate_cast<uchar>(intensity2.val[k]);
}
}
这足以处理具有透明背景的黑色梯形png。或者通过混合两个 Mat 进一步扩展规则:
cv::Vec3b &intensity = mat1.at<cv::Vec3b>(j + y, i + x);
cv::Vec4b intensity2 = mat2.at<cv::Vec4b>(j, i);
auto alphaValue = cv::saturate_cast<uchar>(intensity2.val[3]);
auto alpha = alphaValue / 255.0;
for (int k = 0; k < 2; k++) { //BGR channels only
intensity.val[k] = cv::saturate_cast<uchar>(
intensity2.val[k] * alpha + intensity.val[k] * (1.0 - alpha));
}
我写了一个小函数,以便能够 "stick" 一个图像的像素在另一个图像之上,但不知何故它不起作用:虽然我的 "sticked" 的 "shapes" ] 图像是正确的,颜色不是。
示例花是第一张图片,第二张图片是黑色梯形 png。如您所见,存在多个问题: 1. 颜色显示怪异。实际上没有颜色,只有灰度,还有一些奇怪的条纹作为叠加。 2. 不尊重阿尔法值。叠加图像的白色部分在png中是透明的。
这是我的代码:
void mergeMats(Mat mat1, Mat mat2, int x, int y){
//unsigned char * pixelPtr = (unsigned char *)mat2.data;
//unsigned char * pixelPtr2 = (unsigned char *)mat1.data;
//int cn = mat2.channels();
//int cn2 = mat2.channels();
//Scalar_<unsigned char> bgrPixel;
for (int i = 0; i < mat2.cols; i++){
for (int j = 0; j < mat2.rows; j++){
if (x + i < mat1.cols && y + j < mat1.rows){
Vec3b &intensity = mat1.at<Vec3b>(j+y, i+x);
Vec3b intensity2 = mat2.at<Vec3b>(j, i);
for (int k = 0; k < mat1.channels(); k++) {
intensity.val[k] = saturate_cast<uchar>(intensity2.val[k]);
}
//pixelPtr2[(i + x)*mat1.cols*cn2 + (j + y)*cn2 + 0] = pixelPtr[(i + x)*mat2.cols*cn + (j + y)*cn + 0];
//pixelPtr2[(i + x)*mat1.cols*cn2 + (j + y)*cn2 + 1] = pixelPtr[(i + x)*mat2.cols*cn + (j + y)*cn + 1];
//pixelPtr2[(i + x)*mat1.cols*cn2 + (j + y)*cn2 + 2] = pixelPtr[(i + x)*mat2.cols*cn + (j + y)*cn + 2];
}
}
}
}
评论的代码是另一种方法,但结果相同。 所以,这是我的问题: 1. 我如何解决 2 个问题(1. 颜色...,2. alpha ...) 2. 任何 Mat 对象的像素阵列实际上是如何组织的?我想如果我知道其中的内容,我将更容易操作这些数组。
因为您正在使用错误的类型迭代 mat2
。将 Vec3b intensity2 = mat2.at<Vec3b>(j, i);
更改为:
Vec4b intensity2 = mat2.at<Vec4b>(j, i);
并且消除了奇怪的条纹。并使用intensity2[3]
处理alpha通道。
假设您正在使用-1
标志读取黑色梯形png文件:
auto trapezoidImg = cv::imread("trapezoid.png", -1);
其中 -1
flag 指定读取 alpha 通道。然后trapezoidImg
按以下格式组织:
[B, G, R, A, B, G, R, A, ......;
B, G, R, A, B, G, R, A, ......;
......
B, G, R, A, B, G, R, A, ......]
您可以打印出 trapezoidImg
,例如使用 std::cout
,以找出这种格式。
如果你用at<Vec3b>
读取trapezoidImg
,你得到的实际上是(B, G, R), (A, B, G), (R, A, B), ......, 这就是怪异条纹的由来。因此,使用at<Vec4b>
正确读取(R,G,B,A)强度。
接下来,您应该定义如何处理 Alpha 通道。您可以混合两个 Mat 或覆盖另一个,无论如何。一种简单的方法是仅当 mat2
中的 alpha 通道足够大时才覆盖 mat1
:
cv::Vec3b &intensity = mat1.at<cv::Vec3b>(j + y, i + x);
cv::Vec4b intensity2 = mat2.at<cv::Vec4b>(j, i);
for (int k = 0; k < mat1.channels(); k++) {
if (intensity2.val[3] > 250){ //3 for alpha channel
intensity.val[k] = cv::saturate_cast<uchar>(intensity2.val[k]);
}
}
这足以处理具有透明背景的黑色梯形png。或者通过混合两个 Mat 进一步扩展规则:
cv::Vec3b &intensity = mat1.at<cv::Vec3b>(j + y, i + x);
cv::Vec4b intensity2 = mat2.at<cv::Vec4b>(j, i);
auto alphaValue = cv::saturate_cast<uchar>(intensity2.val[3]);
auto alpha = alphaValue / 255.0;
for (int k = 0; k < 2; k++) { //BGR channels only
intensity.val[k] = cv::saturate_cast<uchar>(
intensity2.val[k] * alpha + intensity.val[k] * (1.0 - alpha));
}