基于像素的皮肤检测技术

介绍一种基于颜色空间的皮肤检测技术,可以检测亚洲人种与白人的皮肤,皮肤检测

人脸识别的基础,也是很多人像识别技术的基础操作,在实际应用中还是非常有用的。

 

基于像素的皮肤检测主要是寻找正确的颜色空间几何,图像处理中,常见的颜色空间

有如下几种

1.      RGB色彩空间 – R代表单色红,G代表单色绿,B代表单色蓝

2.      HSV色彩空间 – H 代表色彩, S代表饱和度,V代表强度值

3.      YCbCr色彩空间 – 是数字电视的色彩空间

 

RGB转换为HSV的Java代码如下:

	public static float[] rgbToHSV(int tr, int tg, int tb) { 		float min, max, delta; 		float hue, satur, value; 		min = Math.min(tr, Math.min(tg, tb)); 		max = Math.max(tr, Math.max(tg, tb)); 		value = max; 		delta = max - min; 		if(max != 0) { 			satur = delta/max; 		} else { 			satur = 0; 			hue = -1; 		} 		 		if(tr == max) { 			hue = (tg - tb)/delta; 		} 		else if(tg == max) { 			hue = 2 + (tb-tr)/delta; 		} else { 			hue = 4 + (tr-tg)/delta; 		} 		hue = hue * 60.0f; 		if(hue < 0) { 			hue = hue + 360; 		} 		return new float[]{hue, satur, value}; 	}

RGB转换为YCbCr的Java代码如下:

	public static int[] rgbToYcrCb(int tr, int tg, int tb) { 		double sum = tr + tg + tb; 		double r = ((double)tr)/sum; 		double g = ((double)tg)/sum; 		double b = ((double)tb)/sum; 		double y = 65.481 * r + 128.553 * g + 24.966 * b + 16.0d; 		double Cr = -37.7745 * r - 74.1592 * g + 111.9337 * b + 128.0d; 		double Cb = 111.9581 * r -93.7509 * g -18.2072 * b + 128.0d; 		return new int[]{(int)y, (int)Cr, (int)Cb}; 	}
一个简单的基于RGB颜色空间的皮肤算法如下:

(R, G, B) is classified as skin if

R > 95 and G > 40 and B > 20and max{R, G, B} – min{R, G, B} > 15 and |R-G| > 15

and R > G and R > B

实现代码如下:

	public boolean isSkin(int tr, int tg, int tb) { 		int max = Math.max(tr, Math.max(tg, tb)); 		int min = Math.min(tr, Math.min(tg, tb)); 		int rg = Math.abs(tr - tg); 		if(tr > 95 && tg > 40 && tb > 20 && rg > 15 &&  				(max - min) > 15 && tr > tg && tr > tb) { 			return true; 		} else { 			return false; 		} 	}

一个简单的基于HSV颜色空间的皮肤算法如下:

(H, S, V) will be classified as skin if

H > 0 and H < 50 and S > 0.23 andS < 0.68

实现代码如下:

	public boolean isSkin(int tr, int tg, int tb) { 		float[] HSV = ColorUtil.rgbToHSV(tr, tg, tb); 		if((HSV[0] > 0.0f && HSV[0] < 50.0f ) && (HSV[1] > 0.23f && HSV[1] < 0.68f)){ 			return true; 		} else { 			return false; 		} 	}

一个简单的基于YCbCr颜色空间的皮肤算法如下:

(Y, Cb, Cr) will be classified as skin if:

Y > 80 and 85<Cb < 135 and 135 <Cr < 180, and (Y,Cb,Cr)= [0,255] 

对于的Java代码如下:

	public boolean isSkin(int tr, int tg, int tb) { 		int y = (int)(tr * 0.299 + tg * 0.587 + tb * 0.114); 		int Cr = tr - y; 		int Cb = tb - y; 		if(y> 80 && y < 255 && Cr > 133 && Cr < 173 && 77 < Cb && Cb < 127) { 			return true; 		} 		return false; 	}
基于上述三个算法实现的皮肤检测的效果如下:


皮肤检测滤镜的源代码如下:

package com.process.blur.study;  import java.awt.Color; import java.awt.image.BufferedImage;  import com.gloomyfish.skin.dection.DefaultSkinDetection; import com.gloomyfish.skin.dection.FastSkinDetection; import com.gloomyfish.skin.dection.GaussianSkinDetection; import com.gloomyfish.skin.dection.HSVSkinDetection; import com.gloomyfish.skin.dection.ISkinDetection;  public class SkinFilter extends AbstractBufferedImageOp { 	private ISkinDetection skinDetector; 	 	public SkinFilter(int type) { 		if(type == 2) { 			skinDetector = new FastSkinDetection(); 		} else if(type == 4) { 			skinDetector = new HSVSkinDetection(); 		} else if(type == 8) { 			skinDetector = new GaussianSkinDetection(); 		} else { 			skinDetector = new DefaultSkinDetection(); 		} 	}  	@Override 	public BufferedImage filter(BufferedImage src, BufferedImage dst) { 		int width = src.getWidth();         int height = src.getHeight();          if ( dst == null )             dst = createCompatibleDestImage( src, null );          int[] inPixels = new int[width*height];         int[] outPixels = new int[width*height];         getRGB( src, 0, 0, width, height, inPixels );         if(skinDetector instanceof GaussianSkinDetection) {         	((GaussianSkinDetection)skinDetector).setDispSample(getDispersion(src));         }         int index = 0;         for(int row=0; row> 24) & 0xff;                 tr = (inPixels[index] >> 16) & 0xff;                 tg = (inPixels[index] >> 8) & 0xff;                 tb = inPixels[index] & 0xff;                 if(skinDetector.isSkin(tr, tg, tb)) {                 	outPixels[index] = (ta << 24) | (tr << 16) | (tg << 8) | tb;                 } else {                 	tr = tg = tb = 0;                 	outPixels[index] = (ta << 24) | (tr << 16) | (tg << 8) | tb;                 }                        	}         }         setRGB( dst, 0, 0, width, height, outPixels );         return dst; 	} 	 	public Color getDispersion(BufferedImage image) {         // calculate means of pixel           int index = 0;         int height = image.getHeight();         int width = image.getWidth();         int[] inPixels = new int[width*height];         getRGB(image, 0, 0, width, height, inPixels );         double redSum = 0, greenSum = 0, blueSum = 0;         Color meanColor = getMean(image);         double redmeans = meanColor.getRed();         double greenmeans = meanColor.getGreen();         double bluemeans = meanColor.getBlue();         double total = height * width;           for(int row=0; row> 24) & 0xff;                   tr = (inPixels[index] >> 16) & 0xff;                   tg = (inPixels[index] >> 8) & 0xff;                   tb = inPixels[index] & 0xff;                  double rd = (tr - redmeans);                 double gd = (tg - greenmeans);                 double bd = (tb - bluemeans);                 redSum += rd * rd;                   greenSum += gd * gd;                   blueSum += bd * bd;               }           }         int reddiff = (int)Math.sqrt((redSum / total));         int greendiff = (int)Math.sqrt((greenSum / total));         int bluediff = (int)Math.sqrt(blueSum / total);         System.out.println(" red dispersion value = " + reddiff);         System.out.println(" green dispersion value = " + greendiff);         System.out.println(" blue dispersion value = " + bluediff); 		return new Color(reddiff, greendiff, bluediff); 	} 	 	public Color getMean(BufferedImage image) {         // calculate means of pixel           int index = 0;         int height = image.getHeight();         int width = image.getWidth();         int[] inPixels = new int[width*height];         getRGB(image, 0, 0, width, height, inPixels );         double redSum = 0, greenSum = 0, blueSum = 0;           double total = height * width;           for(int row=0; row> 24) & 0xff;                   tr = (inPixels[index] >> 16) & 0xff;                   tg = (inPixels[index] >> 8) & 0xff;                   tb = inPixels[index] & 0xff;                   redSum += tr;                   greenSum += tg;                   blueSum +=tb;               }           }         int redmeans = (int)(redSum / total);         int greenmeans = (int)(greenSum / total);         int bluemeans = (int)(blueSum / total);         System.out.println(" red average value = " + redmeans);         System.out.println(" green average value = " + greenmeans);         System.out.println(" blue average value = " + bluemeans); 		return new Color(redmeans, greenmeans, bluemeans); 	} } 

讨论:

皮肤检测中的后续处理非常重要,可以除去噪声,平滑图像,是皮肤检测的结果

更加的准确,输出的更容易接受。


参考引用:

《A New Fast Skin Color Detection Technique》 - Tarek M. Mahmoud

《Improved Automatic Skin Detection in Color Images》 - Filipe Tomaz

                                               and Tiago Candeias and Hamid Shahbazkia

《Skin Detection using HSV color space》- unknown author