Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/objdetect/src/cascadedetect.cpp
16337 views
1
/*M///////////////////////////////////////////////////////////////////////////////////////
2
//
3
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4
//
5
// By downloading, copying, installing or using the software you agree to this license.
6
// If you do not agree to this license, do not download, install,
7
// copy or use the software.
8
//
9
//
10
// License Agreement
11
// For Open Source Computer Vision Library
12
//
13
// Copyright (C) 2008-2013, Itseez Inc., all rights reserved.
14
// Third party copyrights are property of their respective owners.
15
//
16
// Redistribution and use in source and binary forms, with or without modification,
17
// are permitted provided that the following conditions are met:
18
//
19
// * Redistribution's of source code must retain the above copyright notice,
20
// this list of conditions and the following disclaimer.
21
//
22
// * Redistribution's in binary form must reproduce the above copyright notice,
23
// this list of conditions and the following disclaimer in the documentation
24
// and/or other materials provided with the distribution.
25
//
26
// * The name of Itseez Inc. may not be used to endorse or promote products
27
// derived from this software without specific prior written permission.
28
//
29
// This software is provided by the copyright holders and contributors "as is" and
30
// any express or implied warranties, including, but not limited to, the implied
31
// warranties of merchantability and fitness for a particular purpose are disclaimed.
32
// In no event shall the copyright holders or contributors be liable for any direct,
33
// indirect, incidental, special, exemplary, or consequential damages
34
// (including, but not limited to, procurement of substitute goods or services;
35
// loss of use, data, or profits; or business interruption) however caused
36
// and on any theory of liability, whether in contract, strict liability,
37
// or tort (including negligence or otherwise) arising in any way out of
38
// the use of this software, even if advised of the possibility of such damage.
39
//
40
//M*/
41
42
#include "precomp.hpp"
43
#include <cstdio>
44
#include <iostream>
45
46
#include "cascadedetect.hpp"
47
#include "opencv2/objdetect/objdetect_c.h"
48
#include "opencl_kernels_objdetect.hpp"
49
50
namespace cv
51
{
52
53
template<typename _Tp> void copyVectorToUMat(const std::vector<_Tp>& v, UMat& um)
54
{
55
if(v.empty())
56
um.release();
57
Mat(1, (int)(v.size()*sizeof(v[0])), CV_8U, (void*)&v[0]).copyTo(um);
58
}
59
60
void groupRectangles(std::vector<Rect>& rectList, int groupThreshold, double eps,
61
std::vector<int>* weights, std::vector<double>* levelWeights)
62
{
63
CV_INSTRUMENT_REGION();
64
65
if( groupThreshold <= 0 || rectList.empty() )
66
{
67
if( weights && !levelWeights )
68
{
69
size_t i, sz = rectList.size();
70
weights->resize(sz);
71
for( i = 0; i < sz; i++ )
72
(*weights)[i] = 1;
73
}
74
return;
75
}
76
77
std::vector<int> labels;
78
int nclasses = partition(rectList, labels, SimilarRects(eps));
79
80
std::vector<Rect> rrects(nclasses);
81
std::vector<int> rweights(nclasses, 0);
82
std::vector<int> rejectLevels(nclasses, 0);
83
std::vector<double> rejectWeights(nclasses, DBL_MIN);
84
int i, j, nlabels = (int)labels.size();
85
for( i = 0; i < nlabels; i++ )
86
{
87
int cls = labels[i];
88
rrects[cls].x += rectList[i].x;
89
rrects[cls].y += rectList[i].y;
90
rrects[cls].width += rectList[i].width;
91
rrects[cls].height += rectList[i].height;
92
rweights[cls]++;
93
}
94
95
bool useDefaultWeights = false;
96
97
if ( levelWeights && weights && !weights->empty() && !levelWeights->empty() )
98
{
99
for( i = 0; i < nlabels; i++ )
100
{
101
int cls = labels[i];
102
if( (*weights)[i] > rejectLevels[cls] )
103
{
104
rejectLevels[cls] = (*weights)[i];
105
rejectWeights[cls] = (*levelWeights)[i];
106
}
107
else if( ( (*weights)[i] == rejectLevels[cls] ) && ( (*levelWeights)[i] > rejectWeights[cls] ) )
108
rejectWeights[cls] = (*levelWeights)[i];
109
}
110
}
111
else
112
useDefaultWeights = true;
113
114
for( i = 0; i < nclasses; i++ )
115
{
116
Rect r = rrects[i];
117
float s = 1.f/rweights[i];
118
rrects[i] = Rect(saturate_cast<int>(r.x*s),
119
saturate_cast<int>(r.y*s),
120
saturate_cast<int>(r.width*s),
121
saturate_cast<int>(r.height*s));
122
}
123
124
rectList.clear();
125
if( weights )
126
weights->clear();
127
if( levelWeights )
128
levelWeights->clear();
129
130
for( i = 0; i < nclasses; i++ )
131
{
132
Rect r1 = rrects[i];
133
int n1 = rweights[i];
134
double w1 = rejectWeights[i];
135
int l1 = rejectLevels[i];
136
137
// filter out rectangles which don't have enough similar rectangles
138
if( n1 <= groupThreshold )
139
continue;
140
// filter out small face rectangles inside large rectangles
141
for( j = 0; j < nclasses; j++ )
142
{
143
int n2 = rweights[j];
144
145
if( j == i || n2 <= groupThreshold )
146
continue;
147
Rect r2 = rrects[j];
148
149
int dx = saturate_cast<int>( r2.width * eps );
150
int dy = saturate_cast<int>( r2.height * eps );
151
152
if( i != j &&
153
r1.x >= r2.x - dx &&
154
r1.y >= r2.y - dy &&
155
r1.x + r1.width <= r2.x + r2.width + dx &&
156
r1.y + r1.height <= r2.y + r2.height + dy &&
157
(n2 > std::max(3, n1) || n1 < 3) )
158
break;
159
}
160
161
if( j == nclasses )
162
{
163
rectList.push_back(r1);
164
if( weights )
165
weights->push_back(useDefaultWeights ? n1 : l1);
166
if( levelWeights )
167
levelWeights->push_back(w1);
168
}
169
}
170
}
171
172
class MeanshiftGrouping
173
{
174
public:
175
MeanshiftGrouping(const Point3d& densKer, const std::vector<Point3d>& posV,
176
const std::vector<double>& wV, double eps, int maxIter = 20)
177
{
178
densityKernel = densKer;
179
weightsV = wV;
180
positionsV = posV;
181
positionsCount = (int)posV.size();
182
meanshiftV.resize(positionsCount);
183
distanceV.resize(positionsCount);
184
iterMax = maxIter;
185
modeEps = eps;
186
187
for (unsigned i = 0; i<positionsV.size(); i++)
188
{
189
meanshiftV[i] = getNewValue(positionsV[i]);
190
distanceV[i] = moveToMode(meanshiftV[i]);
191
meanshiftV[i] -= positionsV[i];
192
}
193
}
194
195
void getModes(std::vector<Point3d>& modesV, std::vector<double>& resWeightsV, const double eps)
196
{
197
for (size_t i=0; i <distanceV.size(); i++)
198
{
199
bool is_found = false;
200
for(size_t j=0; j<modesV.size(); j++)
201
{
202
if ( getDistance(distanceV[i], modesV[j]) < eps)
203
{
204
is_found=true;
205
break;
206
}
207
}
208
if (!is_found)
209
{
210
modesV.push_back(distanceV[i]);
211
}
212
}
213
214
resWeightsV.resize(modesV.size());
215
216
for (size_t i=0; i<modesV.size(); i++)
217
{
218
resWeightsV[i] = getResultWeight(modesV[i]);
219
}
220
}
221
222
protected:
223
std::vector<Point3d> positionsV;
224
std::vector<double> weightsV;
225
226
Point3d densityKernel;
227
int positionsCount;
228
229
std::vector<Point3d> meanshiftV;
230
std::vector<Point3d> distanceV;
231
int iterMax;
232
double modeEps;
233
234
Point3d getNewValue(const Point3d& inPt) const
235
{
236
Point3d resPoint(.0);
237
Point3d ratPoint(.0);
238
for (size_t i=0; i<positionsV.size(); i++)
239
{
240
Point3d aPt= positionsV[i];
241
Point3d bPt = inPt;
242
Point3d sPt = densityKernel;
243
244
sPt.x *= std::exp(aPt.z);
245
sPt.y *= std::exp(aPt.z);
246
247
aPt.x /= sPt.x;
248
aPt.y /= sPt.y;
249
aPt.z /= sPt.z;
250
251
bPt.x /= sPt.x;
252
bPt.y /= sPt.y;
253
bPt.z /= sPt.z;
254
255
double w = (weightsV[i])*std::exp(-((aPt-bPt).dot(aPt-bPt))/2)/std::sqrt(sPt.dot(Point3d(1,1,1)));
256
257
resPoint += w*aPt;
258
259
ratPoint.x += w/sPt.x;
260
ratPoint.y += w/sPt.y;
261
ratPoint.z += w/sPt.z;
262
}
263
resPoint.x /= ratPoint.x;
264
resPoint.y /= ratPoint.y;
265
resPoint.z /= ratPoint.z;
266
return resPoint;
267
}
268
269
double getResultWeight(const Point3d& inPt) const
270
{
271
double sumW=0;
272
for (size_t i=0; i<positionsV.size(); i++)
273
{
274
Point3d aPt = positionsV[i];
275
Point3d sPt = densityKernel;
276
277
sPt.x *= std::exp(aPt.z);
278
sPt.y *= std::exp(aPt.z);
279
280
aPt -= inPt;
281
282
aPt.x /= sPt.x;
283
aPt.y /= sPt.y;
284
aPt.z /= sPt.z;
285
286
sumW+=(weightsV[i])*std::exp(-(aPt.dot(aPt))/2)/std::sqrt(sPt.dot(Point3d(1,1,1)));
287
}
288
return sumW;
289
}
290
291
Point3d moveToMode(Point3d aPt) const
292
{
293
Point3d bPt;
294
for (int i = 0; i<iterMax; i++)
295
{
296
bPt = aPt;
297
aPt = getNewValue(bPt);
298
if ( getDistance(aPt, bPt) <= modeEps )
299
{
300
break;
301
}
302
}
303
return aPt;
304
}
305
306
double getDistance(Point3d p1, Point3d p2) const
307
{
308
Point3d ns = densityKernel;
309
ns.x *= std::exp(p2.z);
310
ns.y *= std::exp(p2.z);
311
p2 -= p1;
312
p2.x /= ns.x;
313
p2.y /= ns.y;
314
p2.z /= ns.z;
315
return p2.dot(p2);
316
}
317
};
318
//new grouping function with using meanshift
319
static void groupRectangles_meanshift(std::vector<Rect>& rectList, double detectThreshold, std::vector<double>& foundWeights,
320
std::vector<double>& scales, Size winDetSize)
321
{
322
int detectionCount = (int)rectList.size();
323
std::vector<Point3d> hits(detectionCount), resultHits;
324
std::vector<double> hitWeights(detectionCount), resultWeights;
325
Point2d hitCenter;
326
327
for (int i=0; i < detectionCount; i++)
328
{
329
hitWeights[i] = foundWeights[i];
330
hitCenter = (rectList[i].tl() + rectList[i].br())*(0.5); //center of rectangles
331
hits[i] = Point3d(hitCenter.x, hitCenter.y, std::log(scales[i]));
332
}
333
334
rectList.clear();
335
foundWeights.clear();
336
337
double logZ = std::log(1.3);
338
Point3d smothing(8, 16, logZ);
339
340
MeanshiftGrouping msGrouping(smothing, hits, hitWeights, 1e-5, 100);
341
342
msGrouping.getModes(resultHits, resultWeights, 1);
343
344
for (unsigned i=0; i < resultHits.size(); ++i)
345
{
346
347
double scale = std::exp(resultHits[i].z);
348
hitCenter.x = resultHits[i].x;
349
hitCenter.y = resultHits[i].y;
350
Size s( int(winDetSize.width * scale), int(winDetSize.height * scale) );
351
Rect resultRect( int(hitCenter.x-s.width/2), int(hitCenter.y-s.height/2),
352
int(s.width), int(s.height) );
353
354
if (resultWeights[i] > detectThreshold)
355
{
356
rectList.push_back(resultRect);
357
foundWeights.push_back(resultWeights[i]);
358
}
359
}
360
}
361
362
void groupRectangles(std::vector<Rect>& rectList, int groupThreshold, double eps)
363
{
364
CV_INSTRUMENT_REGION();
365
366
groupRectangles(rectList, groupThreshold, eps, 0, 0);
367
}
368
369
void groupRectangles(std::vector<Rect>& rectList, std::vector<int>& weights, int groupThreshold, double eps)
370
{
371
CV_INSTRUMENT_REGION();
372
373
groupRectangles(rectList, groupThreshold, eps, &weights, 0);
374
}
375
//used for cascade detection algorithm for ROC-curve calculating
376
void groupRectangles(std::vector<Rect>& rectList, std::vector<int>& rejectLevels,
377
std::vector<double>& levelWeights, int groupThreshold, double eps)
378
{
379
CV_INSTRUMENT_REGION();
380
381
groupRectangles(rectList, groupThreshold, eps, &rejectLevels, &levelWeights);
382
}
383
//can be used for HOG detection algorithm only
384
void groupRectangles_meanshift(std::vector<Rect>& rectList, std::vector<double>& foundWeights,
385
std::vector<double>& foundScales, double detectThreshold, Size winDetSize)
386
{
387
CV_INSTRUMENT_REGION();
388
389
groupRectangles_meanshift(rectList, detectThreshold, foundWeights, foundScales, winDetSize);
390
}
391
392
393
FeatureEvaluator::~FeatureEvaluator() {}
394
395
bool FeatureEvaluator::read(const FileNode&, Size _origWinSize)
396
{
397
origWinSize = _origWinSize;
398
localSize = lbufSize = Size(0, 0);
399
if (scaleData.empty())
400
scaleData = makePtr<std::vector<ScaleData> >();
401
else
402
scaleData->clear();
403
return true;
404
}
405
406
Ptr<FeatureEvaluator> FeatureEvaluator::clone() const { return Ptr<FeatureEvaluator>(); }
407
int FeatureEvaluator::getFeatureType() const {return -1;}
408
bool FeatureEvaluator::setWindow(Point, int) { return true; }
409
void FeatureEvaluator::getUMats(std::vector<UMat>& bufs)
410
{
411
if (!(sbufFlag & USBUF_VALID))
412
{
413
sbuf.copyTo(usbuf);
414
sbufFlag |= USBUF_VALID;
415
}
416
417
bufs.clear();
418
bufs.push_back(uscaleData);
419
bufs.push_back(usbuf);
420
bufs.push_back(ufbuf);
421
}
422
423
void FeatureEvaluator::getMats()
424
{
425
if (!(sbufFlag & SBUF_VALID))
426
{
427
usbuf.copyTo(sbuf);
428
sbufFlag |= SBUF_VALID;
429
}
430
}
431
432
float FeatureEvaluator::calcOrd(int) const { return 0.; }
433
int FeatureEvaluator::calcCat(int) const { return 0; }
434
435
bool FeatureEvaluator::updateScaleData( Size imgsz, const std::vector<float>& _scales )
436
{
437
if( scaleData.empty() )
438
scaleData = makePtr<std::vector<ScaleData> >();
439
440
size_t i, nscales = _scales.size();
441
bool recalcOptFeatures = nscales != scaleData->size();
442
scaleData->resize(nscales);
443
444
int layer_dy = 0;
445
Point layer_ofs(0,0);
446
Size prevBufSize = sbufSize;
447
sbufSize.width = std::max(sbufSize.width, (int)alignSize(cvRound(imgsz.width/_scales[0]) + 31, 32));
448
recalcOptFeatures = recalcOptFeatures || sbufSize.width != prevBufSize.width;
449
450
for( i = 0; i < nscales; i++ )
451
{
452
FeatureEvaluator::ScaleData& s = scaleData->at(i);
453
if( !recalcOptFeatures && fabs(s.scale - _scales[i]) > FLT_EPSILON*100*_scales[i] )
454
recalcOptFeatures = true;
455
float sc = _scales[i];
456
Size sz;
457
sz.width = cvRound(imgsz.width/sc);
458
sz.height = cvRound(imgsz.height/sc);
459
s.ystep = sc >= 2 ? 1 : 2;
460
s.scale = sc;
461
s.szi = Size(sz.width+1, sz.height+1);
462
463
if( i == 0 )
464
{
465
layer_dy = s.szi.height;
466
}
467
468
if( layer_ofs.x + s.szi.width > sbufSize.width )
469
{
470
layer_ofs = Point(0, layer_ofs.y + layer_dy);
471
layer_dy = s.szi.height;
472
}
473
s.layer_ofs = layer_ofs.y*sbufSize.width + layer_ofs.x;
474
layer_ofs.x += s.szi.width;
475
}
476
477
layer_ofs.y += layer_dy;
478
sbufSize.height = std::max(sbufSize.height, layer_ofs.y);
479
recalcOptFeatures = recalcOptFeatures || sbufSize.height != prevBufSize.height;
480
return recalcOptFeatures;
481
}
482
483
484
bool FeatureEvaluator::setImage( InputArray _image, const std::vector<float>& _scales )
485
{
486
CV_INSTRUMENT_REGION();
487
488
Size imgsz = _image.size();
489
bool recalcOptFeatures = updateScaleData(imgsz, _scales);
490
491
size_t i, nscales = scaleData->size();
492
if (nscales == 0)
493
{
494
return false;
495
}
496
Size sz0 = scaleData->at(0).szi;
497
sz0 = Size(std::max(rbuf.cols, (int)alignSize(sz0.width, 16)), std::max(rbuf.rows, sz0.height));
498
499
if (recalcOptFeatures)
500
{
501
computeOptFeatures();
502
copyVectorToUMat(*scaleData, uscaleData);
503
}
504
505
if (_image.isUMat() && !localSize.empty())
506
{
507
usbuf.create(sbufSize.height*nchannels, sbufSize.width, CV_32S);
508
urbuf.create(sz0, CV_8U);
509
510
for (i = 0; i < nscales; i++)
511
{
512
const ScaleData& s = scaleData->at(i);
513
UMat dst(urbuf, Rect(0, 0, s.szi.width - 1, s.szi.height - 1));
514
resize(_image, dst, dst.size(), 1. / s.scale, 1. / s.scale, INTER_LINEAR_EXACT);
515
computeChannels((int)i, dst);
516
}
517
sbufFlag = USBUF_VALID;
518
}
519
else
520
{
521
Mat image = _image.getMat();
522
sbuf.create(sbufSize.height*nchannels, sbufSize.width, CV_32S);
523
rbuf.create(sz0, CV_8U);
524
525
for (i = 0; i < nscales; i++)
526
{
527
const ScaleData& s = scaleData->at(i);
528
Mat dst(s.szi.height - 1, s.szi.width - 1, CV_8U, rbuf.ptr());
529
resize(image, dst, dst.size(), 1. / s.scale, 1. / s.scale, INTER_LINEAR_EXACT);
530
computeChannels((int)i, dst);
531
}
532
sbufFlag = SBUF_VALID;
533
}
534
535
return true;
536
}
537
538
//---------------------------------------------- HaarEvaluator ---------------------------------------
539
540
bool HaarEvaluator::Feature :: read( const FileNode& node )
541
{
542
FileNode rnode = node[CC_RECTS];
543
FileNodeIterator it = rnode.begin(), it_end = rnode.end();
544
545
int ri;
546
for( ri = 0; ri < RECT_NUM; ri++ )
547
{
548
rect[ri].r = Rect();
549
rect[ri].weight = 0.f;
550
}
551
552
for(ri = 0; it != it_end; ++it, ri++)
553
{
554
FileNodeIterator it2 = (*it).begin();
555
it2 >> rect[ri].r.x >> rect[ri].r.y >>
556
rect[ri].r.width >> rect[ri].r.height >> rect[ri].weight;
557
}
558
559
tilted = (int)node[CC_TILTED] != 0;
560
return true;
561
}
562
563
HaarEvaluator::HaarEvaluator()
564
{
565
optfeaturesPtr = 0;
566
pwin = 0;
567
localSize = Size(4, 2);
568
lbufSize = Size(0, 0);
569
nchannels = 0;
570
tofs = 0;
571
sqofs = 0;
572
varianceNormFactor = 0;
573
hasTiltedFeatures = false;
574
}
575
576
HaarEvaluator::~HaarEvaluator()
577
{
578
}
579
580
bool HaarEvaluator::read(const FileNode& node, Size _origWinSize)
581
{
582
if (!FeatureEvaluator::read(node, _origWinSize))
583
return false;
584
size_t i, n = node.size();
585
CV_Assert(n > 0);
586
if(features.empty())
587
features = makePtr<std::vector<Feature> >();
588
if(optfeatures.empty())
589
optfeatures = makePtr<std::vector<OptFeature> >();
590
if (optfeatures_lbuf.empty())
591
optfeatures_lbuf = makePtr<std::vector<OptFeature> >();
592
features->resize(n);
593
FileNodeIterator it = node.begin();
594
hasTiltedFeatures = false;
595
std::vector<Feature>& ff = *features;
596
sbufSize = Size();
597
ufbuf.release();
598
599
for(i = 0; i < n; i++, ++it)
600
{
601
if(!ff[i].read(*it))
602
return false;
603
if( ff[i].tilted )
604
hasTiltedFeatures = true;
605
}
606
nchannels = hasTiltedFeatures ? 3 : 2;
607
normrect = Rect(1, 1, origWinSize.width - 2, origWinSize.height - 2);
608
609
localSize = lbufSize = Size(0, 0);
610
if (ocl::isOpenCLActivated())
611
{
612
if (ocl::Device::getDefault().isAMD() || ocl::Device::getDefault().isIntel() || ocl::Device::getDefault().isNVidia())
613
{
614
localSize = Size(8, 8);
615
lbufSize = Size(origWinSize.width + localSize.width,
616
origWinSize.height + localSize.height);
617
if (lbufSize.area() > 1024)
618
lbufSize = Size(0, 0);
619
}
620
}
621
622
return true;
623
}
624
625
Ptr<FeatureEvaluator> HaarEvaluator::clone() const
626
{
627
Ptr<HaarEvaluator> ret = makePtr<HaarEvaluator>();
628
*ret = *this;
629
return ret;
630
}
631
632
633
void HaarEvaluator::computeChannels(int scaleIdx, InputArray img)
634
{
635
CV_INSTRUMENT_REGION();
636
637
const ScaleData& s = scaleData->at(scaleIdx);
638
sqofs = hasTiltedFeatures ? sbufSize.area() * 2 : sbufSize.area();
639
640
if (img.isUMat())
641
{
642
int sx = s.layer_ofs % sbufSize.width;
643
int sy = s.layer_ofs / sbufSize.width;
644
int sqy = sy + (sqofs / sbufSize.width);
645
UMat sum(usbuf, Rect(sx, sy, s.szi.width, s.szi.height));
646
UMat sqsum(usbuf, Rect(sx, sqy, s.szi.width, s.szi.height));
647
sqsum.flags = (sqsum.flags & ~UMat::DEPTH_MASK) | CV_32S;
648
649
if (hasTiltedFeatures)
650
{
651
int sty = sy + (tofs / sbufSize.width);
652
UMat tilted(usbuf, Rect(sx, sty, s.szi.width, s.szi.height));
653
integral(img, sum, sqsum, tilted, CV_32S, CV_32S);
654
}
655
else
656
{
657
UMatData* u = sqsum.u;
658
integral(img, sum, sqsum, noArray(), CV_32S, CV_32S);
659
CV_Assert(sqsum.u == u && sqsum.size() == s.szi && sqsum.type()==CV_32S);
660
}
661
}
662
else
663
{
664
Mat sum(s.szi, CV_32S, sbuf.ptr<int>() + s.layer_ofs, sbuf.step);
665
Mat sqsum(s.szi, CV_32S, sum.ptr<int>() + sqofs, sbuf.step);
666
667
if (hasTiltedFeatures)
668
{
669
Mat tilted(s.szi, CV_32S, sum.ptr<int>() + tofs, sbuf.step);
670
integral(img, sum, sqsum, tilted, CV_32S, CV_32S);
671
}
672
else
673
integral(img, sum, sqsum, noArray(), CV_32S, CV_32S);
674
}
675
}
676
677
void HaarEvaluator::computeOptFeatures()
678
{
679
CV_INSTRUMENT_REGION();
680
681
if (hasTiltedFeatures)
682
tofs = sbufSize.area();
683
684
int sstep = sbufSize.width;
685
CV_SUM_OFS( nofs[0], nofs[1], nofs[2], nofs[3], 0, normrect, sstep );
686
687
size_t fi, nfeatures = features->size();
688
const std::vector<Feature>& ff = *features;
689
optfeatures->resize(nfeatures);
690
optfeaturesPtr = &(*optfeatures)[0];
691
for( fi = 0; fi < nfeatures; fi++ )
692
optfeaturesPtr[fi].setOffsets( ff[fi], sstep, tofs );
693
optfeatures_lbuf->resize(nfeatures);
694
695
for( fi = 0; fi < nfeatures; fi++ )
696
optfeatures_lbuf->at(fi).setOffsets(ff[fi], lbufSize.width > 0 ? lbufSize.width : sstep, tofs);
697
698
copyVectorToUMat(*optfeatures_lbuf, ufbuf);
699
}
700
701
bool HaarEvaluator::setWindow( Point pt, int scaleIdx )
702
{
703
const ScaleData& s = getScaleData(scaleIdx);
704
705
if( pt.x < 0 || pt.y < 0 ||
706
pt.x + origWinSize.width >= s.szi.width ||
707
pt.y + origWinSize.height >= s.szi.height )
708
return false;
709
710
pwin = &sbuf.at<int>(pt) + s.layer_ofs;
711
const int* pq = (const int*)(pwin + sqofs);
712
int valsum = CALC_SUM_OFS(nofs, pwin);
713
unsigned valsqsum = (unsigned)(CALC_SUM_OFS(nofs, pq));
714
715
double area = normrect.area();
716
double nf = area * valsqsum - (double)valsum * valsum;
717
if( nf > 0. )
718
{
719
nf = std::sqrt(nf);
720
varianceNormFactor = (float)(1./nf);
721
return area*varianceNormFactor < 1e-1;
722
}
723
else
724
{
725
varianceNormFactor = 1.f;
726
return false;
727
}
728
}
729
730
731
void HaarEvaluator::OptFeature::setOffsets( const Feature& _f, int step, int _tofs )
732
{
733
weight[0] = _f.rect[0].weight;
734
weight[1] = _f.rect[1].weight;
735
weight[2] = _f.rect[2].weight;
736
737
if( _f.tilted )
738
{
739
CV_TILTED_OFS( ofs[0][0], ofs[0][1], ofs[0][2], ofs[0][3], _tofs, _f.rect[0].r, step );
740
CV_TILTED_OFS( ofs[1][0], ofs[1][1], ofs[1][2], ofs[1][3], _tofs, _f.rect[1].r, step );
741
CV_TILTED_OFS( ofs[2][0], ofs[2][1], ofs[2][2], ofs[2][3], _tofs, _f.rect[2].r, step );
742
}
743
else
744
{
745
CV_SUM_OFS( ofs[0][0], ofs[0][1], ofs[0][2], ofs[0][3], 0, _f.rect[0].r, step );
746
CV_SUM_OFS( ofs[1][0], ofs[1][1], ofs[1][2], ofs[1][3], 0, _f.rect[1].r, step );
747
CV_SUM_OFS( ofs[2][0], ofs[2][1], ofs[2][2], ofs[2][3], 0, _f.rect[2].r, step );
748
}
749
}
750
751
Rect HaarEvaluator::getNormRect() const
752
{
753
return normrect;
754
}
755
756
int HaarEvaluator::getSquaresOffset() const
757
{
758
return sqofs;
759
}
760
761
//---------------------------------------------- LBPEvaluator -------------------------------------
762
bool LBPEvaluator::Feature :: read(const FileNode& node )
763
{
764
FileNode rnode = node[CC_RECT];
765
FileNodeIterator it = rnode.begin();
766
it >> rect.x >> rect.y >> rect.width >> rect.height;
767
return true;
768
}
769
770
LBPEvaluator::LBPEvaluator()
771
{
772
features = makePtr<std::vector<Feature> >();
773
optfeatures = makePtr<std::vector<OptFeature> >();
774
scaleData = makePtr<std::vector<ScaleData> >();
775
optfeaturesPtr = 0;
776
pwin = 0;
777
}
778
779
LBPEvaluator::~LBPEvaluator()
780
{
781
}
782
783
bool LBPEvaluator::read( const FileNode& node, Size _origWinSize )
784
{
785
if (!FeatureEvaluator::read(node, _origWinSize))
786
return false;
787
if(features.empty())
788
features = makePtr<std::vector<Feature> >();
789
if(optfeatures.empty())
790
optfeatures = makePtr<std::vector<OptFeature> >();
791
if (optfeatures_lbuf.empty())
792
optfeatures_lbuf = makePtr<std::vector<OptFeature> >();
793
794
features->resize(node.size());
795
optfeaturesPtr = 0;
796
FileNodeIterator it = node.begin(), it_end = node.end();
797
std::vector<Feature>& ff = *features;
798
for(int i = 0; it != it_end; ++it, i++)
799
{
800
if(!ff[i].read(*it))
801
return false;
802
}
803
nchannels = 1;
804
localSize = lbufSize = Size(0, 0);
805
if (ocl::isOpenCLActivated())
806
localSize = Size(8, 8);
807
808
return true;
809
}
810
811
Ptr<FeatureEvaluator> LBPEvaluator::clone() const
812
{
813
Ptr<LBPEvaluator> ret = makePtr<LBPEvaluator>();
814
*ret = *this;
815
return ret;
816
}
817
818
void LBPEvaluator::computeChannels(int scaleIdx, InputArray _img)
819
{
820
const ScaleData& s = scaleData->at(scaleIdx);
821
822
if (_img.isUMat())
823
{
824
int sx = s.layer_ofs % sbufSize.width;
825
int sy = s.layer_ofs / sbufSize.width;
826
UMat sum(usbuf, Rect(sx, sy, s.szi.width, s.szi.height));
827
integral(_img, sum, noArray(), noArray(), CV_32S);
828
}
829
else
830
{
831
Mat sum(s.szi, CV_32S, sbuf.ptr<int>() + s.layer_ofs, sbuf.step);
832
integral(_img, sum, noArray(), noArray(), CV_32S);
833
}
834
}
835
836
void LBPEvaluator::computeOptFeatures()
837
{
838
int sstep = sbufSize.width;
839
840
size_t fi, nfeatures = features->size();
841
const std::vector<Feature>& ff = *features;
842
optfeatures->resize(nfeatures);
843
optfeaturesPtr = &(*optfeatures)[0];
844
for( fi = 0; fi < nfeatures; fi++ )
845
optfeaturesPtr[fi].setOffsets( ff[fi], sstep );
846
copyVectorToUMat(*optfeatures, ufbuf);
847
}
848
849
850
void LBPEvaluator::OptFeature::setOffsets( const Feature& _f, int step )
851
{
852
Rect tr = _f.rect;
853
int w0 = tr.width;
854
int h0 = tr.height;
855
856
CV_SUM_OFS( ofs[0], ofs[1], ofs[4], ofs[5], 0, tr, step );
857
tr.x += 2*w0;
858
CV_SUM_OFS( ofs[2], ofs[3], ofs[6], ofs[7], 0, tr, step );
859
tr.y += 2*h0;
860
CV_SUM_OFS( ofs[10], ofs[11], ofs[14], ofs[15], 0, tr, step );
861
tr.x -= 2*w0;
862
CV_SUM_OFS( ofs[8], ofs[9], ofs[12], ofs[13], 0, tr, step );
863
}
864
865
866
bool LBPEvaluator::setWindow( Point pt, int scaleIdx )
867
{
868
CV_Assert(0 <= scaleIdx && scaleIdx < (int)scaleData->size());
869
const ScaleData& s = scaleData->at(scaleIdx);
870
871
if( pt.x < 0 || pt.y < 0 ||
872
pt.x + origWinSize.width >= s.szi.width ||
873
pt.y + origWinSize.height >= s.szi.height )
874
return false;
875
876
pwin = &sbuf.at<int>(pt) + s.layer_ofs;
877
return true;
878
}
879
880
881
Ptr<FeatureEvaluator> FeatureEvaluator::create( int featureType )
882
{
883
return featureType == HAAR ? Ptr<FeatureEvaluator>(new HaarEvaluator) :
884
featureType == LBP ? Ptr<FeatureEvaluator>(new LBPEvaluator) :
885
Ptr<FeatureEvaluator>();
886
}
887
888
//---------------------------------------- Classifier Cascade --------------------------------------------
889
890
CascadeClassifierImpl::CascadeClassifierImpl()
891
{
892
#ifdef HAVE_OPENCL
893
tryOpenCL = false;
894
#endif
895
}
896
897
CascadeClassifierImpl::~CascadeClassifierImpl()
898
{
899
}
900
901
bool CascadeClassifierImpl::empty() const
902
{
903
return !oldCascade && data.stages.empty();
904
}
905
906
bool CascadeClassifierImpl::load(const String& filename)
907
{
908
oldCascade.release();
909
data = Data();
910
featureEvaluator.release();
911
912
FileStorage fs(filename, FileStorage::READ);
913
if( !fs.isOpened() )
914
return false;
915
916
if( read_(fs.getFirstTopLevelNode()) )
917
return true;
918
919
fs.release();
920
921
oldCascade.reset((CvHaarClassifierCascade*)cvLoad(filename.c_str(), 0, 0, 0));
922
return !oldCascade.empty();
923
}
924
925
void CascadeClassifierImpl::read(const FileNode& node)
926
{
927
read_(node);
928
}
929
930
int CascadeClassifierImpl::runAt( Ptr<FeatureEvaluator>& evaluator, Point pt, int scaleIdx, double& weight )
931
{
932
CV_INSTRUMENT_REGION();
933
934
assert( !oldCascade &&
935
(data.featureType == FeatureEvaluator::HAAR ||
936
data.featureType == FeatureEvaluator::LBP ||
937
data.featureType == FeatureEvaluator::HOG) );
938
939
if( !evaluator->setWindow(pt, scaleIdx) )
940
return -1;
941
if( data.maxNodesPerTree == 1 )
942
{
943
if( data.featureType == FeatureEvaluator::HAAR )
944
return predictOrderedStump<HaarEvaluator>( *this, evaluator, weight );
945
else if( data.featureType == FeatureEvaluator::LBP )
946
return predictCategoricalStump<LBPEvaluator>( *this, evaluator, weight );
947
else
948
return -2;
949
}
950
else
951
{
952
if( data.featureType == FeatureEvaluator::HAAR )
953
return predictOrdered<HaarEvaluator>( *this, evaluator, weight );
954
else if( data.featureType == FeatureEvaluator::LBP )
955
return predictCategorical<LBPEvaluator>( *this, evaluator, weight );
956
else
957
return -2;
958
}
959
}
960
961
void CascadeClassifierImpl::setMaskGenerator(const Ptr<MaskGenerator>& _maskGenerator)
962
{
963
maskGenerator=_maskGenerator;
964
}
965
Ptr<CascadeClassifierImpl::MaskGenerator> CascadeClassifierImpl::getMaskGenerator()
966
{
967
return maskGenerator;
968
}
969
970
Ptr<BaseCascadeClassifier::MaskGenerator> createFaceDetectionMaskGenerator()
971
{
972
return Ptr<BaseCascadeClassifier::MaskGenerator>();
973
}
974
975
class CascadeClassifierInvoker : public ParallelLoopBody
976
{
977
public:
978
CascadeClassifierInvoker( CascadeClassifierImpl& _cc, int _nscales, int _nstripes,
979
const FeatureEvaluator::ScaleData* _scaleData,
980
const int* _stripeSizes, std::vector<Rect>& _vec,
981
std::vector<int>& _levels, std::vector<double>& _weights,
982
bool outputLevels, const Mat& _mask, Mutex* _mtx)
983
{
984
classifier = &_cc;
985
nscales = _nscales;
986
nstripes = _nstripes;
987
scaleData = _scaleData;
988
stripeSizes = _stripeSizes;
989
rectangles = &_vec;
990
rejectLevels = outputLevels ? &_levels : 0;
991
levelWeights = outputLevels ? &_weights : 0;
992
mask = _mask;
993
mtx = _mtx;
994
}
995
996
void operator()(const Range& range) const CV_OVERRIDE
997
{
998
CV_INSTRUMENT_REGION();
999
1000
Ptr<FeatureEvaluator> evaluator = classifier->featureEvaluator->clone();
1001
double gypWeight = 0.;
1002
Size origWinSize = classifier->data.origWinSize;
1003
1004
for( int scaleIdx = 0; scaleIdx < nscales; scaleIdx++ )
1005
{
1006
const FeatureEvaluator::ScaleData& s = scaleData[scaleIdx];
1007
float scalingFactor = s.scale;
1008
int yStep = s.ystep;
1009
int stripeSize = stripeSizes[scaleIdx];
1010
int y0 = range.start*stripeSize;
1011
Size szw = s.getWorkingSize(origWinSize);
1012
int y1 = std::min(range.end*stripeSize, szw.height);
1013
Size winSize(cvRound(origWinSize.width * scalingFactor),
1014
cvRound(origWinSize.height * scalingFactor));
1015
1016
for( int y = y0; y < y1; y += yStep )
1017
{
1018
for( int x = 0; x < szw.width; x += yStep )
1019
{
1020
int result = classifier->runAt(evaluator, Point(x, y), scaleIdx, gypWeight);
1021
if( rejectLevels )
1022
{
1023
if( result == 1 )
1024
result = -(int)classifier->data.stages.size();
1025
if( classifier->data.stages.size() + result == 0 )
1026
{
1027
mtx->lock();
1028
rectangles->push_back(Rect(cvRound(x*scalingFactor),
1029
cvRound(y*scalingFactor),
1030
winSize.width, winSize.height));
1031
rejectLevels->push_back(-result);
1032
levelWeights->push_back(gypWeight);
1033
mtx->unlock();
1034
}
1035
}
1036
else if( result > 0 )
1037
{
1038
mtx->lock();
1039
rectangles->push_back(Rect(cvRound(x*scalingFactor),
1040
cvRound(y*scalingFactor),
1041
winSize.width, winSize.height));
1042
mtx->unlock();
1043
}
1044
if( result == 0 )
1045
x += yStep;
1046
}
1047
}
1048
}
1049
}
1050
1051
CascadeClassifierImpl* classifier;
1052
std::vector<Rect>* rectangles;
1053
int nscales, nstripes;
1054
const FeatureEvaluator::ScaleData* scaleData;
1055
const int* stripeSizes;
1056
std::vector<int> *rejectLevels;
1057
std::vector<double> *levelWeights;
1058
std::vector<float> scales;
1059
Mat mask;
1060
Mutex* mtx;
1061
};
1062
1063
1064
struct getRect { Rect operator ()(const CvAvgComp& e) const { return e.rect; } };
1065
struct getNeighbors { int operator ()(const CvAvgComp& e) const { return e.neighbors; } };
1066
1067
#ifdef HAVE_OPENCL
1068
bool CascadeClassifierImpl::ocl_detectMultiScaleNoGrouping( const std::vector<float>& scales,
1069
std::vector<Rect>& candidates )
1070
{
1071
int featureType = getFeatureType();
1072
std::vector<UMat> bufs;
1073
featureEvaluator->getUMats(bufs);
1074
Size localsz = featureEvaluator->getLocalSize();
1075
if( localsz.empty() )
1076
return false;
1077
Size lbufSize = featureEvaluator->getLocalBufSize();
1078
size_t localsize[] = { (size_t)localsz.width, (size_t)localsz.height };
1079
const int grp_per_CU = 12;
1080
size_t globalsize[] = { grp_per_CU*ocl::Device::getDefault().maxComputeUnits()*localsize[0], localsize[1] };
1081
bool ok = false;
1082
1083
ufacepos.create(1, MAX_FACES*3+1, CV_32S);
1084
UMat ufacepos_count(ufacepos, Rect(0, 0, 1, 1));
1085
ufacepos_count.setTo(Scalar::all(0));
1086
1087
if( ustages.empty() )
1088
{
1089
copyVectorToUMat(data.stages, ustages);
1090
if (!data.stumps.empty())
1091
copyVectorToUMat(data.stumps, unodes);
1092
else
1093
copyVectorToUMat(data.nodes, unodes);
1094
copyVectorToUMat(data.leaves, uleaves);
1095
if( !data.subsets.empty() )
1096
copyVectorToUMat(data.subsets, usubsets);
1097
}
1098
1099
int nstages = (int)data.stages.size();
1100
int splitstage_ocl = 1;
1101
1102
if( featureType == FeatureEvaluator::HAAR )
1103
{
1104
Ptr<HaarEvaluator> haar = featureEvaluator.dynamicCast<HaarEvaluator>();
1105
if( haar.empty() )
1106
return false;
1107
1108
if( haarKernel.empty() )
1109
{
1110
String opts;
1111
if ( !lbufSize.empty() )
1112
opts = format("-D LOCAL_SIZE_X=%d -D LOCAL_SIZE_Y=%d -D SUM_BUF_SIZE=%d -D SUM_BUF_STEP=%d -D NODE_COUNT=%d -D SPLIT_STAGE=%d -D N_STAGES=%d -D MAX_FACES=%d -D HAAR",
1113
localsz.width, localsz.height, lbufSize.area(), lbufSize.width, data.maxNodesPerTree, splitstage_ocl, nstages, MAX_FACES);
1114
else
1115
opts = format("-D LOCAL_SIZE_X=%d -D LOCAL_SIZE_Y=%d -D NODE_COUNT=%d -D SPLIT_STAGE=%d -D N_STAGES=%d -D MAX_FACES=%d -D HAAR",
1116
localsz.width, localsz.height, data.maxNodesPerTree, splitstage_ocl, nstages, MAX_FACES);
1117
haarKernel.create("runHaarClassifier", ocl::objdetect::cascadedetect_oclsrc, opts);
1118
if( haarKernel.empty() )
1119
return false;
1120
}
1121
1122
Rect normrect = haar->getNormRect();
1123
int sqofs = haar->getSquaresOffset();
1124
1125
haarKernel.args((int)scales.size(),
1126
ocl::KernelArg::PtrReadOnly(bufs[0]), // scaleData
1127
ocl::KernelArg::ReadOnlyNoSize(bufs[1]), // sum
1128
ocl::KernelArg::PtrReadOnly(bufs[2]), // optfeatures
1129
1130
// cascade classifier
1131
ocl::KernelArg::PtrReadOnly(ustages),
1132
ocl::KernelArg::PtrReadOnly(unodes),
1133
ocl::KernelArg::PtrReadOnly(uleaves),
1134
1135
ocl::KernelArg::PtrWriteOnly(ufacepos), // positions
1136
normrect, sqofs, data.origWinSize);
1137
ok = haarKernel.run(2, globalsize, localsize, true);
1138
}
1139
else if( featureType == FeatureEvaluator::LBP )
1140
{
1141
if (data.maxNodesPerTree > 1)
1142
return false;
1143
1144
Ptr<LBPEvaluator> lbp = featureEvaluator.dynamicCast<LBPEvaluator>();
1145
if( lbp.empty() )
1146
return false;
1147
1148
if( lbpKernel.empty() )
1149
{
1150
String opts;
1151
if ( !lbufSize.empty() )
1152
opts = format("-D LOCAL_SIZE_X=%d -D LOCAL_SIZE_Y=%d -D SUM_BUF_SIZE=%d -D SUM_BUF_STEP=%d -D SPLIT_STAGE=%d -D N_STAGES=%d -D MAX_FACES=%d -D LBP",
1153
localsz.width, localsz.height, lbufSize.area(), lbufSize.width, splitstage_ocl, nstages, MAX_FACES);
1154
else
1155
opts = format("-D LOCAL_SIZE_X=%d -D LOCAL_SIZE_Y=%d -D SPLIT_STAGE=%d -D N_STAGES=%d -D MAX_FACES=%d -D LBP",
1156
localsz.width, localsz.height, splitstage_ocl, nstages, MAX_FACES);
1157
lbpKernel.create("runLBPClassifierStumpSimple", ocl::objdetect::cascadedetect_oclsrc, opts);
1158
if( lbpKernel.empty() )
1159
return false;
1160
}
1161
1162
int subsetSize = (data.ncategories + 31)/32;
1163
lbpKernel.args((int)scales.size(),
1164
ocl::KernelArg::PtrReadOnly(bufs[0]), // scaleData
1165
ocl::KernelArg::ReadOnlyNoSize(bufs[1]), // sum
1166
ocl::KernelArg::PtrReadOnly(bufs[2]), // optfeatures
1167
1168
// cascade classifier
1169
ocl::KernelArg::PtrReadOnly(ustages),
1170
ocl::KernelArg::PtrReadOnly(unodes),
1171
ocl::KernelArg::PtrReadOnly(usubsets),
1172
subsetSize,
1173
1174
ocl::KernelArg::PtrWriteOnly(ufacepos), // positions
1175
data.origWinSize);
1176
1177
ok = lbpKernel.run(2, globalsize, localsize, true);
1178
}
1179
1180
if( ok )
1181
{
1182
Mat facepos = ufacepos.getMat(ACCESS_READ);
1183
const int* fptr = facepos.ptr<int>();
1184
int nfaces = fptr[0];
1185
nfaces = std::min(nfaces, (int)MAX_FACES);
1186
1187
for( int i = 0; i < nfaces; i++ )
1188
{
1189
const FeatureEvaluator::ScaleData& s = featureEvaluator->getScaleData(fptr[i*3 + 1]);
1190
candidates.push_back(Rect(cvRound(fptr[i*3 + 2]*s.scale),
1191
cvRound(fptr[i*3 + 3]*s.scale),
1192
cvRound(data.origWinSize.width*s.scale),
1193
cvRound(data.origWinSize.height*s.scale)));
1194
}
1195
}
1196
return ok;
1197
}
1198
#endif
1199
1200
bool CascadeClassifierImpl::isOldFormatCascade() const
1201
{
1202
return !oldCascade.empty();
1203
}
1204
1205
int CascadeClassifierImpl::getFeatureType() const
1206
{
1207
return featureEvaluator->getFeatureType();
1208
}
1209
1210
Size CascadeClassifierImpl::getOriginalWindowSize() const
1211
{
1212
return data.origWinSize;
1213
}
1214
1215
void* CascadeClassifierImpl::getOldCascade()
1216
{
1217
return oldCascade;
1218
}
1219
1220
static void detectMultiScaleOldFormat( const Mat& image, Ptr<CvHaarClassifierCascade> oldCascade,
1221
std::vector<Rect>& objects,
1222
std::vector<int>& rejectLevels,
1223
std::vector<double>& levelWeights,
1224
std::vector<CvAvgComp>& vecAvgComp,
1225
double scaleFactor, int minNeighbors,
1226
int flags, Size minObjectSize, Size maxObjectSize,
1227
bool outputRejectLevels = false )
1228
{
1229
MemStorage storage(cvCreateMemStorage(0));
1230
CvMat _image = cvMat(image);
1231
CvSeq* _objects = cvHaarDetectObjectsForROC( &_image, oldCascade, storage, rejectLevels, levelWeights, scaleFactor,
1232
minNeighbors, flags, cvSize(minObjectSize), cvSize(maxObjectSize), outputRejectLevels );
1233
Seq<CvAvgComp>(_objects).copyTo(vecAvgComp);
1234
objects.resize(vecAvgComp.size());
1235
std::transform(vecAvgComp.begin(), vecAvgComp.end(), objects.begin(), getRect());
1236
}
1237
1238
void CascadeClassifierImpl::detectMultiScaleNoGrouping( InputArray _image, std::vector<Rect>& candidates,
1239
std::vector<int>& rejectLevels, std::vector<double>& levelWeights,
1240
double scaleFactor, Size minObjectSize, Size maxObjectSize,
1241
bool outputRejectLevels )
1242
{
1243
CV_INSTRUMENT_REGION();
1244
1245
Size imgsz = _image.size();
1246
Size originalWindowSize = getOriginalWindowSize();
1247
1248
if( maxObjectSize.height == 0 || maxObjectSize.width == 0 )
1249
maxObjectSize = imgsz;
1250
1251
// If a too small image patch is entering the function, break early before any processing
1252
if( (imgsz.height < originalWindowSize.height) || (imgsz.width < originalWindowSize.width) )
1253
return;
1254
1255
std::vector<float> all_scales, scales;
1256
all_scales.reserve(1024);
1257
scales.reserve(1024);
1258
1259
// First calculate all possible scales for the given image and model, then remove undesired scales
1260
// This allows us to cope with single scale detections (minSize == maxSize) that do not fall on precalculated scale
1261
for( double factor = 1; ; factor *= scaleFactor )
1262
{
1263
Size windowSize( cvRound(originalWindowSize.width*factor), cvRound(originalWindowSize.height*factor) );
1264
if( windowSize.width > imgsz.width || windowSize.height > imgsz.height )
1265
break;
1266
all_scales.push_back((float)factor);
1267
}
1268
1269
// This will capture allowed scales and a minSize==maxSize scale, if it is in the precalculated scales
1270
for( size_t index = 0; index < all_scales.size(); index++){
1271
Size windowSize( cvRound(originalWindowSize.width*all_scales[index]), cvRound(originalWindowSize.height*all_scales[index]) );
1272
if( windowSize.width > maxObjectSize.width || windowSize.height > maxObjectSize.height)
1273
break;
1274
if( windowSize.width < minObjectSize.width || windowSize.height < minObjectSize.height )
1275
continue;
1276
scales.push_back(all_scales[index]);
1277
}
1278
1279
// If minSize and maxSize parameter are equal and scales is not filled yet, then the scale was not available in the precalculated scales
1280
// In that case we want to return the most fitting scale (closest corresponding scale using L2 distance)
1281
if( scales.empty() && !all_scales.empty() ){
1282
std::vector<double> distances;
1283
// Calculate distances
1284
for(size_t v = 0; v < all_scales.size(); v++){
1285
Size windowSize( cvRound(originalWindowSize.width*all_scales[v]), cvRound(originalWindowSize.height*all_scales[v]) );
1286
double d = (minObjectSize.width - windowSize.width) * (minObjectSize.width - windowSize.width)
1287
+ (minObjectSize.height - windowSize.height) * (minObjectSize.height - windowSize.height);
1288
distances.push_back(d);
1289
}
1290
// Take the index of lowest value
1291
// Use that index to push the correct scale parameter
1292
size_t iMin=0;
1293
for(size_t i = 0; i < distances.size(); ++i){
1294
if(distances[iMin] > distances[i])
1295
iMin=i;
1296
}
1297
scales.push_back(all_scales[iMin]);
1298
}
1299
1300
candidates.clear();
1301
rejectLevels.clear();
1302
levelWeights.clear();
1303
1304
#ifdef HAVE_OPENCL
1305
bool use_ocl = tryOpenCL && ocl::isOpenCLActivated() &&
1306
OCL_FORCE_CHECK(_image.isUMat()) &&
1307
!featureEvaluator->getLocalSize().empty() &&
1308
(data.minNodesPerTree == data.maxNodesPerTree) &&
1309
!isOldFormatCascade() &&
1310
maskGenerator.empty() &&
1311
!outputRejectLevels;
1312
#endif
1313
1314
Mat grayImage;
1315
_InputArray gray;
1316
1317
if (_image.channels() > 1)
1318
cvtColor(_image, grayImage, COLOR_BGR2GRAY);
1319
else if (_image.isMat())
1320
grayImage = _image.getMat();
1321
else
1322
_image.copyTo(grayImage);
1323
gray = grayImage;
1324
1325
if( !featureEvaluator->setImage(gray, scales) )
1326
return;
1327
1328
#ifdef HAVE_OPENCL
1329
// OpenCL code
1330
CV_OCL_RUN(use_ocl, ocl_detectMultiScaleNoGrouping( scales, candidates ))
1331
1332
if (use_ocl)
1333
tryOpenCL = false;
1334
#endif
1335
1336
// CPU code
1337
featureEvaluator->getMats();
1338
{
1339
Mat currentMask;
1340
if (maskGenerator)
1341
currentMask = maskGenerator->generateMask(gray.getMat());
1342
1343
size_t i, nscales = scales.size();
1344
cv::AutoBuffer<int> stripeSizeBuf(nscales);
1345
int* stripeSizes = stripeSizeBuf.data();
1346
const FeatureEvaluator::ScaleData* s = &featureEvaluator->getScaleData(0);
1347
Size szw = s->getWorkingSize(data.origWinSize);
1348
int nstripes = cvCeil(szw.width/32.);
1349
for( i = 0; i < nscales; i++ )
1350
{
1351
szw = s[i].getWorkingSize(data.origWinSize);
1352
stripeSizes[i] = std::max((szw.height/s[i].ystep + nstripes-1)/nstripes, 1)*s[i].ystep;
1353
}
1354
1355
CascadeClassifierInvoker invoker(*this, (int)nscales, nstripes, s, stripeSizes,
1356
candidates, rejectLevels, levelWeights,
1357
outputRejectLevels, currentMask, &mtx);
1358
parallel_for_(Range(0, nstripes), invoker);
1359
}
1360
}
1361
1362
1363
void CascadeClassifierImpl::detectMultiScale( InputArray _image, std::vector<Rect>& objects,
1364
std::vector<int>& rejectLevels,
1365
std::vector<double>& levelWeights,
1366
double scaleFactor, int minNeighbors,
1367
int flags, Size minObjectSize, Size maxObjectSize,
1368
bool outputRejectLevels )
1369
{
1370
CV_INSTRUMENT_REGION();
1371
1372
CV_Assert( scaleFactor > 1 && _image.depth() == CV_8U );
1373
1374
if( empty() )
1375
return;
1376
1377
if( isOldFormatCascade() )
1378
{
1379
Mat image = _image.getMat();
1380
std::vector<CvAvgComp> fakeVecAvgComp;
1381
detectMultiScaleOldFormat( image, oldCascade, objects, rejectLevels, levelWeights, fakeVecAvgComp, scaleFactor,
1382
minNeighbors, flags, minObjectSize, maxObjectSize, outputRejectLevels );
1383
}
1384
else
1385
{
1386
detectMultiScaleNoGrouping( _image, objects, rejectLevels, levelWeights, scaleFactor, minObjectSize, maxObjectSize,
1387
outputRejectLevels );
1388
const double GROUP_EPS = 0.2;
1389
if( outputRejectLevels )
1390
{
1391
groupRectangles( objects, rejectLevels, levelWeights, minNeighbors, GROUP_EPS );
1392
}
1393
else
1394
{
1395
groupRectangles( objects, minNeighbors, GROUP_EPS );
1396
}
1397
}
1398
}
1399
1400
void CascadeClassifierImpl::detectMultiScale( InputArray _image, std::vector<Rect>& objects,
1401
double scaleFactor, int minNeighbors,
1402
int flags, Size minObjectSize, Size maxObjectSize)
1403
{
1404
CV_INSTRUMENT_REGION();
1405
1406
std::vector<int> fakeLevels;
1407
std::vector<double> fakeWeights;
1408
detectMultiScale( _image, objects, fakeLevels, fakeWeights, scaleFactor,
1409
minNeighbors, flags, minObjectSize, maxObjectSize );
1410
}
1411
1412
void CascadeClassifierImpl::detectMultiScale( InputArray _image, std::vector<Rect>& objects,
1413
std::vector<int>& numDetections, double scaleFactor,
1414
int minNeighbors, int flags, Size minObjectSize,
1415
Size maxObjectSize )
1416
{
1417
CV_INSTRUMENT_REGION();
1418
1419
Mat image = _image.getMat();
1420
CV_Assert( scaleFactor > 1 && image.depth() == CV_8U );
1421
1422
if( empty() )
1423
return;
1424
1425
std::vector<int> fakeLevels;
1426
std::vector<double> fakeWeights;
1427
if( isOldFormatCascade() )
1428
{
1429
std::vector<CvAvgComp> vecAvgComp;
1430
detectMultiScaleOldFormat( image, oldCascade, objects, fakeLevels, fakeWeights, vecAvgComp, scaleFactor,
1431
minNeighbors, flags, minObjectSize, maxObjectSize );
1432
numDetections.resize(vecAvgComp.size());
1433
std::transform(vecAvgComp.begin(), vecAvgComp.end(), numDetections.begin(), getNeighbors());
1434
}
1435
else
1436
{
1437
detectMultiScaleNoGrouping( image, objects, fakeLevels, fakeWeights, scaleFactor, minObjectSize, maxObjectSize );
1438
const double GROUP_EPS = 0.2;
1439
groupRectangles( objects, numDetections, minNeighbors, GROUP_EPS );
1440
}
1441
}
1442
1443
1444
CascadeClassifierImpl::Data::Data()
1445
{
1446
stageType = featureType = ncategories = maxNodesPerTree = minNodesPerTree = 0;
1447
}
1448
1449
bool CascadeClassifierImpl::Data::read(const FileNode &root)
1450
{
1451
static const float THRESHOLD_EPS = 1e-5f;
1452
1453
// load stage params
1454
String stageTypeStr = (String)root[CC_STAGE_TYPE];
1455
if( stageTypeStr == CC_BOOST )
1456
stageType = BOOST;
1457
else
1458
return false;
1459
1460
String featureTypeStr = (String)root[CC_FEATURE_TYPE];
1461
if( featureTypeStr == CC_HAAR )
1462
featureType = FeatureEvaluator::HAAR;
1463
else if( featureTypeStr == CC_LBP )
1464
featureType = FeatureEvaluator::LBP;
1465
else if( featureTypeStr == CC_HOG )
1466
{
1467
featureType = FeatureEvaluator::HOG;
1468
CV_Error(Error::StsNotImplemented, "HOG cascade is not supported in 3.0");
1469
}
1470
else
1471
return false;
1472
1473
origWinSize.width = (int)root[CC_WIDTH];
1474
origWinSize.height = (int)root[CC_HEIGHT];
1475
CV_Assert( origWinSize.height > 0 && origWinSize.width > 0 );
1476
1477
// load feature params
1478
FileNode fn = root[CC_FEATURE_PARAMS];
1479
if( fn.empty() )
1480
return false;
1481
1482
ncategories = fn[CC_MAX_CAT_COUNT];
1483
int subsetSize = (ncategories + 31)/32,
1484
nodeStep = 3 + ( ncategories>0 ? subsetSize : 1 );
1485
1486
// load stages
1487
fn = root[CC_STAGES];
1488
if( fn.empty() )
1489
return false;
1490
1491
stages.reserve(fn.size());
1492
classifiers.clear();
1493
nodes.clear();
1494
stumps.clear();
1495
1496
FileNodeIterator it = fn.begin(), it_end = fn.end();
1497
minNodesPerTree = INT_MAX;
1498
maxNodesPerTree = 0;
1499
1500
for( int si = 0; it != it_end; si++, ++it )
1501
{
1502
FileNode fns = *it;
1503
Stage stage;
1504
stage.threshold = (float)fns[CC_STAGE_THRESHOLD] - THRESHOLD_EPS;
1505
fns = fns[CC_WEAK_CLASSIFIERS];
1506
if(fns.empty())
1507
return false;
1508
stage.ntrees = (int)fns.size();
1509
stage.first = (int)classifiers.size();
1510
stages.push_back(stage);
1511
classifiers.reserve(stages[si].first + stages[si].ntrees);
1512
1513
FileNodeIterator it1 = fns.begin(), it1_end = fns.end();
1514
for( ; it1 != it1_end; ++it1 ) // weak trees
1515
{
1516
FileNode fnw = *it1;
1517
FileNode internalNodes = fnw[CC_INTERNAL_NODES];
1518
FileNode leafValues = fnw[CC_LEAF_VALUES];
1519
if( internalNodes.empty() || leafValues.empty() )
1520
return false;
1521
1522
DTree tree;
1523
tree.nodeCount = (int)internalNodes.size()/nodeStep;
1524
minNodesPerTree = std::min(minNodesPerTree, tree.nodeCount);
1525
maxNodesPerTree = std::max(maxNodesPerTree, tree.nodeCount);
1526
1527
classifiers.push_back(tree);
1528
1529
nodes.reserve(nodes.size() + tree.nodeCount);
1530
leaves.reserve(leaves.size() + leafValues.size());
1531
if( subsetSize > 0 )
1532
subsets.reserve(subsets.size() + tree.nodeCount*subsetSize);
1533
1534
FileNodeIterator internalNodesIter = internalNodes.begin(), internalNodesEnd = internalNodes.end();
1535
1536
for( ; internalNodesIter != internalNodesEnd; ) // nodes
1537
{
1538
DTreeNode node;
1539
node.left = (int)*internalNodesIter; ++internalNodesIter;
1540
node.right = (int)*internalNodesIter; ++internalNodesIter;
1541
node.featureIdx = (int)*internalNodesIter; ++internalNodesIter;
1542
if( subsetSize > 0 )
1543
{
1544
for( int j = 0; j < subsetSize; j++, ++internalNodesIter )
1545
subsets.push_back((int)*internalNodesIter);
1546
node.threshold = 0.f;
1547
}
1548
else
1549
{
1550
node.threshold = (float)*internalNodesIter; ++internalNodesIter;
1551
}
1552
nodes.push_back(node);
1553
}
1554
1555
internalNodesIter = leafValues.begin(), internalNodesEnd = leafValues.end();
1556
1557
for( ; internalNodesIter != internalNodesEnd; ++internalNodesIter ) // leaves
1558
leaves.push_back((float)*internalNodesIter);
1559
}
1560
}
1561
1562
if( maxNodesPerTree == 1 )
1563
{
1564
int nodeOfs = 0, leafOfs = 0;
1565
size_t nstages = stages.size();
1566
for( size_t stageIdx = 0; stageIdx < nstages; stageIdx++ )
1567
{
1568
const Stage& stage = stages[stageIdx];
1569
1570
int ntrees = stage.ntrees;
1571
for( int i = 0; i < ntrees; i++, nodeOfs++, leafOfs+= 2 )
1572
{
1573
const DTreeNode& node = nodes[nodeOfs];
1574
stumps.push_back(Stump(node.featureIdx, node.threshold,
1575
leaves[leafOfs], leaves[leafOfs+1]));
1576
}
1577
}
1578
}
1579
1580
return true;
1581
}
1582
1583
1584
bool CascadeClassifierImpl::read_(const FileNode& root)
1585
{
1586
#ifdef HAVE_OPENCL
1587
tryOpenCL = true;
1588
haarKernel = ocl::Kernel();
1589
lbpKernel = ocl::Kernel();
1590
#endif
1591
ustages.release();
1592
unodes.release();
1593
uleaves.release();
1594
if( !data.read(root) )
1595
return false;
1596
1597
// load features
1598
featureEvaluator = FeatureEvaluator::create(data.featureType);
1599
FileNode fn = root[CC_FEATURES];
1600
if( fn.empty() )
1601
return false;
1602
1603
return featureEvaluator->read(fn, data.origWinSize);
1604
}
1605
1606
void DefaultDeleter<CvHaarClassifierCascade>::operator ()(CvHaarClassifierCascade* obj) const { cvReleaseHaarClassifierCascade(&obj); }
1607
1608
1609
BaseCascadeClassifier::~BaseCascadeClassifier()
1610
{
1611
}
1612
1613
CascadeClassifier::CascadeClassifier() {}
1614
CascadeClassifier::CascadeClassifier(const String& filename)
1615
{
1616
load(filename);
1617
}
1618
1619
CascadeClassifier::~CascadeClassifier()
1620
{
1621
}
1622
1623
bool CascadeClassifier::empty() const
1624
{
1625
return cc.empty() || cc->empty();
1626
}
1627
1628
bool CascadeClassifier::load( const String& filename )
1629
{
1630
cc = makePtr<CascadeClassifierImpl>();
1631
if(!cc->load(filename))
1632
cc.release();
1633
return !empty();
1634
}
1635
1636
bool CascadeClassifier::read(const FileNode &root)
1637
{
1638
Ptr<CascadeClassifierImpl> ccimpl = makePtr<CascadeClassifierImpl>();
1639
bool ok = ccimpl->read_(root);
1640
if( ok )
1641
cc = ccimpl.staticCast<BaseCascadeClassifier>();
1642
else
1643
cc.release();
1644
return ok;
1645
}
1646
1647
void clipObjects(Size sz, std::vector<Rect>& objects,
1648
std::vector<int>* a, std::vector<double>* b)
1649
{
1650
size_t i, j = 0, n = objects.size();
1651
Rect win0 = Rect(0, 0, sz.width, sz.height);
1652
if(a)
1653
{
1654
CV_Assert(a->size() == n);
1655
}
1656
if(b)
1657
{
1658
CV_Assert(b->size() == n);
1659
}
1660
1661
for( i = 0; i < n; i++ )
1662
{
1663
Rect r = win0 & objects[i];
1664
if( !r.empty() )
1665
{
1666
objects[j] = r;
1667
if( i > j )
1668
{
1669
if(a) a->at(j) = a->at(i);
1670
if(b) b->at(j) = b->at(i);
1671
}
1672
j++;
1673
}
1674
}
1675
1676
if( j < n )
1677
{
1678
objects.resize(j);
1679
if(a) a->resize(j);
1680
if(b) b->resize(j);
1681
}
1682
}
1683
1684
void CascadeClassifier::detectMultiScale( InputArray image,
1685
CV_OUT std::vector<Rect>& objects,
1686
double scaleFactor,
1687
int minNeighbors, int flags,
1688
Size minSize,
1689
Size maxSize )
1690
{
1691
CV_INSTRUMENT_REGION();
1692
1693
CV_Assert(!empty());
1694
cc->detectMultiScale(image, objects, scaleFactor, minNeighbors, flags, minSize, maxSize);
1695
clipObjects(image.size(), objects, 0, 0);
1696
}
1697
1698
void CascadeClassifier::detectMultiScale( InputArray image,
1699
CV_OUT std::vector<Rect>& objects,
1700
CV_OUT std::vector<int>& numDetections,
1701
double scaleFactor,
1702
int minNeighbors, int flags,
1703
Size minSize, Size maxSize )
1704
{
1705
CV_INSTRUMENT_REGION();
1706
1707
CV_Assert(!empty());
1708
cc->detectMultiScale(image, objects, numDetections,
1709
scaleFactor, minNeighbors, flags, minSize, maxSize);
1710
clipObjects(image.size(), objects, &numDetections, 0);
1711
}
1712
1713
void CascadeClassifier::detectMultiScale( InputArray image,
1714
CV_OUT std::vector<Rect>& objects,
1715
CV_OUT std::vector<int>& rejectLevels,
1716
CV_OUT std::vector<double>& levelWeights,
1717
double scaleFactor,
1718
int minNeighbors, int flags,
1719
Size minSize, Size maxSize,
1720
bool outputRejectLevels )
1721
{
1722
CV_INSTRUMENT_REGION();
1723
1724
CV_Assert(!empty());
1725
cc->detectMultiScale(image, objects, rejectLevels, levelWeights,
1726
scaleFactor, minNeighbors, flags,
1727
minSize, maxSize, outputRejectLevels);
1728
clipObjects(image.size(), objects, &rejectLevels, &levelWeights);
1729
}
1730
1731
bool CascadeClassifier::isOldFormatCascade() const
1732
{
1733
CV_Assert(!empty());
1734
return cc->isOldFormatCascade();
1735
}
1736
1737
Size CascadeClassifier::getOriginalWindowSize() const
1738
{
1739
CV_Assert(!empty());
1740
return cc->getOriginalWindowSize();
1741
}
1742
1743
int CascadeClassifier::getFeatureType() const
1744
{
1745
CV_Assert(!empty());
1746
return cc->getFeatureType();
1747
}
1748
1749
void* CascadeClassifier::getOldCascade()
1750
{
1751
CV_Assert(!empty());
1752
return cc->getOldCascade();
1753
}
1754
1755
void CascadeClassifier::setMaskGenerator(const Ptr<BaseCascadeClassifier::MaskGenerator>& maskGenerator)
1756
{
1757
CV_Assert(!empty());
1758
cc->setMaskGenerator(maskGenerator);
1759
}
1760
1761
Ptr<BaseCascadeClassifier::MaskGenerator> CascadeClassifier::getMaskGenerator()
1762
{
1763
CV_Assert(!empty());
1764
return cc->getMaskGenerator();
1765
}
1766
1767
} // namespace cv
1768
1769