【新】opencv肤色检测源码

//第一种:RGB color space 
// skin region location using rgb limitation
void ImageSkin::ImageSkinRGB(const Mat& rgb, Mat& _dst)
{
    assert(rgb.channels() == 3 && _dst.channels() == 3);

    static const int R=2;
    static const int G=1;
    static const int B=0;

    Mat dst = Mat(_dst.size(), _dst.type(), 3);
    dst.setTo(Scalar(0, 0, 0));

    for (int h=0; h<rgb.rows; h++)
    {
        for (int w=0; w<rgb.cols; w++)
        {
			// 数组的一份拷贝
			Vec3b prgb = rgb.at<Vec3b>(h, w);
			Vec3b pdst = dst.at<Vec3b>(h, w);
			//auto* pdst111 = &dst.at<Vec3b>(h, w);

            if ((prgb[R]>95 && prgb[G]>40 && prgb[B]>20 &&
                    prgb[R]-prgb[B]>15 && prgb[R]-prgb[G]>15) || //uniform illumination
                    (prgb[R]>200 && prgb[G]>210 && prgb[B]>170 &&
                     abs(prgb[R]-prgb[B])<=15 && prgb[R]>prgb[B] && prgb[G]>prgb[B])
               )
            {
				// 赋值切不可用pdst!
                memcpy(&dst.at<Vec3b>(h, w), &prgb/*rgb.at<Vec3b>(h, w)*/, 3);
				// 同上
				//dst.at<Vec3b>(h, w)[R] = rgb.at<Vec3b>(h, w)[R];
				//dst.at<Vec3b>(h, w)[G] = rgb.at<Vec3b>(h, w)[G];
				//dst.at<Vec3b>(h, w)[B] = rgb.at<Vec3b>(h, w)[B];
            }
        }
    }
	dst.copyTo(_dst);
	dst.release();
}

//第二种:RG color space 
// skin detection in rg space 
void ImageSkin::ImageSkinRG(const Mat& rgb, Mat& gray)
{
    assert(rgb.channels() == 3 && gray.channels() == 1);

    const int R=2;
    const int G=1;
    const int B=0;

    double Aup=-1.8423;
    double Bup=1.5294;
    double Cup=0.0422;
    double Adown=-0.7279;
    double Bdown=0.6066;
    double Cdown=0.1766;

    for (int h=0; h<rgb.rows; h++)
    {
        for (int w=0; w<rgb.cols; w++)
        {
			auto* pGray = &gray.at<uchar>(h, w);
			auto pRGB = rgb.at<Vec3b>(h, w);

            int s=pRGB[R]+pRGB[G]+pRGB[B];
            double r=(double)pRGB[R]/s;
            double g=(double)pRGB[G]/s;
            double Gup=Aup*r*r+Bup*r+Cup;
            double Gdown=Adown*r*r+Bdown*r+Cdown;
            double Wr=(r-0.33)*(r-0.33)+(g-0.33)*(g-0.33);

            if (g<Gup && g>Gdown && Wr>0.004)
            {
                *pGray=255;
            }
            else
            {
                *pGray=0;
            }

        }
    }
}
//
//第三种:otsu阈值化【仅限单通道】
// reference: Rafael C. Gonzalez. Digital Image Processing Using MATLAB
void ImageSkin::ImageThresholdOtsu(const Mat& src, Mat& dst)
{
    int height=src.rows;
    int width=src.cols;

    // 统计直方图
    float histogram[256]= {0};
    for(int i=0; i<height; i++)
    {
        for(int j=0; j<width; j++)
        {
			uchar p = src.at<uchar>(i, j);
            histogram[p]++;
        }
    }

    // 直方图归一化
    int size=height*width;
    for(int i=0; i<256; i++)
    {
        histogram[i]=histogram[i]/size;
    }

    // 求像素平均值
    float avgValue=0;
    for(int i=0; i<256; i++)
    {
        avgValue+=i*histogram[i];
    }

    int tH;
    float maxVariance=0;
    float w=0,u=0;
    for(int i=0; i<256; i++)
    {
        w+=histogram[i];
        u+=i*histogram[i];

        float t=avgValue*w-u;
        float variance=t*t/(w*(1-w));
        if(variance>maxVariance)
        {
            maxVariance=variance;
            tH=i;
        }
    }

    threshold(src, dst, tH, 255, CV_THRESH_BINARY);
}
//
//第四种:Ycrcb之cr分量+otsu阈值化 
void ImageSkin::ImageSkinOtsu(const Mat& src, Mat& dst)
{
    assert(dst.channels() == 1 && src.channels() == 3);

    Mat ycrcb = Mat(src.size(), src.type(), 3);
	Mat yCRcb[3];
	
    cvtColor(src, ycrcb, CV_BGR2YCrCb);
    split(ycrcb, yCRcb);

    ImageThresholdOtsu(yCRcb[1], yCRcb[1]);
	yCRcb[1].copyTo(dst);

	yCRcb[1].release();
	ycrcb.release();
}
//
//第五种:YCrCb中133<=Cr<=173 77<=Cb<=127 
void ImageSkin::ImageSkinYUV(const Mat& src, Mat& dst)
{
    Mat ycrcb = Mat(src.size(), src.type(), 3);
    cvtColor(src, ycrcb, CV_BGR2YUV);	// CV_BGR2YCrCb

    static const int Cb=2;
    static const int Cr=1;
    static const int Y=0;

	dst.setTo(Scalar(0, 0, 0));

    for (int h=0; h<src.rows; h++)
    {
        for (int w=0; w<src.cols; w++)
        {
			const Vec3b* pycrcb = &ycrcb.at<Vec3b>(h, w);
			const Vec3b* psrc = &src.at<Vec3b>(h, w);
			//const uchar* pdst = &dst.at<uchar>(h, w);

            if ((*pycrcb)[Cr]>=133 && (*pycrcb)[Cr]<=173 && (*pycrcb)[Cb]>=77 && (*pycrcb)[Cb]<=127)
            {
                memcpy(&dst.at<Vec3b>(h, w), psrc, 3);
            }
        }
    }

}


测试代码:

int _tmain(int argc, _TCHAR* argv[])
{
	Mat img = imread("..\\test.png", 1);        
	Mat dstRGB = Mat(img.size(), CV_8UC3);    
	Mat dstRG = Mat(img.size(), CV_8UC1);     
	Mat dst_crotsu = Mat(img.size(), CV_8UC1);       
	Mat dst_YUV = Mat(img.size(), CV_8UC3);       

	namedWindow("Original WIN", CV_WINDOW_AUTOSIZE);      
	imshow("Original WIN", img);   
	waitKey(0);      

	ImageSkin ImgS;  
	ImgS.ImageSkinRGB(img, dstRGB);   
	namedWindow("ImageSkinRGB WIN", CV_WINDOW_AUTOSIZE);      
	imshow("ImageSkinRGB WIN", dstRGB);  
	imwrite("..//ImageSkinRGB.jpg", dstRGB);
	waitKey(0);  

	ImgS.ImageSkinRG(img, dstRG);    
	namedWindow("ImageSkinRG WIN", CV_WINDOW_AUTOSIZE);      
	imshow("ImageSkinRG WIN", dstRG);
	imwrite("..//ImageSkinRG.jpg", dstRG);
	waitKey(0);   

	ImgS.ImageSkinOtsu(img, dst_crotsu);      
	namedWindow("ImageSkinOtsu WIN", CV_WINDOW_AUTOSIZE);      
	imshow("ImageSkinOtsu WIN", dst_crotsu); 
	imwrite("..//ImageSkinOtsu.jpg", dst_crotsu);
	waitKey(0);  

	ImgS.ImageSkinYUV(img, dst_YUV);      
	namedWindow("ImageSkinYUV WIN", CV_WINDOW_AUTOSIZE);      
	imshow("ImageSkinYUV WIN", dst_YUV);  
	imwrite("..//ImageSkinYUV.jpg", dst_YUV);
	waitKey(0);  

	return 0;
}

结果显示如图:


本文代码参考了这位大牛的博客点击打开链接,我将它们修改为C++风格,便于自己和其他也习惯C++风格的朋友使用

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ShaderJoy

您的打赏是我继续写博客的动力

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

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

打赏作者

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

抵扣说明:

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

余额充值