test_cascadeandhog.cpp 46 KB

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