标签:
近段时间在搞opencv的视频人脸识别,无奈自带的分类器的准确度,实在是不怎么样,但又能怎样呢?自己又研究不清楚各大类检测算法。
正所谓,功能是由函数完成的,于是自己便看cvHaarDetectObjects 这个识别主函数的源代码,尝试了解并进行改造它,以提高精确度。
可惜实力有限啊,里面的结构非常复杂,参杂着更多的函数体,有一些是网上找不到用法的,导致最终无法整体了解,只搞了一般,这里分享
下我自己总结的注释。
1 CvSeq* cvHaarDetectObjects( const CvArr* _img,//传入图像 2 CvHaarClassifierCascade* cascade, //传入xml路径 3 CvMemStorage* storage,//传入内存容器 4 double scaleFactor,//传入缩放值 5 int minNeighbors, 6 int flags, 7 CvSize minSize, 8 CvSize maxSize ){ 9 10 std::vector<int> fakeLevels;//int 类型的容器 11 std::vector<double> fakeWeights;//double 12 return cvHaarDetectObjectsForROC( _img, cascade, storage, fakeLevels, fakeWeights, 13 scaleFactor, minNeighbors, flags, minSize, maxSize, false );//进入这个参数 14 //执行目标检测,这个函数 15 } 16 17 CvSeq* cvHaarDetectObjectsForROC(const CvArr* _img, 18 CvHaarClassifierCascade* cascade, 19 CvMemStorage* storage, 20 std::vector<int>& rejectLevels, 21 std::vector<double>& levelWeights, 22 double scaleFactor, 23 int minNeighbors, 24 int flags, 25 CvSize minSize, 26 CvSize maxSize, 27 bool outputRejectLevels ){ 28 29 const double GROUP_EPS = 0.2;//定义一个double常数据 30 CvMat stub, *img = (CvMat*)_img;//定义一个矩阵stub和把传入的图片转化为矩阵 31 cv::Ptr<CvMat> temp, sum, tilted, sqsum, normImg, sumcanny, imgSmall;//定义矩阵类 32 CvSeq* result_seq = 0;//定义最终返回的指针数据变量 33 cv::Ptr<CvMemStorage> temp_storage;//内存类的定义 34 35 std::vector<cv::Rect> allCandidates;//矩形类 36 std::vector<cv::Rect> rectList;//矩形类 37 std::vector<int> rweights;//int 容器 38 double factor; 39 int coi; 40 bool doCannyPruning = (flags & CV_HAAR_DO_CANNY_PRUNING) != 0;//这三个都是判断传入的flags是什么类型,这个是做canny边缘处理 41 bool findBiggestObject = (flags & CV_HAAR_FIND_BIGGEST_OBJECT) != 0; 42 bool roughSearch = (flags & CV_HAAR_DO_ROUGH_SEARCH) != 0; 43 //CV_HAAR_DO_CANNY_PRUNING利用Canny边缘检测器来排除一些边缘很少或者很多的图像区域 44 //CV_HAAR_SCALE_IMAGE 按比例正常检测 45 //CV_HAAR_FIND_BIGGEST_OBJECT只检测最大的物体 46 //CV_HAAR_DO_ROUGH_SEARCH只做初略检测 47 48 cv::Mutex mtx;//定义互斥锁,确保线程唯一 49 50 if( !CV_IS_HAAR_CLASSIFIER(cascade) ) 51 CV_Error( !cascade ? CV_StsNullPtr : CV_StsBadArg, "Invalid classifier cascade" );//无效的级联分类器,输出 52 53 if( !storage ) 54 CV_Error( CV_StsNullPtr, "Null storage pointer" );//内存为空 55 56 img = cvGetMat( img, &stub, &coi );//IplImage 到cvMat 的转换 57 if( coi ) 58 CV_Error( CV_BadCOI, "COI is not supported" ); 59 60 if( CV_MAT_DEPTH(img->type) != CV_8U )//对图像的深度判断 61 CV_Error( CV_StsUnsupportedFormat, "Only 8-bit images are supported" ); 62 63 if( scaleFactor <= 1 )//对缩放值的判断 64 CV_Error( CV_StsOutOfRange, "scale factor must be > 1" ); 65 66 if( findBiggestObject ) 67 flags &= ~CV_HAAR_SCALE_IMAGE; 68 69 if( maxSize.height == 0 || maxSize.width == 0 )//判断,如果传进来的检测窗口的尺寸,如果有一个为0,下面赋值为矩阵的行数和列数 70 { 71 maxSize.height = img->rows; 72 maxSize.width = img->cols; 73 } 74 75 temp = cvCreateMat( img->rows, img->cols, CV_8UC1 );//中间值矩阵模板初始化 76 sum = cvCreateMat( img->rows + 1, img->cols + 1, CV_32SC1 );//积分图求和的结果矩阵模板 77 sqsum = cvCreateMat( img->rows + 1, img->cols + 1, CV_64FC1 );////积分图求和的平方的结果 78 79 if( !cascade->hid_cascade ) 80 icvCreateHidHaarClassifierCascade(cascade);//创建分类器,填写 casecade 中相关的头信息,如有多少个 stage, 每个 stage 下有多少个 tree ,每个 tree 下有多少个 node ,以及相关的阈值等信息 81 82 if( cascade->hid_cascade->has_tilted_features ) 83 tilted = cvCreateMat( img->rows + 1, img->cols + 1, CV_32SC1 );//创建用于存放积分图求和并倾斜45度的检测结果矩阵 84 85 result_seq = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvAvgComp), storage );//初始化最总返回结果变量 86 87 if( CV_MAT_CN(img->type) > 1 )//如果由传入的图片转化为的矩阵的数据类型是比32位浮点高为真,进入if语句 88 { 89 cvCvtColor( img, temp, CV_BGR2GRAY );//灰度转化,此时temp指针式灰度数据的 90 img = temp;//把值给会img,temp只起到一个中间保存的作用 91 } 92 93 if( findBiggestObject )//是否只检测最大的物体,是,则进入if语句 94 flags &= ~(CV_HAAR_SCALE_IMAGE|CV_HAAR_DO_CANNY_PRUNING); 95 96 if( flags & CV_HAAR_SCALE_IMAGE )//按比例正常检测,&是位运算 1|1=1, 97 { 98 CvSize winSize0 = cascade->orig_window_size;//获取检测窗口的大小,由分类器返回 99 100 //下面是定义块,如果有定义HAVE_IPP,那么进入下面的数据赋值 101 //但是在CvHaarClassifierCascade结构体里面的CvHidHaarClassifierCascade是空的 102 #ifdef HAVE_IPP 103 int use_ipp = cascade->hid_cascade->ipp_stages != 0; 104 if( use_ipp ) 105 normImg = cvCreateMat( img->rows, img->cols, CV_32FC1 ); 106 #endif 107 108 imgSmall = cvCreateMat( img->rows + 1, img->cols + 1, CV_8UC1 );//创建新矩阵 109 110 for( factor = 1; ; factor *= scaleFactor )//无循环条件的死循环 111 { 112 //定义3个矩形 大小 113 //经输出测试过,矩阵的width和cols是一样大 114 //我们假设上面的 winSize0 的 width,height都是10,factor循环到4,那么winSize的width和height都是40 115 //我们再假设img的width和height都是10,sz的就变为2.5 116 //sz1的就变为负的了,下面直接跳出循环,所以一般图片的w和h都比检测的窗口size要大得多 117 //重新假设他们都是100,那么sz就是25,sz1就是16 118 //此时改factor为5,sz为20,sz1为20-10+1=11 119 //由此可知,随着factor的增大,sz1的双值减小,由于factor *= scaleFactor的,且scaleFactor比1大,所以 120 //sz1必递减 121 //综上述,检测窗口win会越来越大,sz类窗口会越来越小 122 CvSize winSize = { cvRound(winSize0.width*factor), cvRound(winSize0.height*factor) }; 123 CvSize sz = { cvRound( img->cols/factor ), cvRound( img->rows/factor ) }; 124 CvSize sz1 = { sz.width - winSize0.width + 1, sz.height - winSize0.height + 1 }; 125 126 //定义矩形框,icv_object_win_border,这个东西,找遍没找到 127 128 CvRect equRect = { icv_object_win_border, icv_object_win_border, 129 winSize0.width - icv_object_win_border*2, 130 winSize0.height - icv_object_win_border*2 }; 131 132 CvMat img1, sum1, sqsum1, norm1, tilted1, mask1; 133 CvMat* _tilted = 0; 134 135 if( sz1.width <= 0 || sz1.height <= 0 )//当sz1窗口大小为负的时候,循环结束。 136 break; 137 if( winSize.width > maxSize.width || winSize.height > maxSize.height )//当检测窗口过大,也跳出循环 138 break; 139 if( winSize.width < minSize.width || winSize.height < minSize.height )//过小,也跳出,不过它是继续循环 140 continue; 141 142 //在还没跳出循环的情况下,下面分别以sz的宽和高创建矩阵 143 img1 = cvMat( sz.height, sz.width, CV_8UC1, imgSmall->data.ptr ); 144 sum1 = cvMat( sz.height+1, sz.width+1, CV_32SC1, sum->data.ptr ); 145 sqsum1 = cvMat( sz.height+1, sz.width+1, CV_64FC1, sqsum->data.ptr ); 146 if( tilted )//这个是矩阵类 147 { 148 tilted1 = cvMat( sz.height+1, sz.width+1, CV_32SC1, tilted->data.ptr );//一样是初始化 149 _tilted = &tilted1; 150 } 151 152 //这下面的是以sz1为基础初始化的矩阵 153 norm1 = cvMat( sz1.height, sz1.width, CV_32FC1, normImg ? normImg->data.ptr : 0 ); 154 mask1 = cvMat( sz1.height, sz1.width, CV_8UC1, temp->data.ptr ); 155 156 cvResize( img, &img1, CV_INTER_LINEAR );//双线性插值,重新调整img的大小,相关数据存入img1 157 cvIntegral( &img1, &sum1, &sqsum1, _tilted );//由img1开始积分计算,存入sum1、sqsum1、tilted 158 159 int ystep = factor > 2 ? 1 : 2;//这里判断了下factor的大小,大于2,ystep就是1 160 const int LOCS_PER_THREAD = 1000; 161 //接着上面的假设,factor是4,那么此时的yster是1 162 //stripCount就是(11/1 * 11/1+1000/2)/1000 < 1 163 int stripCount = ((sz1.width/ystep)*(sz1.height + ystep-1)/ystep + LOCS_PER_THREAD/2)/LOCS_PER_THREAD; 164 stripCount = std::min(std::max(stripCount, 1), 100); 165 //然后和1对比,找出最大值,再和100比较,找出最小 166 167 #ifdef HAVE_IPP 168 if( use_ipp ) 169 { 170 cv::Mat fsum(sum1.rows, sum1.cols, CV_32F, sum1.data.ptr, sum1.step); 171 cv::Mat(&sum1).convertTo(fsum, CV_32F, 1, -(1<<24)); 172 } 173 else 174 #endif 175 cvSetImagesForHaarClassifierCascade( cascade, &sum1, &sqsum1, _tilted, 1. ); 176 //上面这个函数是为隐藏的cascade(hidden cascade)指定图像积分图像、平方和图像与倾斜和图像、特征矩形,然后让它检测 177 //sum1是上面生成的32bt积分图像,sqsum 单通道64比特图像的平方和图像 178 //tilted 单通道32比特整数格式的图像的倾斜和 179 //1是窗口比例,如果 scale=1, 就只用原始窗口尺寸检测 (只检测同样尺寸大小的目标物体) 180 //- 原始窗口尺寸在函数cvLoadHaarClassifierCascade中定义 (在 "<default_face_cascade>"中缺省为24x24), 181 //如果scale=2, 使用的窗口是上面的两倍 (在face cascade中缺省值是48x48 )。 182 //这样尽管可以将检测速度提高四倍,但同时尺寸小于48x48的人脸将不能被检测到 183 cv::Mat _norm1(&norm1), _mask1(&mask1); 184 185 //HaarDetectObjects_ScaleImage_Invoker进行并行运算(可以返回rejectLevels和levelWeights) 186 cv::parallel_for_(cv::Range(0, stripCount), 187 cv::HaarDetectObjects_ScaleImage_Invoker(cascade, 188 (((sz1.height + stripCount - 1)/stripCount + ystep-1)/ystep)*ystep, 189 factor, cv::Mat(&sum1), cv::Mat(&sqsum1), &_norm1, &_mask1, 190 cv::Rect(equRect), allCandidates, rejectLevels, levelWeights, outputRejectLevels, &mtx)); 191 } 192 } 193 else 194 { 195 int n_factors = 0; 196 cv::Rect scanROI; 197 198 cvIntegral( img, sum, sqsum, tilted );//由img1开始积分计算,存入sum1、sqsum1、tilted 199 200 if( doCannyPruning )//边缘处理 201 { 202 sumcanny = cvCreateMat( img->rows + 1, img->cols + 1, CV_32SC1 ); 203 cvCanny( img, temp, 0, 50, 3 );//得到边缘图像 204 cvIntegral( temp, sumcanny );//再次积分 205 } 206 207 for( n_factors = 0, factor = 1; 208 factor*cascade->orig_window_size.width < img->cols - 10 && 209 factor*cascade->orig_window_size.height < img->rows - 10; 210 n_factors++, factor *= scaleFactor ) 211 ; 212 213 if( findBiggestObject ) 214 { 215 scaleFactor = 1./scaleFactor; 216 factor *= scaleFactor; 217 } 218 else 219 factor = 1; 220 221 for( ; n_factors-- > 0; factor *= scaleFactor ) 222 { 223 const double ystep = std::max( 2., factor ); 224 CvSize winSize = { cvRound( cascade->orig_window_size.width * factor ), 225 cvRound( cascade->orig_window_size.height * factor )}; 226 CvRect equRect = { 0, 0, 0, 0 }; 227 int *p[4] = {0,0,0,0}; 228 int *pq[4] = {0,0,0,0}; 229 int startX = 0, startY = 0; 230 int endX = cvRound((img->cols - winSize.width) / ystep); 231 int endY = cvRound((img->rows - winSize.height) / ystep); 232 233 if( winSize.width < minSize.width || winSize.height < minSize.height ) 234 { 235 if( findBiggestObject ) 236 break; 237 continue; 238 } 239 240 if ( winSize.width > maxSize.width || winSize.height > maxSize.height ) 241 { 242 if( !findBiggestObject ) 243 break; 244 continue; 245 } 246 247 cvSetImagesForHaarClassifierCascade( cascade, sum, sqsum, tilted, factor ); 248 cvZero( temp ); 249 250 if( doCannyPruning ) 251 { 252 equRect.x = cvRound(winSize.width*0.15); 253 equRect.y = cvRound(winSize.height*0.15); 254 equRect.width = cvRound(winSize.width*0.7); 255 equRect.height = cvRound(winSize.height*0.7); 256 257 p[0] = (int*)(sumcanny->data.ptr + equRect.y*sumcanny->step) + equRect.x; 258 p[1] = (int*)(sumcanny->data.ptr + equRect.y*sumcanny->step) 259 + equRect.x + equRect.width; 260 p[2] = (int*)(sumcanny->data.ptr + (equRect.y + equRect.height)*sumcanny->step) + equRect.x; 261 p[3] = (int*)(sumcanny->data.ptr + (equRect.y + equRect.height)*sumcanny->step) 262 + equRect.x + equRect.width; 263 264 pq[0] = (int*)(sum->data.ptr + equRect.y*sum->step) + equRect.x; 265 pq[1] = (int*)(sum->data.ptr + equRect.y*sum->step) 266 + equRect.x + equRect.width; 267 pq[2] = (int*)(sum->data.ptr + (equRect.y + equRect.height)*sum->step) + equRect.x; 268 pq[3] = (int*)(sum->data.ptr + (equRect.y + equRect.height)*sum->step) 269 + equRect.x + equRect.width; 270 } 271 272 if( scanROI.area() > 0 ) 273 { 274 //adjust start_height and stop_height 275 startY = cvRound(scanROI.y / ystep); 276 endY = cvRound((scanROI.y + scanROI.height - winSize.height) / ystep); 277 278 startX = cvRound(scanROI.x / ystep); 279 endX = cvRound((scanROI.x + scanROI.width - winSize.width) / ystep); 280 } 281 282 cv::parallel_for_(cv::Range(startY, endY), 283 cv::HaarDetectObjects_ScaleCascade_Invoker(cascade, winSize, cv::Range(startX, endX), 284 ystep, sum->step, (const int**)p, 285 (const int**)pq, allCandidates, &mtx )); 286 287 if( findBiggestObject && !allCandidates.empty() && scanROI.area() == 0 ) 288 { 289 rectList.resize(allCandidates.size()); 290 std::copy(allCandidates.begin(), allCandidates.end(), rectList.begin()); 291 292 groupRectangles(rectList, std::max(minNeighbors, 1), GROUP_EPS); 293 294 if( !rectList.empty() ) 295 { 296 size_t i, sz = rectList.size(); 297 cv::Rect maxRect; 298 299 for( i = 0; i < sz; i++ ) 300 { 301 if( rectList[i].area() > maxRect.area() ) 302 maxRect = rectList[i]; 303 } 304 305 allCandidates.push_back(maxRect); 306 307 scanROI = maxRect; 308 int dx = cvRound(maxRect.width*GROUP_EPS); 309 int dy = cvRound(maxRect.height*GROUP_EPS); 310 scanROI.x = std::max(scanROI.x - dx, 0); 311 scanROI.y = std::max(scanROI.y - dy, 0); 312 scanROI.width = std::min(scanROI.width + dx*2, img->cols-1-scanROI.x); 313 scanROI.height = std::min(scanROI.height + dy*2, img->rows-1-scanROI.y); 314 315 double minScale = roughSearch ? 0.6 : 0.4; 316 minSize.width = cvRound(maxRect.width*minScale); 317 minSize.height = cvRound(maxRect.height*minScale); 318 } 319 } 320 } 321 } 322 323 //上面的循环结束后,进入到这里 324 rectList.resize(allCandidates.size()); 325 if(!allCandidates.empty()) 326 std::copy(allCandidates.begin(), allCandidates.end(), rectList.begin()); 327 328 if( minNeighbors != 0 || findBiggestObject ) 329 { 330 if( outputRejectLevels ) 331 { 332 groupRectangles(rectList, rejectLevels, levelWeights, minNeighbors, GROUP_EPS ); 333 } 334 else 335 { 336 groupRectangles(rectList, rweights, std::max(minNeighbors, 1), GROUP_EPS); 337 } 338 } 339 else 340 rweights.resize(rectList.size(),0); 341 342 if( findBiggestObject && rectList.size() ) 343 { 344 CvAvgComp result_comp = {{0,0,0,0},0}; 345 346 for( size_t i = 0; i < rectList.size(); i++ ) 347 { 348 cv::Rect r = rectList[i]; 349 if( r.area() > cv::Rect(result_comp.rect).area() ) 350 { 351 result_comp.rect = r; 352 result_comp.neighbors = rweights[i]; 353 } 354 } 355 cvSeqPush( result_seq, &result_comp ); 356 } 357 else 358 { 359 for( size_t i = 0; i < rectList.size(); i++ ) 360 { 361 CvAvgComp c; 362 c.rect = rectList[i]; 363 c.neighbors = !rweights.empty() ? rweights[i] : 0; 364 cvSeqPush( result_seq, &c ); 365 } 366 } 367 368 return result_seq; 369 }
正在看本人博客的这位童鞋,我看你气度不凡,谈吐间隐隐有王者之气,日后必有一番作为!旁边有“推荐”二字,你就顺手把它点了吧,相得准,我分文不收;相不准,你也好回来找我。
标签:
原文地址:http://www.cnblogs.com/linguanh/p/4267432.html