opencv 皮肤检测 椭圆模型+轮廓去噪

一、经过前人学者大量的皮肤统计信息可以知道,如果将皮肤信息映射到YCrCb空间,则在CrCb二维空间中这些皮肤像素点近似成一个椭圆分布。

如果我们得到了一个皮肤CrCb的椭圆,我们只需判断它是否在椭圆内(包括边界),如果是,则可以判断其为皮肤,否则就是非皮肤像素点。

 void ellipse(Mat& img, Point center, Size axes, double angle, double startAngle, double endAngle, const Scalar&color, int thickness=1, int lineType=8, int shift=0)

该函数是用来在指定图片上绘制椭圆弧线的。

参数image为需要绘制椭圆的图像;

参数center是该椭圆的中心点坐标;

参数axes是该椭圆的长半轴和短半轴;

参数angle是该椭圆和水平方向上的旋转夹角;

参数startAngle表示绘制椭圆弧线相对该椭圆自己的水平轴的起始角度;

参数endAngel表示绘制椭圆弧线相对该椭圆自己的水平轴的终止角度;


findContours函数,这个函数的原型为:

void findContours(InputOutputArray image, OutputArrayOfArrays contours, OutputArray hierar-
chy, int mode, int method, Point offset=Point())

参数说明

输入图像image必须为一个2值单通道图像

contours参数为检测的轮廓数组,每一个轮廓用一个point类型的vector表示

hiararchy参数和轮廓个数相同,每个轮廓contours[ i ]对应4个hierarchy元素hierarchy[ i ][ 0 ] ~hierarchy[ i ][ 3 ],分别表示后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号,如果没有对应项,该值设置为负数。

mode表示轮廓的检索模式

    CV_RETR_EXTERNAL表示只检测外轮廓

    CV_RETR_LIST检测的轮廓不建立等级关系

    CV_RETR_CCOMP建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层。

    CV_RETR_TREE建立一个等级树结构的轮廓。具体参考contours.c这个demo

method为轮廓的近似办法

    CV_CHAIN_APPROX_NONE存储所有的轮廓点,相邻的两个点的像素位置差不超过1,即max(abs(x1-x2),abs(y2-y1))==1

    CV_CHAIN_APPROX_SIMPLE压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息

    CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法

offset表示代表轮廓点的偏移量,可以设置为任意值。对ROI图像中找出的轮廓,并要在整个图像中进行分析时,这个参数还是很有用的。

findContours后会对输入的2值图像改变,所以如果不想改变该2值图像,需创建新mat来存放,findContours后的轮廓信息contours可能过于复杂不平滑,可以用approxPolyDP函数对该多边形曲线做适当近似

contourArea函数可以得到当前轮廓包含区域的大小,方便轮廓的筛选

findContours经常与drawContours配合使用,用来将轮廓绘制出来。其中第一个参数image表示目标图像,第二个参数contours表示输入的轮廓组,每一组轮廓由点vector构成,第三个参数contourIdx指明画第几个轮廓,如果该参数为负值,则画全部轮廓,第四个参数color为轮廓的颜色,第五个参数thickness为轮廓的线宽,如果为负值或CV_FILLED表示填充轮廓内部,第六个参数lineType为线型,第七个参数为轮廓结构信息,第八个参数为maxLevel

得到了复杂轮廓往往不适合特征的检测,这里再介绍一个点集凸包络的提取函数convexHull,输入参数就可以是contours组中的一个轮廓,返回外凸包络的点集

还可以得到轮廓的外包络矩形,使用函数boundingRect,如果想得到旋转的外包络矩形,使用函数minAreaRect,返回值为RotatedRect;也可以得到轮廓的外包络圆,对应的函数为minEnclosingCircle;想得到轮廓的外包络椭圆,对应的函数为fitEllipse,返回值也是RotatedRect,可以用ellipse函数画出对应的椭圆

如果想根据多边形的轮廓信息得到多边形的多阶矩,可以使用类moments,这个类可以得到多边形和光栅形状的3阶以内的所有矩,类内有变量m00,m10,m01,m20,m11,m02,m30,m21,m12,m03,比如多边形的质心为 x = m10 / m00,y = m01 / m00。

如果想获得一点与多边形封闭轮廓的信息,可以调用pointPolygonTest函数,这个函数返回值为该点距离轮廓最近边界的距离,为正值为在轮廓内部,负值为在轮廓外部,0表示在边界上。

二、结果


opencv 皮肤检测 椭圆模型+轮廓去噪_第1张图片opencv 皮肤检测 椭圆模型+轮廓去噪_第2张图片


三、代码

<span style="font-size:14px;"></pre><pre name="code" class="cpp">#include <opencv2/core/core.hpp>  
#include <opencv2/highgui/highgui.hpp>  
#include <opencv2/face.hpp>
#include <opencv2/imgproc/imgproc.hpp>  
#include <iostream>  
#include <vector>  
  
using namespace std;  
using namespace cv;  
  
Mat input_image;  
Mat output_mask;  
Mat output_image;  
Mat mask;  
  
int main(int argc,char *argv[])  
{  
    if (2 != argc) 
    {
        cout << "Please enter the image list!" <<endl;
        return -1;
    }
    vector<string>  file_names;
    FILE *file_list =  fopen(argv[1],"r");
    char buf[255];
    memset(&buf,0,sizeof(buf));

    while(fgets(buf,255,file_list))
    {
        if(buf[strlen(buf)-1] == '\n') 
            buf[strlen(buf)-1] = '\0';
        file_names.push_back(string(buf));

    }
    
    fclose(file_list);
    int count = file_names.size();
    
    Mat skinCrCbHist = Mat::zeros(Size(256, 256), CV_8UC1);  
    ellipse(skinCrCbHist, Point(113, 155.6), Size(25,12), -20, 0.0, 360.0, Scalar(255, 255, 255), -1);  
  
    Mat element = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1) );  
    for(int  i=0; i<count; i++)
    {  
        string img_nm = file_names[i];
        string img_mask = "mask" + img_nm;
        int pos = img_nm.rfind('.');
        string img_fmt = img_nm.substr(pos+1);
        if("jpg" != img_fmt)
        {
          cout << "Unknown format: " << img_fmt << endl;
          continue;
        }
        input_image=imread(img_nm,1);  
        if(input_image.empty())  
            return 0;  
  
        Mat ycrcb_image;  
        output_mask = Mat::zeros(input_image.size(), CV_8UC1);  
        cvtColor(input_image, ycrcb_image, CV_BGR2YCrCb); 
          
        for(int i = 0; i < input_image.rows; i++) 
        {  
            uchar* p = (uchar*)output_mask.ptr<uchar>(i);  
            Vec3b* ycrcb = (Vec3b*)ycrcb_image.ptr<Vec3b>(i);  
            for(int j = 0; j < input_image.cols; j++)  
            {  
                if(skinCrCbHist.at<uchar>(ycrcb[j][1], ycrcb[j][2]) > 0)  
                    p[j] = 255;  
            }  
        }     
        morphologyEx(output_mask,output_mask,MORPH_CLOSE,element);   
        vector< vector<Point> > contours;   
        vector< vector<Point> > filterContours; 
        vector< Vec4i > hierarchy;   
        contours.clear();    
        hierarchy.clear();   
        filterContours.clear();  
  
        findContours(output_mask, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);    
        for (size_t i = 0; i < contours.size(); i++)   
        {  
             if (fabs(contourArea(Mat(contours[i]))) > 2000&&fabs(arcLength(Mat(contours[i]),true))>500) 
                 filterContours.push_back(contours[i]);  
        }  
          
        output_mask.setTo(0);  
        drawContours(output_mask, filterContours, -1, Scalar(255,0,0), CV_FILLED);     
        input_image.copyTo(output_image, output_mask);  
        imwrite(img_nm,output_image);
        imwrite(img_mask,output_mask);
        // namedWindow("input image",0);
        // // namedWindow("output mask",0);
        // namedWindow("output image",0);
        // imshow("input image", input_image);  
        // imshow("output image", output_image);  
          
        output_image.setTo(0);  
        // waitKey(0);
    }  
    return 0;  
} </span>


说明:


你可能感兴趣的:(皮肤检测,椭圆模型轮廓)