浅谈OpenCV人脸检测.docx
《浅谈OpenCV人脸检测.docx》由会员分享,可在线阅读,更多相关《浅谈OpenCV人脸检测.docx(24页珍藏版)》请在冰豆网上搜索。

浅谈OpenCV人脸检测
浅谈OpenCV人脸检测
OpenCV的人脸检测主要是调用训练好的cascade(Haar分类器)来进行模式匹配。
cvHaarDetectObjects,先将图像灰度化,根据传入参数判断是否进行canny边缘处理(默认不使用),再进行匹配。
匹配后收集找出的匹配块,过滤噪声,计算相邻个数如果超过了规定值(传入的min_neighbors)就当成输出结果,否则删去。
匹配循环:
将匹配分类器放大scale(传入值)倍,同时原图缩小scale倍,进行匹配,直到匹配分类器的大小大于原图,则返回匹配结果。
匹配的时候调用cvRunHaarClassifierCascade来进行匹配,将所有结果存入CvSeq*Seq (可动态增长元素序列),将结果传给cvHaarDetectObjects。
cvRunHaarClassifierCascade函数整体是根据传入的图像和cascade来进行匹配。
并且可以根据传入的cascade类型不同(树型、stump(不完整的树)或其他的),进行不同的匹配方式。
函数cvRunHaarClassifierCascade用于对单幅图片的检测。
在函数调用前首先利用cvSetImagesForHaarClassifierCascade设定积分图和合适的比例系数(=>窗口尺寸)。
当分析的矩形框全部通过级联分类器每一层的时返回正值(这是一个候选目标),否则返回0或负值。
为了了解OpenCV人脸检测中寻找匹配图像的详细过程,就把cvHaarDetectObjects和cvRunHaarClassifierCascade的源文件详细看了一遍,并打上了注释。
方便大家阅读。
附cvHaarDetectObjects代码:
CV_IMPLCvSeq*
cvHaarDetectObjects(constCvArr*_img,
CvHaarClassifierCascade*cascade,
CvMemStorage*storage,doublescale_factor,
intmin_neighbors,intflags,CvSizemin_size)
{
intsplit_stage=2;
CvMatstub,*img=(CvMat*)_img; //CvMat多通道矩阵 *img=_img指针代换传入图
CvMat*temp=0,*sum=0,*tilted=0,*sqsum=0,*norm_img=0,*sumcanny=0,*img_small=0;
CvSeq*seq=0;
CvSeq*seq2=0; //CvSeq可动态增长元素序列
CvSeq*idx_seq=0;
CvSeq*result_seq=0;
CvMemStorage*temp_storage=0;
CvAvgComp*comps=0;
inti;
#ifdef_OPENMP
CvSeq*seq_thread[CV_MAX_THREADS]={0};
intmax_threads=0;
#endif
CV_FUNCNAME(“cvHaarDetectObjects”);
__BEGIN__;
doublefactor;
intnpass=2,coi; //npass=2
intdo_canny_pruning=flags&CV_HAAR_DO_CANNY_PRUNING; //true做canny边缘处理
if(!
CV_IS_HAAR_CLASSIFIER(cascade))
CV_ERROR(!
cascade?
CV_StsNullPtr:
CV_StsBadArg,“Invalidclassifiercascade”);
if(!
storage)
CV_ERROR(CV_StsNullPtr,“Nullstoragepointer”);
CV_CALL(img=cvGetMat(img,&stub,&coi));
if(coi)
CV_ERROR(CV_BadCOI,“COIisnotsupported”); //一些出错代码
if(CV_MAT_DEPTH(img->type)!
=CV_8U)
CV_ERROR(CV_StsUnsupportedFormat,“Only8-bitimagesaresupported”);
CV_CALL(temp=cvCreateMat(img->rows,img->cols,CV_8UC1));
CV_CALL(sum=cvCreateMat(img->rows+1,img->cols+1,CV_32SC1));
CV_CALL(sqsum=cvCreateMat(img->rows+1,img->cols+1,CV_64FC1));
CV_CALL(temp_storage=cvCreateChildMemStorage(storage));
#ifdef_OPENMP
max_threads=cvGetNumThreads();
for(i=0;i {
CvMemStorage*temp_storage_thread;
CV_CALL(temp_storage_thread=cvCreateMemStorage(0)); //CV_CALL就是运行,假如出错就报错。
CV_CALL(seq_thread[i]=cvCreateSeq(0,sizeof(CvSeq), //CvSeq可动态增长元素序列
sizeof(CvRect),temp_storage_thread));
}
#endif
if(!
cascade->hid_cascade)
CV_CALL(icvCreateHidHaarClassifierCascade(cascade));
if(cascade->hid_cascade->has_tilted_features)
tilted=cvCreateMat(img->rows+1,img->cols+1,CV_32SC1); //多通道矩阵图像长宽+14通道
seq=cvCreateSeq(0,sizeof(CvSeq),sizeof(CvRect),temp_storage); //创建序列seq 矩形
seq2=cvCreateSeq(0,sizeof(CvSeq),sizeof(CvAvgComp),temp_storage); //创建序列seq2 矩形和邻近
result_seq=cvCreateSeq(0,sizeof(CvSeq),sizeof(CvAvgComp),storage); //创建序列result_seq 矩形和邻近
if(min_neighbors==0)
seq=result_seq;
if(CV_MAT_CN(img->type)>1)
{
cvCvtColor(img,temp,CV_BGR2GRAY); //img转为灰度
img=temp;
}
if(flags&CV_HAAR_SCALE_IMAGE) //flag&&匹配图
{
CvSizewin_size0=cascade->orig_window_size; //CvSizewin_size0为分类器的原始大小
intuse_ipp=cascade->hid_cascade->ipp_stages!
=0&&
icvApplyHaarClassifier_32s32f_C1R_p!
=0; //IPP相关函数
if(use_ipp)
CV_CALL(norm_img=cvCreateMat(img->rows,img->cols,CV_32FC1)); //图像的矩阵化4通道.
CV_CALL(img_small=cvCreateMat(img->rows+1,img->cols+1,CV_8UC1)); //小图矩阵化单通道长宽+1
for(factor=1;;factor*=scale_factor) //成scale_factor倍数匹配
{
intpositive=0;
intx,y;
CvSizewin_size={cvRound(win_size0.width*factor),
cvRound(win_size0.height*factor)}; //winsize 分类器行列(扩大factor倍)
CvSizesz={cvRound(img->cols/factor),cvRound(img->rows/factor)}; //sz图像行列(缩小factor倍)三个Cvsize
CvSizesz1={sz.width–win_size0.width,sz.height–win_size0.height}; //sz1图像减分类器行列
CvRectrect1={icv_object_win_border,icv_object_win_border,
win_size0.width–icv_object_win_border*2, //icv_object_win_border(int)初始值=1
win_size0.height–icv_object_win_border*2}; //矩形框rect1
CvMatimg1,sum1,sqsum1,norm1,tilted1,mask1; //多通道矩阵
CvMat*_tilted=0;
if(sz1.width<=0||sz1.height<=0) //图片宽或高小于分类器–>跳出
break;
if(win_size.width继续
continue;
//CV_8UC1见定义.
//#defineCV_MAKETYPE(depth,cn)((depth)+(((cn)-1)<//深度+(cn-1)左移3位 depth,depth+8,depth+16,depth+24.
img1=cvMat(sz.height,sz.width,CV_8UC1,img_small->data.ptr); //小图的矩阵化img1单通道
sum1=cvMat(sz.height+1,sz.width+1,CV_32SC1,sum->data.ptr); //长宽+14通道8位 多通道矩阵
sqsum1=cvMat(sz.height+1,sz.width+1,CV_64FC1,sqsum->data.ptr); //长宽+14通道16位
if(tilted)
{
tilted1=cvMat(sz.height+1,sz.width+1,CV_32SC1,tilted->data.ptr); //长宽+14通道8位
_tilted=&tilted1; //长宽+14通道8位
}
norm1=cvMat(sz1.height,sz1.width,CV_32FC1,norm_img?
norm_img->data.ptr:
0); //norm1图像减分类器行列4通道
mask1=cvMat(sz1.height,sz1.width,CV_8UC1,temp->data.ptr); //mask1灰度图
cvResize(img,&img1,CV_INTER_LINEAR); //img双线性插值输出到img1
cvIntegral(&img1,&sum1,&sqsum1,_tilted); //计算积分图像
if(use_ipp&&icvRectStdDev_32s32f_C1R_p(sum1.data.i,sum1.step,
sqsum1.data.db,sqsum1.step,norm1.data.fl,norm1.step,sz1,rect1)<0)
use_ipp=0;
if(use_ipp) //如果ipp=true (intel视频处理加速等的函数库)
{
positive=mask1.cols*mask1.rows; //mask1长乘宽–>positive
cvSet(&mask1,cvScalarAll(255)); //mask1赋值为255
for(i=0;icount;i++)
{
if(icvApplyHaarClassifier_32s32f_C1R_p(sum1.data.i,sum1.step,
norm1.data.fl,norm1.step,mask1.data.ptr,mask1.step,
sz1,&positive,cascade->hid_cascade->stage_classifier[i].threshold,
cascade->hid_cascade->ipp_stages[i])<0)
{
use_ipp=0; //ipp=false;
break;
}
if(positive<=0)
break;
}
}
if(!
use_ipp) //如果ipp=false
{
cvSetImagesForHaarClassifierCascade(cascade,&sum1,&sqsum1,0,1.);
for(y=0,positive=0;y for(x=0;x {
mask1.data.ptr[mask1.step*y+x]=
cvRunHaarClassifierCascade(cascade,cvPoint(x,y),0)>0; //匹配图像.
positive+=mask1.data.ptr[mask1.step*y+x];
}
}
if(positive>0)
{
for(y=0;y for(x=0;x if(mask1.data.ptr[mask1.step*y+x]!
=0)
{
CvRectobj_rect={cvRound(y*factor),cvRound(x*factor),
win_size.width,win_size.height};
cvSeqPush(seq,&obj_rect); //将匹配块放到seq中
}
}
}
}
else //!
(flag&&匹配图)
{
cvIntegral(img,sum,sqsum,tilted);
if(do_canny_pruning)
{
sumcanny=cvCreateMat(img->rows+1,img->cols+1,CV_32SC1); //如果做canny边缘检测
cvCanny(img,temp,0,50,3);
cvIntegral(temp,sumcanny);
}
if((unsigned)split_stage>=(unsigned)cascade->count||
cascade->hid_cascade->is_tree) {
split_stage=cascade->count;
npass=1;
}
for(factor=1;factor*cascade-