OpenCV皮肤检测技术对比与总结

opencv做皮肤检测的技术总结。那首先列一些现在主流的皮肤检测的方法都有哪些:

  1. RGB color space
  2. Ycrcb之cr分量+otsu阈值化
  3. YCrCb中133<=Cr<=173 77<=Cb<=127
  4. HSV中 7<H<20 28<S<256 50<V<256
  5. 基于椭圆皮肤模型的皮肤检测
  6. opencv自带肤色检测类AdaptiveSkinDetector

一、基于RGB的皮肤检测

根据RGB颜色模型找出定义好的肤色范围内的像素点,范围外的像素点设为黑色。

查阅资料后可以知道,前人做了大量研究,肤色在RGB模型下的范围基本满足以下约束:

在均匀光照下应满足以下判别式:

R>95 AND G>40 B>20 AND MAX(R,G,B)-MIN(R,G,B)>15 AND ABS(R-G)>15 AND R>G AND R>B

在侧光拍摄环境下:

R>220 AND G>210 AND B>170 AND ABS(R-G)<=15 AND R>B AND G>B

既然判别式已经确定了,所以按照判别式写程序就很简单了。

/*基于RGB范围的皮肤检测*/
Mat RGB_detect(Mat& img)
{
    /*
        R>95 AND G>40 B>20 AND MAX(R,G,B)-MIN(R,G,B)>15 AND ABS(R-G)>15 AND R>G AND R>B
            OR
        R>220 AND G>210 AND B>170 AND ABS(R-G)<=15 AND R>B AND G>B
    */
    Mat detect = img.clone();
    detect.setTo(0);
    if (img.empty() || img.channels() != 3)
    {
        return detect;
    }
    for (int i = 0; i < img.rows; i++)
    {
        for (int j = 0; j < img.cols; j++)
        {
            uchar *p_detect = detect.ptr<uchar>(i, j);
            uchar *p_img = img.ptr<uchar>(i, j);
            if ((p_img[2] > 95 && p_img[1]>40 && p_img[0] > 20 &&
                (MAX(p_img[0], MAX(p_img[1], p_img[2])) - MIN(p_img[0], MIN(p_img[1], p_img[2])) > 15) &&
                abs(p_img[2] - p_img[1]) > 15 && p_img[2] > p_img[1] && p_img[1] > p_img[0]) ||
                (p_img[2] > 200 && p_img[1] > 210 && p_img[0] > 170 && abs(p_img[2] - p_img[1]) <= 15 &&
                p_img[2] > p_img[0] &&  p_img[1] > p_img[0]))
            {
                p_detect[0] = p_img[0];
                p_detect[1] = p_img[1];
                p_detect[2] = p_img[2];
            }
        }
    }
    return detect;
}

检测效果如下:

从检测结果可以看出,皮肤的检测效果并不好,首先皮肤检测的完整性并不高,一些稍微光线不好的区域也没法检测出皮肤来。第二,这种基于RBG范围来判定皮肤的算法太受光线的影响了,鲁棒性确实不好。

二、基于椭圆皮肤模型的皮肤检测

经过前人学者大量的皮肤统计信息可以知道,如果将皮肤信息映射到YCrCb空间,则在CrCb二维空间中这些皮肤像素点近似成一个椭圆分布。因此如果我们得到了一个CrCb的椭圆,下次来一个坐标(Cr, Cb)我们只需判断它是否在椭圆内(包括边界),如果是,则可以判断其为皮肤,否则就是非皮肤像素点。

/*基于椭圆皮肤模型的皮肤检测*/
Mat ellipse_detect(Mat& src)
{
    Mat img = src.clone();
    Mat skinCrCbHist = Mat::zeros(Size(256, 256), CV_8UC1);
    //利用opencv自带的椭圆生成函数先生成一个肤色椭圆模型
    ellipse(skinCrCbHist, Point(113, 155.6), Size(23.4, 15.2), 43.0, 0.0, 360.0, Scalar(255, 255, 255), -1);
    Mat ycrcb_image;
    Mat output_mask = Mat::zeros(img.size(), CV_8UC1);
    cvtColor(img, ycrcb_image, CV_BGR2YCrCb); //首先转换成到YCrCb空间
    for (int i = 0; i < img.cols; i++)   //利用椭圆皮肤模型进行皮肤检测
        for (int j = 0; j < img.rows; j++)
        {
            Vec3b ycrcb = ycrcb_image.at<Vec3b>(j, i);
            if (skinCrCbHist.at<uchar>(ycrcb[1], ycrcb[2]) > 0)   //如果该落在皮肤模型椭圆区域内,该点就是皮肤像素点
                output_mask.at<uchar>(j, i) = 255;
        }
    Mat detect;
    img.copyTo(detect,output_mask);  //返回肤色图
    return detect;
}

 检测效果:

这种基于肤色椭圆模型的算法的皮肤检测较上面算法在效果上有着较大的提升,基本上改检测的皮肤都检测到了,对光线的抗干扰能力也是比较强的,检测出来的图像都比较干净,背景杂质较少。

三、YCrCb颜色空间Cr分量+Otsu法阈值分割

这里先简单介绍YCrCb颜色空间。

YCrCb即YUV,其中“Y”表示明亮度(Luminance或Luma),也就是灰阶值;而“U”和“V” 表示的则是色度(Chrominance或Chroma),作用是描述影像色彩及饱和度,用于指定像素的颜色。“亮度”是透过RGB输入信号来建立的,方法是将RGB信号的特定部分叠加到一起。“色度”则定义了颜色的两个方面─色调与饱和度,分别用Cr和Cb来表示。其中,Cr反映了RGB输入信号红色部分与RGB信号亮度值之间的差异。而Cb反映的是RGB输入信号蓝色部分与RGB信号亮度值之间的差异。

该方法的原理也很简单:

a.将RGB图像转换到YCrCb颜色空间,提取Cr分量图像

b.对Cr做自二值化阈值分割处理(Otsu法)

/*YCrCb颜色空间Cr分量+Otsu法*/
Mat YCrCb_Otsu_detect(Mat& src)
{
    Mat ycrcb_image;
    cvtColor(src, ycrcb_image, CV_BGR2YCrCb); //首先转换成到YCrCb空间
    Mat detect;
    vector<Mat> channels;
    split(ycrcb_image, channels);
    Mat output_mask = channels[1];
    threshold(output_mask, output_mask, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);
    src.copyTo(detect, output_mask);
    return detect;
}

 检测效果:

四、基于YCrCb颜色空间Cr,Cb范围筛选法

这个方法跟法一其实大同小异,只是颜色空间不同而已。据资料显示,正常黄种人的Cr分量大约在133至173之间,Cb分量大约在77至127之间。大家可以根据自己项目需求放大或缩小这两个分量的范围,会有不同的效果。

/*YCrCb颜色空间Cr,Cb范围筛选法*/
Mat YCrCb_detect(Mat & src)
{
    Mat ycrcb_image;
    int Cr = 1;
    int Cb = 2;
    cvtColor(src, ycrcb_image, CV_BGR2YCrCb); //首先转换成到YCrCb空间
    Mat output_mask = Mat::zeros(src.size(), CV_8UC1);
    for (int i = 0; i < src.rows; i++)
    {
        for (int j = 0; j < src.cols; j++)
        {
            uchar *p_mask = output_mask.ptr<uchar>(i, j);
            uchar *p_src = ycrcb_image.ptr<uchar>(i, j);
            if (p_src[Cr] >= 133 && p_src[Cr] <= 173 && p_src[Cb] >= 77 && p_src[Cb] <= 127)
            {
                p_mask[0] = 255;
            }
        }
    }
    Mat detect;
    src.copyTo(detect, output_mask);;
    return detect;
}

 检测效果:

五、HSV颜色空间H范围筛选法

同样地,也是在不同的颜色空间下采取相应的颜色范围将皮肤分割出来。

/*HSV颜色空间H范围筛选法*/
Mat HSV_detector(Mat& src)
{
    Mat hsv_image;
    int h = 0;
    int s = 1;
    int v = 2;
    cvtColor(src, hsv_image, CV_BGR2HSV); //首先转换成到YCrCb空间
    Mat output_mask = Mat::zeros(src.size(), CV_8UC1);
    for (int i = 0; i < src.rows; i++)
    {
        for (int j = 0; j < src.cols; j++)
        {
            uchar *p_mask = output_mask.ptr<uchar>(i, j);
            uchar *p_src = hsv_image.ptr<uchar>(i, j);
            if (p_src[h] >= 0 && p_src[h] <= 20 && p_src[s] >=48 && p_src[v] >=50)
            {
                p_mask[0] = 255;
            }
        }
    }
    Mat detect;
    src.copyTo(detect, output_mask);;
    return detect;
}

检测效果:

 

这篇文章对各大主流的皮肤检测算法做了个总结和实现。其实说白了,每个算法的思想都是大同小异的,都是根据总结出来的一些经验,设定皮肤颜色的范围,再将其过滤出来,不同的只是过滤的过程在不同的颜色空间下进行而已。我们可以根据自己的应用场景,适当地修改这些范围,以获得满意的结果。可以改善的方向就是,我们可以用合适的滤波器或者形态学处理一些噪声,来使得提取出来的皮肤更为干净。

  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
OpenCV 中,可以通过 YCrCb 颜色空间来实现皮肤检测皮肤区域通常在 YCrCb 颜色空间中的一个比较小的范围内。可以使用以下代码来实现皮肤检测: ```python import cv2 # 读取图像 img = cv2.imread('image.jpg') # 将图像转换为 YCrCb 颜色空间 img_YCrCb = cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb) # 定义皮肤颜色范围 min_YCrCb = np.array([0, 133, 77], np.uint8) max_YCrCb = np.array([255, 173, 127], np.uint8) # 根据皮肤颜色范围提取皮肤区域 mask = cv2.inRange(img_YCrCb, min_YCrCb, max_YCrCb) # 对提取的皮肤区域进行形态学操作,以减少噪声 kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)) mask = cv2.erode(mask, kernel, iterations=1) mask = cv2.dilate(mask, kernel, iterations=1) # 在原始图像中显示皮肤区域 result = cv2.bitwise_and(img, img, mask=mask) cv2.imshow('result', result) cv2.waitKey(0) cv2.destroyAllWindows() ``` 对于凹凸检测,可以使用轮廓检测和凸包检测来实现。可以使用以下代码来实现凹凸检测: ```python import cv2 import numpy as np # 读取图像 img = cv2.imread('image.jpg') # 将图像转换为 YCrCb 颜色空间 img_YCrCb = cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb) # 定义皮肤颜色范围 min_YCrCb = np.array([0, 133, 77], np.uint8) max_YCrCb = np.array([255, 173, 127], np.uint8) # 根据皮肤颜色范围提取皮肤区域 mask = cv2.inRange(img_YCrCb, min_YCrCb, max_YCrCb) # 对提取的皮肤区域进行形态学操作,以减少噪声 kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)) mask = cv2.erode(mask, kernel, iterations=1) mask = cv2.dilate(mask, kernel, iterations=1) # 轮廓检测 contours, hierarchy = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # 凸包检测 hull = [] for i in range(len(contours)): hull.append(cv2.convexHull(contours[i], False)) # 在原始图像中绘制轮廓和凸包 drawing = np.zeros((mask.shape[0], mask.shape[1], 3), dtype=np.uint8) color_contours = (0, 255, 0) color_hull = (0, 0, 255) for i in range(len(contours)): cv2.drawContours(drawing, contours, i, color_contours, 1, 8, hierarchy) cv2.drawContours(drawing, hull, i, color_hull, 1, 8) cv2.imshow('result', drawing) cv2.waitKey(0) cv2.destroyAllWindows() ``` 通过这种方法,可以实现皮肤检测和凹凸检测

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

金戈鐡馬

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值