任务描述

用Ai制作图像,是一件非常简单的事情, 比如说, 导引ai先后生成了上面的人体简化图像, 但是 让它在抠除背景的时候,AI在处理精准性上表现很差,此时我们可以尝试的方式最少有两种。
PS抠像
第1种是用在线ps的魔棒工具进行抠像,但是抠除后的边缘锯齿感明显,整体效果非常的差 ,基本不可用:


AI抠像
然后我们还可以尝试使用即梦AI来抠像,效果比在线ps好了很多,但是仍然肉眼可见到边缘有背景色的存在:


自己写算法抠像
那么如何才能做到 让背景色从视觉上完全无感呢?
这里有一个小技巧,它处理的核心理念就是:越接近目标色,那么让它的透明通道值越接近255,越接近背景色,那么让它的透明通道值越接近于0,这样的话,在扣除色与目标色之间会形成完全平滑的透明过渡,从视觉上来说,会形成绝对的连贯性。
例如,用C#进行的算法处理,核心源码:
internal static class ChromaKeyProcessor
{public static Bitmap EnsureRgba32(Bitmap source){if (source.PixelFormat == PixelFormat.Format32bppArgb){return (Bitmap)source.Clone();}var rgba = new Bitmap(source.Width, source.Height, PixelFormat.Format32bppArgb);using (var g = Graphics.FromImage(rgba)){g.DrawImage(source, 0, 0, source.Width, source.Height);}return rgba;}public static void ApplyAlphaFromTwoColors(Bitmap rgba, Color keepColor, Color removeColor){if (rgba.PixelFormat != PixelFormat.Format32bppArgb)throw new ArgumentException("需要 Format32bppArgb 位图。");int keepR = keepColor.R, keepG = keepColor.G, keepB = keepColor.B;int removeR = removeColor.R, removeG = removeColor.G, removeB = removeColor.B;var rect = new Rectangle(0, 0, rgba.Width, rgba.Height);var data = rgba.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);try{int stride = data.Stride;int bytes = Math.Abs(stride) * rgba.Height;var buffer = new byte[bytes];Marshal.Copy(data.Scan0, buffer, 0, bytes);for (int y = 0; y < rgba.Height; y++){int row = y * stride;for (int x = 0; x < rgba.Width; x++){int i = row + x * 4;int b = buffer[i];int g = buffer[i + 1];int r = buffer[i + 2];double dKeep = ColorDistanceSq(r, g, b, keepR, keepG, keepB);double dRemove = ColorDistanceSq(r, g, b, removeR, removeG, removeB);byte alpha = ComputeAlpha(dKeep, dRemove);buffer[i + 3] = alpha;}}Marshal.Copy(buffer, 0, data.Scan0, bytes);}finally{rgba.UnlockBits(data);}}/// <summary>/// 将带透明通道的图叠到指定背景色上,用于预览。/// </summary>public static Bitmap CompositeOnBackground(Bitmap rgba, Color background){var display = new Bitmap(rgba.Width, rgba.Height, PixelFormat.Format32bppArgb);using (var g = Graphics.FromImage(display)){g.Clear(background);g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;g.DrawImage(rgba, 0, 0);}return display;}private static double ColorDistanceSq(int r1, int g1, int b1, int r2, int g2, int b2){int dr = r1 - r2;int dg = g1 - g2;int db = b1 - b2;return dr * dr + dg * dg + db * db;}/// <summary>/// alpha = 255 * dRemove / (dKeep + dRemove)/// 在保留色处 dKeep=0 → 255;在抠除色处 dRemove=0 → 0。/// </summary>private static byte ComputeAlpha(double dKeep, double dRemove){const double epsilon = 1e-6;if (dKeep < epsilon && dRemove < epsilon)return 255;if (dKeep < epsilon)return 255;if (dRemove < epsilon)return 0;double sum = dKeep + dRemove;double t = dRemove / sum;if (t < 0) t = 0;if (t > 1) t = 1;return (byte)(t * 255.0 + 0.5);}
}
处理后的效果基本达到了完全可用的状态 :


细节的表现非常完美。

本文使用C#实现了一种 通过透明过渡方式 来进行抠像的方法,比 在线PS 和 即梦AI 的抠像处理效果更加完美,视觉上与原图高度一致。