在 OpenCV 中合并两个 Mat-Objects

Posted

技术标签:

【中文标题】在 OpenCV 中合并两个 Mat-Objects【英文标题】:Merge two Mat-Objects in OpenCV 【发布时间】:2015-02-28 12:54:31 【问题描述】:

我写了一个小函数,以便能够将图像的像素“粘贴”在另一个图像之上,但不知何故它不起作用:虽然我的“粘贴”图像的“形状”是正确的,但颜色是不是。

示例花是第一张图片,作为第二张图片,我有一个黑色梯形 png。如您所见,存在多个问题: 1.颜色显示怪异。实际上没有颜色,只有灰度,还有一些奇怪的条纹作为叠加。 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】:

因为您使用错误类型迭代 mat2。将Vec3b intensity2 = mat2.at&lt;Vec3b&gt;(j, i); 更改为:

Vec4b intensity2 = mat2.at<Vec4b>(j, i);

并且奇怪的条纹被消除了。并使用intensity2[3]处理alpha通道。


假设您正在使用-1 标志读取黑色梯形 png 文件:

auto trapezoidImg = cv::imread("trapezoid.png", -1);

-1flag 指定读取 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&lt;Vec3b&gt; 阅读trapezoidImg,您实际上得到的是(B, G, R), (A, B, G), (R, A, B), ... ...,这就是奇怪的条纹的来源。因此,请使用at&lt;Vec4b&gt;正确读取(R,G,B,A)强度。

接下来,您应该定义如何处理 Alpha 通道。您可以混合两个垫子或覆盖另一个垫子,无论如何。一种简单的方法是仅当 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));

【讨论】:

感谢您的回答!它现在几乎可以工作了。我唯一的问题是 alpha:我的背景图像是从视频中提供的,因此没有 alpha 通道。我试图通过在通道循环之前添加if (intensity2.val[3] == 0) continue; 来解决这个问题,然后循环 mat1.channels()-times,最终将前景和背景像素与uchar alpha = intensity2.val[3];intensity.val[k] = saturate_cast&lt;uchar&gt;((intensity2.val[k] * alpha) + (intensity.val[k] * (1.0 - alpha))); 混合在一起 - 这不起作用,不幸的是...... uchar alpha = intensity2.val[3]; 是一个介于 0 ~ 255 之间的值。所以(1.0 - alpha) 可能不起作用。 哇,非常感谢!你所有的答案真的帮了我很多=)!现在工作正常!

以上是关于在 OpenCV 中合并两个 Mat-Objects的主要内容,如果未能解决你的问题,请参考以下文章

Python-OpenCV中图像合并显示

在 OpenCV 中对齐两个图像

在 OpenCV 的窗口中添加两个 Mat

利用OpenCV的函数split()和merge()实现通道的分离与合并

Opencv 最小外接矩形合并拼接

如何在 Python 中使用 OpenCV 合并 2 个灰度图像