我有一张需要更改背景颜色的图像(例如,将下面示例图像的背景更改为蓝色)。
但是,图像是抗锯齿的,所以我不能简单地用不同的颜色替换背景颜色。
我尝试过的一种方法是创建第二个图像,它只是背景并更改它的颜色并将两个图像合并为一个,但是这不起作用,因为两个图像之间的边界是模糊的。
有什么方法可以做到这一点,或者我没有考虑过其他一些方法来实现这一点?
我有一张需要更改背景颜色的图像(例如,将下面示例图像的背景更改为蓝色)。
但是,图像是抗锯齿的,所以我不能简单地用不同的颜色替换背景颜色。
我尝试过的一种方法是创建第二个图像,它只是背景并更改它的颜色并将两个图像合并为一个,但是这不起作用,因为两个图像之间的边界是模糊的。
有什么方法可以做到这一点,或者我没有考虑过其他一些方法来实现这一点?
只使用 GDI+
Image image = Image.FromFile("cloud.png");
Bitmap bmp = new Bitmap(image.Width, image.Height);
using (Graphics g = Graphics.FromImage(bmp)) {
g.Clear(Color.SkyBlue);
g.InterpolationMode = InterpolationMode.NearestNeighbor;
g.PixelOffsetMode = PixelOffsetMode.None;
g.DrawImage(image, Point.Empty);
}
导致:
图像中的每个像素都是一个 (R, G, B) 向量,其中每个分量都在 [0, 1] 范围内。您需要一个变换 T,它将图像中的所有像素在以下约束下转换为新的 (R', G', B'):
一种直接的方法是选择以下变换 T:
T(c) = C* .* c (where .* denotes element-wise multiplication)
这只是标准的图像乘法。
如果您不担心性能,您可以使用(非常慢的)方法GetPixel
并SetPixel
在您的身上Bitmap
为其中的每个像素应用此变换。如果不清楚如何执行此操作,请在评论中说明,我会为该部分添加详细说明。
将此与 LarsTech 提出的方法进行比较。这里介绍的方法在顶部;LarsTech 提出的方法在底部。请注意底部图标上的不良边缘效果(边缘有白雾)。
这是两者的图像差异:
如果您的源图像具有透明(即透明白色)背景和黑色前景(如您的示例),那么您可以简单地进行变换 T(a, r, g, b) = (a, 0, 0, 0 ) 然后按照 LarsTech 的建议将图像绘制在您想要的任何背景颜色之上。
背景去除/替换,IMO 更多的是艺术而不是科学,您不会找到适合此问题的所有解决方案的算法,但取决于您对解决此问题的绝望或感兴趣程度,您可能需要考虑以下解释:
假设您有一张彩色图像。
使用您选择的解码机制并生成彩色图像的灰度/亮度图像。
绘制像素 (x) 的数值与该值 (y) 的图像中像素数的关系图(比喻地说)。阿卡。亮度直方图。
现在,如果您的背景足够大(或小),您会看到图形的一部分,表示构成背景的一系列像素的分布。您可能希望选择稍宽的范围来处理抗锯齿(基于您在处理相似图像时定义的固定偏移量)并将其称为背景的亮度范围。
如果您知道定义背景的像素范围之外的至少一个像素(样本/中值像素值),这将使您的生活更轻松,这样您就可以“查找”定义背景的图形部分。
一旦你有了背景的亮度像素范围,你可以遍历原始图像像素,将它们的亮度值与你拥有的范围进行比较,如果它在范围内,将原始图像中的像素替换为所需的颜色,最好是亮度基于原始像素和样本像素移动,因此被替换的背景也看起来是抗锯齿的。
这不是一个完美的解决方案,在很多情况下它可能会失败/部分失败,但它同样适用于您随问题附加的示例图像。
还有很多性能改进机会,包括 GPGPU 等。
另一种可能的解决方案是使用一些预先构建的第三方图像处理库,有一些开源,例如 Camellia,但我不确定提供了哪些功能以及它们的复杂程度。