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 // Intel License Agreement 11 // For Open Source Computer Vision Library 12 // 13 // Copyright (C) 2000, Intel Corporation, 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 Intel Corporation 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 Intel Corporation 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 "test_precomp.hpp" 43 #include "opencv2/imgproc.hpp" 44 #include "opencv2/objdetect/objdetect_c.h" 45 46 using namespace cv; 47 using namespace std; 48 49 //#define GET_STAT 50 51 #define DIST_E "distE" 52 #define S_E "sE" 53 #define NO_PAIR_E "noPairE" 54 //#define TOTAL_NO_PAIR_E "totalNoPairE" 55 56 #define DETECTOR_NAMES "detector_names" 57 #define DETECTORS "detectors" 58 #define IMAGE_FILENAMES "image_filenames" 59 #define VALIDATION "validation" 60 #define FILENAME "fn" 61 62 #define C_SCALE_CASCADE "scale_cascade" 63 64 class CV_DetectorTest : public cvtest::BaseTest 65 { 66 public: 67 CV_DetectorTest(); 68 protected: 69 virtual int prepareData( FileStorage& fs ); 70 virtual void run( int startFrom ); 71 virtual string& getValidationFilename(); 72 73 virtual void readDetector( const FileNode& fn ) = 0; 74 virtual void writeDetector( FileStorage& fs, int di ) = 0; 75 int runTestCase( int detectorIdx, vector<vector<Rect> >& objects ); 76 virtual int detectMultiScale( int di, const Mat& img, vector<Rect>& objects ) = 0; 77 int validate( int detectorIdx, vector<vector<Rect> >& objects ); 78 79 struct 80 { 81 float dist; 82 float s; 83 float noPair; 84 //float totalNoPair; 85 } eps; 86 vector<string> detectorNames; 87 vector<string> detectorFilenames; 88 vector<string> imageFilenames; 89 vector<Mat> images; 90 string validationFilename; 91 string configFilename; 92 FileStorage validationFS; 93 bool write_results; 94 }; 95 96 CV_DetectorTest::CV_DetectorTest() 97 { 98 configFilename = "dummy"; 99 write_results = false; 100 } 101 102 string& CV_DetectorTest::getValidationFilename() 103 { 104 return validationFilename; 105 } 106 107 int CV_DetectorTest::prepareData( FileStorage& _fs ) 108 { 109 if( !_fs.isOpened() ) 110 test_case_count = -1; 111 else 112 { 113 FileNode fn = _fs.getFirstTopLevelNode(); 114 115 fn[DIST_E] >> eps.dist; 116 fn[S_E] >> eps.s; 117 fn[NO_PAIR_E] >> eps.noPair; 118 // fn[TOTAL_NO_PAIR_E] >> eps.totalNoPair; 119 120 // read detectors 121 if( fn[DETECTOR_NAMES].size() != 0 ) 122 { 123 FileNodeIterator it = fn[DETECTOR_NAMES].begin(); 124 for( ; it != fn[DETECTOR_NAMES].end(); ) 125 { 126 String _name; 127 it >> _name; 128 detectorNames.push_back(_name); 129 readDetector(fn[DETECTORS][_name]); 130 } 131 } 132 test_case_count = (int)detectorNames.size(); 133 134 // read images filenames and images 135 string dataPath = ts->get_data_path(); 136 if( fn[IMAGE_FILENAMES].size() != 0 ) 137 { 138 for( FileNodeIterator it = fn[IMAGE_FILENAMES].begin(); it != fn[IMAGE_FILENAMES].end(); ) 139 { 140 String filename; 141 it >> filename; 142 imageFilenames.push_back(filename); 143 Mat img = imread( dataPath+filename, 1 ); 144 images.push_back( img ); 145 } 146 } 147 } 148 return cvtest::TS::OK; 149 } 150 151 void CV_DetectorTest::run( int ) 152 { 153 string dataPath = ts->get_data_path(); 154 string vs_filename = dataPath + getValidationFilename(); 155 156 write_results = !validationFS.open( vs_filename, FileStorage::READ ); 157 158 int code; 159 if( !write_results ) 160 { 161 code = prepareData( validationFS ); 162 } 163 else 164 { 165 FileStorage fs0(dataPath + configFilename, FileStorage::READ ); 166 code = prepareData(fs0); 167 } 168 169 if( code < 0 ) 170 { 171 ts->set_failed_test_info( code ); 172 return; 173 } 174 175 if( write_results ) 176 { 177 validationFS.release(); 178 validationFS.open( vs_filename, FileStorage::WRITE ); 179 validationFS << FileStorage::getDefaultObjectName(validationFilename) << "{"; 180 181 validationFS << DIST_E << eps.dist; 182 validationFS << S_E << eps.s; 183 validationFS << NO_PAIR_E << eps.noPair; 184 // validationFS << TOTAL_NO_PAIR_E << eps.totalNoPair; 185 186 // write detector names 187 validationFS << DETECTOR_NAMES << "["; 188 vector<string>::const_iterator nit = detectorNames.begin(); 189 for( ; nit != detectorNames.end(); ++nit ) 190 { 191 validationFS << *nit; 192 } 193 validationFS << "]"; // DETECTOR_NAMES 194 195 // write detectors 196 validationFS << DETECTORS << "{"; 197 assert( detectorNames.size() == detectorFilenames.size() ); 198 nit = detectorNames.begin(); 199 for( int di = 0; nit != detectorNames.end(); ++nit, di++ ) 200 { 201 validationFS << *nit << "{"; 202 writeDetector( validationFS, di ); 203 validationFS << "}"; 204 } 205 validationFS << "}"; 206 207 // write image filenames 208 validationFS << IMAGE_FILENAMES << "["; 209 vector<string>::const_iterator it = imageFilenames.begin(); 210 for( int ii = 0; it != imageFilenames.end(); ++it, ii++ ) 211 { 212 char buf[10]; 213 sprintf( buf, "%s%d", "img_", ii ); 214 //cvWriteComment( validationFS.fs, buf, 0 ); 215 validationFS << *it; 216 } 217 validationFS << "]"; // IMAGE_FILENAMES 218 219 validationFS << VALIDATION << "{"; 220 } 221 222 int progress = 0; 223 for( int di = 0; di < test_case_count; di++ ) 224 { 225 progress = update_progress( progress, di, test_case_count, 0 ); 226 if( write_results ) 227 validationFS << detectorNames[di] << "{"; 228 vector<vector<Rect> > objects; 229 int temp_code = runTestCase( di, objects ); 230 231 if (!write_results && temp_code == cvtest::TS::OK) 232 temp_code = validate( di, objects ); 233 234 if (temp_code != cvtest::TS::OK) 235 code = temp_code; 236 237 if( write_results ) 238 validationFS << "}"; // detectorNames[di] 239 } 240 241 if( write_results ) 242 { 243 validationFS << "}"; // VALIDATION 244 validationFS << "}"; // getDefaultObjectName 245 } 246 247 if ( test_case_count <= 0 || imageFilenames.size() <= 0 ) 248 { 249 ts->printf( cvtest::TS::LOG, "validation file is not determined or not correct" ); 250 code = cvtest::TS::FAIL_INVALID_TEST_DATA; 251 } 252 ts->set_failed_test_info( code ); 253 } 254 255 int CV_DetectorTest::runTestCase( int detectorIdx, vector<vector<Rect> >& objects ) 256 { 257 string dataPath = ts->get_data_path(), detectorFilename; 258 if( !detectorFilenames[detectorIdx].empty() ) 259 detectorFilename = dataPath + detectorFilenames[detectorIdx]; 260 printf("detector %s\n", detectorFilename.c_str()); 261 262 for( int ii = 0; ii < (int)imageFilenames.size(); ++ii ) 263 { 264 vector<Rect> imgObjects; 265 Mat image = images[ii]; 266 if( image.empty() ) 267 { 268 char msg[30]; 269 sprintf( msg, "%s %d %s", "image ", ii, " can not be read" ); 270 ts->printf( cvtest::TS::LOG, msg ); 271 return cvtest::TS::FAIL_INVALID_TEST_DATA; 272 } 273 int code = detectMultiScale( detectorIdx, image, imgObjects ); 274 if( code != cvtest::TS::OK ) 275 return code; 276 277 objects.push_back( imgObjects ); 278 279 if( write_results ) 280 { 281 char buf[10]; 282 sprintf( buf, "%s%d", "img_", ii ); 283 string imageIdxStr = buf; 284 validationFS << imageIdxStr << "[:"; 285 for( vector<Rect>::const_iterator it = imgObjects.begin(); 286 it != imgObjects.end(); ++it ) 287 { 288 validationFS << it->x << it->y << it->width << it->height; 289 } 290 validationFS << "]"; // imageIdxStr 291 } 292 } 293 return cvtest::TS::OK; 294 } 295 296 297 bool isZero( uchar i ) {return i == 0;} 298 299 int CV_DetectorTest::validate( int detectorIdx, vector<vector<Rect> >& objects ) 300 { 301 assert( imageFilenames.size() == objects.size() ); 302 int imageIdx = 0; 303 int totalNoPair = 0, totalValRectCount = 0; 304 305 for( vector<vector<Rect> >::const_iterator it = objects.begin(); 306 it != objects.end(); ++it, imageIdx++ ) // for image 307 { 308 Size imgSize = images[imageIdx].size(); 309 float dist = min(imgSize.height, imgSize.width) * eps.dist; 310 float wDiff = imgSize.width * eps.s; 311 float hDiff = imgSize.height * eps.s; 312 313 int noPair = 0; 314 315 // read validation rectangles 316 char buf[10]; 317 sprintf( buf, "%s%d", "img_", imageIdx ); 318 string imageIdxStr = buf; 319 FileNode node = validationFS.getFirstTopLevelNode()[VALIDATION][detectorNames[detectorIdx]][imageIdxStr]; 320 vector<Rect> valRects; 321 if( node.size() != 0 ) 322 { 323 for( FileNodeIterator it2 = node.begin(); it2 != node.end(); ) 324 { 325 Rect r; 326 it2 >> r.x >> r.y >> r.width >> r.height; 327 valRects.push_back(r); 328 } 329 } 330 totalValRectCount += (int)valRects.size(); 331 332 // compare rectangles 333 vector<uchar> map(valRects.size(), 0); 334 for( vector<Rect>::const_iterator cr = it->begin(); 335 cr != it->end(); ++cr ) 336 { 337 // find nearest rectangle 338 Point2f cp1 = Point2f( cr->x + (float)cr->width/2.0f, cr->y + (float)cr->height/2.0f ); 339 int minIdx = -1, vi = 0; 340 float minDist = (float)norm( Point(imgSize.width, imgSize.height) ); 341 for( vector<Rect>::const_iterator vr = valRects.begin(); 342 vr != valRects.end(); ++vr, vi++ ) 343 { 344 Point2f cp2 = Point2f( vr->x + (float)vr->width/2.0f, vr->y + (float)vr->height/2.0f ); 345 float curDist = (float)norm(cp1-cp2); 346 if( curDist < minDist ) 347 { 348 minIdx = vi; 349 minDist = curDist; 350 } 351 } 352 if( minIdx == -1 ) 353 { 354 noPair++; 355 } 356 else 357 { 358 Rect vr = valRects[minIdx]; 359 if( map[minIdx] != 0 || (minDist > dist) || (abs(cr->width - vr.width) > wDiff) || 360 (abs(cr->height - vr.height) > hDiff) ) 361 noPair++; 362 else 363 map[minIdx] = 1; 364 } 365 } 366 noPair += (int)count_if( map.begin(), map.end(), isZero ); 367 totalNoPair += noPair; 368 369 EXPECT_LE(noPair, cvRound(valRects.size()*eps.noPair)+1) 370 << "detector " << detectorNames[detectorIdx] << " has overrated count of rectangles without pair on " 371 << imageFilenames[imageIdx] << " image"; 372 373 if (::testing::Test::HasFailure()) 374 break; 375 } 376 377 EXPECT_LE(totalNoPair, cvRound(totalValRectCount*eps./*total*/noPair)+1) 378 << "detector " << detectorNames[detectorIdx] << " has overrated count of rectangles without pair on all images set"; 379 380 if (::testing::Test::HasFailure()) 381 return cvtest::TS::FAIL_BAD_ACCURACY; 382 383 return cvtest::TS::OK; 384 } 385 386 //----------------------------------------------- CascadeDetectorTest ----------------------------------- 387 class CV_CascadeDetectorTest : public CV_DetectorTest 388 { 389 public: 390 CV_CascadeDetectorTest(); 391 protected: 392 virtual void readDetector( const FileNode& fn ); 393 virtual void writeDetector( FileStorage& fs, int di ); 394 virtual int detectMultiScale( int di, const Mat& img, vector<Rect>& objects ); 395 virtual int detectMultiScale_C( const string& filename, int di, const Mat& img, vector<Rect>& objects ); 396 vector<int> flags; 397 }; 398 399 CV_CascadeDetectorTest::CV_CascadeDetectorTest() 400 { 401 validationFilename = "cascadeandhog/cascade.xml"; 402 configFilename = "cascadeandhog/_cascade.xml"; 403 } 404 405 void CV_CascadeDetectorTest::readDetector( const FileNode& fn ) 406 { 407 String filename; 408 int flag; 409 fn[FILENAME] >> filename; 410 detectorFilenames.push_back(filename); 411 fn[C_SCALE_CASCADE] >> flag; 412 if( flag ) 413 flags.push_back( 0 ); 414 else 415 flags.push_back( CASCADE_SCALE_IMAGE ); 416 } 417 418 void CV_CascadeDetectorTest::writeDetector( FileStorage& fs, int di ) 419 { 420 int sc = flags[di] & CASCADE_SCALE_IMAGE ? 0 : 1; 421 fs << FILENAME << detectorFilenames[di]; 422 fs << C_SCALE_CASCADE << sc; 423 } 424 425 426 int CV_CascadeDetectorTest::detectMultiScale_C( const string& filename, 427 int di, const Mat& img, 428 vector<Rect>& objects ) 429 { 430 Ptr<CvHaarClassifierCascade> c_cascade(cvLoadHaarClassifierCascade(filename.c_str(), cvSize(0,0))); 431 Ptr<CvMemStorage> storage(cvCreateMemStorage()); 432 433 if( !c_cascade ) 434 { 435 ts->printf( cvtest::TS::LOG, "cascade %s can not be opened"); 436 return cvtest::TS::FAIL_INVALID_TEST_DATA; 437 } 438 Mat grayImg; 439 cvtColor( img, grayImg, COLOR_BGR2GRAY ); 440 equalizeHist( grayImg, grayImg ); 441 442 CvMat c_gray = grayImg; 443 CvSeq* rs = cvHaarDetectObjects(&c_gray, c_cascade, storage, 1.1, 3, flags[di] ); 444 445 objects.clear(); 446 for( int i = 0; i < rs->total; i++ ) 447 { 448 Rect r = *(Rect*)cvGetSeqElem(rs, i); 449 objects.push_back(r); 450 } 451 452 return cvtest::TS::OK; 453 } 454 455 int CV_CascadeDetectorTest::detectMultiScale( int di, const Mat& img, 456 vector<Rect>& objects) 457 { 458 string dataPath = ts->get_data_path(), filename; 459 filename = dataPath + detectorFilenames[di]; 460 const string pattern = "haarcascade_frontalface_default.xml"; 461 462 if( filename.size() >= pattern.size() && 463 strcmp(filename.c_str() + (filename.size() - pattern.size()), 464 pattern.c_str()) == 0 ) 465 return detectMultiScale_C(filename, di, img, objects); 466 467 CascadeClassifier cascade( filename ); 468 if( cascade.empty() ) 469 { 470 ts->printf( cvtest::TS::LOG, "cascade %s can not be opened"); 471 return cvtest::TS::FAIL_INVALID_TEST_DATA; 472 } 473 Mat grayImg; 474 cvtColor( img, grayImg, COLOR_BGR2GRAY ); 475 equalizeHist( grayImg, grayImg ); 476 cascade.detectMultiScale( grayImg, objects, 1.1, 3, flags[di] ); 477 return cvtest::TS::OK; 478 } 479 480 //----------------------------------------------- HOGDetectorTest ----------------------------------- 481 class CV_HOGDetectorTest : public CV_DetectorTest 482 { 483 public: 484 CV_HOGDetectorTest(); 485 protected: 486 virtual void readDetector( const FileNode& fn ); 487 virtual void writeDetector( FileStorage& fs, int di ); 488 virtual int detectMultiScale( int di, const Mat& img, vector<Rect>& objects ); 489 }; 490 491 CV_HOGDetectorTest::CV_HOGDetectorTest() 492 { 493 validationFilename = "cascadeandhog/hog.xml"; 494 } 495 496 void CV_HOGDetectorTest::readDetector( const FileNode& fn ) 497 { 498 String filename; 499 if( fn[FILENAME].size() != 0 ) 500 fn[FILENAME] >> filename; 501 detectorFilenames.push_back( filename); 502 } 503 504 void CV_HOGDetectorTest::writeDetector( FileStorage& fs, int di ) 505 { 506 fs << FILENAME << detectorFilenames[di]; 507 } 508 509 int CV_HOGDetectorTest::detectMultiScale( int di, const Mat& img, 510 vector<Rect>& objects) 511 { 512 HOGDescriptor hog; 513 if( detectorFilenames[di].empty() ) 514 hog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector()); 515 else 516 assert(0); 517 hog.detectMultiScale(img, objects); 518 return cvtest::TS::OK; 519 } 520 521 //----------------------------------------------- HOGDetectorReadWriteTest ----------------------------------- 522 TEST(Objdetect_HOGDetectorReadWrite, regression) 523 { 524 // Inspired by bug #2607 525 Mat img; 526 img = imread(cvtest::TS::ptr()->get_data_path() + "/cascadeandhog/images/karen-and-rob.png"); 527 ASSERT_FALSE(img.empty()); 528 529 HOGDescriptor hog; 530 hog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector()); 531 532 string tempfilename = cv::tempfile(".xml"); 533 FileStorage fs(tempfilename, FileStorage::WRITE); 534 hog.write(fs, "myHOG"); 535 536 fs.open(tempfilename, FileStorage::READ); 537 remove(tempfilename.c_str()); 538 539 FileNode n = fs["opencv_storage"]["myHOG"]; 540 541 ASSERT_NO_THROW(hog.read(n)); 542 } 543 544 545 546 TEST(Objdetect_CascadeDetector, regression) { CV_CascadeDetectorTest test; test.safe_run(); } 547 TEST(Objdetect_HOGDetector, regression) { CV_HOGDetectorTest test; test.safe_run(); } 548 549 550 //----------------------------------------------- HOG SSE2 compatible test ----------------------------------- 551 552 class HOGDescriptorTester : 553 public cv::HOGDescriptor 554 { 555 HOGDescriptor* actual_hog; 556 cvtest::TS* ts; 557 mutable bool failed; 558 559 public: 560 HOGDescriptorTester(HOGDescriptor& instance) : 561 cv::HOGDescriptor(instance), actual_hog(&instance), 562 ts(cvtest::TS::ptr()), failed(false) 563 { } 564 565 virtual void computeGradient(const Mat& img, Mat& grad, Mat& qangle, 566 Size paddingTL, Size paddingBR) const; 567 568 virtual void detect(const Mat& img, 569 vector<Point>& hits, vector<double>& weights, double hitThreshold = 0.0, 570 Size winStride = Size(), Size padding = Size(), 571 const vector<Point>& locations = vector<Point>()) const; 572 573 virtual void detect(const Mat& img, vector<Point>& hits, double hitThreshold = 0.0, 574 Size winStride = Size(), Size padding = Size(), 575 const vector<Point>& locations = vector<Point>()) const; 576 577 virtual void compute(InputArray img, vector<float>& descriptors, 578 Size winStride = Size(), Size padding = Size(), 579 const vector<Point>& locations = vector<Point>()) const; 580 581 bool is_failed() const; 582 }; 583 584 struct HOGCacheTester 585 { 586 struct BlockData 587 { 588 BlockData() : histOfs(0), imgOffset() {} 589 int histOfs; 590 Point imgOffset; 591 }; 592 593 struct PixData 594 { 595 size_t gradOfs, qangleOfs; 596 int histOfs[4]; 597 float histWeights[4]; 598 float gradWeight; 599 }; 600 601 HOGCacheTester(); 602 HOGCacheTester(const HOGDescriptorTester* descriptor, 603 const Mat& img, Size paddingTL, Size paddingBR, 604 bool useCache, Size cacheStride); 605 virtual ~HOGCacheTester() { } 606 virtual void init(const HOGDescriptorTester* descriptor, 607 const Mat& img, Size paddingTL, Size paddingBR, 608 bool useCache, Size cacheStride); 609 610 Size windowsInImage(Size imageSize, Size winStride) const; 611 Rect getWindow(Size imageSize, Size winStride, int idx) const; 612 613 const float* getBlock(Point pt, float* buf); 614 virtual void normalizeBlockHistogram(float* histogram) const; 615 616 vector<PixData> pixData; 617 vector<BlockData> blockData; 618 619 bool useCache; 620 vector<int> ymaxCached; 621 Size winSize, cacheStride; 622 Size nblocks, ncells; 623 int blockHistogramSize; 624 int count1, count2, count4; 625 Point imgoffset; 626 Mat_<float> blockCache; 627 Mat_<uchar> blockCacheFlags; 628 629 Mat grad, qangle; 630 const HOGDescriptorTester* descriptor; 631 }; 632 633 HOGCacheTester::HOGCacheTester() 634 { 635 useCache = false; 636 blockHistogramSize = count1 = count2 = count4 = 0; 637 descriptor = 0; 638 } 639 640 HOGCacheTester::HOGCacheTester(const HOGDescriptorTester* _descriptor, 641 const Mat& _img, Size _paddingTL, Size _paddingBR, 642 bool _useCache, Size _cacheStride) 643 { 644 init(_descriptor, _img, _paddingTL, _paddingBR, _useCache, _cacheStride); 645 } 646 647 void HOGCacheTester::init(const HOGDescriptorTester* _descriptor, 648 const Mat& _img, Size _paddingTL, Size _paddingBR, 649 bool _useCache, Size _cacheStride) 650 { 651 descriptor = _descriptor; 652 cacheStride = _cacheStride; 653 useCache = _useCache; 654 655 descriptor->computeGradient(_img, grad, qangle, _paddingTL, _paddingBR); 656 imgoffset = _paddingTL; 657 658 winSize = descriptor->winSize; 659 Size blockSize = descriptor->blockSize; 660 Size blockStride = descriptor->blockStride; 661 Size cellSize = descriptor->cellSize; 662 int i, j, nbins = descriptor->nbins; 663 int rawBlockSize = blockSize.width*blockSize.height; 664 665 nblocks = Size((winSize.width - blockSize.width)/blockStride.width + 1, 666 (winSize.height - blockSize.height)/blockStride.height + 1); 667 ncells = Size(blockSize.width/cellSize.width, blockSize.height/cellSize.height); 668 blockHistogramSize = ncells.width*ncells.height*nbins; 669 670 if( useCache ) 671 { 672 Size cacheSize((grad.cols - blockSize.width)/cacheStride.width+1, 673 (winSize.height/cacheStride.height)+1); 674 blockCache.create(cacheSize.height, cacheSize.width*blockHistogramSize); 675 blockCacheFlags.create(cacheSize); 676 size_t cacheRows = blockCache.rows; 677 ymaxCached.resize(cacheRows); 678 for(size_t ii = 0; ii < cacheRows; ii++ ) 679 ymaxCached[ii] = -1; 680 } 681 682 Mat_<float> weights(blockSize); 683 float sigma = (float)descriptor->getWinSigma(); 684 float scale = 1.f/(sigma*sigma*2); 685 686 for(i = 0; i < blockSize.height; i++) 687 for(j = 0; j < blockSize.width; j++) 688 { 689 float di = i - blockSize.height*0.5f; 690 float dj = j - blockSize.width*0.5f; 691 weights(i,j) = std::exp(-(di*di + dj*dj)*scale); 692 } 693 694 blockData.resize(nblocks.width*nblocks.height); 695 pixData.resize(rawBlockSize*3); 696 697 // Initialize 2 lookup tables, pixData & blockData. 698 // Here is why: 699 // 700 // The detection algorithm runs in 4 nested loops (at each pyramid layer): 701 // loop over the windows within the input image 702 // loop over the blocks within each window 703 // loop over the cells within each block 704 // loop over the pixels in each cell 705 // 706 // As each of the loops runs over a 2-dimensional array, 707 // we could get 8(!) nested loops in total, which is very-very slow. 708 // 709 // To speed the things up, we do the following: 710 // 1. loop over windows is unrolled in the HOGDescriptor::{compute|detect} methods; 711 // inside we compute the current search window using getWindow() method. 712 // Yes, it involves some overhead (function call + couple of divisions), 713 // but it's tiny in fact. 714 // 2. loop over the blocks is also unrolled. Inside we use pre-computed blockData[j] 715 // to set up gradient and histogram pointers. 716 // 3. loops over cells and pixels in each cell are merged 717 // (since there is no overlap between cells, each pixel in the block is processed once) 718 // and also unrolled. Inside we use PixData[k] to access the gradient values and 719 // update the histogram 720 // 721 count1 = count2 = count4 = 0; 722 for( j = 0; j < blockSize.width; j++ ) 723 for( i = 0; i < blockSize.height; i++ ) 724 { 725 PixData* data = 0; 726 float cellX = (j+0.5f)/cellSize.width - 0.5f; 727 float cellY = (i+0.5f)/cellSize.height - 0.5f; 728 int icellX0 = cvFloor(cellX); 729 int icellY0 = cvFloor(cellY); 730 int icellX1 = icellX0 + 1, icellY1 = icellY0 + 1; 731 cellX -= icellX0; 732 cellY -= icellY0; 733 734 if( (unsigned)icellX0 < (unsigned)ncells.width && 735 (unsigned)icellX1 < (unsigned)ncells.width ) 736 { 737 if( (unsigned)icellY0 < (unsigned)ncells.height && 738 (unsigned)icellY1 < (unsigned)ncells.height ) 739 { 740 data = &pixData[rawBlockSize*2 + (count4++)]; 741 data->histOfs[0] = (icellX0*ncells.height + icellY0)*nbins; 742 data->histWeights[0] = (1.f - cellX)*(1.f - cellY); 743 data->histOfs[1] = (icellX1*ncells.height + icellY0)*nbins; 744 data->histWeights[1] = cellX*(1.f - cellY); 745 data->histOfs[2] = (icellX0*ncells.height + icellY1)*nbins; 746 data->histWeights[2] = (1.f - cellX)*cellY; 747 data->histOfs[3] = (icellX1*ncells.height + icellY1)*nbins; 748 data->histWeights[3] = cellX*cellY; 749 } 750 else 751 { 752 data = &pixData[rawBlockSize + (count2++)]; 753 if( (unsigned)icellY0 < (unsigned)ncells.height ) 754 { 755 icellY1 = icellY0; 756 cellY = 1.f - cellY; 757 } 758 data->histOfs[0] = (icellX0*ncells.height + icellY1)*nbins; 759 data->histWeights[0] = (1.f - cellX)*cellY; 760 data->histOfs[1] = (icellX1*ncells.height + icellY1)*nbins; 761 data->histWeights[1] = cellX*cellY; 762 data->histOfs[2] = data->histOfs[3] = 0; 763 data->histWeights[2] = data->histWeights[3] = 0; 764 } 765 } 766 else 767 { 768 if( (unsigned)icellX0 < (unsigned)ncells.width ) 769 { 770 icellX1 = icellX0; 771 cellX = 1.f - cellX; 772 } 773 774 if( (unsigned)icellY0 < (unsigned)ncells.height && 775 (unsigned)icellY1 < (unsigned)ncells.height ) 776 { 777 data = &pixData[rawBlockSize + (count2++)]; 778 data->histOfs[0] = (icellX1*ncells.height + icellY0)*nbins; 779 data->histWeights[0] = cellX*(1.f - cellY); 780 data->histOfs[1] = (icellX1*ncells.height + icellY1)*nbins; 781 data->histWeights[1] = cellX*cellY; 782 data->histOfs[2] = data->histOfs[3] = 0; 783 data->histWeights[2] = data->histWeights[3] = 0; 784 } 785 else 786 { 787 data = &pixData[count1++]; 788 if( (unsigned)icellY0 < (unsigned)ncells.height ) 789 { 790 icellY1 = icellY0; 791 cellY = 1.f - cellY; 792 } 793 data->histOfs[0] = (icellX1*ncells.height + icellY1)*nbins; 794 data->histWeights[0] = cellX*cellY; 795 data->histOfs[1] = data->histOfs[2] = data->histOfs[3] = 0; 796 data->histWeights[1] = data->histWeights[2] = data->histWeights[3] = 0; 797 } 798 } 799 data->gradOfs = (grad.cols*i + j)*2; 800 data->qangleOfs = (qangle.cols*i + j)*2; 801 data->gradWeight = weights(i,j); 802 } 803 804 assert( count1 + count2 + count4 == rawBlockSize ); 805 // defragment pixData 806 for( j = 0; j < count2; j++ ) 807 pixData[j + count1] = pixData[j + rawBlockSize]; 808 for( j = 0; j < count4; j++ ) 809 pixData[j + count1 + count2] = pixData[j + rawBlockSize*2]; 810 count2 += count1; 811 count4 += count2; 812 813 // initialize blockData 814 for( j = 0; j < nblocks.width; j++ ) 815 for( i = 0; i < nblocks.height; i++ ) 816 { 817 BlockData& data = blockData[j*nblocks.height + i]; 818 data.histOfs = (j*nblocks.height + i)*blockHistogramSize; 819 data.imgOffset = Point(j*blockStride.width,i*blockStride.height); 820 } 821 } 822 823 const float* HOGCacheTester::getBlock(Point pt, float* buf) 824 { 825 float* blockHist = buf; 826 assert(descriptor != 0); 827 828 Size blockSize = descriptor->blockSize; 829 pt += imgoffset; 830 831 CV_Assert( (unsigned)pt.x <= (unsigned)(grad.cols - blockSize.width) && 832 (unsigned)pt.y <= (unsigned)(grad.rows - blockSize.height) ); 833 834 if( useCache ) 835 { 836 CV_Assert( pt.x % cacheStride.width == 0 && 837 pt.y % cacheStride.height == 0 ); 838 Point cacheIdx(pt.x/cacheStride.width, 839 (pt.y/cacheStride.height) % blockCache.rows); 840 if( pt.y != ymaxCached[cacheIdx.y] ) 841 { 842 Mat_<uchar> cacheRow = blockCacheFlags.row(cacheIdx.y); 843 cacheRow = (uchar)0; 844 ymaxCached[cacheIdx.y] = pt.y; 845 } 846 847 blockHist = &blockCache[cacheIdx.y][cacheIdx.x*blockHistogramSize]; 848 uchar& computedFlag = blockCacheFlags(cacheIdx.y, cacheIdx.x); 849 if( computedFlag != 0 ) 850 return blockHist; 851 computedFlag = (uchar)1; // set it at once, before actual computing 852 } 853 854 int k, C1 = count1, C2 = count2, C4 = count4; 855 const float* gradPtr = grad.ptr<float>(pt.y) + pt.x*2; 856 const uchar* qanglePtr = qangle.ptr(pt.y) + pt.x*2; 857 858 CV_Assert( blockHist != 0 ); 859 for( k = 0; k < blockHistogramSize; k++ ) 860 blockHist[k] = 0.f; 861 862 const PixData* _pixData = &pixData[0]; 863 864 for( k = 0; k < C1; k++ ) 865 { 866 const PixData& pk = _pixData[k]; 867 const float* a = gradPtr + pk.gradOfs; 868 float w = pk.gradWeight*pk.histWeights[0]; 869 const uchar* h = qanglePtr + pk.qangleOfs; 870 int h0 = h[0], h1 = h[1]; 871 float* hist = blockHist + pk.histOfs[0]; 872 float t0 = hist[h0] + a[0]*w; 873 float t1 = hist[h1] + a[1]*w; 874 hist[h0] = t0; hist[h1] = t1; 875 } 876 877 for( ; k < C2; k++ ) 878 { 879 const PixData& pk = _pixData[k]; 880 const float* a = gradPtr + pk.gradOfs; 881 float w, t0, t1, a0 = a[0], a1 = a[1]; 882 const uchar* h = qanglePtr + pk.qangleOfs; 883 int h0 = h[0], h1 = h[1]; 884 885 float* hist = blockHist + pk.histOfs[0]; 886 w = pk.gradWeight*pk.histWeights[0]; 887 t0 = hist[h0] + a0*w; 888 t1 = hist[h1] + a1*w; 889 hist[h0] = t0; hist[h1] = t1; 890 891 hist = blockHist + pk.histOfs[1]; 892 w = pk.gradWeight*pk.histWeights[1]; 893 t0 = hist[h0] + a0*w; 894 t1 = hist[h1] + a1*w; 895 hist[h0] = t0; hist[h1] = t1; 896 } 897 898 for( ; k < C4; k++ ) 899 { 900 const PixData& pk = _pixData[k]; 901 const float* a = gradPtr + pk.gradOfs; 902 float w, t0, t1, a0 = a[0], a1 = a[1]; 903 const uchar* h = qanglePtr + pk.qangleOfs; 904 int h0 = h[0], h1 = h[1]; 905 906 float* hist = blockHist + pk.histOfs[0]; 907 w = pk.gradWeight*pk.histWeights[0]; 908 t0 = hist[h0] + a0*w; 909 t1 = hist[h1] + a1*w; 910 hist[h0] = t0; hist[h1] = t1; 911 912 hist = blockHist + pk.histOfs[1]; 913 w = pk.gradWeight*pk.histWeights[1]; 914 t0 = hist[h0] + a0*w; 915 t1 = hist[h1] + a1*w; 916 hist[h0] = t0; hist[h1] = t1; 917 918 hist = blockHist + pk.histOfs[2]; 919 w = pk.gradWeight*pk.histWeights[2]; 920 t0 = hist[h0] + a0*w; 921 t1 = hist[h1] + a1*w; 922 hist[h0] = t0; hist[h1] = t1; 923 924 hist = blockHist + pk.histOfs[3]; 925 w = pk.gradWeight*pk.histWeights[3]; 926 t0 = hist[h0] + a0*w; 927 t1 = hist[h1] + a1*w; 928 hist[h0] = t0; hist[h1] = t1; 929 } 930 931 normalizeBlockHistogram(blockHist); 932 933 return blockHist; 934 } 935 936 void HOGCacheTester::normalizeBlockHistogram(float* _hist) const 937 { 938 float* hist = &_hist[0], partSum[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; 939 size_t i, sz = blockHistogramSize; 940 941 for (i = 0; i <= sz - 4; i += 4) 942 { 943 partSum[0] += hist[i] * hist[i]; 944 partSum[1] += hist[i+1] * hist[i+1]; 945 partSum[2] += hist[i+2] * hist[i+2]; 946 partSum[3] += hist[i+3] * hist[i+3]; 947 } 948 float t0 = partSum[0] + partSum[1]; 949 float t1 = partSum[2] + partSum[3]; 950 float sum = t0 + t1; 951 for( ; i < sz; i++ ) 952 sum += hist[i]*hist[i]; 953 954 float scale = 1.f/(std::sqrt(sum)+sz*0.1f), thresh = (float)descriptor->L2HysThreshold; 955 partSum[0] = partSum[1] = partSum[2] = partSum[3] = 0.0f; 956 for(i = 0; i <= sz - 4; i += 4) 957 { 958 hist[i] = std::min(hist[i]*scale, thresh); 959 hist[i+1] = std::min(hist[i+1]*scale, thresh); 960 hist[i+2] = std::min(hist[i+2]*scale, thresh); 961 hist[i+3] = std::min(hist[i+3]*scale, thresh); 962 partSum[0] += hist[i]*hist[i]; 963 partSum[1] += hist[i+1]*hist[i+1]; 964 partSum[2] += hist[i+2]*hist[i+2]; 965 partSum[3] += hist[i+3]*hist[i+3]; 966 } 967 t0 = partSum[0] + partSum[1]; 968 t1 = partSum[2] + partSum[3]; 969 sum = t0 + t1; 970 for( ; i < sz; i++ ) 971 { 972 hist[i] = std::min(hist[i]*scale, thresh); 973 sum += hist[i]*hist[i]; 974 } 975 976 scale = 1.f/(std::sqrt(sum)+1e-3f); 977 for( i = 0; i < sz; i++ ) 978 hist[i] *= scale; 979 } 980 981 Size HOGCacheTester::windowsInImage(Size imageSize, Size winStride) const 982 { 983 return Size((imageSize.width - winSize.width)/winStride.width + 1, 984 (imageSize.height - winSize.height)/winStride.height + 1); 985 } 986 987 Rect HOGCacheTester::getWindow(Size imageSize, Size winStride, int idx) const 988 { 989 int nwindowsX = (imageSize.width - winSize.width)/winStride.width + 1; 990 int y = idx / nwindowsX; 991 int x = idx - nwindowsX*y; 992 return Rect( x*winStride.width, y*winStride.height, winSize.width, winSize.height ); 993 } 994 995 inline bool HOGDescriptorTester::is_failed() const 996 { 997 return failed; 998 } 999 1000 static inline int gcd(int a, int b) { return (a % b == 0) ? b : gcd (b, a % b); } 1001 1002 void HOGDescriptorTester::detect(const Mat& img, 1003 vector<Point>& hits, vector<double>& weights, double hitThreshold, 1004 Size winStride, Size padding, const vector<Point>& locations) const 1005 { 1006 if (failed) 1007 return; 1008 1009 hits.clear(); 1010 if( svmDetector.empty() ) 1011 return; 1012 1013 if( winStride == Size() ) 1014 winStride = cellSize; 1015 Size cacheStride(gcd(winStride.width, blockStride.width), 1016 gcd(winStride.height, blockStride.height)); 1017 size_t nwindows = locations.size(); 1018 padding.width = (int)alignSize(std::max(padding.width, 0), cacheStride.width); 1019 padding.height = (int)alignSize(std::max(padding.height, 0), cacheStride.height); 1020 Size paddedImgSize(img.cols + padding.width*2, img.rows + padding.height*2); 1021 1022 HOGCacheTester cache(this, img, padding, padding, nwindows == 0, cacheStride); 1023 1024 if( !nwindows ) 1025 nwindows = cache.windowsInImage(paddedImgSize, winStride).area(); 1026 1027 const HOGCacheTester::BlockData* blockData = &cache.blockData[0]; 1028 1029 int nblocks = cache.nblocks.area(); 1030 int blockHistogramSize = cache.blockHistogramSize; 1031 size_t dsize = getDescriptorSize(); 1032 1033 double rho = svmDetector.size() > dsize ? svmDetector[dsize] : 0; 1034 vector<float> blockHist(blockHistogramSize); 1035 1036 for( size_t i = 0; i < nwindows; i++ ) 1037 { 1038 Point pt0; 1039 if( !locations.empty() ) 1040 { 1041 pt0 = locations[i]; 1042 if( pt0.x < -padding.width || pt0.x > img.cols + padding.width - winSize.width || 1043 pt0.y < -padding.height || pt0.y > img.rows + padding.height - winSize.height ) 1044 continue; 1045 } 1046 else 1047 { 1048 pt0 = cache.getWindow(paddedImgSize, winStride, (int)i).tl() - Point(padding); 1049 CV_Assert(pt0.x % cacheStride.width == 0 && pt0.y % cacheStride.height == 0); 1050 } 1051 double s = rho; 1052 const float* svmVec = &svmDetector[0]; 1053 int j, k; 1054 for( j = 0; j < nblocks; j++, svmVec += blockHistogramSize ) 1055 { 1056 const HOGCacheTester::BlockData& bj = blockData[j]; 1057 Point pt = pt0 + bj.imgOffset; 1058 1059 const float* vec = cache.getBlock(pt, &blockHist[0]); 1060 for( k = 0; k <= blockHistogramSize - 4; k += 4 ) 1061 s += vec[k]*svmVec[k] + vec[k+1]*svmVec[k+1] + 1062 vec[k+2]*svmVec[k+2] + vec[k+3]*svmVec[k+3]; 1063 for( ; k < blockHistogramSize; k++ ) 1064 s += vec[k]*svmVec[k]; 1065 } 1066 if( s >= hitThreshold ) 1067 { 1068 hits.push_back(pt0); 1069 weights.push_back(s); 1070 } 1071 } 1072 1073 // validation 1074 std::vector<Point> actual_find_locations; 1075 std::vector<double> actual_weights; 1076 actual_hog->detect(img, actual_find_locations, actual_weights, 1077 hitThreshold, winStride, padding, locations); 1078 1079 if (!std::equal(hits.begin(), hits.end(), 1080 actual_find_locations.begin())) 1081 { 1082 ts->printf(cvtest::TS::SUMMARY, "Found locations are not equal (see detect function)\n"); 1083 ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); 1084 ts->set_gtest_status(); 1085 failed = true; 1086 return; 1087 } 1088 1089 const double eps = 0.0; 1090 double diff_norm = cvtest::norm(actual_weights, weights, NORM_L2); 1091 if (diff_norm > eps) 1092 { 1093 ts->printf(cvtest::TS::SUMMARY, "Weights for found locations aren't equal.\n" 1094 "Norm of the difference is %lf\n", diff_norm); 1095 ts->printf(cvtest::TS::LOG, "Channels: %d\n", img.channels()); 1096 failed = true; 1097 ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); 1098 ts->set_gtest_status(); 1099 return; 1100 } 1101 } 1102 1103 void HOGDescriptorTester::detect(const Mat& img, vector<Point>& hits, double hitThreshold, 1104 Size winStride, Size padding, const vector<Point>& locations) const 1105 { 1106 vector<double> weightsV; 1107 detect(img, hits, weightsV, hitThreshold, winStride, padding, locations); 1108 } 1109 1110 void HOGDescriptorTester::compute(InputArray _img, vector<float>& descriptors, 1111 Size winStride, Size padding, const vector<Point>& locations) const 1112 { 1113 Mat img = _img.getMat(); 1114 1115 if( winStride == Size() ) 1116 winStride = cellSize; 1117 Size cacheStride(gcd(winStride.width, blockStride.width), 1118 gcd(winStride.height, blockStride.height)); 1119 size_t nwindows = locations.size(); 1120 padding.width = (int)alignSize(std::max(padding.width, 0), cacheStride.width); 1121 padding.height = (int)alignSize(std::max(padding.height, 0), cacheStride.height); 1122 Size paddedImgSize(img.cols + padding.width*2, img.rows + padding.height*2); 1123 1124 HOGCacheTester cache(this, img, padding, padding, nwindows == 0, cacheStride); 1125 1126 if( !nwindows ) 1127 nwindows = cache.windowsInImage(paddedImgSize, winStride).area(); 1128 1129 const HOGCacheTester::BlockData* blockData = &cache.blockData[0]; 1130 1131 int nblocks = cache.nblocks.area(); 1132 int blockHistogramSize = cache.blockHistogramSize; 1133 size_t dsize = getDescriptorSize(); 1134 descriptors.resize(dsize*nwindows); 1135 1136 for( size_t i = 0; i < nwindows; i++ ) 1137 { 1138 float* descriptor = &descriptors[i*dsize]; 1139 1140 Point pt0; 1141 if( !locations.empty() ) 1142 { 1143 pt0 = locations[i]; 1144 if( pt0.x < -padding.width || pt0.x > img.cols + padding.width - winSize.width || 1145 pt0.y < -padding.height || pt0.y > img.rows + padding.height - winSize.height ) 1146 continue; 1147 } 1148 else 1149 { 1150 pt0 = cache.getWindow(paddedImgSize, winStride, (int)i).tl() - Point(padding); 1151 CV_Assert(pt0.x % cacheStride.width == 0 && pt0.y % cacheStride.height == 0); 1152 } 1153 1154 for( int j = 0; j < nblocks; j++ ) 1155 { 1156 const HOGCacheTester::BlockData& bj = blockData[j]; 1157 Point pt = pt0 + bj.imgOffset; 1158 1159 float* dst = descriptor + bj.histOfs; 1160 const float* src = cache.getBlock(pt, dst); 1161 if( src != dst ) 1162 for( int k = 0; k < blockHistogramSize; k++ ) 1163 dst[k] = src[k]; 1164 } 1165 } 1166 1167 // validation 1168 std::vector<float> actual_descriptors; 1169 actual_hog->compute(img, actual_descriptors, winStride, padding, locations); 1170 1171 double diff_norm = cvtest::norm(actual_descriptors, descriptors, NORM_L2); 1172 const double eps = 0.0; 1173 if (diff_norm > eps) 1174 { 1175 ts->printf(cvtest::TS::SUMMARY, "Norm of the difference: %lf\n", diff_norm); 1176 ts->printf(cvtest::TS::SUMMARY, "Found descriptors are not equal (see compute function)\n"); 1177 ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); 1178 ts->printf(cvtest::TS::LOG, "Channels: %d\n", img.channels()); 1179 ts->set_gtest_status(); 1180 failed = true; 1181 return; 1182 } 1183 } 1184 1185 void HOGDescriptorTester::computeGradient(const Mat& img, Mat& grad, Mat& qangle, 1186 Size paddingTL, Size paddingBR) const 1187 { 1188 CV_Assert( img.type() == CV_8U || img.type() == CV_8UC3 ); 1189 1190 Size gradsize(img.cols + paddingTL.width + paddingBR.width, 1191 img.rows + paddingTL.height + paddingBR.height); 1192 grad.create(gradsize, CV_32FC2); // <magnitude*(1-alpha), magnitude*alpha> 1193 qangle.create(gradsize, CV_8UC2); // [0..nbins-1] - quantized gradient orientation 1194 Size wholeSize; 1195 Point roiofs; 1196 img.locateROI(wholeSize, roiofs); 1197 1198 int i, x, y; 1199 int cn = img.channels(); 1200 1201 Mat_<float> _lut(1, 256); 1202 const float* lut = &_lut(0,0); 1203 1204 if( gammaCorrection ) 1205 for( i = 0; i < 256; i++ ) 1206 _lut(0,i) = std::sqrt((float)i); 1207 else 1208 for( i = 0; i < 256; i++ ) 1209 _lut(0,i) = (float)i; 1210 1211 AutoBuffer<int> mapbuf(gradsize.width + gradsize.height + 4); 1212 int* xmap = (int*)mapbuf + 1; 1213 int* ymap = xmap + gradsize.width + 2; 1214 1215 const int borderType = (int)BORDER_REFLECT_101; 1216 1217 for( x = -1; x < gradsize.width + 1; x++ ) 1218 xmap[x] = borderInterpolate(x - paddingTL.width + roiofs.x, 1219 wholeSize.width, borderType) - roiofs.x; 1220 for( y = -1; y < gradsize.height + 1; y++ ) 1221 ymap[y] = borderInterpolate(y - paddingTL.height + roiofs.y, 1222 wholeSize.height, borderType) - roiofs.y; 1223 1224 // x- & y- derivatives for the whole row 1225 int width = gradsize.width; 1226 AutoBuffer<float> _dbuf(width*4); 1227 float* dbuf = _dbuf; 1228 Mat Dx(1, width, CV_32F, dbuf); 1229 Mat Dy(1, width, CV_32F, dbuf + width); 1230 Mat Mag(1, width, CV_32F, dbuf + width*2); 1231 Mat Angle(1, width, CV_32F, dbuf + width*3); 1232 1233 int _nbins = nbins; 1234 float angleScale = (float)(_nbins/CV_PI); 1235 for( y = 0; y < gradsize.height; y++ ) 1236 { 1237 const uchar* imgPtr = img.ptr(ymap[y]); 1238 const uchar* prevPtr = img.ptr(ymap[y-1]); 1239 const uchar* nextPtr = img.ptr(ymap[y+1]); 1240 float* gradPtr = (float*)grad.ptr(y); 1241 uchar* qanglePtr = (uchar*)qangle.ptr(y); 1242 1243 if( cn == 1 ) 1244 { 1245 for( x = 0; x < width; x++ ) 1246 { 1247 int x1 = xmap[x]; 1248 dbuf[x] = (float)(lut[imgPtr[xmap[x+1]]] - lut[imgPtr[xmap[x-1]]]); 1249 dbuf[width + x] = (float)(lut[nextPtr[x1]] - lut[prevPtr[x1]]); 1250 } 1251 } 1252 else 1253 { 1254 for( x = 0; x < width; x++ ) 1255 { 1256 int x1 = xmap[x]*3; 1257 float dx0, dy0, dx, dy, mag0, mag; 1258 const uchar* p2 = imgPtr + xmap[x+1]*3; 1259 const uchar* p0 = imgPtr + xmap[x-1]*3; 1260 1261 dx0 = lut[p2[2]] - lut[p0[2]]; 1262 dy0 = lut[nextPtr[x1+2]] - lut[prevPtr[x1+2]]; 1263 mag0 = dx0*dx0 + dy0*dy0; 1264 1265 dx = lut[p2[1]] - lut[p0[1]]; 1266 dy = lut[nextPtr[x1+1]] - lut[prevPtr[x1+1]]; 1267 mag = dx*dx + dy*dy; 1268 1269 if( mag0 < mag ) 1270 { 1271 dx0 = dx; 1272 dy0 = dy; 1273 mag0 = mag; 1274 } 1275 1276 dx = lut[p2[0]] - lut[p0[0]]; 1277 dy = lut[nextPtr[x1]] - lut[prevPtr[x1]]; 1278 mag = dx*dx + dy*dy; 1279 1280 if( mag0 < mag ) 1281 { 1282 dx0 = dx; 1283 dy0 = dy; 1284 mag0 = mag; 1285 } 1286 1287 dbuf[x] = dx0; 1288 dbuf[x+width] = dy0; 1289 } 1290 } 1291 1292 cartToPolar( Dx, Dy, Mag, Angle, false ); 1293 for( x = 0; x < width; x++ ) 1294 { 1295 float mag = dbuf[x+width*2], angle = dbuf[x+width*3]*angleScale - 0.5f; 1296 int hidx = cvFloor(angle); 1297 angle -= hidx; 1298 gradPtr[x*2] = mag*(1.f - angle); 1299 gradPtr[x*2+1] = mag*angle; 1300 if( hidx < 0 ) 1301 hidx += _nbins; 1302 else if( hidx >= _nbins ) 1303 hidx -= _nbins; 1304 assert( (unsigned)hidx < (unsigned)_nbins ); 1305 1306 qanglePtr[x*2] = (uchar)hidx; 1307 hidx++; 1308 hidx &= hidx < _nbins ? -1 : 0; 1309 qanglePtr[x*2+1] = (uchar)hidx; 1310 } 1311 } 1312 1313 // validation 1314 Mat actual_mats[2], reference_mats[2] = { grad, qangle }; 1315 const char* args[] = { "Gradient's", "Qangles's" }; 1316 actual_hog->computeGradient(img, actual_mats[0], actual_mats[1], paddingTL, paddingBR); 1317 1318 const double eps = 0.0; 1319 for (i = 0; i < 2; ++i) 1320 { 1321 double diff_norm = cvtest::norm(reference_mats[i], actual_mats[i], NORM_L2); 1322 if (diff_norm > eps) 1323 { 1324 ts->printf(cvtest::TS::LOG, "%s matrices are not equal\n" 1325 "Norm of the difference is %lf\n", args[i], diff_norm); 1326 ts->printf(cvtest::TS::LOG, "Channels: %d\n", img.channels()); 1327 ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); 1328 ts->set_gtest_status(); 1329 failed = true; 1330 return; 1331 } 1332 } 1333 } 1334 1335 TEST(Objdetect_HOGDetector_Strict, accuracy) 1336 { 1337 cvtest::TS* ts = cvtest::TS::ptr(); 1338 RNG& rng = ts->get_rng(); 1339 1340 HOGDescriptor actual_hog; 1341 actual_hog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector()); 1342 HOGDescriptorTester reference_hog(actual_hog); 1343 1344 const unsigned int test_case_count = 5; 1345 for (unsigned int i = 0; i < test_case_count && !reference_hog.is_failed(); ++i) 1346 { 1347 // creating a matrix 1348 Size ssize(rng.uniform(1, 10) * actual_hog.winSize.width, 1349 rng.uniform(1, 10) * actual_hog.winSize.height); 1350 int type = rng.uniform(0, 1) > 0 ? CV_8UC1 : CV_8UC3; 1351 Mat image(ssize, type); 1352 rng.fill(image, RNG::UNIFORM, 0, 256, true); 1353 1354 // checking detect 1355 std::vector<Point> hits; 1356 std::vector<double> weights; 1357 reference_hog.detect(image, hits, weights); 1358 1359 // checking compute 1360 std::vector<float> descriptors; 1361 reference_hog.compute(image, descriptors); 1362 } 1363 } 1364 1365 TEST(Objdetect_CascadeDetector, small_img) 1366 { 1367 String root = cvtest::TS::ptr()->get_data_path() + "cascadeandhog/cascades/"; 1368 String cascades[] = 1369 { 1370 root + "haarcascade_frontalface_alt.xml", 1371 root + "lbpcascade_frontalface.xml", 1372 String() 1373 }; 1374 1375 vector<Rect> objects; 1376 RNG rng((uint64)-1); 1377 1378 for( int i = 0; !cascades[i].empty(); i++ ) 1379 { 1380 printf("%d. %s\n", i, cascades[i].c_str()); 1381 CascadeClassifier cascade(cascades[i]); 1382 for( int j = 0; j < 100; j++ ) 1383 { 1384 int width = rng.uniform(1, 100); 1385 int height = rng.uniform(1, 100); 1386 Mat img(height, width, CV_8U); 1387 randu(img, 0, 256); 1388 cascade.detectMultiScale(img, objects); 1389 } 1390 } 1391 } 1392