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 图像,而不是 BGRAimage2[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(或 OpenCV)中多边形集的 Voronoi 图