Opencv之人脸肤色检测总结
2014-04-12 12:04
435 查看
1.RGB颜色空间肤色检测
在human
skin color clustering for face detection一文中提出如下判别算式:
opencv代码非常简单:
2.二次多项式模型
在Adaptive
skin color modeling using the skin locus.pdf 一文中提出如下判别算式:
其中
代码也比较简单:
效果图:
3.反向投影法肤色检测
这个方法来源《学习opencv》里面的反向投影,原理如下:
使用 模型直方图 (代表手掌的皮肤色调) 来检测测试图像中的皮肤区域。以下是检测的步骤
对测试图像中的每个像素 (
),获取色调数据并找到该色调(
)在直方图中的bin的位置。
查询 模型直方图 中对应的bin -
-
并读取该bin的数值。
将此数值储存在新的图像中(BackProjection)。 你也可以先归一化 模型直方图 ,这样测试图像的输出就可以在屏幕显示了。
通过对测试图像中的每个像素采用以上步骤.
也可以参考:反向投影
其实现代码:
4.YCrCb颜色空间Cr分量+Ostu法
这个方法暂时没有找到论文来源,参考别人的博客,原理很简单:
a.将RGB图像转换到YCrCb颜色空间,提取Cr分量图像
b.对Cr做自适应二值化处理(Ostu法)
代码:
Ostu法:
效果图:
对于肤色检测后,应该加上一些去噪处理,比如形态学的开运算;对于检测特定目标(人脸),在形态学处理后计算出图像轮廓,有选择性地选择一些轮廓,比如像素面积大于一定阈值来进行限制。
5.opencv自带肤色检测类AdaptiveSkinDetector
CvAdaptiveSkinDetector类中的2个比较重要的函数:
CvAdaptiveSkinDetector(int samplingDivider = 1, int morphingMethod = MORPHING_METHOD_NONE);
该函数为类的构造函数,其中参数1表示的是样本采样的间隔,默认情况下为1,即表示不进行降采样;参数2为图形学操作方式,即对用皮肤检测后的图像进行图形学操作。其取值有3种可能,如果为MORPHING_METHOD_ERODE,则表示只进行一次腐蚀操作;如果为MORPHING_METHOD_ERODE_ERODE,则表示连续进行2次腐蚀操作;如果为MORPHING_METHOD_ERODE_DILATE,则表示先进行一次腐蚀操作,后进行一次膨胀操作。
virtual void process(IplImage *inputBGRImage, IplImage *outputHueMask);
该函数为皮肤检测的核心函数,参数1为需要进行皮肤检测的输入图像;参数2为输出皮肤的掩膜图像,如果其值为1,代表该像素为皮肤,否则当其为0时,代表为非皮肤。另外需要注意的是,这个函数只有opencv的c版本的,因为CvAdaptiveSkinDetector这个类放在opencv源码里的contrib目录里,即表示比较新的但不成熟的算法
代码:
效果图:
6.椭圆模型肤色检测
经过前人学者大量的皮肤统计信息可以知道,如果将皮肤信息映射到YCrCb空间,则在CrCb二维空间中这些皮肤像素点近似成一个椭圆分布。因此如果我们得到了一个CrCb的椭圆,下次来一个坐标(Cr, Cb)我们只需判断它是否在椭圆内(包括边界),如果是,则可以判断其为皮肤,否则就是非皮肤像素点。
肤色在CrCb坐标系下,类聚图像如下:
该方法与反向投影法比较类似。
代码如下:
效果图:
在human
skin color clustering for face detection一文中提出如下判别算式:
opencv代码非常简单:
void SkinRGB(IplImage* src,IplImage* dst) { //RGB颜色空间 //均匀照明:R>95,G>40,B>20,R-B>15,R-G>15 //侧向照明:R>200,G>210,B>170,R-B<=15,R>B,G>B int height=src->height,width=src->width,channel=src->nChannels,step=src->widthStep; int b=0,g=1,r=2; cvZero(dst); unsigned char* p_src=(unsigned char*)src->imageData; unsigned char* p_dst=(unsigned char*)dst->imageData; for(int j=0;j<height;j++) { for(int i=0;i<width;i++) { if((p_src[j*step+i*channel+r]>95&&p_src[j*step+i*channel+g]>40&&p_src[j*step+i*channel+b]>20&& (p_src[j*step+i*channel+r]-p_src[j*step+i*channel+b])>15&&(p_src[j*step+i*channel+r]-p_src[j*step+i*channel+g])>15)|| (p_src[j*step+i*channel+r]>200&&p_src[j*step+i*channel+g]>210&&p_src[j*step+i*channel+b]>170&& (p_src[j*step+i*channel+r]-p_src[j*step+i*channel+b])<=15&&p_src[j*step+i*channel+r]>p_src[j*step+i*channel+b]&& p_src[j*step+i*channel+g]>p_src[j*step+i*channel+b])) p_dst[j*width+i]=255; } } }效果图:
2.二次多项式模型
在Adaptive
skin color modeling using the skin locus.pdf 一文中提出如下判别算式:
其中
代码也比较简单:
void cvSkinRG(IplImage* src,IplImage* dst) { //二项式混合模型 //来源:Adaptive skin color modeling using the skin locus int b=0,g=1,r=2; double Aup=-1.8423,Bup=1.5294,Cup=0.0422,Adown=-0.7279,Bdown=0.6066,Cdown=0.1766; double r_ratio=0.0,g_ratio=0.0,sum=0.0,Rup=0.0,Rdown=0.0,wr=0.0; int height=src->height,width=src->width,channel=src->nChannels,step=src->widthStep; if(dst==NULL) dst=cvCreateImage(cvGetSize(src),IPL_DEPTH_8U,1); cvZero(dst); unsigned char* p_src=(unsigned char*)src->imageData; unsigned char* p_dst=(unsigned char*)dst->imageData; for(int j=0;j<height;j++) { for(int i=0;i<width;i++) { sum=p_src[j*step+i*channel+r]+p_src[j*step+i*channel+g]+p_src[j*step+i*channel+b]; r_ratio=p_src[j*step+i*channel+r]/sum; g_ratio=p_src[j*step+i*channel+g]/sum; Rup=Aup*r_ratio*r_ratio+Bup*r_ratio+Cup; Rdown=Adown*r_ratio*r_ratio+Bdown*r_ratio+Cdown; wr=(r_ratio-0.33)*(r_ratio-0.33)+(g_ratio-0.33)*(g_ratio-0.33); if(g_ratio<Rup && g_ratio>Rdown && wr>0.004) p_dst[j*width+i]=255; } } }
效果图:
3.反向投影法肤色检测
这个方法来源《学习opencv》里面的反向投影,原理如下:
使用 模型直方图 (代表手掌的皮肤色调) 来检测测试图像中的皮肤区域。以下是检测的步骤
对测试图像中的每个像素 (
),获取色调数据并找到该色调(
)在直方图中的bin的位置。
查询 模型直方图 中对应的bin -
-
并读取该bin的数值。
将此数值储存在新的图像中(BackProjection)。 你也可以先归一化 模型直方图 ,这样测试图像的输出就可以在屏幕显示了。
通过对测试图像中的每个像素采用以上步骤.
也可以参考:反向投影
其实现代码:
int main() { /**********************************\ * back Project * \**********************************/ //一维直方图 //获取直方图模型 IplImage* src=cvLoadImage("base.jpg",1);//下载base图像,base图像为目标颜色,即包含人脸颜色的图像 IplImage* hsv_src=cvCreateImage(cvGetSize(src),8,3);//创建一幅与src同样大小3通道的hsv图像 cvCvtColor(src,hsv_src,CV_RGB2HSV);//将src从rgb颜色空间转换到hsv颜色空间 IplImage* h_plane=cvCreateImage(cvGetSize(src),8,1);//创建一幅与src同样大小单通道的h图像 cvSplit(hsv_src,h_plane,0,0,0);//取hsv图像中h分量 // 计算base的h分量直方图 //------------------------------------------------------------- int dims=1; int size[]={180}; float h_range[]={0,180}; float* ranges[]={h_range}; IplImage* hsv_test=NULL; IplImage* h_test_plane=NULL; IplImage* back_proj=NULL; IplImage* img_dst=NULL; CvHistogram* hist=cvCreateHist(dims,size,CV_HIST_ARRAY,ranges,1); cvCalcHist(&h_plane,hist,0,0);
//--------------------------------------------------------------- IplImage* img_src=NULL; IplImage* test=NULL; CvCapture* capture=cvCreateCameraCapture(0); cvNamedWindow("Input Video",1); cvNamedWindow("Skin Detect",1); while(1) { img_src=cvQueryFrame(capture); if(!img_src) break; cvShowImage("Input Video",img_src); if(test==NULL) { test=cvCreateImage(cvGetSize(img_src),img_src->depth,img_src->nChannels); } cvZero(test); cvCopy(img_src,test); if(hsv_test==NULL) hsv_test=cvCreateImage(cvGetSize(test),8,3); cvZero(hsv_test); cvCvtColor(test,hsv_test,CV_RGB2HSV);//转换到hsv空间 if(h_test_plane==NULL) h_test_plane=cvCreateImage(cvGetSize(test),8,1); cvZero(h_test_plane); cvSplit(hsv_test,h_test_plane,0,0,0);//取h分量 if(back_proj==NULL) back_proj=cvCreateImage(cvGetSize(test),8,1);//用于存取肤色检测结果 cvZero(back_proj); cvCalcBackProject(&h_test_plane,back_proj,hist);//反向投影计算投影 cvThreshold(back_proj,back_proj,250,255,CV_THRESH_BINARY); cvShowImage("Skin Detect",<span style="font-family: sans-serif;">back_proj</span>); char c=cvWaitKey(33); if(c==27) break; } cvWaitKey(0); }效果图:
4.YCrCb颜色空间Cr分量+Ostu法
这个方法暂时没有找到论文来源,参考别人的博客,原理很简单:
a.将RGB图像转换到YCrCb颜色空间,提取Cr分量图像
b.对Cr做自适应二值化处理(Ostu法)
代码:
void cvSkinOtsu(IplImage* src, IplImage* dst) { //Cr自适应阈值法 // IplImage* img_ycrcb=cvCreateImage(cvGetSize(src),IPL_DEPTH_8U,3); IplImage* img_cr=cvCreateImage(cvGetSize(src),IPL_DEPTH_8U,1); cvCvtColor(src,img_ycrcb,CV_BGR2YCrCb); cvSplit(img_ycrcb,0,img_cr,0,0); cvThresholdOtsu(img_cr,img_cr); cvCopy(img_cr,dst); cvReleaseImage(&img_ycrcb); cvReleaseImage(&img_cr); }
Ostu法:
void cvThresholdOtsu(IplImage* src, IplImage* dst) { int height=src->height,width=src->width,threshold=0; double histogram[256]={0}; double average=0.0,max_variance=0.0,w=0.0,u=0.0; IplImage* temp=cvCreateImage(cvGetSize(src),src->depth,1); if(src->nChannels!=1) cvCvtColor(src,temp,CV_BGR2GRAY); else cvCopy(src,temp); unsigned char* p_temp=(unsigned char*)temp->imageData; //计算灰度直方图 // for(int j=0;j<height;j++) { for(int i=0;i<width;i++) { histogram[p_temp[j*width+i]]++; } } for(int i=0;i<256;i++) histogram[i]=histogram[i]/(double)(height*width); //计算平局值 for(int i=0;i<256;i++) average+=i*histogram[i]; for(int i=0;i<256;i++) { w+=histogram[i]; u+=i*histogram[i]; double t=average*w-u; double variance=t*t/(w*(1-w)); if(variance>max_variance) { max_variance=variance; threshold=i; } } cvThreshold(temp,dst,threshold,255,CV_THRESH_BINARY); cvReleaseImage(&temp); }
效果图:
对于肤色检测后,应该加上一些去噪处理,比如形态学的开运算;对于检测特定目标(人脸),在形态学处理后计算出图像轮廓,有选择性地选择一些轮廓,比如像素面积大于一定阈值来进行限制。
5.opencv自带肤色检测类AdaptiveSkinDetector
CvAdaptiveSkinDetector类中的2个比较重要的函数:
CvAdaptiveSkinDetector(int samplingDivider = 1, int morphingMethod = MORPHING_METHOD_NONE);
该函数为类的构造函数,其中参数1表示的是样本采样的间隔,默认情况下为1,即表示不进行降采样;参数2为图形学操作方式,即对用皮肤检测后的图像进行图形学操作。其取值有3种可能,如果为MORPHING_METHOD_ERODE,则表示只进行一次腐蚀操作;如果为MORPHING_METHOD_ERODE_ERODE,则表示连续进行2次腐蚀操作;如果为MORPHING_METHOD_ERODE_DILATE,则表示先进行一次腐蚀操作,后进行一次膨胀操作。
virtual void process(IplImage *inputBGRImage, IplImage *outputHueMask);
该函数为皮肤检测的核心函数,参数1为需要进行皮肤检测的输入图像;参数2为输出皮肤的掩膜图像,如果其值为1,代表该像素为皮肤,否则当其为0时,代表为非皮肤。另外需要注意的是,这个函数只有opencv的c版本的,因为CvAdaptiveSkinDetector这个类放在opencv源码里的contrib目录里,即表示比较新的但不成熟的算法
代码:
#include <opencv\cv.h> #include <opencv\highgui.h> #include <contrib\contrib.hpp> #include <core\core.hpp> #include <imgproc\imgproc.hpp> #include<time.h> int main() { CvCapture* capture=cvCreateCameraCapture(0); cvNamedWindow("Input Video",1); cvNamedWindow("Output Video",1); IplImage* img_src=NULL; IplImage* input_img=NULL; IplImage* output_mask=NULL; IplImage* output_img=NULL; clock_t start,finish; double duration; CvAdaptiveSkinDetector skin_detector(1,CvAdaptiveSkinDetector::MORPHING_METHOD_ERODE_DILATE); //定义肤色检测算子 while(1) { img_src=cvQueryFrame(capture); if(!img_src) break; cvShowImage("Input Video",img_src); if(input_img==NULL) { input_img=cvCreateImage(cvGetSize(img_src),img_src->depth,img_src->nChannels); } cvCopy(img_src,input_img); output_img=cvCreateImage(cvGetSize(img_src),img_src->depth,img_src->nChannels); cvZero(output_img); if(output_mask==NULL) { output_mask=cvCreateImage(cvGetSize(img_src),img_src->depth,1); } //肤色检测 // start=clock(); skin_detector.process(input_img,output_mask); finish=clock(); duration=(double)(finish-start)/CLOCKS_PER_SEC; printf("elapsed time :%.0f 毫秒\n",duration*1000); cvCopy(img_src,output_img,output_mask); cvShowImage("Output Video",output_img); char c=cvWaitKey(33); if(c==27)break; } cvReleaseCapture(&capture); cvDestroyWindow("Video"); }
效果图:
6.椭圆模型肤色检测
经过前人学者大量的皮肤统计信息可以知道,如果将皮肤信息映射到YCrCb空间,则在CrCb二维空间中这些皮肤像素点近似成一个椭圆分布。因此如果我们得到了一个CrCb的椭圆,下次来一个坐标(Cr, Cb)我们只需判断它是否在椭圆内(包括边界),如果是,则可以判断其为皮肤,否则就是非皮肤像素点。
肤色在CrCb坐标系下,类聚图像如下:
该方法与反向投影法比较类似。
代码如下:
#include <opencv\cv.h> #include <opencv\highgui.h> #include <time.h> int main() { //肤色椭圆 IplImage* skin_ellipse=cvCreateImage(cvSize(256,256),IPL_DEPTH_8U,1); cvZero(skin_ellipse); cvEllipse(skin_ellipse, cvPoint(113, 155.6), cvSize(23.4, 15.2), 43.0, 0.0, 360.0, cvScalar(255, 255, 255), -1); CvCapture* capture=cvCreateCameraCapture(0); IplImage* img_src=NULL; IplImage* img_ycrcb=NULL; IplImage* output_mask=NULL; IplImage* img_dst=NULL; CvMemStorage* storage=cvCreateMemStorage(0); CvSeq* first_contour; bool create_bool=false; cvNamedWindow("Input Video",1); cvNamedWindow("Skin Detect",1); clock_t start,finish; double duration; while(1) { img_src=cvQueryFrame(capture); if(!img_src) break; cvShowImage("Input Video",img_src); if(!create_bool) { img_ycrcb=cvCreateImage(cvGetSize(img_src),IPL_DEPTH_8U,3); img_dst=cvCreateImage(cvGetSize(img_src),img_src->depth,img_src->nChannels); output_mask=cvCreateImage(cvGetSize(img_src),IPL_DEPTH_8U,1); create_bool=true; } cvZero(img_dst); cvZero(output_mask); start=clock(); cvCvtColor(img_src,img_ycrcb,CV_BGR2YCrCb); unsigned char* p_ellipse=(unsigned char*)skin_ellipse->imageData; for(int i=0;i<img_ycrcb->height;i++) { unsigned char* p_mask=(unsigned char*)(output_mask->imageData+i*output_mask->widthStep); unsigned char* p_ycrcb=(unsigned char*)(img_ycrcb->imageData+i*img_ycrcb->widthStep); for(int j=0;j<img_ycrcb->width;j++) { if(p_ellipse[p_ycrcb[j*3+1]*skin_ellipse->widthStep+p_ycrcb[j*3+2]] > 0) p_mask[j] = 255; } } finish=clock(); duration=(double)(finish-start)/CLOCKS_PER_SEC; printf("elapsed time :%.0f 毫秒\n",duration*1000); cvCopy(img_src,img_dst,output_mask); cvShowImage("Skin Detect",img_dst); char c=cvWaitKey(33); if(c==27) break; } cvReleaseImage(&img_src); cvReleaseCapture(&capture); cvReleaseImage(&img_ycrcb); cvReleaseImage(&output_mask); cvDestroyWindow("Skin Detect"); cvDestroyWindow("Input Video"); cvWaitKey(0); }
效果图:
相关文章推荐
- opencv的haarcascade模型人脸检测输出框的得分总结
- Atitti opencv2.4 实现的人脸检测 attilax总结
- Atitti opencv2.4 实现的人脸检测 attilax总结
- 基于OpenCV的人脸检测总结
- opencv椭圆肤色模型人脸检测
- OpenCV 人脸检测自学(4)_opencv_trainCascade输出的xml格式总结
- 利用openCV中的cvCanny函数检测人脸的边缘
- 彩色图像中的人脸(肤色)检测
- 人脸识别中的检测(在Opencv中加入了QT)
- OpenCV训练自己的人脸检测级连分类器并测试
- keras系列︱人脸表情分类与识别:opencv人脸检测+Keras情绪分类(四)
- 基于python OpenCV实现动态人脸检测
- Python-OpenCV人脸检测(代码)
- AdaBoost中利用Haar特征进行人脸识别算法分析与总结2——级联分类器与检测过程
- Opencv实现ARM上的人脸检测(ubuntu10.04 / QT / OK6410开发板)
- OpenCV Using Python——RGB颜色空间中的肤色检测
- OpenCV Using Python——RGB颜色空间和YCbCr颜色空间的混合肤色检测
- win10+qt+vs2013+opencv人脸检测详细步骤
- openCV人脸检测内存泄露问题
- OpenCV实践之路——人脸检测(C++/Python)