我有一个图像需要更改背景颜色(例如,将下面示例图像的背景更改为蓝色)。
但是,图像是抗锯齿的,所以我不能简单地用不同的颜色替换背景颜色。
我尝试过的一种方法是创建第二个图像,仅作为背景并更改其颜色,然后将两个图像合并为一个,但是这不起作用,因为两个图像之间的边界模糊。
有什么方法可以做到这一点,或者有其他我没有考虑过的方法来实现这一点吗?
仅使用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,但我不确定提供了哪些功能以及它们有多复杂。