转自:http://www.haogongju.net/art/1441498
上一篇文章《浅析人脸检测之Haar分类器方法》我大致讲了下Haar分类器训练及检测的原理,原本想写一篇单独解析OpenCV Haar分类器代码的文章,但是手头有了新的任务,这个计划暂时要拖后了,我简单把之前给检测部分代码写的注释贴过来。只是为了记录一下,注释的不够细致,无甚价值。
1. cvHaarDetectObjectsForROC
1 CvSeq*
2 cvHaarDetectObjectsForROC(
const CvArr* _img,
3 CvHaarClassif ierCascade* cascade, CvMemStorage* storage,
4 std::vector<
int>& rejectLevels, std::vector<
double>& levelWeights,
5
double scaleFactor,
int minNeighbors,
int flags,
6 CvSize minSize, CvSize maxSize,
bool outputRejectLevels )
7 {
8
const
double GROUP_EPS =
0.2;
9 CvMat stub, *img = (CvMat*)_img;
10 cv::Ptr<CvMat> temp, sum, tilted, sqsum, normImg, sumcanny, imgSmall;
11 CvSeq* result_seq =
0;
12 cv::Ptr<CvMemStorage> temp_storage;
13
14 cv::ConcurrentRectVector allCandidates;
15 std::vector<cv::Rect> rectList;
16 std::vector<
int> rweights;
17
double factor;
18
int coi;
19
bool doCanny Pruning = (flags & CV_HAAR_DO_CANNY_PRUNING) !=
0;
//
平滑区域过滤标识
20
bool findBiggestObject = (flags & CV_HAAR_FIND_BIGGEST_OBJECT) !=
0;
//
返回最大目标标识
21
bool roughSearch = (flags & CV_HAAR_DO_ROUGH_SEARCH) !=
0;
//
只返回第一个目标标识
22
23
//
错误
24
if( !CV_IS_HAAR_CLASSIFIER(cascade) )
25 CV_Error( !cascade ? CV_StsNullPtr : CV_StsBadArg,
"
Invalid classifier cascade
" );
26
27
if( !storage )
28 CV_Error( CV_StsNullPtr,
"
Null storage pointer
" );
29
30 img = cvGetMat( img, &stub, &coi );
31
if( coi )
32 CV_Error( CV_BadCOI,
"
COI is not supported
" );
33
34
if( CV_MAT_DEPTH(img->type) != CV_8U )
35 CV_Error( CV_StsUnsupportedFormat,
"
Only 8-bit images are supported
" );
36
37
if( scaleFactor <=
1 )
38 CV_Error( CV_StsOutOfRange,
"
scale factor must be > 1
" );
39
40
//
如果存在查找最大目标属性则去除SCALE_IMAGE属性
41
if( findBiggestObject )
42 flags &= ~CV_HAAR_SCALE_IMAGE;
43
44
if( maxSize.height ==
0 || maxSize.width ==
0 )
45 {
46 maxSize.height = img->rows;
47 maxSize.width = img->cols;
48 }
49
50 temp = cvCreateMat( img->rows, img->cols, CV_8 UC1 );
51
//
注意这里rows+1.cols+1是为了建立一个0值的缓存区以提高计算效率
52
sum = cvCreateMat( img->rows +
1, img->cols +
1, CV_32SC1 );
//
积分图求和的结果
53
sqsum = cvCreateMat( img->rows +
1, img->cols +
1, CV_64FC1 );
//
积分图求和的平方的结果
54
55
if( !cascade->hid_cascade )
56 icvCreateHidHaarClassifierCascade(cascade);;
//
创建级联分类器
57
58
if( cascade->hid_cascade->has_tilted_features )
59 tilted = cvCreateMat( img->rows +
1, img->cols +
1, CV_32SC1 );
//
积分图求和并倾斜45度的结果
60
61 result_seq = cvCreateSeq(
0,
sizeof(CvSeq),
sizeof(CvAvg Comp), storage );
//
创建用于存放检测结果的数组
62
63
if( CV_MAT_CN(img->type) >
1 )
64 {
65 cvCvtColor( img, temp, CV_BGR2GRAY );
//
灰度处理
66
img = temp;
67 }
68
//
如果存在查找最大目标属性则去除SCALE_IMAGE和平滑过滤属性
69
if( findBiggestObject )
70 flags &= ~(CV_HAAR_SCALE_IMAGE|CV_HAAR_DO_CANNY_PRUNING);
71
72
//
缩放图片而不改变检测窗口大小
73
/*
if( flags & CV_HAAR_SCALE_IMAGE )
74
{
75
CvSize winSize0 = cascade->orig_window_size;
76
#ifdef HAVE_IPP
77
int use_ipp = cascade->hid_cascade->ipp_stages != 0;
78
79
if( use_ipp )
80
normImg = cvCreateMat( img->rows, img->cols, CV_32FC1 );
81
#endif
82
imgSmall = cvCreateMat( img->rows + 1, img->cols + 1, CV_8UC1 );
83
84
for( factor = 1; ; factor *= scaleFactor )
85
{
86
CvSize winSize = { cvRound(winSize0.width*factor),
87
cvRound(winSize0.height*factor) };
88
CvSize sz = { cvRound( img->cols/factor ), cvRound( img->rows/factor ) };
89
CvSize sz1 = { sz.width - winSize0.width + 1, sz.height - winSize0.height + 1 };
90
91
CvRect equRect = { icv_object_win_border, icv_object_win_border,
92
winSize0.width - icv_object_win_border*2,
93
winSize0.height - icv_object_win_border*2 };
94
95
CvMat img1, sum1, sqsum1, norm1, tilted1, mask1;
96
CvMat* _tilted = 0;
97
98
if( sz1.width <= 0 || sz1.height <= 0 )
99
break;
100
if( winSize.width > maxSize.width || winSize.height > maxSize.height )
101
break;
102
if( winSize.width < minSize.width || winSize.height < minSize.height )
103
continue;
104
105
img1 = cvMat( sz.height, sz.width, CV_8UC1, imgSmall->data.ptr );
106
sum1 = cvMat( sz.height+1, sz.width+1, CV_32SC1, sum->data.ptr );
107
sqsum1 = cvMat( sz.height+1, sz.width+1, CV_64FC1, sqsum->data.ptr );
108
if( tilted )
109
{
110
tilted1 = cvMat( sz.height+1, sz.width+1, CV_32SC1, tilted->data.ptr );
111
_tilted = &tilted1;
112
}
113
norm1 = cvMat( sz1.height, sz1.width, CV_32FC1, normImg ? normImg->data.ptr : 0 );
114
mask1 = cvMat( sz1.height, sz1.width, CV_8UC1, temp->data.ptr );
115
116
cvResize( img, &img1, CV_INTER_LINEAR );
117
cvIntegral( &img1, &sum1, &sqsum1, _tilted );
118
119
int ystep = factor > 2 ? 1 : 2;
120
#ifdef HAVE_TBB
121
const int LOCS_PER_THREAD = 1000;
122
int stripCount = ((sz1.width/ystep)*(sz1.height + ystep-1)/ystep + LOCS_PER_THREAD/2)/LOCS_PER_THREAD;
123
stripCount = std::min(std::max(stripCount, 1), 100);
124
#else
125
const int stripCount = 1;
126
#endif
127
128
#ifdef HAVE_IPP
129
if( use_ipp )
130
{
131
cv::Mat fsum(sum1.rows, sum1.cols, CV_32F, sum1.data.ptr, sum1.step);
132
cv::Mat(&sum1).convertTo(fsum, CV_32F, 1, -(1<<24));
133
}
134
else
135
#endif
136
cvSetImagesForHaarClassifierCascade( cascade, &sum1, &sqsum1, _tilted, 1. );
137
138
cv::Mat _norm1(&norm1), _mask1(&mask1);
139
cv::parallel_for(cv::BlockedRange(0, stripCount),
140
cv::HaarDetectObjects_ScaleImage_Invoker(cascade,
141
(((sz1.height + stripCount - 1)/stripCount + ystep-1)/ystep)*ystep,
142
factor, cv::Mat(&sum1), cv::Mat(&sqsum1), &_norm1, &_mask1,
143
cv::Rect(equRect), allCandidates, rejectLevels, levelWeights, outputRejectLevels));
144
}
145
}
146
else
*/
147 {
148
int n_factors =
0;
//
代表检测窗口有多少种尺寸
149
cv::Rect scanROI;
//
感兴趣区域
150
151 cvIntegral( img, sum, sqsum, tilted );
//
积分图计算
152
153
//
存在平滑过滤属性
154
if( doCannyPruning )
155 {
156 sumcanny = cvCreateMat( img->rows +
1, img->cols +
1, CV_32SC1 );
157 cvCanny( img, temp,
0,
50,
3 );
158 cvIntegral( temp, sumcanny );
159 }
160
161
//
得到检测窗口有多少种尺寸,即n_factors值
162
for( n_factors =
0, factor =
1;
163 factor*cascade->orig_window_size.width < img->cols -
10 &&
164 factor*cascade->orig_window_size.height < img->rows -
10;
165 n_factors++, factor *= scaleFactor )
166 ;
167
//
初始化检测窗口的比例
168
if( findBiggestObject )
169 {
170 scaleFactor =
1./scaleFactor;
171 factor *= scaleFactor;
172 }
173
else
174 factor =
1;
175
176
for( ; n_factors-- >
0; factor *= scaleFactor )
177 {
178
const
double ystep = std::max(
2., factor );
179
//
得到新的检测窗口size
180
CvSize winSize = { cvRound( cascade->orig_window_size.width * factor ),
181 cvRound( cascade->orig_window_size.height * factor )};
182 CvRect equRect = {
0,
0,
0,
0 };
183
int *p[
4] = {
0,
0,
0,
0};
184
int *pq[
4] = {
0,
0,
0,
0};
185
int startX =
0, startY =
0;
186
int endX = cvRound((img->cols - winSize.width) / ystep);
187
int endY = cvRound((img->rows - winSize.height) / ystep);
188
189
//
忽略小于最小窗口限制的检测窗口size
190
if( winSize.width < minSize.width || winSize.height < minSize.height )
191 {
192
if( findBiggestObject )
193
break;
194
continue;
195 }
196
197
//
为分类器设置积分图及检测窗口比例
198
cvSetImagesForHaarClassifierCascade( cascade, sum, sqsum, tilted, factor );
199 cvZero( temp );
200
201
if( doCannyPruning )
202 {
203 equRect.x = cvRound(winSize.width*
0.15);
204 equRect.y = cvRound(winSize.height*
0.15);
205 equRect.width = cvRound(winSize.width*
0.7);
206 equRect.height = cvRound(winSize.height*
0.7);
207
208 p[
0] = (
int*)(sumcanny->data.ptr + equRect.y*sumcanny->step) + equRect.x;
209 p[
1] = (
int*)(sumcanny->data.ptr + equRect.y*sumcanny->step)
210 + equRect.x + equRect.width;
211 p[
2] = (
int*)(sumcanny->data.ptr + (equRect.y + equRect.height)*sumcanny->step) + equRect.x;
212 p[
3] = (
int*)(sumcanny->data.ptr + (equRect.y + equRect.height)*sumcanny->step)
213 + equRect.x + equRect.width;
214
215 pq[
0] = (
int*)(sum->data.ptr + equRect.y*sum->step) + equRect.x;
216 pq[
1] = (
int*)(sum->data.ptr + equRect.y*sum->step)
217 + equRect.x + equRect.width;
218 pq[
2] = (
int*)(sum->data.ptr + (equRect.y + equRect.height)*sum->step) + equRect.x;
219 pq[
3] = (
int*)(sum->data.ptr + (equRect.y + equRect.height)*sum->step)
220 + equRect.x + equRect.width;
221 }
222
223
//
根据ROI重置
224
if( scanROI.area() >
0 )
225 {
226
//
adjust start_height and stop_height
227
startY = cvRound(scanROI.y / ystep);
228 endY = cvRound((scanROI.y + scanROI.height - winSize.height) / ystep);
229
230 startX = cvRound(scanROI.x / ystep);
231 endX = cvRound((scanROI.x + scanROI.width - winSize.width) / ystep);
232 }
233
//
parallel_for?并行运算?
234
//
在当前尺寸下对每一个位置进行分类器的检测
235
cv::parallel_for(cv::BlockedRange(startY, endY),
236 cv::HaarDetectObjects_ScaleCascade_Invoker(cascade, winSize, cv::Range(startX, endX),
237 ystep, sum->step, (
const
int**)p,
238 (
const
int**)pq, allCandidates ));
239
240
if( findBiggestObject && !allCandidates.empty() && scanROI.area() ==
0 )
241 {
242 rectList.resize(allCandidates.size());
243 std::copy(allCandidates.begin(), allCandidates.end(), rectList.begin());
244
245 groupRectangles(rectList, std::max(minNeighbors,
1), GROUP_EPS);
246
247
if( !rectList.empty() )
248 {
249 size_t i, sz = rectList.size();
250 cv::Rect maxRect;
251
252
for( i =
0; i < sz; i++ )
253 {
254
if( rectList[i].area() > maxRect.area() )
255 maxRect = rectList[i];
256 }
257
258 allCandidates.push_back(maxRect);
259
260 scanROI = maxRect;
261
int dx = cvRound(maxRect.width*GROUP_EPS);
262
int dy = cvRound(maxRect.height*GROUP_EPS);
263 scanROI.x = std::max(scanROI.x - dx,
0);
264 scanROI.y = std::max(scanROI.y - dy,
0);
265 scanROI.width = std::min(scanROI.width + dx*
2, img->cols-
1-scanROI.x);
266 scanROI.height = std::min(scanROI.height + dy*
2, img->rows-
1-scanROI.y);
267
268
double minScale = roughSearch ?
0.6 :
0.4;
269 minSize.width = cvRound(maxRect.width*minScale);
270 minSize.height = cvRound(maxRect.height*minScale);
271 }
272 }
273 }
274 }
275
276 rectList.resize(allCandidates.size());
277
if(!allCandidates.empty())
278 std::copy(allCandidates.begin(), allCandidates.end(), rectList.begin());
279
280
//
相邻重复检测区域进行组合
281
if( minNeighbors !=
0 || findBiggestObject )
282 {
283
if( outputRejectLevels )
284 {
285 groupRectangles(rectList, rejectLevels, levelWeights, minNeighbors, GROUP_EPS );
286 }
287
else
288 {
289 groupRectangles(rectList, rweights, std::max(minNeighbors,
1), GROUP_EPS);
290 }
291 }
292
else
293 rweights.resize(rectList.size(),
0);
294
295
//
获得检测结果
296
if( findBiggestObject && rectList.size() )
297 {
298 CvAvgComp result_comp = {{
0,
0,
0,
0},
0};
299
300
for( size_t i =
0; i < rectList.size(); i++ )
301 {
302 cv::Rect r = rectList[i];
303
if( r.area() > cv::Rect(result_comp.rect).area() )
304 {
305 result_comp.rect = r;
306 result_comp.neighbors = rweights[i];
307 }
308 }
309 cvSeqPush( result_seq, &result_comp );
310 }
311
else
312 {
313
for( size_t i =
0; i < rectList.size(); i++ )
314 {
315 CvAvgComp c;
316 c.rect = rectList[i];
317 c.neighbors = !rweights.empty() ? rweights[i] :
0;
318 cvSeqPush( result_seq, &c );
319 }
320 }
321
322
return result_seq;
323 }