C#肤色检测
上篇有提到BabyMaker项目需要提取照片中人的肤色,我对于图像处理一直停留在零经验,在翻阅数篇论文依旧毫无思路准备放弃之际,看到了一个大牛用C#写的肤色检测算法,算法可以去除图片中和皮肤无关的信息,仅保留皮肤。
下面就是经过处理以后的图片了
效果非常明显,这样就只剩下皮肤信息了,最简单方法(我能想到的唯一方法…)的当就是取出每个像素点的颜色然后取个平均。
public static Color getAverageColor(Bitmap bmp)
{
int width = bmp.Width;
int height = bmp.Height;
int sum = 0; //总像素点
int r = 0, g = 0, b = 0, a = 0;
Color c = Color.Empty;
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
c = bmp.GetPixel(i, j);
if (c.R + c.G + c.B != 0) //排除黑色
{
r += c.R;
g += c.G;
b += c.B;
a += c.A;
sum++;
}
}
}
if (sum != 0) //防止碰到黑人.....
{
r = r / sum;
g = g / sum;
b = b / sum;
a = a / sum;
}
c = Color.FromArgb(a, r, g, b);
return c;
}
很多人说C#的setpixel和getpixel操作效率非常点,想想也是,这么大一张图片,一个一个像素遍历,可以考虑使用BitmapData用指针直接在内存中操作图片颜色,速度提升明显,不过需要用unsafe围起来,附上大牛的算法(完全看不懂)。
public static Bitmap skinDetect(Bitmap a)
{
Rectangle rect = new Rectangle(0, 0, a.Width, a.Height);
BitmapData bmpData = a.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
int stride = bmpData.Stride;
unsafe
{
byte* pIn = (byte*)bmpData.Scan0.ToPointer();
byte* P;
int R, G, B;
double r, g, Fupr, Flor, Wrg;
for (int y = 0; y < a.Height; y++)
{
for (int x = 0; x < a.Width; x++)
{
P = pIn;
B = P[0];
G = P[1];
R = P[2];
if (R + G + B == 0)
{
r = 0;
g = 0;
}
else
{
r = (R / (R + G + B));
g = (G / (R + G + B));
}
Fupr = (1.0743 * r + 0.1452 - 1.3767 * r * r);
Flor = (0.5601 * r + 0.1766 - 0.776 * r * r);
Wrg = (r - 0.33) * (r - 0.33) + (g - 0.33) * (g - 0.33);
if ((R - G >= 45) && ((R > G) && (G > B)) && (Fupr > g) && (Wrg >= 0.0004))
{
P[0] = (byte)B;
P[1] = (byte)G;
P[2] = (byte)R;
}
else
{
P[0] = 0;
P[1] = 0;
P[2] = 0;
}
pIn += 3;
}
pIn += stride - a.Width * 3;
}
}
a.UnlockBits(bmpData);
return a;
}