近段时间在搞opencv的视频人脸识别,无奈自带的分类器的准确度,实在是不怎么样,但又能怎样呢?自己又研究不清楚各大类检测算法。

正所谓,功能是由函数完成的,于是自己便看cvHaarDetectObjects 这个识别主函数的源代码,尝试了解并进行改造它,以提高精确度。

可惜实力有限啊,里面的结构非常复杂,参杂着更多的函数体,有一些是网上找不到用法的,导致最终无法整体了解,只搞了一般,这里分享

下我自己总结的注释。


 CvSeq* cvHaarDetectObjects( const CvArr* _img,//传入图像
CvHaarClassifierCascade* cascade, //传入xml路径
CvMemStorage* storage,//传入内存容器
double scaleFactor,//传入缩放值
int minNeighbors,
int flags,
CvSize minSize,
CvSize maxSize ){ std::vector<int> fakeLevels;//int 类型的容器
std::vector<double> fakeWeights;//double
return cvHaarDetectObjectsForROC( _img, cascade, storage, fakeLevels, fakeWeights,
scaleFactor, minNeighbors, flags, minSize, maxSize, false );//进入这个参数
//执行目标检测,这个函数
} CvSeq* cvHaarDetectObjectsForROC(const CvArr* _img,
CvHaarClassifierCascade* cascade,
CvMemStorage* storage,
std::vector<int>& rejectLevels,
std::vector<double>& levelWeights,
double scaleFactor,
int minNeighbors,
int flags,
CvSize minSize,
CvSize maxSize,
bool outputRejectLevels ){ const double GROUP_EPS = 0.2;//定义一个double常数据
CvMat stub, *img = (CvMat*)_img;//定义一个矩阵stub和把传入的图片转化为矩阵
cv::Ptr<CvMat> temp, sum, tilted, sqsum, normImg, sumcanny, imgSmall;//定义矩阵类
CvSeq* result_seq = ;//定义最终返回的指针数据变量
cv::Ptr<CvMemStorage> temp_storage;//内存类的定义 std::vector<cv::Rect> allCandidates;//矩形类
std::vector<cv::Rect> rectList;//矩形类
std::vector<int> rweights;//int 容器
double factor;
int coi;
bool doCannyPruning = (flags & CV_HAAR_DO_CANNY_PRUNING) != ;//这三个都是判断传入的flags是什么类型,这个是做canny边缘处理
bool findBiggestObject = (flags & CV_HAAR_FIND_BIGGEST_OBJECT) != ;
bool roughSearch = (flags & CV_HAAR_DO_ROUGH_SEARCH) != ;
//CV_HAAR_DO_CANNY_PRUNING利用Canny边缘检测器来排除一些边缘很少或者很多的图像区域
//CV_HAAR_SCALE_IMAGE 按比例正常检测
//CV_HAAR_FIND_BIGGEST_OBJECT只检测最大的物体
//CV_HAAR_DO_ROUGH_SEARCH只做初略检测 cv::Mutex mtx;//定义互斥锁,确保线程唯一 if( !CV_IS_HAAR_CLASSIFIER(cascade) )
CV_Error( !cascade ? CV_StsNullPtr : CV_StsBadArg, "Invalid classifier cascade" );//无效的级联分类器,输出 if( !storage )
CV_Error( CV_StsNullPtr, "Null storage pointer" );//内存为空 img = cvGetMat( img, &stub, &coi );//IplImage 到cvMat 的转换
if( coi )
CV_Error( CV_BadCOI, "COI is not supported" ); if( CV_MAT_DEPTH(img->type) != CV_8U )//对图像的深度判断
CV_Error( CV_StsUnsupportedFormat, "Only 8-bit images are supported" ); if( scaleFactor <= )//对缩放值的判断
CV_Error( CV_StsOutOfRange, "scale factor must be > 1" ); if( findBiggestObject )
flags &= ~CV_HAAR_SCALE_IMAGE; if( maxSize.height == || maxSize.width == )//判断,如果传进来的检测窗口的尺寸,如果有一个为0,下面赋值为矩阵的行数和列数
{
maxSize.height = img->rows;
maxSize.width = img->cols;
} temp = cvCreateMat( img->rows, img->cols, CV_8UC1 );//中间值矩阵模板初始化
sum = cvCreateMat( img->rows + , img->cols + , CV_32SC1 );//积分图求和的结果矩阵模板
sqsum = cvCreateMat( img->rows + , img->cols + , CV_64FC1 );////积分图求和的平方的结果 if( !cascade->hid_cascade )
icvCreateHidHaarClassifierCascade(cascade);//创建分类器,填写 casecade 中相关的头信息,如有多少个 stage, 每个 stage 下有多少个 tree ,每个 tree 下有多少个 node ,以及相关的阈值等信息 if( cascade->hid_cascade->has_tilted_features )
tilted = cvCreateMat( img->rows + , img->cols + , CV_32SC1 );//创建用于存放积分图求和并倾斜45度的检测结果矩阵 result_seq = cvCreateSeq( , sizeof(CvSeq), sizeof(CvAvgComp), storage );//初始化最总返回结果变量 if( CV_MAT_CN(img->type) > )//如果由传入的图片转化为的矩阵的数据类型是比32位浮点高为真,进入if语句
{
cvCvtColor( img, temp, CV_BGR2GRAY );//灰度转化,此时temp指针式灰度数据的
img = temp;//把值给会img,temp只起到一个中间保存的作用
} if( findBiggestObject )//是否只检测最大的物体,是,则进入if语句
flags &= ~(CV_HAAR_SCALE_IMAGE|CV_HAAR_DO_CANNY_PRUNING); if( flags & CV_HAAR_SCALE_IMAGE )//按比例正常检测,&是位运算 1|1=1,
{
CvSize winSize0 = cascade->orig_window_size;//获取检测窗口的大小,由分类器返回 //下面是定义块,如果有定义HAVE_IPP,那么进入下面的数据赋值
//但是在CvHaarClassifierCascade结构体里面的CvHidHaarClassifierCascade是空的
#ifdef HAVE_IPP
int use_ipp = cascade->hid_cascade->ipp_stages != ;
if( use_ipp )
normImg = cvCreateMat( img->rows, img->cols, CV_32FC1 );
#endif imgSmall = cvCreateMat( img->rows + , img->cols + , CV_8UC1 );//创建新矩阵 for( factor = ; ; factor *= scaleFactor )//无循环条件的死循环
{
//定义3个矩形 大小
//经输出测试过,矩阵的width和cols是一样大
//我们假设上面的 winSize0 的 width,height都是10,factor循环到4,那么winSize的width和height都是40
//我们再假设img的width和height都是10,sz的就变为2.5
//sz1的就变为负的了,下面直接跳出循环,所以一般图片的w和h都比检测的窗口size要大得多
//重新假设他们都是100,那么sz就是25,sz1就是16
//此时改factor为5,sz为20,sz1为20-10+1=11
//由此可知,随着factor的增大,sz1的双值减小,由于factor *= scaleFactor的,且scaleFactor比1大,所以
//sz1必递减
//综上述,检测窗口win会越来越大,sz类窗口会越来越小
CvSize winSize = { cvRound(winSize0.width*factor), cvRound(winSize0.height*factor) };
CvSize sz = { cvRound( img->cols/factor ), cvRound( img->rows/factor ) };
CvSize sz1 = { sz.width - winSize0.width + , sz.height - winSize0.height + }; //定义矩形框,icv_object_win_border,这个东西,找遍没找到 CvRect equRect = { icv_object_win_border, icv_object_win_border,
winSize0.width - icv_object_win_border*,
winSize0.height - icv_object_win_border* }; CvMat img1, sum1, sqsum1, norm1, tilted1, mask1;
CvMat* _tilted = ; if( sz1.width <= || sz1.height <= )//当sz1窗口大小为负的时候,循环结束。
break;
if( winSize.width > maxSize.width || winSize.height > maxSize.height )//当检测窗口过大,也跳出循环
break;
if( winSize.width < minSize.width || winSize.height < minSize.height )//过小,也跳出,不过它是继续循环
continue; //在还没跳出循环的情况下,下面分别以sz的宽和高创建矩阵
img1 = cvMat( sz.height, sz.width, CV_8UC1, imgSmall->data.ptr );
sum1 = cvMat( sz.height+, sz.width+, CV_32SC1, sum->data.ptr );
sqsum1 = cvMat( sz.height+, sz.width+, CV_64FC1, sqsum->data.ptr );
if( tilted )//这个是矩阵类
{
tilted1 = cvMat( sz.height+, sz.width+, CV_32SC1, tilted->data.ptr );//一样是初始化
_tilted = &tilted1;
} //这下面的是以sz1为基础初始化的矩阵
norm1 = cvMat( sz1.height, sz1.width, CV_32FC1, normImg ? normImg->data.ptr : );
mask1 = cvMat( sz1.height, sz1.width, CV_8UC1, temp->data.ptr ); cvResize( img, &img1, CV_INTER_LINEAR );//双线性插值,重新调整img的大小,相关数据存入img1
cvIntegral( &img1, &sum1, &sqsum1, _tilted );//由img1开始积分计算,存入sum1、sqsum1、tilted int ystep = factor > ? : ;//这里判断了下factor的大小,大于2,ystep就是1
const int LOCS_PER_THREAD = ;
//接着上面的假设,factor是4,那么此时的yster是1
//stripCount就是(11/1 * 11/1+1000/2)/1000 < 1
int stripCount = ((sz1.width/ystep)*(sz1.height + ystep-)/ystep + LOCS_PER_THREAD/)/LOCS_PER_THREAD;
stripCount = std::min(std::max(stripCount, ), );
//然后和1对比,找出最大值,再和100比较,找出最小 #ifdef HAVE_IPP
if( use_ipp )
{
cv::Mat fsum(sum1.rows, sum1.cols, CV_32F, sum1.data.ptr, sum1.step);
cv::Mat(&sum1).convertTo(fsum, CV_32F, , -(<<));
}
else
#endif
cvSetImagesForHaarClassifierCascade( cascade, &sum1, &sqsum1, _tilted, . );
//上面这个函数是为隐藏的cascade(hidden cascade)指定图像积分图像、平方和图像与倾斜和图像、特征矩形,然后让它检测
//sum1是上面生成的32bt积分图像,sqsum 单通道64比特图像的平方和图像
//tilted 单通道32比特整数格式的图像的倾斜和
//1是窗口比例,如果 scale=1, 就只用原始窗口尺寸检测 (只检测同样尺寸大小的目标物体)
//- 原始窗口尺寸在函数cvLoadHaarClassifierCascade中定义 (在 "<default_face_cascade>"中缺省为24x24),
//如果scale=2, 使用的窗口是上面的两倍 (在face cascade中缺省值是48x48 )。
//这样尽管可以将检测速度提高四倍,但同时尺寸小于48x48的人脸将不能被检测到
cv::Mat _norm1(&norm1), _mask1(&mask1); //HaarDetectObjects_ScaleImage_Invoker进行并行运算(可以返回rejectLevels和levelWeights)
cv::parallel_for_(cv::Range(, stripCount),
cv::HaarDetectObjects_ScaleImage_Invoker(cascade,
(((sz1.height + stripCount - )/stripCount + ystep-)/ystep)*ystep,
factor, cv::Mat(&sum1), cv::Mat(&sqsum1), &_norm1, &_mask1,
cv::Rect(equRect), allCandidates, rejectLevels, levelWeights, outputRejectLevels, &mtx));
}
}
else
{
int n_factors = ;
cv::Rect scanROI; cvIntegral( img, sum, sqsum, tilted );//由img1开始积分计算,存入sum1、sqsum1、tilted if( doCannyPruning )//边缘处理
{
sumcanny = cvCreateMat( img->rows + , img->cols + , CV_32SC1 );
cvCanny( img, temp, , , );//得到边缘图像
cvIntegral( temp, sumcanny );//再次积分
} for( n_factors = , factor = ;
factor*cascade->orig_window_size.width < img->cols - &&
factor*cascade->orig_window_size.height < img->rows - ;
n_factors++, factor *= scaleFactor )
; if( findBiggestObject )
{
scaleFactor = ./scaleFactor;
factor *= scaleFactor;
}
else
factor = ; for( ; n_factors-- > ; factor *= scaleFactor )
{
const double ystep = std::max( ., factor );
CvSize winSize = { cvRound( cascade->orig_window_size.width * factor ),
cvRound( cascade->orig_window_size.height * factor )};
CvRect equRect = { , , , };
int *p[] = {,,,};
int *pq[] = {,,,};
int startX = , startY = ;
int endX = cvRound((img->cols - winSize.width) / ystep);
int endY = cvRound((img->rows - winSize.height) / ystep); if( winSize.width < minSize.width || winSize.height < minSize.height )
{
if( findBiggestObject )
break;
continue;
} if ( winSize.width > maxSize.width || winSize.height > maxSize.height )
{
if( !findBiggestObject )
break;
continue;
} cvSetImagesForHaarClassifierCascade( cascade, sum, sqsum, tilted, factor );
cvZero( temp ); if( doCannyPruning )
{
equRect.x = cvRound(winSize.width*0.15);
equRect.y = cvRound(winSize.height*0.15);
equRect.width = cvRound(winSize.width*0.7);
equRect.height = cvRound(winSize.height*0.7); p[] = (int*)(sumcanny->data.ptr + equRect.y*sumcanny->step) + equRect.x;
p[] = (int*)(sumcanny->data.ptr + equRect.y*sumcanny->step)
+ equRect.x + equRect.width;
p[] = (int*)(sumcanny->data.ptr + (equRect.y + equRect.height)*sumcanny->step) + equRect.x;
p[] = (int*)(sumcanny->data.ptr + (equRect.y + equRect.height)*sumcanny->step)
+ equRect.x + equRect.width; pq[] = (int*)(sum->data.ptr + equRect.y*sum->step) + equRect.x;
pq[] = (int*)(sum->data.ptr + equRect.y*sum->step)
+ equRect.x + equRect.width;
pq[] = (int*)(sum->data.ptr + (equRect.y + equRect.height)*sum->step) + equRect.x;
pq[] = (int*)(sum->data.ptr + (equRect.y + equRect.height)*sum->step)
+ equRect.x + equRect.width;
} if( scanROI.area() > )
{
//adjust start_height and stop_height
startY = cvRound(scanROI.y / ystep);
endY = cvRound((scanROI.y + scanROI.height - winSize.height) / ystep); startX = cvRound(scanROI.x / ystep);
endX = cvRound((scanROI.x + scanROI.width - winSize.width) / ystep);
} cv::parallel_for_(cv::Range(startY, endY),
cv::HaarDetectObjects_ScaleCascade_Invoker(cascade, winSize, cv::Range(startX, endX),
ystep, sum->step, (const int**)p,
(const int**)pq, allCandidates, &mtx )); if( findBiggestObject && !allCandidates.empty() && scanROI.area() == )
{
rectList.resize(allCandidates.size());
std::copy(allCandidates.begin(), allCandidates.end(), rectList.begin()); groupRectangles(rectList, std::max(minNeighbors, ), GROUP_EPS); if( !rectList.empty() )
{
size_t i, sz = rectList.size();
cv::Rect maxRect; for( i = ; i < sz; i++ )
{
if( rectList[i].area() > maxRect.area() )
maxRect = rectList[i];
} allCandidates.push_back(maxRect); scanROI = maxRect;
int dx = cvRound(maxRect.width*GROUP_EPS);
int dy = cvRound(maxRect.height*GROUP_EPS);
scanROI.x = std::max(scanROI.x - dx, );
scanROI.y = std::max(scanROI.y - dy, );
scanROI.width = std::min(scanROI.width + dx*, img->cols--scanROI.x);
scanROI.height = std::min(scanROI.height + dy*, img->rows--scanROI.y); double minScale = roughSearch ? 0.6 : 0.4;
minSize.width = cvRound(maxRect.width*minScale);
minSize.height = cvRound(maxRect.height*minScale);
}
}
}
} //上面的循环结束后,进入到这里
rectList.resize(allCandidates.size());
if(!allCandidates.empty())
std::copy(allCandidates.begin(), allCandidates.end(), rectList.begin()); if( minNeighbors != || findBiggestObject )
{
if( outputRejectLevels )
{
groupRectangles(rectList, rejectLevels, levelWeights, minNeighbors, GROUP_EPS );
}
else
{
groupRectangles(rectList, rweights, std::max(minNeighbors, ), GROUP_EPS);
}
}
else
rweights.resize(rectList.size(),); if( findBiggestObject && rectList.size() )
{
CvAvgComp result_comp = {{,,,},}; for( size_t i = ; i < rectList.size(); i++ )
{
cv::Rect r = rectList[i];
if( r.area() > cv::Rect(result_comp.rect).area() )
{
result_comp.rect = r;
result_comp.neighbors = rweights[i];
}
}
cvSeqPush( result_seq, &result_comp );
}
else
{
for( size_t i = ; i < rectList.size(); i++ )
{
CvAvgComp c;
c.rect = rectList[i];
c.neighbors = !rweights.empty() ? rweights[i] : ;
cvSeqPush( result_seq, &c );
}
} return result_seq;
}

正在看本人博客的这位童鞋,我看你气度不凡,谈吐间隐隐有王者之气,日后必有一番作为!旁边有“推荐”二字,你就顺手把它点了吧,相得准,我分文不收;相不准,你也好回来找我。

关于opencv中人脸识别主函数的部分注释详解。的更多相关文章

  1. python中使用Opencv进行人脸识别

    上一节讲到人脸检测,现在讲一下人脸识别.具体是通过程序采集图像并进行训练,并且基于这些训练的图像对人脸进行动态识别. 人脸识别前所需要的人脸库可以通过两种方式获得:1.自己从视频获取图像   2.从人 ...

  2. 转:基于开源项目OpenCV的人脸识别Demo版整理(不仅可以识别人脸,还可以识别眼睛鼻子嘴等)【模式识别中的翘楚】

    文章来自于:http://blog.renren.com/share/246648717/8171467499 基于开源项目OpenCV的人脸识别Demo版整理(不仅可以识别人脸,还可以识别眼睛鼻子嘴 ...

  3. 使用OpenCV进行人脸识别

    不断维护的地址:http://plzcoding.com/face-recognition-with-opencv/ 怎样使用OpenCV进行人脸识别 本文大部分来自OpenCV官网上的Face Re ...

  4. 基于OpenCV的人脸识别[iOS开发笔记(2)]

    开始了OpenCV的试水工作了... 1.Get ready 在OpenCV中我们会使用函数cv::CascadeClassifier 来进行人脸检测.但是在使用本函数之前我们需要添加一个XML文件对 ...

  5. 基于 OpenCV 的人脸识别

    基于 OpenCV 的人脸识别 一点背景知识 OpenCV 是一个开源的计算机视觉和机器学习库.它包含成千上万优化过的算法,为各种计算机视觉应用提供了一个通用工具包.根据这个项目的关于页面,OpenC ...

  6. PyQt5+Caffe+Opencv搭建人脸识别登录界面

    PyQt5+Caffe+Opencv搭建人脸识别登录界面(转载) 最近开始学习Qt,结合之前学习过的caffe一起搭建了一个人脸识别登录系统的程序,新手可能有理解不到位的情况,还请大家多多指教. 我的 ...

  7. PHP函数call_user_func和call_user_func_array详解

    今天在群里面,有个叫lewis的在问call_user_func_array的用法,因为之前一直没有用过,也不能说什么,于是看一下手册,发现是这么写的: call_user_func_array (P ...

  8. JScript中的条件注释详解(转载自网络)

    JScript中的条件注释详解-转载 这篇文章主要介绍了JScript中的条件注释详解,本文讲解了@cc_on.@if.@set.@_win32.@_win16.@_mac等条件注释语句及可用于条件编 ...

  9. 转载~kxcfzyk:Linux C语言多线程库Pthread中条件变量的的正确用法逐步详解

    Linux C语言多线程库Pthread中条件变量的的正确用法逐步详解   多线程c语言linuxsemaphore条件变量 (本文的读者定位是了解Pthread常用多线程API和Pthread互斥锁 ...

随机推荐

  1. 基于zepto的H5/移动端tab切换触摸拖动加载更多数据

    以前实现移动端的滑动加载更多实现的方法是当滚动条快到页面底部时就自动加载更多的数据,在这方面很多人都用的是"西门的后花园"写的一个叫dropload的插件,这个插件用起来也很好,很 ...

  2. iOS 10 开发问题总结

    兼容iOS 10 资料整理笔记   1.Notification(通知) 自从Notification被引入之后,苹果就不断的更新优化,但这些更新优化只是小打小闹,直至现在iOS 10开始真正的进行大 ...

  3. ArcGIS Server SOE开发之奇怪异常:

    添加之后结果显示如下:fjsontokenezkBvir0Tj5q31UEst7pTFPwrwocmHklCajKeh-xXM91qWdBXDuQMmtGcaHaaXCJ 具体如下: 该SOE扩展在另 ...

  4. 利用DOS批处理实现定时关机操作

    10月1放假回来,寝室晚上10:30就停电了,最无法让人理解的是第二天早上8:00才来电.原来晚上电脑都是不关机的,开着WiFi一直到天亮,可是现在不行了,电脑如果一直开着第二天早上起来电脑肯定没电, ...

  5. JSP :运行最简单的 JSP 程序

    160916 1. 代码和显示效果 <%@ page contentType="text/html; charset=GB2312" %> <%@ page im ...

  6. java接口中定义成员变量

    //抽象类中可以定义如下成员变量:public abstract class People { public String name; public int age; public abstract ...

  7. Morris.js和flot绘制折线图的比较

    [文章摘要] 最近用开源的AdminLTE做框架感觉效果特别好,其针对图表库Morris.js和flot都提供了不错的支持,也都提供了这两者的例子.不过Morris.js是基于Raphael.js来的 ...

  8. redis总结

    redis总结 redis与memcached redis支持更多的数据结构 redis支持数据持久化 redis支持两种存储方式:snapshot(快照)和aof(append only mode) ...

  9. MySQL 存储过程

    MySQL 存储过程 存储过程是通过给定的语法格式编写自定义的数据库API,类似于给数据库编写可执行函数. 简介 存储过程是一组为了完成特定功能的SQL语句集合,是经过编译后存储在数据库中. 存储过程 ...

  10. 久违的问候-----eclipse中搭建maven项目2016年

    好久没有写过博客了,可是一直向别人推荐自己的博客,深感惭愧!今天再次在寒冷之夜继续code,config,write. 接下来,我们就来谈下eclipse中搭建maven web工程的步骤!虽然就是一 ...