在OpenCV中合并两个Mat-Objects

时间:2015-02-28 12:54:32

标签: c++ arrays image opencv mat

我写了一个小功能,以便能够"坚持"图像的像素在另一个图像的顶部,但它在某种程度上不起作用:而#34;形状"我的"坚持"图像是对的,颜色不是。 enter image description here

示例花是第一张图像,作为第二张图像,我有一个黑色梯形png。如您所见,存在多个问题: 这些颜色很奇怪。实际上没有颜色,只有灰度和一些奇怪的条纹作为叠加。 2.不尊重Alpha值。叠加图像的白色部分在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-Object的像素数组实际上是如何组织的?我想如果我知道这些数组中的哪些数组,那么操作这些数组会更容易。

1 个答案:

答案 0 :(得分:2)

因为您正在使用错误的类型迭代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来确定此格式。

如果您使用trapezoidImg阅读at<Vec3b>,您实际上得到的是(B,G,R),(A,B,G),(R,A,B) ,...... ,这就是奇怪的条纹来自哪里。因此,请使用at<Vec4b>正确读取(R,G,B,A)强度。

接下来,您应该定义如何处理Alpha通道。你可以混合两个Mat或覆盖另一个,无论如何。一种简单的方法是仅在mat1中的Alpha通道足够大时覆盖mat2

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));
}