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; }