基于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;
}
效果
这些肤色检测算法准确率很低,大家笑一笑就好了。