单色图最简单、最完美的抠像方法 - 透明渐变抠像 - C#
任务描述

用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 的抠像处理效果更加完美,视觉上与原图高度一致。
浙公网安备 33010602011771号