test_stereomatching.cpp 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945
  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. This is a regression test for stereo matching algorithms. This test gets some quality metrics
  43. described in "A Taxonomy and Evaluation of Dense Two-Frame Stereo Correspondence Algorithms".
  44. Daniel Scharstein, Richard Szeliski
  45. */
  46. #include "test_precomp.hpp"
  47. namespace opencv_test { namespace {
  48. const float EVAL_BAD_THRESH = 1.f;
  49. const int EVAL_TEXTURELESS_WIDTH = 3;
  50. const float EVAL_TEXTURELESS_THRESH = 4.f;
  51. const float EVAL_DISP_THRESH = 1.f;
  52. const float EVAL_DISP_GAP = 2.f;
  53. const int EVAL_DISCONT_WIDTH = 9;
  54. const int EVAL_IGNORE_BORDER = 10;
  55. const int ERROR_KINDS_COUNT = 6;
  56. //============================== quality measuring functions =================================================
  57. /*
  58. Calculate textureless regions of image (regions where the squared horizontal intensity gradient averaged over
  59. a square window of size=evalTexturelessWidth is below a threshold=evalTexturelessThresh) and textured regions.
  60. */
  61. void computeTextureBasedMasks( const Mat& _img, Mat* texturelessMask, Mat* texturedMask,
  62. int texturelessWidth = EVAL_TEXTURELESS_WIDTH, float texturelessThresh = EVAL_TEXTURELESS_THRESH )
  63. {
  64. if( !texturelessMask && !texturedMask )
  65. return;
  66. if( _img.empty() )
  67. CV_Error( Error::StsBadArg, "img is empty" );
  68. Mat img = _img;
  69. if( _img.channels() > 1)
  70. {
  71. Mat tmp; cvtColor( _img, tmp, COLOR_BGR2GRAY ); img = tmp;
  72. }
  73. Mat dxI; Sobel( img, dxI, CV_32FC1, 1, 0, 3 );
  74. Mat dxI2; pow( dxI / 8.f/*normalize*/, 2, dxI2 );
  75. Mat avgDxI2; boxFilter( dxI2, avgDxI2, CV_32FC1, Size(texturelessWidth,texturelessWidth) );
  76. if( texturelessMask )
  77. *texturelessMask = avgDxI2 < texturelessThresh;
  78. if( texturedMask )
  79. *texturedMask = avgDxI2 >= texturelessThresh;
  80. }
  81. void checkTypeAndSizeOfDisp( const Mat& dispMap, const Size* sz )
  82. {
  83. if( dispMap.empty() )
  84. CV_Error( Error::StsBadArg, "dispMap is empty" );
  85. if( dispMap.type() != CV_32FC1 )
  86. CV_Error( Error::StsBadArg, "dispMap must have CV_32FC1 type" );
  87. if( sz && (dispMap.rows != sz->height || dispMap.cols != sz->width) )
  88. CV_Error( Error::StsBadArg, "dispMap has incorrect size" );
  89. }
  90. void checkTypeAndSizeOfMask( const Mat& mask, Size sz )
  91. {
  92. if( mask.empty() )
  93. CV_Error( Error::StsBadArg, "mask is empty" );
  94. if( mask.type() != CV_8UC1 )
  95. CV_Error( Error::StsBadArg, "mask must have CV_8UC1 type" );
  96. if( mask.rows != sz.height || mask.cols != sz.width )
  97. CV_Error( Error::StsBadArg, "mask has incorrect size" );
  98. }
  99. void checkDispMapsAndUnknDispMasks( const Mat& leftDispMap, const Mat& rightDispMap,
  100. const Mat& leftUnknDispMask, const Mat& rightUnknDispMask )
  101. {
  102. // check type and size of disparity maps
  103. checkTypeAndSizeOfDisp( leftDispMap, 0 );
  104. if( !rightDispMap.empty() )
  105. {
  106. Size sz = leftDispMap.size();
  107. checkTypeAndSizeOfDisp( rightDispMap, &sz );
  108. }
  109. // check size and type of unknown disparity maps
  110. if( !leftUnknDispMask.empty() )
  111. checkTypeAndSizeOfMask( leftUnknDispMask, leftDispMap.size() );
  112. if( !rightUnknDispMask.empty() )
  113. checkTypeAndSizeOfMask( rightUnknDispMask, rightDispMap.size() );
  114. // check values of disparity maps (known disparity values musy be positive)
  115. double leftMinVal = 0, rightMinVal = 0;
  116. if( leftUnknDispMask.empty() )
  117. minMaxLoc( leftDispMap, &leftMinVal );
  118. else
  119. minMaxLoc( leftDispMap, &leftMinVal, 0, 0, 0, ~leftUnknDispMask );
  120. if( !rightDispMap.empty() )
  121. {
  122. if( rightUnknDispMask.empty() )
  123. minMaxLoc( rightDispMap, &rightMinVal );
  124. else
  125. minMaxLoc( rightDispMap, &rightMinVal, 0, 0, 0, ~rightUnknDispMask );
  126. }
  127. if( leftMinVal < 0 || rightMinVal < 0)
  128. CV_Error( Error::StsBadArg, "known disparity values must be positive" );
  129. }
  130. /*
  131. Calculate occluded regions of reference image (left image) (regions that are occluded in the matching image (right image),
  132. i.e., where the forward-mapped disparity lands at a location with a larger (nearer) disparity) and non occluded regions.
  133. */
  134. void computeOcclusionBasedMasks( const Mat& leftDisp, const Mat& _rightDisp,
  135. Mat* occludedMask, Mat* nonOccludedMask,
  136. const Mat& leftUnknDispMask = Mat(), const Mat& rightUnknDispMask = Mat(),
  137. float dispThresh = EVAL_DISP_THRESH )
  138. {
  139. if( !occludedMask && !nonOccludedMask )
  140. return;
  141. checkDispMapsAndUnknDispMasks( leftDisp, _rightDisp, leftUnknDispMask, rightUnknDispMask );
  142. Mat rightDisp;
  143. if( _rightDisp.empty() )
  144. {
  145. if( !rightUnknDispMask.empty() )
  146. CV_Error( Error::StsBadArg, "rightUnknDispMask must be empty if _rightDisp is empty" );
  147. rightDisp.create(leftDisp.size(), CV_32FC1);
  148. rightDisp.setTo(Scalar::all(0) );
  149. for( int leftY = 0; leftY < leftDisp.rows; leftY++ )
  150. {
  151. for( int leftX = 0; leftX < leftDisp.cols; leftX++ )
  152. {
  153. if( !leftUnknDispMask.empty() && leftUnknDispMask.at<uchar>(leftY,leftX) )
  154. continue;
  155. float leftDispVal = leftDisp.at<float>(leftY, leftX);
  156. int rightX = leftX - cvRound(leftDispVal), rightY = leftY;
  157. if( rightX >= 0)
  158. rightDisp.at<float>(rightY,rightX) = max(rightDisp.at<float>(rightY,rightX), leftDispVal);
  159. }
  160. }
  161. }
  162. else
  163. _rightDisp.copyTo(rightDisp);
  164. if( occludedMask )
  165. {
  166. occludedMask->create(leftDisp.size(), CV_8UC1);
  167. occludedMask->setTo(Scalar::all(0) );
  168. }
  169. if( nonOccludedMask )
  170. {
  171. nonOccludedMask->create(leftDisp.size(), CV_8UC1);
  172. nonOccludedMask->setTo(Scalar::all(0) );
  173. }
  174. for( int leftY = 0; leftY < leftDisp.rows; leftY++ )
  175. {
  176. for( int leftX = 0; leftX < leftDisp.cols; leftX++ )
  177. {
  178. if( !leftUnknDispMask.empty() && leftUnknDispMask.at<uchar>(leftY,leftX) )
  179. continue;
  180. float leftDispVal = leftDisp.at<float>(leftY, leftX);
  181. int rightX = leftX - cvRound(leftDispVal), rightY = leftY;
  182. if( rightX < 0 && occludedMask )
  183. occludedMask->at<uchar>(leftY, leftX) = 255;
  184. else
  185. {
  186. if( !rightUnknDispMask.empty() && rightUnknDispMask.at<uchar>(rightY,rightX) )
  187. continue;
  188. float rightDispVal = rightDisp.at<float>(rightY, rightX);
  189. if( rightDispVal > leftDispVal + dispThresh )
  190. {
  191. if( occludedMask )
  192. occludedMask->at<uchar>(leftY, leftX) = 255;
  193. }
  194. else
  195. {
  196. if( nonOccludedMask )
  197. nonOccludedMask->at<uchar>(leftY, leftX) = 255;
  198. }
  199. }
  200. }
  201. }
  202. }
  203. /*
  204. Calculate depth discontinuty regions: pixels whose neiboring disparities differ by more than
  205. dispGap, dilated by window of width discontWidth.
  206. */
  207. void computeDepthDiscontMask( const Mat& disp, Mat& depthDiscontMask, const Mat& unknDispMask = Mat(),
  208. float dispGap = EVAL_DISP_GAP, int discontWidth = EVAL_DISCONT_WIDTH )
  209. {
  210. if( disp.empty() )
  211. CV_Error( Error::StsBadArg, "disp is empty" );
  212. if( disp.type() != CV_32FC1 )
  213. CV_Error( Error::StsBadArg, "disp must have CV_32FC1 type" );
  214. if( !unknDispMask.empty() )
  215. checkTypeAndSizeOfMask( unknDispMask, disp.size() );
  216. Mat curDisp; disp.copyTo( curDisp );
  217. if( !unknDispMask.empty() )
  218. curDisp.setTo( Scalar(std::numeric_limits<float>::min()), unknDispMask );
  219. Mat maxNeighbDisp; dilate( curDisp, maxNeighbDisp, Mat(3, 3, CV_8UC1, Scalar(1)) );
  220. if( !unknDispMask.empty() )
  221. curDisp.setTo( Scalar(std::numeric_limits<float>::max()), unknDispMask );
  222. Mat minNeighbDisp; erode( curDisp, minNeighbDisp, Mat(3, 3, CV_8UC1, Scalar(1)) );
  223. depthDiscontMask = max( (Mat)(maxNeighbDisp-disp), (Mat)(disp-minNeighbDisp) ) > dispGap;
  224. if( !unknDispMask.empty() )
  225. depthDiscontMask &= ~unknDispMask;
  226. dilate( depthDiscontMask, depthDiscontMask, Mat(discontWidth, discontWidth, CV_8UC1, Scalar(1)) );
  227. }
  228. /*
  229. Get evaluation masks excluding a border.
  230. */
  231. Mat getBorderedMask( Size maskSize, int border = EVAL_IGNORE_BORDER )
  232. {
  233. CV_Assert( border >= 0 );
  234. Mat mask(maskSize, CV_8UC1, Scalar(0));
  235. int w = maskSize.width - 2*border, h = maskSize.height - 2*border;
  236. if( w < 0 || h < 0 )
  237. mask.setTo(Scalar(0));
  238. else
  239. mask( Rect(Point(border,border),Size(w,h)) ).setTo(Scalar(255));
  240. return mask;
  241. }
  242. /*
  243. Calculate root-mean-squared error between the computed disparity map (computedDisp) and ground truth map (groundTruthDisp).
  244. */
  245. float dispRMS( const Mat& computedDisp, const Mat& groundTruthDisp, const Mat& mask )
  246. {
  247. checkTypeAndSizeOfDisp( groundTruthDisp, 0 );
  248. Size sz = groundTruthDisp.size();
  249. checkTypeAndSizeOfDisp( computedDisp, &sz );
  250. int pointsCount = sz.height*sz.width;
  251. if( !mask.empty() )
  252. {
  253. checkTypeAndSizeOfMask( mask, sz );
  254. pointsCount = countNonZero(mask);
  255. }
  256. return 1.f/sqrt((float)pointsCount) * (float)cvtest::norm(computedDisp, groundTruthDisp, NORM_L2, mask);
  257. }
  258. /*
  259. Calculate fraction of bad matching pixels.
  260. */
  261. float badMatchPxlsFraction( const Mat& computedDisp, const Mat& groundTruthDisp, const Mat& mask,
  262. float _badThresh = EVAL_BAD_THRESH )
  263. {
  264. int badThresh = cvRound(_badThresh);
  265. checkTypeAndSizeOfDisp( groundTruthDisp, 0 );
  266. Size sz = groundTruthDisp.size();
  267. checkTypeAndSizeOfDisp( computedDisp, &sz );
  268. Mat badPxlsMap;
  269. absdiff( computedDisp, groundTruthDisp, badPxlsMap );
  270. badPxlsMap = badPxlsMap > badThresh;
  271. int pointsCount = sz.height*sz.width;
  272. if( !mask.empty() )
  273. {
  274. checkTypeAndSizeOfMask( mask, sz );
  275. badPxlsMap = badPxlsMap & mask;
  276. pointsCount = countNonZero(mask);
  277. }
  278. return 1.f/pointsCount * countNonZero(badPxlsMap);
  279. }
  280. //===================== regression test for stereo matching algorithms ==============================
  281. const string ALGORITHMS_DIR = "stereomatching/algorithms/";
  282. const string DATASETS_DIR = "stereomatching/datasets/";
  283. const string DATASETS_FILE = "datasets.xml";
  284. const string RUN_PARAMS_FILE = "_params.xml";
  285. const string RESULT_FILE = "_res.xml";
  286. const string LEFT_IMG_NAME = "im2.png";
  287. const string RIGHT_IMG_NAME = "im6.png";
  288. const string TRUE_LEFT_DISP_NAME = "disp2.png";
  289. const string TRUE_RIGHT_DISP_NAME = "disp6.png";
  290. string ERROR_PREFIXES[] = { "borderedAll",
  291. "borderedNoOccl",
  292. "borderedOccl",
  293. "borderedTextured",
  294. "borderedTextureless",
  295. "borderedDepthDiscont" }; // size of ERROR_KINDS_COUNT
  296. string ROI_PREFIXES[] = { "roiX",
  297. "roiY",
  298. "roiWidth",
  299. "roiHeight" };
  300. const string RMS_STR = "RMS";
  301. const string BAD_PXLS_FRACTION_STR = "BadPxlsFraction";
  302. const string ROI_STR = "ValidDisparityROI";
  303. class QualityEvalParams
  304. {
  305. public:
  306. QualityEvalParams() { setDefaults(); }
  307. QualityEvalParams( int _ignoreBorder )
  308. {
  309. setDefaults();
  310. ignoreBorder = _ignoreBorder;
  311. }
  312. void setDefaults()
  313. {
  314. badThresh = EVAL_BAD_THRESH;
  315. texturelessWidth = EVAL_TEXTURELESS_WIDTH;
  316. texturelessThresh = EVAL_TEXTURELESS_THRESH;
  317. dispThresh = EVAL_DISP_THRESH;
  318. dispGap = EVAL_DISP_GAP;
  319. discontWidth = EVAL_DISCONT_WIDTH;
  320. ignoreBorder = EVAL_IGNORE_BORDER;
  321. }
  322. float badThresh;
  323. int texturelessWidth;
  324. float texturelessThresh;
  325. float dispThresh;
  326. float dispGap;
  327. int discontWidth;
  328. int ignoreBorder;
  329. };
  330. class CV_StereoMatchingTest : public cvtest::BaseTest
  331. {
  332. public:
  333. CV_StereoMatchingTest()
  334. { rmsEps.resize( ERROR_KINDS_COUNT, 0.01f ); fracEps.resize( ERROR_KINDS_COUNT, 1.e-6f ); }
  335. protected:
  336. // assumed that left image is a reference image
  337. virtual int runStereoMatchingAlgorithm( const Mat& leftImg, const Mat& rightImg,
  338. Rect& calcROI, Mat& leftDisp, Mat& rightDisp, int caseIdx ) = 0; // return ignored border width
  339. int readDatasetsParams( FileStorage& fs );
  340. virtual int readRunParams( FileStorage& fs );
  341. void writeErrors( const string& errName, const vector<float>& errors, FileStorage* fs = 0 );
  342. void writeROI( const Rect& calcROI, FileStorage* fs = 0 );
  343. void readErrors( FileNode& fn, const string& errName, vector<float>& errors );
  344. void readROI( FileNode& fn, Rect& trueROI );
  345. int compareErrors( const vector<float>& calcErrors, const vector<float>& validErrors,
  346. const vector<float>& eps, const string& errName );
  347. int compareROI( const Rect& calcROI, const Rect& validROI );
  348. int processStereoMatchingResults( FileStorage& fs, int caseIdx, bool isWrite,
  349. const Mat& leftImg, const Mat& rightImg,
  350. const Rect& calcROI,
  351. const Mat& trueLeftDisp, const Mat& trueRightDisp,
  352. const Mat& leftDisp, const Mat& rightDisp,
  353. const QualityEvalParams& qualityEvalParams );
  354. void run( int );
  355. vector<float> rmsEps;
  356. vector<float> fracEps;
  357. struct DatasetParams
  358. {
  359. int dispScaleFactor;
  360. int dispUnknVal;
  361. };
  362. map<string, DatasetParams> datasetsParams;
  363. vector<string> caseNames;
  364. vector<string> caseDatasets;
  365. };
  366. void CV_StereoMatchingTest::run(int)
  367. {
  368. string dataPath = ts->get_data_path() + "cv/";
  369. string algorithmName = name;
  370. CV_Assert( !algorithmName.empty() );
  371. if( dataPath.empty() )
  372. {
  373. ts->printf( cvtest::TS::LOG, "dataPath is empty" );
  374. ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ARG_CHECK );
  375. return;
  376. }
  377. FileStorage datasetsFS( dataPath + DATASETS_DIR + DATASETS_FILE, FileStorage::READ );
  378. int code = readDatasetsParams( datasetsFS );
  379. if( code != cvtest::TS::OK )
  380. {
  381. ts->set_failed_test_info( code );
  382. return;
  383. }
  384. FileStorage runParamsFS( dataPath + ALGORITHMS_DIR + algorithmName + RUN_PARAMS_FILE, FileStorage::READ );
  385. code = readRunParams( runParamsFS );
  386. if( code != cvtest::TS::OK )
  387. {
  388. ts->set_failed_test_info( code );
  389. return;
  390. }
  391. string fullResultFilename = dataPath + ALGORITHMS_DIR + algorithmName + RESULT_FILE;
  392. FileStorage resFS( fullResultFilename, FileStorage::READ );
  393. bool isWrite = true; // write or compare results
  394. if( resFS.isOpened() )
  395. isWrite = false;
  396. else
  397. {
  398. resFS.open( fullResultFilename, FileStorage::WRITE );
  399. if( !resFS.isOpened() )
  400. {
  401. ts->printf( cvtest::TS::LOG, "file %s can not be read or written\n", fullResultFilename.c_str() );
  402. ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ARG_CHECK );
  403. return;
  404. }
  405. resFS << "stereo_matching" << "{";
  406. }
  407. int progress = 0, caseCount = (int)caseNames.size();
  408. for( int ci = 0; ci < caseCount; ci++)
  409. {
  410. progress = update_progress( progress, ci, caseCount, 0 );
  411. printf("progress: %d%%\n", progress);
  412. fflush(stdout);
  413. string datasetName = caseDatasets[ci];
  414. string datasetFullDirName = dataPath + DATASETS_DIR + datasetName + "/";
  415. Mat leftImg = imread(datasetFullDirName + LEFT_IMG_NAME);
  416. Mat rightImg = imread(datasetFullDirName + RIGHT_IMG_NAME);
  417. Mat trueLeftDisp = imread(datasetFullDirName + TRUE_LEFT_DISP_NAME, 0);
  418. Mat trueRightDisp = imread(datasetFullDirName + TRUE_RIGHT_DISP_NAME, 0);
  419. Rect calcROI;
  420. if( leftImg.empty() || rightImg.empty() || trueLeftDisp.empty() )
  421. {
  422. ts->printf( cvtest::TS::LOG, "images or left ground-truth disparities of dataset %s can not be read", datasetName.c_str() );
  423. code = cvtest::TS::FAIL_INVALID_TEST_DATA;
  424. continue;
  425. }
  426. int dispScaleFactor = datasetsParams[datasetName].dispScaleFactor;
  427. Mat tmp;
  428. trueLeftDisp.convertTo( tmp, CV_32FC1, 1.f/dispScaleFactor );
  429. trueLeftDisp = tmp;
  430. tmp.release();
  431. if( !trueRightDisp.empty() )
  432. {
  433. trueRightDisp.convertTo( tmp, CV_32FC1, 1.f/dispScaleFactor );
  434. trueRightDisp = tmp;
  435. tmp.release();
  436. }
  437. Mat leftDisp, rightDisp;
  438. int ignBorder = max(runStereoMatchingAlgorithm(leftImg, rightImg, calcROI, leftDisp, rightDisp, ci), EVAL_IGNORE_BORDER);
  439. leftDisp.convertTo( tmp, CV_32FC1 );
  440. leftDisp = tmp;
  441. tmp.release();
  442. rightDisp.convertTo( tmp, CV_32FC1 );
  443. rightDisp = tmp;
  444. tmp.release();
  445. int tempCode = processStereoMatchingResults( resFS, ci, isWrite,
  446. leftImg, rightImg, calcROI, trueLeftDisp, trueRightDisp, leftDisp, rightDisp, QualityEvalParams(ignBorder));
  447. code = tempCode==cvtest::TS::OK ? code : tempCode;
  448. }
  449. if( isWrite )
  450. resFS << "}"; // "stereo_matching"
  451. ts->set_failed_test_info( code );
  452. }
  453. void calcErrors( const Mat& leftImg, const Mat& /*rightImg*/,
  454. const Mat& trueLeftDisp, const Mat& trueRightDisp,
  455. const Mat& trueLeftUnknDispMask, const Mat& trueRightUnknDispMask,
  456. const Mat& calcLeftDisp, const Mat& /*calcRightDisp*/,
  457. vector<float>& rms, vector<float>& badPxlsFractions,
  458. const QualityEvalParams& qualityEvalParams )
  459. {
  460. Mat texturelessMask, texturedMask;
  461. computeTextureBasedMasks( leftImg, &texturelessMask, &texturedMask,
  462. qualityEvalParams.texturelessWidth, qualityEvalParams.texturelessThresh );
  463. Mat occludedMask, nonOccludedMask;
  464. computeOcclusionBasedMasks( trueLeftDisp, trueRightDisp, &occludedMask, &nonOccludedMask,
  465. trueLeftUnknDispMask, trueRightUnknDispMask, qualityEvalParams.dispThresh);
  466. Mat depthDiscontMask;
  467. computeDepthDiscontMask( trueLeftDisp, depthDiscontMask, trueLeftUnknDispMask,
  468. qualityEvalParams.dispGap, qualityEvalParams.discontWidth);
  469. Mat borderedKnownMask = getBorderedMask( leftImg.size(), qualityEvalParams.ignoreBorder ) & ~trueLeftUnknDispMask;
  470. nonOccludedMask &= borderedKnownMask;
  471. occludedMask &= borderedKnownMask;
  472. texturedMask &= nonOccludedMask; // & borderedKnownMask
  473. texturelessMask &= nonOccludedMask; // & borderedKnownMask
  474. depthDiscontMask &= nonOccludedMask; // & borderedKnownMask
  475. rms.resize(ERROR_KINDS_COUNT);
  476. rms[0] = dispRMS( calcLeftDisp, trueLeftDisp, borderedKnownMask );
  477. rms[1] = dispRMS( calcLeftDisp, trueLeftDisp, nonOccludedMask );
  478. rms[2] = dispRMS( calcLeftDisp, trueLeftDisp, occludedMask );
  479. rms[3] = dispRMS( calcLeftDisp, trueLeftDisp, texturedMask );
  480. rms[4] = dispRMS( calcLeftDisp, trueLeftDisp, texturelessMask );
  481. rms[5] = dispRMS( calcLeftDisp, trueLeftDisp, depthDiscontMask );
  482. badPxlsFractions.resize(ERROR_KINDS_COUNT);
  483. badPxlsFractions[0] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, borderedKnownMask, qualityEvalParams.badThresh );
  484. badPxlsFractions[1] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, nonOccludedMask, qualityEvalParams.badThresh );
  485. badPxlsFractions[2] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, occludedMask, qualityEvalParams.badThresh );
  486. badPxlsFractions[3] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, texturedMask, qualityEvalParams.badThresh );
  487. badPxlsFractions[4] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, texturelessMask, qualityEvalParams.badThresh );
  488. badPxlsFractions[5] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, depthDiscontMask, qualityEvalParams.badThresh );
  489. }
  490. int CV_StereoMatchingTest::processStereoMatchingResults( FileStorage& fs, int caseIdx, bool isWrite,
  491. const Mat& leftImg, const Mat& rightImg,
  492. const Rect& calcROI,
  493. const Mat& trueLeftDisp, const Mat& trueRightDisp,
  494. const Mat& leftDisp, const Mat& rightDisp,
  495. const QualityEvalParams& qualityEvalParams )
  496. {
  497. // rightDisp is not used in current test virsion
  498. int code = cvtest::TS::OK;
  499. CV_Assert( fs.isOpened() );
  500. CV_Assert( trueLeftDisp.type() == CV_32FC1 );
  501. CV_Assert( trueRightDisp.empty() || trueRightDisp.type() == CV_32FC1 );
  502. CV_Assert( leftDisp.type() == CV_32FC1 && (rightDisp.empty() || rightDisp.type() == CV_32FC1) );
  503. // get masks for unknown ground truth disparity values
  504. Mat leftUnknMask, rightUnknMask;
  505. DatasetParams params = datasetsParams[caseDatasets[caseIdx]];
  506. absdiff( trueLeftDisp, Scalar(params.dispUnknVal), leftUnknMask );
  507. leftUnknMask = leftUnknMask < std::numeric_limits<float>::epsilon();
  508. CV_Assert(leftUnknMask.type() == CV_8UC1);
  509. if( !trueRightDisp.empty() )
  510. {
  511. absdiff( trueRightDisp, Scalar(params.dispUnknVal), rightUnknMask );
  512. rightUnknMask = rightUnknMask < std::numeric_limits<float>::epsilon();
  513. CV_Assert(rightUnknMask.type() == CV_8UC1);
  514. }
  515. // calculate errors
  516. vector<float> rmss, badPxlsFractions;
  517. calcErrors( leftImg, rightImg, trueLeftDisp, trueRightDisp, leftUnknMask, rightUnknMask,
  518. leftDisp, rightDisp, rmss, badPxlsFractions, qualityEvalParams );
  519. if( isWrite )
  520. {
  521. fs << caseNames[caseIdx] << "{";
  522. fs.writeComment( RMS_STR, 0 );
  523. writeErrors( RMS_STR, rmss, &fs );
  524. fs.writeComment( BAD_PXLS_FRACTION_STR, 0 );
  525. writeErrors( BAD_PXLS_FRACTION_STR, badPxlsFractions, &fs );
  526. fs.writeComment( ROI_STR, 0 );
  527. writeROI( calcROI, &fs );
  528. fs << "}"; // datasetName
  529. }
  530. else // compare
  531. {
  532. ts->printf( cvtest::TS::LOG, "\nquality of case named %s\n", caseNames[caseIdx].c_str() );
  533. ts->printf( cvtest::TS::LOG, "%s\n", RMS_STR.c_str() );
  534. writeErrors( RMS_STR, rmss );
  535. ts->printf( cvtest::TS::LOG, "%s\n", BAD_PXLS_FRACTION_STR.c_str() );
  536. writeErrors( BAD_PXLS_FRACTION_STR, badPxlsFractions );
  537. ts->printf( cvtest::TS::LOG, "%s\n", ROI_STR.c_str() );
  538. writeROI( calcROI );
  539. FileNode fn = fs.getFirstTopLevelNode()[caseNames[caseIdx]];
  540. vector<float> validRmss, validBadPxlsFractions;
  541. Rect validROI;
  542. readErrors( fn, RMS_STR, validRmss );
  543. readErrors( fn, BAD_PXLS_FRACTION_STR, validBadPxlsFractions );
  544. readROI( fn, validROI );
  545. int tempCode = compareErrors( rmss, validRmss, rmsEps, RMS_STR );
  546. code = tempCode==cvtest::TS::OK ? code : tempCode;
  547. tempCode = compareErrors( badPxlsFractions, validBadPxlsFractions, fracEps, BAD_PXLS_FRACTION_STR );
  548. code = tempCode==cvtest::TS::OK ? code : tempCode;
  549. tempCode = compareROI( calcROI, validROI );
  550. code = tempCode==cvtest::TS::OK ? code : tempCode;
  551. }
  552. return code;
  553. }
  554. int CV_StereoMatchingTest::readDatasetsParams( FileStorage& fs )
  555. {
  556. if( !fs.isOpened() )
  557. {
  558. ts->printf( cvtest::TS::LOG, "datasetsParams can not be read " );
  559. return cvtest::TS::FAIL_INVALID_TEST_DATA;
  560. }
  561. datasetsParams.clear();
  562. FileNode fn = fs.getFirstTopLevelNode();
  563. CV_Assert(fn.isSeq());
  564. for( int i = 0; i < (int)fn.size(); i+=3 )
  565. {
  566. String _name = fn[i];
  567. DatasetParams params;
  568. String sf = fn[i+1]; params.dispScaleFactor = atoi(sf.c_str());
  569. String uv = fn[i+2]; params.dispUnknVal = atoi(uv.c_str());
  570. datasetsParams[_name] = params;
  571. }
  572. return cvtest::TS::OK;
  573. }
  574. int CV_StereoMatchingTest::readRunParams( FileStorage& fs )
  575. {
  576. if( !fs.isOpened() )
  577. {
  578. ts->printf( cvtest::TS::LOG, "runParams can not be read " );
  579. return cvtest::TS::FAIL_INVALID_TEST_DATA;
  580. }
  581. caseNames.clear();;
  582. caseDatasets.clear();
  583. return cvtest::TS::OK;
  584. }
  585. void CV_StereoMatchingTest::writeErrors( const string& errName, const vector<float>& errors, FileStorage* fs )
  586. {
  587. CV_Assert( (int)errors.size() == ERROR_KINDS_COUNT );
  588. vector<float>::const_iterator it = errors.begin();
  589. if( fs )
  590. for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++it )
  591. *fs << ERROR_PREFIXES[i] + errName << *it;
  592. else
  593. for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++it )
  594. ts->printf( cvtest::TS::LOG, "%s = %f\n", string(ERROR_PREFIXES[i]+errName).c_str(), *it );
  595. }
  596. void CV_StereoMatchingTest::writeROI( const Rect& calcROI, FileStorage* fs )
  597. {
  598. if( fs )
  599. {
  600. *fs << ROI_PREFIXES[0] << calcROI.x;
  601. *fs << ROI_PREFIXES[1] << calcROI.y;
  602. *fs << ROI_PREFIXES[2] << calcROI.width;
  603. *fs << ROI_PREFIXES[3] << calcROI.height;
  604. }
  605. else
  606. {
  607. ts->printf( cvtest::TS::LOG, "%s = %d\n", ROI_PREFIXES[0].c_str(), calcROI.x );
  608. ts->printf( cvtest::TS::LOG, "%s = %d\n", ROI_PREFIXES[1].c_str(), calcROI.y );
  609. ts->printf( cvtest::TS::LOG, "%s = %d\n", ROI_PREFIXES[2].c_str(), calcROI.width );
  610. ts->printf( cvtest::TS::LOG, "%s = %d\n", ROI_PREFIXES[3].c_str(), calcROI.height );
  611. }
  612. }
  613. void CV_StereoMatchingTest::readErrors( FileNode& fn, const string& errName, vector<float>& errors )
  614. {
  615. errors.resize( ERROR_KINDS_COUNT );
  616. vector<float>::iterator it = errors.begin();
  617. for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++it )
  618. fn[ERROR_PREFIXES[i]+errName] >> *it;
  619. }
  620. void CV_StereoMatchingTest::readROI( FileNode& fn, Rect& validROI )
  621. {
  622. fn[ROI_PREFIXES[0]] >> validROI.x;
  623. fn[ROI_PREFIXES[1]] >> validROI.y;
  624. fn[ROI_PREFIXES[2]] >> validROI.width;
  625. fn[ROI_PREFIXES[3]] >> validROI.height;
  626. }
  627. int CV_StereoMatchingTest::compareErrors( const vector<float>& calcErrors, const vector<float>& validErrors,
  628. const vector<float>& eps, const string& errName )
  629. {
  630. CV_Assert( (int)calcErrors.size() == ERROR_KINDS_COUNT );
  631. CV_Assert( (int)validErrors.size() == ERROR_KINDS_COUNT );
  632. CV_Assert( (int)eps.size() == ERROR_KINDS_COUNT );
  633. vector<float>::const_iterator calcIt = calcErrors.begin(),
  634. validIt = validErrors.begin(),
  635. epsIt = eps.begin();
  636. bool ok = true;
  637. for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++calcIt, ++validIt, ++epsIt )
  638. if( *calcIt - *validIt > *epsIt )
  639. {
  640. ts->printf( cvtest::TS::LOG, "bad accuracy of %s (valid=%f; calc=%f)\n", string(ERROR_PREFIXES[i]+errName).c_str(), *validIt, *calcIt );
  641. ok = false;
  642. }
  643. return ok ? cvtest::TS::OK : cvtest::TS::FAIL_BAD_ACCURACY;
  644. }
  645. int CV_StereoMatchingTest::compareROI( const Rect& calcROI, const Rect& validROI )
  646. {
  647. int compare[4][2] = {
  648. { calcROI.x, validROI.x },
  649. { calcROI.y, validROI.y },
  650. { calcROI.width, validROI.width },
  651. { calcROI.height, validROI.height },
  652. };
  653. bool ok = true;
  654. for (int i = 0; i < 4; i++)
  655. {
  656. if (compare[i][0] != compare[i][1])
  657. {
  658. ts->printf( cvtest::TS::LOG, "bad accuracy of %s (valid=%d; calc=%d)\n", ROI_PREFIXES[i].c_str(), compare[i][1], compare[i][0] );
  659. ok = false;
  660. }
  661. }
  662. return ok ? cvtest::TS::OK : cvtest::TS::FAIL_BAD_ACCURACY;
  663. }
  664. //----------------------------------- StereoBM test -----------------------------------------------------
  665. class CV_StereoBMTest : public CV_StereoMatchingTest
  666. {
  667. public:
  668. CV_StereoBMTest()
  669. {
  670. name = "stereobm";
  671. fill(rmsEps.begin(), rmsEps.end(), 0.4f);
  672. fill(fracEps.begin(), fracEps.end(), 0.022f);
  673. }
  674. protected:
  675. struct RunParams
  676. {
  677. int ndisp;
  678. int mindisp;
  679. int winSize;
  680. };
  681. vector<RunParams> caseRunParams;
  682. virtual int readRunParams( FileStorage& fs )
  683. {
  684. int code = CV_StereoMatchingTest::readRunParams( fs );
  685. FileNode fn = fs.getFirstTopLevelNode();
  686. CV_Assert(fn.isSeq());
  687. for( int i = 0; i < (int)fn.size(); i+=5 )
  688. {
  689. String caseName = fn[i], datasetName = fn[i+1];
  690. RunParams params;
  691. String ndisp = fn[i+2]; params.ndisp = atoi(ndisp.c_str());
  692. String mindisp = fn[i+3]; params.mindisp = atoi(mindisp.c_str());
  693. String winSize = fn[i+4]; params.winSize = atoi(winSize.c_str());
  694. caseNames.push_back( caseName );
  695. caseDatasets.push_back( datasetName );
  696. caseRunParams.push_back( params );
  697. }
  698. return code;
  699. }
  700. virtual int runStereoMatchingAlgorithm( const Mat& _leftImg, const Mat& _rightImg,
  701. Rect& calcROI, Mat& leftDisp, Mat& /*rightDisp*/, int caseIdx )
  702. {
  703. RunParams params = caseRunParams[caseIdx];
  704. CV_Assert( params.ndisp%16 == 0 );
  705. CV_Assert( _leftImg.type() == CV_8UC3 && _rightImg.type() == CV_8UC3 );
  706. Mat leftImg; cvtColor( _leftImg, leftImg, COLOR_BGR2GRAY );
  707. Mat rightImg; cvtColor( _rightImg, rightImg, COLOR_BGR2GRAY );
  708. Ptr<StereoBM> bm = StereoBM::create( params.ndisp, params.winSize );
  709. Mat tempDisp;
  710. bm->setMinDisparity(params.mindisp);
  711. Rect cROI(0, 0, _leftImg.cols, _leftImg.rows);
  712. calcROI = getValidDisparityROI(cROI, cROI, params.mindisp, params.ndisp, params.winSize);
  713. bm->compute( leftImg, rightImg, tempDisp );
  714. tempDisp.convertTo(leftDisp, CV_32F, 1./StereoMatcher::DISP_SCALE);
  715. //check for fixed-type disparity data type
  716. Mat_<float> fixedFloatDisp;
  717. bm->compute( leftImg, rightImg, fixedFloatDisp );
  718. EXPECT_LT(cvtest::norm(fixedFloatDisp, leftDisp, cv::NORM_L2 | cv::NORM_RELATIVE),
  719. 0.005 + DBL_EPSILON);
  720. if (params.mindisp != 0)
  721. for (int y = 0; y < leftDisp.rows; y++)
  722. for (int x = 0; x < leftDisp.cols; x++)
  723. {
  724. if (leftDisp.at<float>(y, x) < params.mindisp)
  725. leftDisp.at<float>(y, x) = -1./StereoMatcher::DISP_SCALE; // treat disparity < mindisp as no disparity
  726. }
  727. return params.winSize/2;
  728. }
  729. };
  730. TEST(Calib3d_StereoBM, regression) { CV_StereoBMTest test; test.safe_run(); }
  731. /* < preFilter, < preFilterCap, SADWindowSize > >*/
  732. typedef tuple < int, tuple < int, int > > BufferBM_Params_t;
  733. typedef testing::TestWithParam< BufferBM_Params_t > Calib3d_StereoBM_BufferBM;
  734. const int preFilters[] =
  735. {
  736. StereoBM::PREFILTER_NORMALIZED_RESPONSE,
  737. StereoBM::PREFILTER_XSOBEL
  738. };
  739. const tuple < int, int > useShortsConditions[] =
  740. {
  741. make_tuple(30, 19),
  742. make_tuple(32, 23)
  743. };
  744. TEST_P(Calib3d_StereoBM_BufferBM, memAllocsTest)
  745. {
  746. const int preFilter = get<0>(GetParam());
  747. const int preFilterCap = get<0>(get<1>(GetParam()));
  748. const int SADWindowSize = get<1>(get<1>(GetParam()));
  749. String path = cvtest::TS::ptr()->get_data_path() + "cv/stereomatching/datasets/teddy/";
  750. Mat leftImg = imread(path + "im2.png", 0);
  751. ASSERT_FALSE(leftImg.empty());
  752. Mat rightImg = imread(path + "im6.png", 0);
  753. ASSERT_FALSE(rightImg.empty());
  754. Mat leftDisp;
  755. {
  756. Ptr<StereoBM> bm = StereoBM::create(16,9);
  757. bm->setPreFilterType(preFilter);
  758. bm->setPreFilterCap(preFilterCap);
  759. bm->setBlockSize(SADWindowSize);
  760. bm->compute( leftImg, rightImg, leftDisp);
  761. ASSERT_FALSE(leftDisp.empty());
  762. }
  763. }
  764. INSTANTIATE_TEST_CASE_P(/*nothing*/, Calib3d_StereoBM_BufferBM,
  765. testing::Combine(
  766. testing::ValuesIn(preFilters),
  767. testing::ValuesIn(useShortsConditions)
  768. )
  769. );
  770. //----------------------------------- StereoSGBM test -----------------------------------------------------
  771. class CV_StereoSGBMTest : public CV_StereoMatchingTest
  772. {
  773. public:
  774. CV_StereoSGBMTest()
  775. {
  776. name = "stereosgbm";
  777. fill(rmsEps.begin(), rmsEps.end(), 0.25f);
  778. fill(fracEps.begin(), fracEps.end(), 0.01f);
  779. }
  780. protected:
  781. struct RunParams
  782. {
  783. int ndisp;
  784. int winSize;
  785. int mode;
  786. };
  787. vector<RunParams> caseRunParams;
  788. virtual int readRunParams( FileStorage& fs )
  789. {
  790. int code = CV_StereoMatchingTest::readRunParams(fs);
  791. FileNode fn = fs.getFirstTopLevelNode();
  792. CV_Assert(fn.isSeq());
  793. for( int i = 0; i < (int)fn.size(); i+=5 )
  794. {
  795. String caseName = fn[i], datasetName = fn[i+1];
  796. RunParams params;
  797. String ndisp = fn[i+2]; params.ndisp = atoi(ndisp.c_str());
  798. String winSize = fn[i+3]; params.winSize = atoi(winSize.c_str());
  799. String mode = fn[i+4]; params.mode = atoi(mode.c_str());
  800. caseNames.push_back( caseName );
  801. caseDatasets.push_back( datasetName );
  802. caseRunParams.push_back( params );
  803. }
  804. return code;
  805. }
  806. virtual int runStereoMatchingAlgorithm( const Mat& leftImg, const Mat& rightImg,
  807. Rect& calcROI, Mat& leftDisp, Mat& /*rightDisp*/, int caseIdx )
  808. {
  809. RunParams params = caseRunParams[caseIdx];
  810. CV_Assert( params.ndisp%16 == 0 );
  811. Ptr<StereoSGBM> sgbm = StereoSGBM::create( 0, params.ndisp, params.winSize,
  812. 10*params.winSize*params.winSize,
  813. 40*params.winSize*params.winSize,
  814. 1, 63, 10, 100, 32, params.mode );
  815. Rect cROI(0, 0, leftImg.cols, leftImg.rows);
  816. calcROI = getValidDisparityROI(cROI, cROI, 0, params.ndisp, params.winSize);
  817. sgbm->compute( leftImg, rightImg, leftDisp );
  818. CV_Assert( leftDisp.type() == CV_16SC1 );
  819. leftDisp/=16;
  820. return 0;
  821. }
  822. };
  823. TEST(Calib3d_StereoSGBM, regression) { CV_StereoSGBMTest test; test.safe_run(); }
  824. TEST(Calib3d_StereoSGBM_HH4, regression)
  825. {
  826. String path = cvtest::TS::ptr()->get_data_path() + "cv/stereomatching/datasets/teddy/";
  827. Mat leftImg = imread(path + "im2.png", 0);
  828. ASSERT_FALSE(leftImg.empty());
  829. Mat rightImg = imread(path + "im6.png", 0);
  830. ASSERT_FALSE(rightImg.empty());
  831. Mat testData = imread(path + "disp2_hh4.png",-1);
  832. ASSERT_FALSE(testData.empty());
  833. Mat leftDisp;
  834. Mat toCheck;
  835. {
  836. Ptr<StereoSGBM> sgbm = StereoSGBM::create( 0, 48, 3, 90, 360, 1, 63, 10, 100, 32, StereoSGBM::MODE_HH4);
  837. sgbm->compute( leftImg, rightImg, leftDisp);
  838. CV_Assert( leftDisp.type() == CV_16SC1 );
  839. leftDisp.convertTo(toCheck, CV_16UC1,1,16);
  840. }
  841. Mat diff;
  842. absdiff(toCheck, testData,diff);
  843. CV_Assert( countNonZero(diff)==0);
  844. }
  845. }} // namespace