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

8
我正在使用Emgu.CV执行一些基本的图像操作和合成。我的图像以Image<Bgra,Byte>的形式加载。
问题#1:当我使用Image<,>.Add()方法时,无论alpha值如何,图像都会被混合在一起。相反,我想将它们叠加在彼此上方,并使用包含的alpha通道来确定应该如何混合图像。因此,如果我调用image1.Add(image2),则image2中的任何完全不透明像素都将完全覆盖image1中的像素,而半透明像素将根据alpha值混合。
这是我试图以可视化方式完成的内容。有一个带有一些“透明孔”的城市图像和青蛙后面。这就是它应该看起来的样子: enter image description here 这就是openCV生成的内容。 This is what OpenCV (Emgu.CV) produces when I call "add" 我如何在OpenCV中获得这种效果?并且它是否与调用Add()一样快?
问题#2:有没有一种方法可以原地执行此组合,而不是在每次调用Add()时创建新图像?(例如,image1.AddImageInPlace(image2)修改了image1的字节?)
注意:寻找Emgu.CV内的答案,因为我正在使用它来处理透视变形。

嗨,你能否提供这两张图片给我们,以便我们尝试复制吗?谢谢! - jlengrand
4个回答

6

在 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:
  • 一些文章 尝试绕过此限制,但我自己没有测试过;
  • 最简单的解决方案是使用另一个API来加载图像并混合它,请查看blImageBlending;
  • 另一个不那么轻量级的选择是使用 Qt
如果你的版本已经支持带有RGBA的PNG:

编辑:

最近我也遇到了这个问题,并在这个答案中展示了如何处理它。


我不理解你的编辑。位移贴图与 alpha 混合有什么关系? - kdbanman
"位移贴图滤镜"的实现会叠加两张图片,即根据 alpha 通道值将它们混合在一起。该代码可在以下链接中找到:https://github.com/karlphillip/GraphicsProgramming/blob/master/cvDisplacementMapFilter/main.cpp - karlphillip
感谢澄清! - kdbanman

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。 - Osiris
image2[j, i].Blue * alpha 经常会溢出 255,同样适用于您的绿色和红色计算。 - kdbanman

0

以Osiris的建议为起点,并在维基百科上查看了alpha合成,最终我得出了以下对于我的目的非常有效的方法。

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

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(); 图像[] 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使用率。 - Emile

0

我在互联网上找到了一篇有趣的博客文章,我认为它与你正在尝试做的事情相关。

请查看Creating Overlays Methodarchive.org link)。您可以使用这个想法来实现自己的函数,以按照您上述提到的方式添加两个图像,使图像中的某些特定区域透明,同时保留其余部分。


1
链接已失效。 - mico

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接