基于肤色高斯概率模型的人脸检测

  • 肤色高斯概率模型,主要将颜色空间从RGB转换为YCrCb,计算肤色的相似度,确定根据概率阈值筛选出肤色区域。但高斯概率模型中肤色均值及协方差矩阵需要提前确定,并且肤色均值对检测结果影响非常大,需要根据实际情况进行调整。
    #include <iostream>
        #include "opencv2/opencv.hpp"
        #include <ctime>  
        using namespace std;
        using namespace cv;
        void fillHole(Mat * src);//基于漫水填充的孔洞填充
        #define PI 3.14156
        void main ()
    {
        Mat src = imread("1.jpg");
        if (src.empty())
        {
            cout << "read image error!" << endl;
            return ;
        }
        //基于高斯肤色概率模型的人脸检测结果
        const char * src_namedWindow = "src";
        imshow (src_namedWindow , src);
        Mat tempImg ;
        cvtColor (src, tempImg,CV_RGB2YCrCb );//将颜色空间从RGB转换为YCrCb
        vector <Mat> channels;
        split (tempImg,channels);//分离出CrCb通道
        Mat Cr_Img, Cb_Img;
        Cr_Img = channels[1];
        Cb_Img = channels[2];   
        //利用二维高斯概率模型计算每个像素点的肤色相似度
        Mat pro(Cr_Img.size(), CV_32FC1 );//定义相似度矩阵
        for (int i = 0; i<src.rows ; i++)
            for (int j = 0; j < src.cols ; j++)
            {       
                Mat chr = (Mat_<double>(1, 2) << Cr_Img.at<uchar>(i, j), Cb_Img.at<uchar>(i, j) );//定义色度矩阵
                Mat m = (Mat_<double>(1, 2) << 120.4316, 148.5599);//肤色均值,即定义高斯分布的期望值。
                Mat cov = (Mat_<double>(2, 2)<< 97.0946, 24.4700, 24.4700, 141.9966);//协方差矩阵
                Mat ss=(-0.5)*(chr-m)*cov.inv()*(chr-m).t();
                pro.at<float>(i,j)=exp(ss.at<double>(0))/(2*PI *sqrt(determinant (cov)));//计算相似度
                //cout << exp(ss.at<double>(0))/(2*PI*sqrt(determinant (cov)))<< endl;
            }

        //imshow ("相似度矩阵", pro );
        //相似度矩阵归一化
        //double min1=0, max1=0;
        //minMaxIdx (pro, &min1, &max1);
        //cout << max1 << endl;
        //Mat nor = pro/max1;
        Mat nor;
        normalize (pro, nor,0,1,NORM_MINMAX );
        //imshow("归一化矩阵", nor);
        Mat mask1 = Mat :: zeros (pro.size(), CV_32FC1 );//肤色区模板
        Mat mask2 (mask1.size(), CV_8UC1 ); 
        double thresh_face = 0.5;//肤色阈值
        for (int i = 0; i < nor.rows; i++ )
            for ( int j = 0; j < nor.cols ; j++)
            {
                if (nor.at<float>(i,j) > thresh_face )
                    mask1.at<float>(i,j) = 1;
                else 
                    mask1.at<float>(i,j) = 0;
            }
        imshow("人脸模板", mask1);      
        Mat element1 = getStructuringElement (MORPH_RECT,Size(3,3));
        erode(mask1, mask1,element1, Point(-1,-1), 2);
        Mat element2 = getStructuringElement (MORPH_ELLIPSE, Size(5,5));
        dilate (mask1, mask1,element2);
        threshold(mask1, mask2, 0,255,CV_THRESH_BINARY );//将mask1转为二值图
        mask2.convertTo (mask2, CV_8UC1 );//将矩阵转为图片
        //二值化图孔洞填充
        fillHole(&mask2);
        imshow ("填充后", mask2);
        //轮廓查找
        vector<vector<Point>> contours;
        vector<Vec4i> hierarchy;
        findContours (mask2, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE );
        //筛选轮廓
        vector<int> label;
        int add_lable = 0;
        vector<Rect> boundRect(contours.size());//定义外接矩形集合
        for (int i =0; i < contours.size(); i++)
        {
            double con_area = contourArea (contours[i]);
            double con_length = arcLength (contours[i], true);
            int x0 =0, y0 =0, w0 =0, h0 =0;
            boundRect[i] = boundingRect (contours[i]);
            x0 = boundRect[i].x;
            y0 = boundRect[i].y;
            w0 = boundRect[i].width;
            h0 = boundRect[i].height;
            if (con_area > 400 && w0 > 20 && h0 > 20 && (h0/w0)>0.6 && (h0/w0)<2 )
            {
                drawContours (mask2, contours,i, Scalar (255, 0, 0), 1, 8);
                rectangle (src, Point(x0, y0), Point(x0+w0, y0+h0), Scalar (0, 255, 0), 2, 8);
                putText (src, "Face", Point(x0+w0/4, y0), CV_FONT_HERSHEY_SIMPLEX,0.5,Scalar(0,255,0), 2 );
                label.push_back (i);
            }   
        }
        imshow("mask2", mask2);
        imshow("人脸区",src);
        waitKey(0);
        //system ("pause");
    }
    void fillHole2(Mat *src)//利用漫水填充进行孔洞填充
    {
        Mat temp = Mat::zeros((src->size().height ) + 2, (src->size().width)+2, src->type());
        src->copyTo (temp(Range (1,(src->size().height ) + 1),Range (1,(src->size().width)+1)));
        floodFill (temp, Point(0,0),Scalar (255));
        //imshow("floodFill", temp);
        Mat cutImg;//将图像还原为原来大小
        temp(Range (1,(src->size().height ) + 1),Range (1,(src->size().width)+1)).copyTo (cutImg );
        *src = (*src) | (~ cutImg );
    }

参考文献

孔洞填充
https://bbs.csdn.net/topics/391542633?page=1
https://blog.csdn.net/qq_20823641/article/details/52186629
https://blog.csdn.net/wxplol/article/details/73137397
图像计算
https://blog.csdn.net/fengbingchun/article/details/72357082
https://blog.csdn.net/qq_29796317/article/details/73136083
人脸检测—基于肤色高斯概率的检测实现
https://wenku.baidu.com/view/0478da5d4431b90d6c85c7aa.html
https://blog.csdn.net/mao19931004/article/details/48933435
https://blog.csdn.net/tiemaxiaosu/article/details/51741486
http://blog.sina.com.cn/s/blog_4a540be60102uwcr.html
https://blog.csdn.net/zdx19880830/article/details/45892739
https://blog.csdn.net/wj080211140/article/details/23384927

猜你喜欢

转载自blog.csdn.net/u010623565/article/details/80330423