目前公开的大量皮肤检测算法是典型的基于颜色先验的目标检测算法,研究成果也比较多,在此我选择使用比较多的基于椭圆皮肤模型的皮肤检测算法进行简单介绍。在深度学习的加持下,目前该类算法很少商用化了,仅作为学习用途了。
完整的代码已上传至:椭圆肤色模型opencv代码
一、基本原理
基于椭圆皮肤模型的皮肤检测主要是利用YCrCb(或YUV)颜色空间中的Cr和Cb分量来进行肤色判断。在这种颜色空间中,肤色像素点在CrCb二维平面上呈现出近似椭圆形的分布。因此,通过定义一个肤色椭圆模型,可以判断一个像素点是否属于肤色区域。
二、具体步骤
-
图像转换:
- 将输入的图像从BGR(或RGB)颜色空间转换到YCrCb颜色空间。
- 提取Cr和Cb分量,忽略Y分量(亮度),因为肤色检测主要依赖于色度信息。
-
椭圆模型定义:
- 根据前人的实验数据,定义一个肤色椭圆模型。这个模型通常包括椭圆的中心点、长半轴、短半轴和旋转角度等参数。该参数在《一种基于椭圆肤色模型的人脸检测方法》等论文中有给出,在这里不再重复说明椭圆参数的计算过程,在代码中有详细给出。
- 在OpenCV中,可以使用
ellipse
函数来绘制这个椭圆模型。
-
肤色判断:
- 遍历图像中的每个像素点,获取其Cr和Cb值。
- 将这些值代入到肤色椭圆模型中,判断该点是否在椭圆内(包括边界)。
- 如果在椭圆内,则将该点标记为肤色点;否则,标记为非肤色点。
-
后处理:
- 对得到的肤色区域进行降噪处理,如中值滤波、高斯滤波等,以去除噪声点。
- 提取肤色区域的轮廓,并根据轮廓面积等特征进行进一步的筛选和处理。
三、注意事项
-
光线影响:
- 在进行肤色检测时,需要注意光线对检测结果的影响。由于YCrCb颜色空间中的Y分量代表亮度,因此肤色检测不会受到光线亮度变化的影响。但是,如果光线颜色发生变化(如色温变化),可能会对Cr和Cb分量产生影响,从而影响肤色检测的准确性。
-
肤色差异:
- 不同人的肤色存在差异,因此肤色椭圆模型可能需要根据实际情况进行调整。例如,对于深色皮肤的人群,可能需要定义一个更大的肤色椭圆模型来包含更多的肤色像素点。
-
算法优化:
- 在实际应用中,可以通过优化算法来提高肤色检测的速度和准确性。例如,可以使用积分图像来加速像素点的遍历过程,或者使用更复杂的机器学习算法来自动调整肤色椭圆模型的参数。
四、opencv实现例程
/*基于椭圆皮肤模型的皮肤检测
*返回皮肤区域的掩膜图像
*/
cv::Mat ellipse_detect(cv::Mat& input)
{
cv::Mat skin_model = cv::Mat::zeros(cv::Size(256, 256), CV_8UC1);
//利用opencv自带的椭圆生成函数先生成一个肤色椭圆模型
cv::ellipse(skin_model, cv::Point(113, 155.6), cv::Size(23.4, 15.2),
43.0, 0.0, 360.0, cv::Scalar(255, 255, 255), -1);
cv::imshow("椭圆皮肤模型", skin_model);
cv::Mat ycrcb;
cv::Mat mask = cv::Mat::zeros(input.size(), CV_8UC1);
//首先转换成到YCrCb空间
cvtColor(input, ycrcb, cv::COLOR_BGR2YCrCb);
for (unsigned int r = 0; r < input.rows; r++)
{
cv::Vec3b* ycrcb_data_ptr = (cv::Vec3b*)ycrcb.ptr(r);
unsigned char* mask_data_ptr = mask.ptr(r);
for (unsigned int c = 0; c < input.cols; c++)
{
cv::Vec3b ycrcb_value = ycrcb_data_ptr[c];
//如果以cr、cb的值作为像素坐标,在皮肤图像中落在椭圆中,则该点对应的原始图像判断为皮肤区域
if (skin_model.at<unsigned char>(ycrcb_value[1], ycrcb_value[2]) > 0)
mask_data_ptr[c] = 255;
}
}
return mask;
}
测试主函数:
#include "opencv2/opencv.hpp"
cv::Mat ellipse_detect(cv::Mat& input);
int main()
{
cv::Mat image = cv::imread("./image/leijun.png", cv::IMREAD_COLOR);
cv::Mat mask = ellipse_detect(image);
cv::imshow("原始图", image);
cv::imshow("皮肤掩膜", mask);
cv::waitKey();
return 0;
}
运行结果截图如下:
- 基于颜色的皮肤检测算法,一般来说都在光照稳定、角度合适的特殊场景下应用,适应性较差。
- 基于颜色的皮肤检测算法,已经被深度学习类下人脸分割算法所代替,现行的商业应用均采用类似的方案。