OpenCV (Emgu.CV)——用 alpha 合成图像

Posted

技术标签:

【中文标题】OpenCV (Emgu.CV)——用 alpha 合成图像【英文标题】:OpenCV (Emgu.CV) -- compositing images with alpha 【发布时间】:2012-08-11 02:37:00 【问题描述】:

我正在使用 Emgu.CV 执行一些基本的图像处理和合成。我的图片加载为Image<Bgra,Byte>

问题 #1: 当我使用 Image<,>.Add() 方法时,无论 alpha 值如何,图像总是混合在一起。相反,我希望它们一个接一个地合成,并使用包含的 alpha 通道来确定应该如何混合图像。因此,如果我调用image1.Add(image2),image2 中的任何完全不透明像素都会完全覆盖 image1 中的像素,而半透明像素将根据 alpha 值进行混合。

这就是我试图以视觉形式做的事情。有一个城市形象,剪掉了一些“透明的洞”,后面是一只青蛙。它应该是这样的:

这就是 openCV 产生的结果。

如何使用 OpenCV 获得这种效果?它会和拨打Add()一样快吗?

问题 #2: 有没有办法就地执行此合成,而不是每次调用 Add() 时创建一个新图像? (例如image1.AddImageInPlace(image2)修改了image1的字节?)

注意:在 Emgu.CV 中寻找答案,我之所以使用它是因为它可以很好地处理透视变形。

【问题讨论】:

嘿。有没有办法可以向我们提供这两个图像,以便我们可以尝试复制?谢谢! 【参考方案1】:

在 OpenCV 2.4 之前,不支持带有 alpha 通道的 PNG。

要验证您当前的版本是否支持它,请在加载您确定为 RGBA 的图像后打印通道数。如果支持,应用程序将输出数字 4,否则将输出数字 3 (RGB)。使用 C API 你会这样做:

IplImage* t_img = cvLoadImage(argv[1], CV_LOAD_IMAGE_UNCHANGED);
if (!t_img)

    printf("!!! Unable to load transparent image.\n");
    return -1;

printf("Channels: %d\n", t_img->nChannels);

如果无法更新 OpenCV

There are some posts 试图绕过这个限制,但我自己没有测试过; 最简单的解决方案是使用另一个 API 来加载图像并进行混合,请查看 blImageBlending; 另一个不那么轻量级的替代方法是useQt

如果您的版本已经支持带 RGBA 的 PNG

看看Emulating photoshop’s blending modes in OpenCV。它实现了几个Photoshop blending modes,我想您能够将该代码转换为.Net。

编辑:

我最近不得不处理这个问题,我已经在 this answer 上演示了如何处理它。

【讨论】:

我不明白您的编辑。置换贴图与 alpha 混合有什么关系? The implementation of "displacement map filter" overlays 2 images,即根据 Alpha 通道值将它们混合在一起。 感谢您的澄清!【参考方案2】:

您必须遍历每个像素。我假设图像 1 是青蛙图像,图像 2 是城市图像,图像 1 总是大于图像 2。

//to simulate image1.AddInPlace(image2)
 int image2w = image2.Width;
 int image2h = image2.Height;
 int i,j;
 var alpha;
 for (i = 0; i < w; i++)
 
     for (j = 0; j < h; j++)
     
           //alpha=255 is opaque > image2 should be used
           alpha = image2[3][j,i].Intensity;
           image1[j, i] 
               = new Bgra(
               image2[j, i].Blue * alpha + (image1[j, i].Blue * (255-alpha)),
               image2[j, i].Green * alpha + (image1[j, i].Green * (255-alpha)),
               image2[j, i].Red * alpha + (image1[j, i].Red * (255-alpha)));
      
 

【讨论】:

我还假设 image1 是 BGR 图像,而不是 BGRA image2[j, i].Blue * alpha 会经常溢出超过 255,绿色和红色计算也是如此。【参考方案3】:

以 Osiris 的建议为起点,并在 Wikipedia 上查看了 alpha compositing,我最终得到了以下内容,它非常适合我的目的。

这与 Emgucv 一起使用。我希望在 Emgucv 中可以使用 opencv gpu::AlphaComposite 方法,我相信它可以为我完成以下工作,但可惜我使用的版本似乎没有实现它们。

static public Image<Bgra, Byte> Overlay( Image<Bgra, Byte> image1, Image<Bgra, Byte> image2 )
        

            Image<Bgra, Byte> result = image1.Copy();
            Image<Bgra, Byte> src = image2;
            Image<Bgra, Byte> dst = image1;

            int rows = result.Rows;
            int cols = result.Cols;
            for (int y = 0; y < rows; ++y)
            
                for (int x = 0; x < cols; ++x)
                
                    // http://en.wikipedia.org/wiki/Alpha_compositing
                    double  srcA = 1.0/255 * src.Data[y, x, 3];
                    double dstA = 1.0/255 * dst.Data[y, x, 3];
                    double outA = (srcA + (dstA - dstA * srcA));
                    result.Data[y, x, 0] = (Byte)(((src.Data[y, x, 0] * srcA) + (dst.Data[y, x, 0] * (1 - srcA))) / outA);  // Blue
                    result.Data[y, x, 1] = (Byte)(((src.Data[y, x, 1] * srcA) + (dst.Data[y, x, 1] * (1 - srcA))) / outA);  // Green
                    result.Data[y, x, 2] = (Byte)(((src.Data[y, x, 2] * srcA) + (dst.Data[y, x, 2] * (1 - srcA))) / outA); // Red
                    result.Data[y, x, 3] = (Byte)(outA*255);
                
            
            return result;
        

较新的版本,使用 emgucv 方法。而不是一个循环。不确定它会提高性能。 双单位 = 1.0 / 255.0; 图像[] dstS = dst.Split(); 图像[] srcS = src.Split(); Image[] rs = result.Split();

        Image<Gray, double> srcA = srcS[3] * unit;
        Image<Gray, double> dstA = dstS[3] * unit;
        Image<Gray, double> outA = srcA.Add(dstA.Sub(dstA.Mul(srcA)));// (srcA + (dstA - dstA * srcA));

        // Red.
        rs[0] = srcS[0].Mul(srcA).Add(dstS[0].Mul(1 - srcA)).Mul(outA.Pow(-1.0)); // Mul.Pow is divide.
        rs[1] = srcS[1].Mul(srcA).Add(dstS[1].Mul(1 - srcA)).Mul(outA.Pow(-1.0));
        rs[2] = srcS[2].Mul(srcA).Add(dstS[2].Mul(1 - srcA)).Mul(outA.Pow(-1.0));
        rs[3] = outA.Mul(255);

        // Merge image back together.
        CvInvoke.cvMerge(rs[0], rs[1], rs[2], rs[3], result);
        return result.Convert<Bgra, Byte>();

【讨论】:

注意,这实际上是一个糟糕的函数,在分析时它代表了 80% 的 cpu 使用率。【参考方案4】:

我在互联网上发现了一篇有趣的博客文章,我认为这与您正在尝试做的事情有关。

请查看Creating Overlays Method (archive.org link)。您可以使用这个想法来实现您自己的功能,以您上面提到的方式添加两个图像,使图像中的某些特定区域透明,而其余部分保持不变。

【讨论】:

链接已失效

以上是关于OpenCV (Emgu.CV)——用 alpha 合成图像的主要内容,如果未能解决你的问题,请参考以下文章

如何将轮廓层次结构从 python openCV 转换为 emgu cv 以查找封闭轮廓

Emgu.CV

来自 Emgu CV(或 OpenCV)中多边形集的 Voronoi 图

如何调用emgu cv的capture类创建变量

“Emgu.CV.CvInvoke”的类型初始值设定项引发异常。

Emgu.CV.CvInvoke的类型初始值设定项引发异常