Opencv学习笔记(十二)基于椭圆模型的人体肤色检测

前言:

这里需要首先介绍一下一种颜色空间叫做YCrCb(YUV)空间:

YCrCb色彩空间,主要用于优化彩色视频信号的传输,使其向后相容老式黑白电视。与RGB视频信号传输相比,它最大的优点在于只需占用极少的频宽。

其中“Y”表示明亮度,“亮度”是透过RGB输入信号来建立的,方法是将RGB信号的特定部分叠加到一起。

“U”和“V” 表示的则是色度。“色度”则定义了颜色的两个方面─色调与饱和度,分别用Cr和Cb来表示。其中,Cr反映了RGB输入信号红色部分与RGB信号亮度值之间的差异。而Cb反映的是RGB输入信号蓝色部分与RGB信号亮度值之间的差异。
一般地,我们在进行肤色检测时常常只需要是用到Cr和Cb两个通道,通过过滤掉Y通道之后,我们的肤色能够达到一个比较好的识别度。

步骤:

1、用Mat::zeros(src.size(), CV_8UC1);创建一张黑色背景的图像,然后通过ellipse()函数绘制出一个白色实心的椭圆,如下图:

Opencv学习笔记(十二)基于椭圆模型的人体肤色检测_第1张图片

2、接下来我们就可以将待检测图像转换成YCrCb颜色空间,在进行一系列的降噪处理,进而过滤掉一些噪声,然后提取出Cr和Cb通道,再遍历图像,只需得到该像素点的Cr,Cb两个坐标,在上面的椭圆中找到该坐标的值,如果大于0,则为皮肤,反之亦然。

3、对得到的图像进行提取轮廓,再通过轮廓面积筛选,剔除掉误检测的噪声点,即可。

代码展示:

#include 
#include
#include 
#include 
using namespace std;
using namespace cv;

vector imread_model();

int main()
{
 Mat src, background, med_src, gray_src, dest, ycrcb_image, gray_diff;
 VideoCapture capture(0);
 if (!capture.isOpened()) {
  printf("could not find the video file...\n");
  return -1;
 }

 //构建椭圆模型
 Mat skinMat = Mat::zeros(Size(256, 256), CV_8UC1);
 ellipse(skinMat, Point(113, 155.6), Size(26, 22), 43.0, 0.0, 360, Scalar(255, 255, 255), -1);

//定义结构元素
 Mat element = getStructuringElement(MORPH_RECT, Size(4, 4), Point(-1, -1));
 
 Mat YCrCbMat;
 //加载模板图像轮廓
 while (1)
 { 
  capture >> src;
  Mat temp = Mat::zeros(src.size(), CV_8UC1);
  //颜色空间转换YCrCb
  cvtColor(src, YCrCbMat, COLOR_BGR2YCrCb);
  //椭圆肤色模型检测
  for (int i = 0; i < src.rows; i++)
  {
   uchar *p1 = (uchar*)temp.ptr(i);
   Vec3b *p2 = (Vec3b*)YCrCbMat.ptr(i);
   for (int j = 0; j < src.cols; j++)
   {
    //颜色判断
    if (skinMat.at(p2[j][1], p2[j][2]) > 0) p1[j] = 255;
   }
  }

GaussianBlur(temp, temp, Size(5, 5),0,0);
  //形态学闭操作
  morphologyEx(temp, temp, MORPH_CLOSE, element);

  //定义轮廓参数
  vector > contours;
  vector > tempcontours;
  vector hierarchy;

  //查找连通域
  //CV_RETR_EXTERNAL:只检测出最外轮廓
  findContours(temp, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);

  Mat tmp(src.size(), CV_8UC1, Scalar::all(0));
  //筛选的轮廓
  for (int i = 0; i < contours.size(); i++)
  {
   //判断区域面积
   if (fabs(contourArea(contours[i])) > 1000) tempcontours.push_back(contours[i]);
   for (int j = 0; j < contours[i].size(); j++) tmp.at(contours[i][j].y, contours[i][j].x) = 255;
  }

Mat drawing = Mat::zeros(src.size(), CV_8UC3);
  drawContours(drawing, tempcontours, -1, Scalar(255, 255, 255), FILLED);
  vector mu(tempcontours.size());
  vector  mc(tempcontours.size());
  Rect rect;
  for (int i = 0; i < tempcontours.size(); i++)
  {
   rect = boundingRect(tempcontours[i]);
   mu[i] = moments(tempcontours[i], false);        //计算轮廓矩 
   mc[i] = Point2f(mu[i].m10 / mu[i].m00, mu[i].m01 / mu[i].m00);     //计算轮廓中心
   circle(drawing, mc[i], 3, Scalar(0, 0, 255), -1, LINE_AA, 0);      //画中心圆
   rectangle(drawing, rect,Scalar(0,255,0),2,LINE_AA,0);
  }
imshow("result", drawing);
if (waitKey(30) > 0)
  {
   break;
  }
 }
 capture.release();
 return 0;
}

效果:

Opencv学习笔记(十二)基于椭圆模型的人体肤色检测_第2张图片

这里注明一下,由于项目需要在形态学滤波的时候定义的结构元素较大,可能会滤去必要的信息,如不需要可以将滤波的代码删去。

如果有哪位代佬知道更好,愿意的话可以跟我分享一下,非常感谢!!!

希望对读者有所帮助,喜欢的话可以关注一下我的公众号,我会把学习笔记发在上面,大家可以一起共同学习!

Opencv学习笔记(十二)基于椭圆模型的人体肤色检测_第3张图片
Alt

你可能感兴趣的:(Opencv)