基于RGB颜色空间的简单阈值肤色识别

在human skin color clustering for face detection一文中提出如下简单的判别算式:

R>95 And G>40 And B>20 And R>G And R>B And Max(R,G,B)-Min(R,G,B)>15 And Abs(R-G)>15

算法非常之简单,同样主要把复杂的判断条件放到后面去判断,能有效的降低程序的执行时间,代码实现如下:

Mat SkinDetection(Mat src) {
	int row = src.rows;
	int col = src.cols;
	int channels = src.channels();
	Mat dst(row, col, CV_8UC3);
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			int B = src.at<Vec3b>(i, j)[0];
			int G = src.at<Vec3b>(i, j)[1];
			int R = src.at<Vec3b>(i, j)[2];
			for (int k = 0; k < 3; k++) {
				dst.at<Vec3b>(i, j)[k] = src.at<Vec3b>(i, j)[k];
			}
			int maxx, minn;
			if (R > 95 && G > 40 && B > 20 && R > B && R > G && abs(R - G) > 15) {
				if (B >= G) {
					maxx = B;
					minn = G;
				}
				else {
					maxx = G;
					minn = B;
				}
				if (R > maxx)
					maxx = R;
				else if (R < minn)
					minn = R;
				if (maxx - minn > 15) {
					for (int k = 0; k < 3; k++) {
						dst.at<Vec3b>(i, j)[k] = 255;
					}
				}

			}
		}
	}
	return dst;
}

效果

基于YCbCr颜色空间的简单阈值肤色识别

该算法则更为简单,将图像转换到YCbCr颜色空间,然后按下述计算式判断是否属于皮肤区域:
(Cb > 77 And Cb < 127) And (Cr > 133 And Cr < 173)

const float YCbCrYRF = 0.299F;              // RGB转YCbCr的系数(浮点类型)
const float YCbCrYGF = 0.587F;
const float YCbCrYBF = 0.114F;
const float YCbCrCbRF = -0.168736F;
const float YCbCrCbGF = -0.331264F;
const float YCbCrCbBF = 0.500000F;
const float YCbCrCrRF = 0.500000F;
const float YCbCrCrGF = -0.418688F;
const float YCbCrCrBF = -0.081312F;

const float RGBRYF = 1.00000F;            // YCbCr转RGB的系数(浮点类型)
const float RGBRCbF = 0.0000F;
const float RGBRCrF = 1.40200F;
const float RGBGYF = 1.00000F;
const float RGBGCbF = -0.34414F;
const float RGBGCrF = -0.71414F;
const float RGBBYF = 1.00000F;
const float RGBBCbF = 1.77200F;
const float RGBBCrF = 0.00000F;

const int Shift = 20;
const int HalfShiftValue = 1 << (Shift - 1);

const int YCbCrYRI = (int)(YCbCrYRF * (1 << Shift) + 0.5);         // RGB转YCbCr的系数(整数类型)
const int YCbCrYGI = (int)(YCbCrYGF * (1 << Shift) + 0.5);
const int YCbCrYBI = (int)(YCbCrYBF * (1 << Shift) + 0.5);
const int YCbCrCbRI = (int)(YCbCrCbRF * (1 << Shift) + 0.5);
const int YCbCrCbGI = (int)(YCbCrCbGF * (1 << Shift) + 0.5);
const int YCbCrCbBI = (int)(YCbCrCbBF * (1 << Shift) + 0.5);
const int YCbCrCrRI = (int)(YCbCrCrRF * (1 << Shift) + 0.5);
const int YCbCrCrGI = (int)(YCbCrCrGF * (1 << Shift) + 0.5);
const int YCbCrCrBI = (int)(YCbCrCrBF * (1 << Shift) + 0.5);

const int RGBRYI = (int)(RGBRYF * (1 << Shift) + 0.5);              // YCbCr转RGB的系数(整数类型)
const int RGBRCbI = (int)(RGBRCbF * (1 << Shift) + 0.5);
const int RGBRCrI = (int)(RGBRCrF * (1 << Shift) + 0.5);
const int RGBGYI = (int)(RGBGYF * (1 << Shift) + 0.5);
const int RGBGCbI = (int)(RGBGCbF * (1 << Shift) + 0.5);
const int RGBGCrI = (int)(RGBGCrF * (1 << Shift) + 0.5);
const int RGBBYI = (int)(RGBBYF * (1 << Shift) + 0.5);
const int RGBBCbI = (int)(RGBBCbF * (1 << Shift) + 0.5);
const int RGBBCrI = (int)(RGBBCrF * (1 << Shift) + 0.5);

Mat RGB2YCbCr(Mat src) {
	int row = src.rows;
	int col = src.cols;
	Mat dst(row, col, CV_8UC3);
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			int Blue = src.at<Vec3b>(i, j)[0];
			int Green = src.at<Vec3b>(i, j)[1];
			int Red = src.at<Vec3b>(i, j)[2];
			dst.at<Vec3b>(i, j)[0] = (int)((YCbCrYRI * Red + YCbCrYGI * Green + YCbCrYBI * Blue + HalfShiftValue) >> Shift);
			dst.at<Vec3b>(i, j)[1] = (int)(128 + ((YCbCrCbRI * Red + YCbCrCbGI * Green + YCbCrCbBI * Blue + HalfShiftValue) >> Shift));
			dst.at<Vec3b>(i, j)[2] = (int)(128 + ((YCbCrCrRI * Red + YCbCrCrGI * Green + YCbCrCrBI * Blue + HalfShiftValue) >> Shift));
		}
	}
	return dst;
}

Mat SkinDetection2(Mat src) {
	int row = src.rows;
	int col = src.cols;
	int channels = src.channels();
	Mat dst(row, col, CV_8UC3);
	Mat temp = RGB2YCbCr(src);
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			for (int k = 0; k < 3; k++) {
				dst.at<Vec3b>(i, j)[k] = src.at<Vec3b>(i, j)[k];
			}
			int Y = temp.at<Vec3b>(i, j)[0];
			int Cb = temp.at<Vec3b>(i, j)[1];
			int Cr = temp.at<Vec3b>(i, j)[2];
			if (Cb > 77 && Cb < 127 && Cr > 133 && Cr < 173) {
				for (int k = 0; k < 3; k++) {
					dst.at<Vec3b>(i, j)[k] = 255;
				}
			}
		}
	}
	return dst;
}

效果



这些肤色检测算法准确率很低,大家笑一笑就好了。

参考博客

https://www.cnblogs.com/Imageshop/p/3265353.html