test_stereo.cpp 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047
  1. /*M///////////////////////////////////////////////////////////////////////////////////////
  2. //
  3. // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
  4. //
  5. // By downloading, copying, installing or using the software you agree to this license.
  6. // If you do not agree to this license, do not download, install,
  7. // copy or use the software.
  8. //
  9. //
  10. // License Agreement
  11. // For Open Source Computer Vision Library
  12. //
  13. // Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
  14. // Copyright (C) 2009, Willow Garage Inc., all rights reserved.
  15. // Third party copyrights are property of their respective owners.
  16. //
  17. // Redistribution and use in source and binary forms, with or without modification,
  18. // are permitted provided that the following conditions are met:
  19. //
  20. // * Redistribution's of source code must retain the above copyright notice,
  21. // this list of conditions and the following disclaimer.
  22. //
  23. // * Redistribution's in binary form must reproduce the above copyright notice,
  24. // this list of conditions and the following disclaimer in the documentation
  25. // and/or other materials provided with the distribution.
  26. //
  27. // * The name of the copyright holders may not be used to endorse or promote products
  28. // derived from this software without specific prior written permission.
  29. //
  30. // This software is provided by the copyright holders and contributors "as is" and
  31. // any express or implied warranties, including, but not limited to, the implied
  32. // warranties of merchantability and fitness for a particular purpose are disclaimed.
  33. // In no event shall the Intel Corporation or contributors be liable for any direct,
  34. // indirect, incidental, special, exemplary, or consequential damages
  35. // (including, but not limited to, procurement of substitute goods or services;
  36. // loss of use, data, or profits; or business interruption) however caused
  37. // and on any theory of liability, whether in contract, strict liability,
  38. // or tort (including negligence or otherwise) arising in any way out of
  39. // the use of this software, even if advised of the possibility of such damage.
  40. //
  41. //M*/
  42. #include "test_precomp.hpp"
  43. #ifdef HAVE_CUDA
  44. namespace opencv_test { namespace {
  45. //////////////////////////////////////////////////////////////////////////
  46. // StereoBM
  47. struct StereoBM : testing::TestWithParam<cv::cuda::DeviceInfo>
  48. {
  49. cv::cuda::DeviceInfo devInfo;
  50. virtual void SetUp()
  51. {
  52. devInfo = GetParam();
  53. cv::cuda::setDevice(devInfo.deviceID());
  54. }
  55. };
  56. CUDA_TEST_P(StereoBM, Regression)
  57. {
  58. cv::Mat left_image = readImage("stereobm/aloe-L.png", cv::IMREAD_GRAYSCALE);
  59. cv::Mat right_image = readImage("stereobm/aloe-R.png", cv::IMREAD_GRAYSCALE);
  60. cv::Mat disp_gold = readImage("stereobm/aloe-disp.png", cv::IMREAD_GRAYSCALE);
  61. ASSERT_FALSE(left_image.empty());
  62. ASSERT_FALSE(right_image.empty());
  63. ASSERT_FALSE(disp_gold.empty());
  64. cv::Ptr<cv::StereoBM> bm = cv::cuda::createStereoBM(128, 19);
  65. cv::cuda::GpuMat disp;
  66. bm->compute(loadMat(left_image), loadMat(right_image), disp);
  67. EXPECT_MAT_NEAR(disp_gold, disp, 0.0);
  68. }
  69. CUDA_TEST_P(StereoBM, PrefilterXSobelRegression)
  70. {
  71. cv::Mat left_image = readImage("stereobm/aloe-L.png", cv::IMREAD_GRAYSCALE);
  72. cv::Mat right_image = readImage("stereobm/aloe-R.png", cv::IMREAD_GRAYSCALE);
  73. cv::Mat disp_gold = readImage("stereobm/aloe-disp-prefilter-xsobel.png", cv::IMREAD_GRAYSCALE);
  74. ASSERT_FALSE(left_image.empty());
  75. ASSERT_FALSE(right_image.empty());
  76. ASSERT_FALSE(disp_gold.empty());
  77. cv::Ptr<cv::StereoBM> bm = cv::cuda::createStereoBM(128, 19);
  78. cv::cuda::GpuMat disp;
  79. bm->setPreFilterType(cv::StereoBM::PREFILTER_XSOBEL);
  80. bm->compute(loadMat(left_image), loadMat(right_image), disp);
  81. EXPECT_MAT_NEAR(disp_gold, disp, 0.0);
  82. }
  83. CUDA_TEST_P(StereoBM, PrefilterNormRegression)
  84. {
  85. cv::Mat left_image = readImage("stereobm/aloe-L.png", cv::IMREAD_GRAYSCALE);
  86. cv::Mat right_image = readImage("stereobm/aloe-R.png", cv::IMREAD_GRAYSCALE);
  87. cv::Mat disp_gold = readImage("stereobm/aloe-disp-prefilter-norm.png", cv::IMREAD_GRAYSCALE);
  88. ASSERT_FALSE(left_image.empty());
  89. ASSERT_FALSE(right_image.empty());
  90. ASSERT_FALSE(disp_gold.empty());
  91. cv::Ptr<cv::StereoBM> bm = cv::cuda::createStereoBM(128, 19);
  92. cv::cuda::GpuMat disp;
  93. bm->setPreFilterType(cv::StereoBM::PREFILTER_NORMALIZED_RESPONSE);
  94. bm->setPreFilterSize(9);
  95. bm->compute(loadMat(left_image), loadMat(right_image), disp);
  96. EXPECT_MAT_NEAR(disp_gold, disp, 0.0);
  97. }
  98. CUDA_TEST_P(StereoBM, Streams)
  99. {
  100. cv::cuda::Stream stream;
  101. cv::Mat left_image = readImage("stereobm/aloe-L.png", cv::IMREAD_GRAYSCALE);
  102. cv::Mat right_image = readImage("stereobm/aloe-R.png", cv::IMREAD_GRAYSCALE);
  103. cv::Mat disp_gold = readImage("stereobm/aloe-disp.png", cv::IMREAD_GRAYSCALE);
  104. ASSERT_FALSE(left_image.empty());
  105. ASSERT_FALSE(right_image.empty());
  106. ASSERT_FALSE(disp_gold.empty());
  107. cv::Ptr<cv::cuda::StereoBM> bm = cv::cuda::createStereoBM(128, 19);
  108. cv::cuda::GpuMat disp;
  109. bm->compute(loadMat(left_image), loadMat(right_image), disp, stream);
  110. stream.waitForCompletion();
  111. EXPECT_MAT_NEAR(disp_gold, disp, 0.0);
  112. }
  113. CUDA_TEST_P(StereoBM, Uniqueness_Regression)
  114. {
  115. cv::Mat left_image = readImage("stereobm/aloe-L.png", cv::IMREAD_GRAYSCALE);
  116. cv::Mat right_image = readImage("stereobm/aloe-R.png", cv::IMREAD_GRAYSCALE);
  117. cv::Mat disp_gold = readImage("stereobm/aloe-disp-uniqueness15.png", cv::IMREAD_GRAYSCALE);
  118. ASSERT_FALSE(left_image.empty());
  119. ASSERT_FALSE(right_image.empty());
  120. ASSERT_FALSE(disp_gold.empty());
  121. cv::Ptr<cv::StereoBM> bm = cv::cuda::createStereoBM(128, 19);
  122. cv::cuda::GpuMat disp;
  123. bm->setUniquenessRatio(15);
  124. bm->compute(loadMat(left_image), loadMat(right_image), disp);
  125. EXPECT_MAT_NEAR(disp_gold, disp, 0.0);
  126. }
  127. INSTANTIATE_TEST_CASE_P(CUDA_Stereo, StereoBM, ALL_DEVICES);
  128. //////////////////////////////////////////////////////////////////////////
  129. // StereoBeliefPropagation
  130. struct StereoBeliefPropagation : testing::TestWithParam<cv::cuda::DeviceInfo>
  131. {
  132. cv::cuda::DeviceInfo devInfo;
  133. virtual void SetUp()
  134. {
  135. devInfo = GetParam();
  136. cv::cuda::setDevice(devInfo.deviceID());
  137. }
  138. };
  139. CUDA_TEST_P(StereoBeliefPropagation, Regression)
  140. {
  141. cv::Mat left_image = readImage("stereobp/aloe-L.png");
  142. cv::Mat right_image = readImage("stereobp/aloe-R.png");
  143. cv::Mat disp_gold = readImage("stereobp/aloe-disp.png", cv::IMREAD_GRAYSCALE);
  144. ASSERT_FALSE(left_image.empty());
  145. ASSERT_FALSE(right_image.empty());
  146. ASSERT_FALSE(disp_gold.empty());
  147. cv::Ptr<cv::cuda::StereoBeliefPropagation> bp = cv::cuda::createStereoBeliefPropagation(64, 8, 2, CV_16S);
  148. bp->setMaxDataTerm(25.0);
  149. bp->setDataWeight(0.1);
  150. bp->setMaxDiscTerm(15.0);
  151. bp->setDiscSingleJump(1.0);
  152. cv::cuda::GpuMat disp;
  153. bp->compute(loadMat(left_image), loadMat(right_image), disp);
  154. cv::Mat h_disp(disp);
  155. h_disp.convertTo(h_disp, disp_gold.depth());
  156. EXPECT_MAT_NEAR(disp_gold, h_disp, 0.0);
  157. }
  158. INSTANTIATE_TEST_CASE_P(CUDA_Stereo, StereoBeliefPropagation, ALL_DEVICES);
  159. //////////////////////////////////////////////////////////////////////////
  160. // StereoConstantSpaceBP
  161. struct StereoConstantSpaceBP : testing::TestWithParam<cv::cuda::DeviceInfo>
  162. {
  163. cv::cuda::DeviceInfo devInfo;
  164. virtual void SetUp()
  165. {
  166. devInfo = GetParam();
  167. cv::cuda::setDevice(devInfo.deviceID());
  168. }
  169. };
  170. CUDA_TEST_P(StereoConstantSpaceBP, Regression)
  171. {
  172. cv::Mat left_image = readImage("csstereobp/aloe-L.png");
  173. cv::Mat right_image = readImage("csstereobp/aloe-R.png");
  174. cv::Mat disp_gold;
  175. if (supportFeature(devInfo, cv::cuda::FEATURE_SET_COMPUTE_20))
  176. disp_gold = readImage("csstereobp/aloe-disp.png", cv::IMREAD_GRAYSCALE);
  177. else
  178. disp_gold = readImage("csstereobp/aloe-disp_CC1X.png", cv::IMREAD_GRAYSCALE);
  179. ASSERT_FALSE(left_image.empty());
  180. ASSERT_FALSE(right_image.empty());
  181. ASSERT_FALSE(disp_gold.empty());
  182. cv::Ptr<cv::cuda::StereoConstantSpaceBP> csbp = cv::cuda::createStereoConstantSpaceBP(128, 16, 4, 4);
  183. cv::cuda::GpuMat disp;
  184. csbp->compute(loadMat(left_image), loadMat(right_image), disp);
  185. cv::Mat h_disp(disp);
  186. h_disp.convertTo(h_disp, disp_gold.depth());
  187. EXPECT_MAT_SIMILAR(disp_gold, h_disp, 1e-4);
  188. }
  189. INSTANTIATE_TEST_CASE_P(CUDA_Stereo, StereoConstantSpaceBP, ALL_DEVICES);
  190. ////////////////////////////////////////////////////////////////////////////////
  191. // reprojectImageTo3D
  192. PARAM_TEST_CASE(ReprojectImageTo3D, cv::cuda::DeviceInfo, cv::Size, MatDepth, UseRoi)
  193. {
  194. cv::cuda::DeviceInfo devInfo;
  195. cv::Size size;
  196. int depth;
  197. bool useRoi;
  198. virtual void SetUp()
  199. {
  200. devInfo = GET_PARAM(0);
  201. size = GET_PARAM(1);
  202. depth = GET_PARAM(2);
  203. useRoi = GET_PARAM(3);
  204. cv::cuda::setDevice(devInfo.deviceID());
  205. }
  206. };
  207. CUDA_TEST_P(ReprojectImageTo3D, Accuracy)
  208. {
  209. cv::Mat disp = randomMat(size, depth, 5.0, 30.0);
  210. cv::Mat Q = randomMat(cv::Size(4, 4), CV_32FC1, 0.1, 1.0);
  211. cv::cuda::GpuMat dst;
  212. cv::cuda::reprojectImageTo3D(loadMat(disp, useRoi), dst, Q, 3);
  213. cv::Mat dst_gold;
  214. cv::reprojectImageTo3D(disp, dst_gold, Q, false);
  215. EXPECT_MAT_NEAR(dst_gold, dst, 1e-5);
  216. }
  217. INSTANTIATE_TEST_CASE_P(CUDA_Stereo, ReprojectImageTo3D, testing::Combine(
  218. ALL_DEVICES,
  219. DIFFERENT_SIZES,
  220. testing::Values(MatDepth(CV_8U), MatDepth(CV_16S)),
  221. WHOLE_SUBMAT));
  222. ////////////////////////////////////////////////////////////////////////////////
  223. // StereoSGM
  224. /*
  225. This is a regression test for stereo matching algorithms. This test gets some quality metrics
  226. described in "A Taxonomy and Evaluation of Dense Two-Frame Stereo Correspondence Algorithms".
  227. Daniel Scharstein, Richard Szeliski
  228. */
  229. const float EVAL_BAD_THRESH = 1.f;
  230. const int EVAL_TEXTURELESS_WIDTH = 3;
  231. const float EVAL_TEXTURELESS_THRESH = 4.f;
  232. const float EVAL_DISP_THRESH = 1.f;
  233. const float EVAL_DISP_GAP = 2.f;
  234. const int EVAL_DISCONT_WIDTH = 9;
  235. const int EVAL_IGNORE_BORDER = 10;
  236. const int ERROR_KINDS_COUNT = 6;
  237. //============================== quality measuring functions =================================================
  238. /*
  239. Calculate textureless regions of image (regions where the squared horizontal intensity gradient averaged over
  240. a square window of size=evalTexturelessWidth is below a threshold=evalTexturelessThresh) and textured regions.
  241. */
  242. void computeTextureBasedMasks(const Mat& _img, Mat* texturelessMask, Mat* texturedMask,
  243. int texturelessWidth = EVAL_TEXTURELESS_WIDTH, float texturelessThresh = EVAL_TEXTURELESS_THRESH)
  244. {
  245. if (!texturelessMask && !texturedMask)
  246. return;
  247. if (_img.empty())
  248. CV_Error(Error::StsBadArg, "img is empty");
  249. Mat img = _img;
  250. if (_img.channels() > 1)
  251. {
  252. Mat tmp; cvtColor(_img, tmp, COLOR_BGR2GRAY); img = tmp;
  253. }
  254. Mat dxI; Sobel(img, dxI, CV_32FC1, 1, 0, 3);
  255. Mat dxI2; pow(dxI / 8.f/*normalize*/, 2, dxI2);
  256. Mat avgDxI2; boxFilter(dxI2, avgDxI2, CV_32FC1, Size(texturelessWidth, texturelessWidth));
  257. if (texturelessMask)
  258. *texturelessMask = avgDxI2 < texturelessThresh;
  259. if (texturedMask)
  260. *texturedMask = avgDxI2 >= texturelessThresh;
  261. }
  262. void checkTypeAndSizeOfDisp(const Mat& dispMap, const Size* sz)
  263. {
  264. if (dispMap.empty())
  265. CV_Error(Error::StsBadArg, "dispMap is empty");
  266. if (dispMap.type() != CV_32FC1)
  267. CV_Error(Error::StsBadArg, "dispMap must have CV_32FC1 type");
  268. if (sz && (dispMap.rows != sz->height || dispMap.cols != sz->width))
  269. CV_Error(Error::StsBadArg, "dispMap has incorrect size");
  270. }
  271. void checkTypeAndSizeOfMask(const Mat& mask, Size sz)
  272. {
  273. if (mask.empty())
  274. CV_Error(Error::StsBadArg, "mask is empty");
  275. if (mask.type() != CV_8UC1)
  276. CV_Error(Error::StsBadArg, "mask must have CV_8UC1 type");
  277. if (mask.rows != sz.height || mask.cols != sz.width)
  278. CV_Error(Error::StsBadArg, "mask has incorrect size");
  279. }
  280. void checkDispMapsAndUnknDispMasks(const Mat& leftDispMap, const Mat& rightDispMap,
  281. const Mat& leftUnknDispMask, const Mat& rightUnknDispMask)
  282. {
  283. // check type and size of disparity maps
  284. checkTypeAndSizeOfDisp(leftDispMap, 0);
  285. if (!rightDispMap.empty())
  286. {
  287. Size sz = leftDispMap.size();
  288. checkTypeAndSizeOfDisp(rightDispMap, &sz);
  289. }
  290. // check size and type of unknown disparity maps
  291. if (!leftUnknDispMask.empty())
  292. checkTypeAndSizeOfMask(leftUnknDispMask, leftDispMap.size());
  293. if (!rightUnknDispMask.empty())
  294. checkTypeAndSizeOfMask(rightUnknDispMask, rightDispMap.size());
  295. // check values of disparity maps (known disparity values musy be positive)
  296. double leftMinVal = 0, rightMinVal = 0;
  297. if (leftUnknDispMask.empty())
  298. minMaxLoc(leftDispMap, &leftMinVal);
  299. else
  300. minMaxLoc(leftDispMap, &leftMinVal, 0, 0, 0, ~leftUnknDispMask);
  301. if (!rightDispMap.empty())
  302. {
  303. if (rightUnknDispMask.empty())
  304. minMaxLoc(rightDispMap, &rightMinVal);
  305. else
  306. minMaxLoc(rightDispMap, &rightMinVal, 0, 0, 0, ~rightUnknDispMask);
  307. }
  308. if (leftMinVal < 0 || rightMinVal < 0)
  309. CV_Error(Error::StsBadArg, "known disparity values must be positive");
  310. }
  311. /*
  312. Calculate occluded regions of reference image (left image) (regions that are occluded in the matching image (right image),
  313. i.e., where the forward-mapped disparity lands at a location with a larger (nearer) disparity) and non occluded regions.
  314. */
  315. void computeOcclusionBasedMasks(const Mat& leftDisp, const Mat& _rightDisp,
  316. Mat* occludedMask, Mat* nonOccludedMask,
  317. const Mat& leftUnknDispMask = Mat(), const Mat& rightUnknDispMask = Mat(),
  318. float dispThresh = EVAL_DISP_THRESH)
  319. {
  320. if (!occludedMask && !nonOccludedMask)
  321. return;
  322. checkDispMapsAndUnknDispMasks(leftDisp, _rightDisp, leftUnknDispMask, rightUnknDispMask);
  323. Mat rightDisp;
  324. if (_rightDisp.empty())
  325. {
  326. if (!rightUnknDispMask.empty())
  327. CV_Error(Error::StsBadArg, "rightUnknDispMask must be empty if _rightDisp is empty");
  328. rightDisp.create(leftDisp.size(), CV_32FC1);
  329. rightDisp.setTo(Scalar::all(0));
  330. for (int leftY = 0; leftY < leftDisp.rows; leftY++)
  331. {
  332. for (int leftX = 0; leftX < leftDisp.cols; leftX++)
  333. {
  334. if (!leftUnknDispMask.empty() && leftUnknDispMask.at<uchar>(leftY, leftX))
  335. continue;
  336. float leftDispVal = leftDisp.at<float>(leftY, leftX);
  337. int rightX = leftX - cvRound(leftDispVal), rightY = leftY;
  338. if (rightX >= 0)
  339. rightDisp.at<float>(rightY, rightX) = max(rightDisp.at<float>(rightY, rightX), leftDispVal);
  340. }
  341. }
  342. }
  343. else
  344. _rightDisp.copyTo(rightDisp);
  345. if (occludedMask)
  346. {
  347. occludedMask->create(leftDisp.size(), CV_8UC1);
  348. occludedMask->setTo(Scalar::all(0));
  349. }
  350. if (nonOccludedMask)
  351. {
  352. nonOccludedMask->create(leftDisp.size(), CV_8UC1);
  353. nonOccludedMask->setTo(Scalar::all(0));
  354. }
  355. for (int leftY = 0; leftY < leftDisp.rows; leftY++)
  356. {
  357. for (int leftX = 0; leftX < leftDisp.cols; leftX++)
  358. {
  359. if (!leftUnknDispMask.empty() && leftUnknDispMask.at<uchar>(leftY, leftX))
  360. continue;
  361. float leftDispVal = leftDisp.at<float>(leftY, leftX);
  362. int rightX = leftX - cvRound(leftDispVal), rightY = leftY;
  363. if (rightX < 0 && occludedMask)
  364. occludedMask->at<uchar>(leftY, leftX) = 255;
  365. else
  366. {
  367. if (!rightUnknDispMask.empty() && rightUnknDispMask.at<uchar>(rightY, rightX))
  368. continue;
  369. float rightDispVal = rightDisp.at<float>(rightY, rightX);
  370. if (rightDispVal > leftDispVal + dispThresh)
  371. {
  372. if (occludedMask)
  373. occludedMask->at<uchar>(leftY, leftX) = 255;
  374. }
  375. else
  376. {
  377. if (nonOccludedMask)
  378. nonOccludedMask->at<uchar>(leftY, leftX) = 255;
  379. }
  380. }
  381. }
  382. }
  383. }
  384. /*
  385. Calculate depth discontinuty regions: pixels whose neiboring disparities differ by more than
  386. dispGap, dilated by window of width discontWidth.
  387. */
  388. void computeDepthDiscontMask(const Mat& disp, Mat& depthDiscontMask, const Mat& unknDispMask = Mat(),
  389. float dispGap = EVAL_DISP_GAP, int discontWidth = EVAL_DISCONT_WIDTH)
  390. {
  391. if (disp.empty())
  392. CV_Error(Error::StsBadArg, "disp is empty");
  393. if (disp.type() != CV_32FC1)
  394. CV_Error(Error::StsBadArg, "disp must have CV_32FC1 type");
  395. if (!unknDispMask.empty())
  396. checkTypeAndSizeOfMask(unknDispMask, disp.size());
  397. Mat curDisp; disp.copyTo(curDisp);
  398. if (!unknDispMask.empty())
  399. curDisp.setTo(Scalar(std::numeric_limits<float>::min()), unknDispMask);
  400. Mat maxNeighbDisp; dilate(curDisp, maxNeighbDisp, Mat(3, 3, CV_8UC1, Scalar(1)));
  401. if (!unknDispMask.empty())
  402. curDisp.setTo(Scalar(std::numeric_limits<float>::max()), unknDispMask);
  403. Mat minNeighbDisp; erode(curDisp, minNeighbDisp, Mat(3, 3, CV_8UC1, Scalar(1)));
  404. depthDiscontMask = max((Mat)(maxNeighbDisp - disp), (Mat)(disp - minNeighbDisp)) > dispGap;
  405. if (!unknDispMask.empty())
  406. depthDiscontMask &= ~unknDispMask;
  407. dilate(depthDiscontMask, depthDiscontMask, Mat(discontWidth, discontWidth, CV_8UC1, Scalar(1)));
  408. }
  409. /*
  410. Get evaluation masks excluding a border.
  411. */
  412. Mat getBorderedMask(Size maskSize, int border = EVAL_IGNORE_BORDER)
  413. {
  414. CV_Assert(border >= 0);
  415. Mat mask(maskSize, CV_8UC1, Scalar(0));
  416. int w = maskSize.width - 2 * border, h = maskSize.height - 2 * border;
  417. if (w < 0 || h < 0)
  418. mask.setTo(Scalar(0));
  419. else
  420. mask(Rect(Point(border, border), Size(w, h))).setTo(Scalar(255));
  421. return mask;
  422. }
  423. /*
  424. Calculate root-mean-squared error between the computed disparity map (computedDisp) and ground truth map (groundTruthDisp).
  425. */
  426. float dispRMS(const Mat& computedDisp, const Mat& groundTruthDisp, const Mat& mask)
  427. {
  428. checkTypeAndSizeOfDisp(groundTruthDisp, 0);
  429. Size sz = groundTruthDisp.size();
  430. checkTypeAndSizeOfDisp(computedDisp, &sz);
  431. int pointsCount = sz.height*sz.width;
  432. if (!mask.empty())
  433. {
  434. checkTypeAndSizeOfMask(mask, sz);
  435. pointsCount = countNonZero(mask);
  436. }
  437. return 1.f / sqrt((float)pointsCount) * (float)cvtest::norm(computedDisp, groundTruthDisp, NORM_L2, mask);
  438. }
  439. /*
  440. Calculate fraction of bad matching pixels.
  441. */
  442. float badMatchPxlsFraction(const Mat& computedDisp, const Mat& groundTruthDisp, const Mat& mask,
  443. float _badThresh = EVAL_BAD_THRESH)
  444. {
  445. int badThresh = cvRound(_badThresh);
  446. checkTypeAndSizeOfDisp(groundTruthDisp, 0);
  447. Size sz = groundTruthDisp.size();
  448. checkTypeAndSizeOfDisp(computedDisp, &sz);
  449. Mat badPxlsMap;
  450. absdiff(computedDisp, groundTruthDisp, badPxlsMap);
  451. badPxlsMap = badPxlsMap > badThresh;
  452. int pointsCount = sz.height*sz.width;
  453. if (!mask.empty())
  454. {
  455. checkTypeAndSizeOfMask(mask, sz);
  456. badPxlsMap = badPxlsMap & mask;
  457. pointsCount = countNonZero(mask);
  458. }
  459. return 1.f / pointsCount * countNonZero(badPxlsMap);
  460. }
  461. //===================== regression test for stereo matching algorithms ==============================
  462. const string ALGORITHMS_DIR = "stereomatching/algorithms/";
  463. const string DATASETS_DIR = "stereomatching/datasets/";
  464. const string DATASETS_FILE = "datasets.xml";
  465. const string RUN_PARAMS_FILE = "_params.xml";
  466. const string RESULT_FILE = "_res.xml";
  467. const string LEFT_IMG_NAME = "im2.png";
  468. const string RIGHT_IMG_NAME = "im6.png";
  469. const string TRUE_LEFT_DISP_NAME = "disp2.png";
  470. const string TRUE_RIGHT_DISP_NAME = "disp6.png";
  471. string ERROR_PREFIXES[] = { "borderedAll",
  472. "borderedNoOccl",
  473. "borderedOccl",
  474. "borderedTextured",
  475. "borderedTextureless",
  476. "borderedDepthDiscont" }; // size of ERROR_KINDS_COUNT
  477. string ROI_PREFIXES[] = { "roiX",
  478. "roiY",
  479. "roiWidth",
  480. "roiHeight" };
  481. const string RMS_STR = "RMS";
  482. const string BAD_PXLS_FRACTION_STR = "BadPxlsFraction";
  483. const string ROI_STR = "ValidDisparityROI";
  484. class QualityEvalParams
  485. {
  486. public:
  487. QualityEvalParams()
  488. {
  489. setDefaults();
  490. }
  491. QualityEvalParams(int _ignoreBorder)
  492. {
  493. setDefaults();
  494. ignoreBorder = _ignoreBorder;
  495. }
  496. void setDefaults()
  497. {
  498. badThresh = EVAL_BAD_THRESH;
  499. texturelessWidth = EVAL_TEXTURELESS_WIDTH;
  500. texturelessThresh = EVAL_TEXTURELESS_THRESH;
  501. dispThresh = EVAL_DISP_THRESH;
  502. dispGap = EVAL_DISP_GAP;
  503. discontWidth = EVAL_DISCONT_WIDTH;
  504. ignoreBorder = EVAL_IGNORE_BORDER;
  505. }
  506. float badThresh;
  507. int texturelessWidth;
  508. float texturelessThresh;
  509. float dispThresh;
  510. float dispGap;
  511. int discontWidth;
  512. int ignoreBorder;
  513. };
  514. class CV_StereoMatchingTest : public cvtest::BaseTest
  515. {
  516. public:
  517. CV_StereoMatchingTest()
  518. {
  519. rmsEps.resize(ERROR_KINDS_COUNT, 0.01f); fracEps.resize(ERROR_KINDS_COUNT, 1.e-6f);
  520. }
  521. protected:
  522. // assumed that left image is a reference image
  523. virtual int runStereoMatchingAlgorithm(const Mat& leftImg, const Mat& rightImg,
  524. Rect& calcROI, Mat& leftDisp, Mat& rightDisp, int caseIdx) = 0; // return ignored border width
  525. int readDatasetsParams(FileStorage& fs);
  526. virtual int readRunParams(FileStorage& fs);
  527. void writeErrors(const string& errName, const vector<float>& errors, FileStorage* fs = 0);
  528. void writeROI(const Rect& calcROI, FileStorage* fs = 0);
  529. void readErrors(FileNode& fn, const string& errName, vector<float>& errors);
  530. void readROI(FileNode& fn, Rect& trueROI);
  531. int compareErrors(const vector<float>& calcErrors, const vector<float>& validErrors,
  532. const vector<float>& eps, const string& errName);
  533. int compareROI(const Rect& calcROI, const Rect& validROI);
  534. int processStereoMatchingResults(FileStorage& fs, int caseIdx, bool isWrite,
  535. const Mat& leftImg, const Mat& rightImg,
  536. const Rect& calcROI,
  537. const Mat& trueLeftDisp, const Mat& trueRightDisp,
  538. const Mat& leftDisp, const Mat& rightDisp,
  539. const QualityEvalParams& qualityEvalParams);
  540. void run(int);
  541. vector<float> rmsEps;
  542. vector<float> fracEps;
  543. struct DatasetParams
  544. {
  545. int dispScaleFactor;
  546. int dispUnknVal;
  547. };
  548. map<string, DatasetParams> datasetsParams;
  549. vector<string> caseNames;
  550. vector<string> caseDatasets;
  551. };
  552. void CV_StereoMatchingTest::run(int)
  553. {
  554. addDataSearchSubDirectory("cv");
  555. string algorithmName = name;
  556. assert(!algorithmName.empty());
  557. FileStorage datasetsFS(findDataFile(DATASETS_DIR + DATASETS_FILE), FileStorage::READ);
  558. int code = readDatasetsParams(datasetsFS);
  559. if (code != cvtest::TS::OK)
  560. {
  561. ts->set_failed_test_info(code);
  562. return;
  563. }
  564. FileStorage runParamsFS(findDataFile(ALGORITHMS_DIR + algorithmName + RUN_PARAMS_FILE), FileStorage::READ);
  565. code = readRunParams(runParamsFS);
  566. if (code != cvtest::TS::OK)
  567. {
  568. ts->set_failed_test_info(code);
  569. return;
  570. }
  571. string fullResultFilename = findDataDirectory(ALGORITHMS_DIR) + algorithmName + RESULT_FILE;
  572. FileStorage resFS(fullResultFilename, FileStorage::READ);
  573. bool isWrite = true; // write or compare results
  574. if (resFS.isOpened())
  575. isWrite = false;
  576. else
  577. {
  578. resFS.open(fullResultFilename, FileStorage::WRITE);
  579. if (!resFS.isOpened())
  580. {
  581. ts->printf(cvtest::TS::LOG, "file %s can not be read or written\n", fullResultFilename.c_str());
  582. ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ARG_CHECK);
  583. return;
  584. }
  585. resFS << "stereo_matching" << "{";
  586. }
  587. int progress = 0, caseCount = (int)caseNames.size();
  588. for (int ci = 0; ci < caseCount; ci++)
  589. {
  590. progress = update_progress(progress, ci, caseCount, 0);
  591. printf("progress: %d%%\n", progress);
  592. fflush(stdout);
  593. string datasetName = caseDatasets[ci];
  594. string datasetFullDirName = findDataDirectory(DATASETS_DIR) + datasetName + "/";
  595. Mat leftImg = imread(datasetFullDirName + LEFT_IMG_NAME);
  596. Mat rightImg = imread(datasetFullDirName + RIGHT_IMG_NAME);
  597. Mat trueLeftDisp = imread(datasetFullDirName + TRUE_LEFT_DISP_NAME, 0);
  598. Mat trueRightDisp = imread(datasetFullDirName + TRUE_RIGHT_DISP_NAME, 0);
  599. Rect calcROI;
  600. if (leftImg.empty() || rightImg.empty() || trueLeftDisp.empty())
  601. {
  602. ts->printf(cvtest::TS::LOG, "images or left ground-truth disparities of dataset %s can not be read", datasetName.c_str());
  603. code = cvtest::TS::FAIL_INVALID_TEST_DATA;
  604. continue;
  605. }
  606. int dispScaleFactor = datasetsParams[datasetName].dispScaleFactor;
  607. Mat tmp;
  608. trueLeftDisp.convertTo(tmp, CV_32FC1, 1.f / dispScaleFactor);
  609. trueLeftDisp = tmp;
  610. tmp.release();
  611. if (!trueRightDisp.empty())
  612. {
  613. trueRightDisp.convertTo(tmp, CV_32FC1, 1.f / dispScaleFactor);
  614. trueRightDisp = tmp;
  615. tmp.release();
  616. }
  617. Mat leftDisp, rightDisp;
  618. int ignBorder = max(runStereoMatchingAlgorithm(leftImg, rightImg, calcROI, leftDisp, rightDisp, ci), EVAL_IGNORE_BORDER);
  619. leftDisp.convertTo(tmp, CV_32FC1);
  620. leftDisp = tmp;
  621. tmp.release();
  622. rightDisp.convertTo(tmp, CV_32FC1);
  623. rightDisp = tmp;
  624. tmp.release();
  625. int tempCode = processStereoMatchingResults(resFS, ci, isWrite,
  626. leftImg, rightImg, calcROI, trueLeftDisp, trueRightDisp, leftDisp, rightDisp, QualityEvalParams(ignBorder));
  627. code = tempCode == cvtest::TS::OK ? code : tempCode;
  628. }
  629. if (isWrite)
  630. resFS << "}"; // "stereo_matching"
  631. ts->set_failed_test_info(code);
  632. }
  633. void calcErrors(const Mat& leftImg, const Mat& /*rightImg*/,
  634. const Mat& trueLeftDisp, const Mat& trueRightDisp,
  635. const Mat& trueLeftUnknDispMask, const Mat& trueRightUnknDispMask,
  636. const Mat& calcLeftDisp, const Mat& /*calcRightDisp*/,
  637. vector<float>& rms, vector<float>& badPxlsFractions,
  638. const QualityEvalParams& qualityEvalParams)
  639. {
  640. Mat texturelessMask, texturedMask;
  641. computeTextureBasedMasks(leftImg, &texturelessMask, &texturedMask,
  642. qualityEvalParams.texturelessWidth, qualityEvalParams.texturelessThresh);
  643. Mat occludedMask, nonOccludedMask;
  644. computeOcclusionBasedMasks(trueLeftDisp, trueRightDisp, &occludedMask, &nonOccludedMask,
  645. trueLeftUnknDispMask, trueRightUnknDispMask, qualityEvalParams.dispThresh);
  646. Mat depthDiscontMask;
  647. computeDepthDiscontMask(trueLeftDisp, depthDiscontMask, trueLeftUnknDispMask,
  648. qualityEvalParams.dispGap, qualityEvalParams.discontWidth);
  649. Mat borderedKnownMask = getBorderedMask(leftImg.size(), qualityEvalParams.ignoreBorder) & ~trueLeftUnknDispMask;
  650. nonOccludedMask &= borderedKnownMask;
  651. occludedMask &= borderedKnownMask;
  652. texturedMask &= nonOccludedMask; // & borderedKnownMask
  653. texturelessMask &= nonOccludedMask; // & borderedKnownMask
  654. depthDiscontMask &= nonOccludedMask; // & borderedKnownMask
  655. rms.resize(ERROR_KINDS_COUNT);
  656. rms[0] = dispRMS(calcLeftDisp, trueLeftDisp, borderedKnownMask);
  657. rms[1] = dispRMS(calcLeftDisp, trueLeftDisp, nonOccludedMask);
  658. rms[2] = dispRMS(calcLeftDisp, trueLeftDisp, occludedMask);
  659. rms[3] = dispRMS(calcLeftDisp, trueLeftDisp, texturedMask);
  660. rms[4] = dispRMS(calcLeftDisp, trueLeftDisp, texturelessMask);
  661. rms[5] = dispRMS(calcLeftDisp, trueLeftDisp, depthDiscontMask);
  662. badPxlsFractions.resize(ERROR_KINDS_COUNT);
  663. badPxlsFractions[0] = badMatchPxlsFraction(calcLeftDisp, trueLeftDisp, borderedKnownMask, qualityEvalParams.badThresh);
  664. badPxlsFractions[1] = badMatchPxlsFraction(calcLeftDisp, trueLeftDisp, nonOccludedMask, qualityEvalParams.badThresh);
  665. badPxlsFractions[2] = badMatchPxlsFraction(calcLeftDisp, trueLeftDisp, occludedMask, qualityEvalParams.badThresh);
  666. badPxlsFractions[3] = badMatchPxlsFraction(calcLeftDisp, trueLeftDisp, texturedMask, qualityEvalParams.badThresh);
  667. badPxlsFractions[4] = badMatchPxlsFraction(calcLeftDisp, trueLeftDisp, texturelessMask, qualityEvalParams.badThresh);
  668. badPxlsFractions[5] = badMatchPxlsFraction(calcLeftDisp, trueLeftDisp, depthDiscontMask, qualityEvalParams.badThresh);
  669. }
  670. int CV_StereoMatchingTest::processStereoMatchingResults(FileStorage& fs, int caseIdx, bool isWrite,
  671. const Mat& leftImg, const Mat& rightImg,
  672. const Rect& calcROI,
  673. const Mat& trueLeftDisp, const Mat& trueRightDisp,
  674. const Mat& leftDisp, const Mat& rightDisp,
  675. const QualityEvalParams& qualityEvalParams)
  676. {
  677. // rightDisp is not used in current test virsion
  678. int code = cvtest::TS::OK;
  679. assert(fs.isOpened());
  680. assert(trueLeftDisp.type() == CV_32FC1);
  681. assert(trueRightDisp.empty() || trueRightDisp.type() == CV_32FC1);
  682. assert(leftDisp.type() == CV_32FC1 && (rightDisp.empty() || rightDisp.type() == CV_32FC1));
  683. // get masks for unknown ground truth disparity values
  684. Mat leftUnknMask, rightUnknMask;
  685. DatasetParams params = datasetsParams[caseDatasets[caseIdx]];
  686. absdiff(trueLeftDisp, Scalar(params.dispUnknVal), leftUnknMask);
  687. leftUnknMask = leftUnknMask < std::numeric_limits<float>::epsilon();
  688. assert(leftUnknMask.type() == CV_8UC1);
  689. if (!trueRightDisp.empty())
  690. {
  691. absdiff(trueRightDisp, Scalar(params.dispUnknVal), rightUnknMask);
  692. rightUnknMask = rightUnknMask < std::numeric_limits<float>::epsilon();
  693. assert(rightUnknMask.type() == CV_8UC1);
  694. }
  695. // calculate errors
  696. vector<float> rmss, badPxlsFractions;
  697. calcErrors(leftImg, rightImg, trueLeftDisp, trueRightDisp, leftUnknMask, rightUnknMask,
  698. leftDisp, rightDisp, rmss, badPxlsFractions, qualityEvalParams);
  699. if (isWrite)
  700. {
  701. fs << caseNames[caseIdx] << "{";
  702. fs.writeComment(RMS_STR, 0);
  703. writeErrors(RMS_STR, rmss, &fs);
  704. fs.writeComment(BAD_PXLS_FRACTION_STR, 0);
  705. writeErrors(BAD_PXLS_FRACTION_STR, badPxlsFractions, &fs);
  706. fs.writeComment(ROI_STR, 0);
  707. writeROI(calcROI, &fs);
  708. fs << "}"; // datasetName
  709. }
  710. else // compare
  711. {
  712. ts->printf(cvtest::TS::LOG, "\nquality of case named %s\n", caseNames[caseIdx].c_str());
  713. ts->printf(cvtest::TS::LOG, "%s\n", RMS_STR.c_str());
  714. writeErrors(RMS_STR, rmss);
  715. ts->printf(cvtest::TS::LOG, "%s\n", BAD_PXLS_FRACTION_STR.c_str());
  716. writeErrors(BAD_PXLS_FRACTION_STR, badPxlsFractions);
  717. ts->printf(cvtest::TS::LOG, "%s\n", ROI_STR.c_str());
  718. writeROI(calcROI);
  719. FileNode fn = fs.getFirstTopLevelNode()[caseNames[caseIdx]];
  720. vector<float> validRmss, validBadPxlsFractions;
  721. Rect validROI;
  722. readErrors(fn, RMS_STR, validRmss);
  723. readErrors(fn, BAD_PXLS_FRACTION_STR, validBadPxlsFractions);
  724. readROI(fn, validROI);
  725. int tempCode = compareErrors(rmss, validRmss, rmsEps, RMS_STR);
  726. code = tempCode == cvtest::TS::OK ? code : tempCode;
  727. tempCode = compareErrors(badPxlsFractions, validBadPxlsFractions, fracEps, BAD_PXLS_FRACTION_STR);
  728. code = tempCode == cvtest::TS::OK ? code : tempCode;
  729. tempCode = compareROI(calcROI, validROI);
  730. code = tempCode == cvtest::TS::OK ? code : tempCode;
  731. }
  732. return code;
  733. }
  734. int CV_StereoMatchingTest::readDatasetsParams(FileStorage& fs)
  735. {
  736. if (!fs.isOpened())
  737. {
  738. ts->printf(cvtest::TS::LOG, "datasetsParams can not be read ");
  739. return cvtest::TS::FAIL_INVALID_TEST_DATA;
  740. }
  741. datasetsParams.clear();
  742. FileNode fn = fs.getFirstTopLevelNode();
  743. assert(fn.isSeq());
  744. for (int i = 0; i < (int)fn.size(); i += 3)
  745. {
  746. String _name = fn[i];
  747. DatasetParams params;
  748. String sf = fn[i + 1]; params.dispScaleFactor = atoi(sf.c_str());
  749. String uv = fn[i + 2]; params.dispUnknVal = atoi(uv.c_str());
  750. datasetsParams[_name] = params;
  751. }
  752. return cvtest::TS::OK;
  753. }
  754. int CV_StereoMatchingTest::readRunParams(FileStorage& fs)
  755. {
  756. if (!fs.isOpened())
  757. {
  758. ts->printf(cvtest::TS::LOG, "runParams can not be read ");
  759. return cvtest::TS::FAIL_INVALID_TEST_DATA;
  760. }
  761. caseNames.clear();;
  762. caseDatasets.clear();
  763. return cvtest::TS::OK;
  764. }
  765. void CV_StereoMatchingTest::writeErrors(const string& errName, const vector<float>& errors, FileStorage* fs)
  766. {
  767. assert((int)errors.size() == ERROR_KINDS_COUNT);
  768. vector<float>::const_iterator it = errors.begin();
  769. if (fs)
  770. for (int i = 0; i < ERROR_KINDS_COUNT; i++, ++it)
  771. *fs << ERROR_PREFIXES[i] + errName << *it;
  772. else
  773. for (int i = 0; i < ERROR_KINDS_COUNT; i++, ++it)
  774. ts->printf(cvtest::TS::LOG, "%s = %f\n", string(ERROR_PREFIXES[i] + errName).c_str(), *it);
  775. }
  776. void CV_StereoMatchingTest::writeROI(const Rect& calcROI, FileStorage* fs)
  777. {
  778. if (fs)
  779. {
  780. *fs << ROI_PREFIXES[0] << calcROI.x;
  781. *fs << ROI_PREFIXES[1] << calcROI.y;
  782. *fs << ROI_PREFIXES[2] << calcROI.width;
  783. *fs << ROI_PREFIXES[3] << calcROI.height;
  784. }
  785. else
  786. {
  787. ts->printf(cvtest::TS::LOG, "%s = %d\n", ROI_PREFIXES[0].c_str(), calcROI.x);
  788. ts->printf(cvtest::TS::LOG, "%s = %d\n", ROI_PREFIXES[1].c_str(), calcROI.y);
  789. ts->printf(cvtest::TS::LOG, "%s = %d\n", ROI_PREFIXES[2].c_str(), calcROI.width);
  790. ts->printf(cvtest::TS::LOG, "%s = %d\n", ROI_PREFIXES[3].c_str(), calcROI.height);
  791. }
  792. }
  793. void CV_StereoMatchingTest::readErrors(FileNode& fn, const string& errName, vector<float>& errors)
  794. {
  795. errors.resize(ERROR_KINDS_COUNT);
  796. vector<float>::iterator it = errors.begin();
  797. for (int i = 0; i < ERROR_KINDS_COUNT; i++, ++it)
  798. fn[ERROR_PREFIXES[i] + errName] >> *it;
  799. }
  800. void CV_StereoMatchingTest::readROI(FileNode& fn, Rect& validROI)
  801. {
  802. fn[ROI_PREFIXES[0]] >> validROI.x;
  803. fn[ROI_PREFIXES[1]] >> validROI.y;
  804. fn[ROI_PREFIXES[2]] >> validROI.width;
  805. fn[ROI_PREFIXES[3]] >> validROI.height;
  806. }
  807. int CV_StereoMatchingTest::compareErrors(const vector<float>& calcErrors, const vector<float>& validErrors,
  808. const vector<float>& eps, const string& errName)
  809. {
  810. assert((int)calcErrors.size() == ERROR_KINDS_COUNT);
  811. assert((int)validErrors.size() == ERROR_KINDS_COUNT);
  812. assert((int)eps.size() == ERROR_KINDS_COUNT);
  813. vector<float>::const_iterator calcIt = calcErrors.begin(),
  814. validIt = validErrors.begin(),
  815. epsIt = eps.begin();
  816. bool ok = true;
  817. for (int i = 0; i < ERROR_KINDS_COUNT; i++, ++calcIt, ++validIt, ++epsIt)
  818. if (*calcIt - *validIt > *epsIt)
  819. {
  820. ts->printf(cvtest::TS::LOG, "bad accuracy of %s (valid=%f; calc=%f)\n", string(ERROR_PREFIXES[i] + errName).c_str(), *validIt, *calcIt);
  821. ok = false;
  822. }
  823. return ok ? cvtest::TS::OK : cvtest::TS::FAIL_BAD_ACCURACY;
  824. }
  825. int CV_StereoMatchingTest::compareROI(const Rect& calcROI, const Rect& validROI)
  826. {
  827. int compare[4][2] = {
  828. { calcROI.x, validROI.x },
  829. { calcROI.y, validROI.y },
  830. { calcROI.width, validROI.width },
  831. { calcROI.height, validROI.height },
  832. };
  833. bool ok = true;
  834. for (int i = 0; i < 4; i++)
  835. {
  836. if (compare[i][0] != compare[i][1])
  837. {
  838. 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]);
  839. ok = false;
  840. }
  841. }
  842. return ok ? cvtest::TS::OK : cvtest::TS::FAIL_BAD_ACCURACY;
  843. }
  844. //----------------------------------- StereoSGM test -----------------------------------------------------
  845. class CV_Cuda_StereoSGMTest : public CV_StereoMatchingTest
  846. {
  847. public:
  848. CV_Cuda_StereoSGMTest()
  849. {
  850. name = "cuda_stereosgm";
  851. fill(rmsEps.begin(), rmsEps.end(), 0.25f);
  852. fill(fracEps.begin(), fracEps.end(), 0.01f);
  853. }
  854. protected:
  855. struct RunParams
  856. {
  857. int ndisp;
  858. int mode;
  859. };
  860. vector<RunParams> caseRunParams;
  861. virtual int readRunParams(FileStorage& fs)
  862. {
  863. int code = CV_StereoMatchingTest::readRunParams(fs);
  864. FileNode fn = fs.getFirstTopLevelNode();
  865. assert(fn.isSeq());
  866. for (int i = 0; i < (int)fn.size(); i += 4)
  867. {
  868. String caseName = fn[i], datasetName = fn[i + 1];
  869. RunParams params;
  870. String ndisp = fn[i + 2]; params.ndisp = atoi(ndisp.c_str());
  871. String mode = fn[i + 3]; params.mode = atoi(mode.c_str());
  872. caseNames.push_back(caseName);
  873. caseDatasets.push_back(datasetName);
  874. caseRunParams.push_back(params);
  875. }
  876. return code;
  877. }
  878. virtual int runStereoMatchingAlgorithm(const Mat& leftImg, const Mat& rightImg,
  879. Rect& calcROI, Mat& leftDisp, Mat& /*rightDisp*/, int caseIdx)
  880. {
  881. RunParams params = caseRunParams[caseIdx];
  882. assert(params.ndisp % 16 == 0);
  883. Ptr<StereoMatcher> sgm = createStereoSGM(0, params.ndisp, 10, 120, 5, params.mode);
  884. cv::Mat G1, G2;
  885. cv::cvtColor(leftImg, G1, cv::COLOR_RGB2GRAY);
  886. cv::cvtColor(rightImg, G2, cv::COLOR_RGB2GRAY);
  887. cv::cuda::GpuMat d_leftImg, d_rightImg, d_leftDisp;
  888. d_leftImg.upload(G1);
  889. d_rightImg.upload(G2);
  890. sgm->compute(d_leftImg, d_rightImg, d_leftDisp);
  891. d_leftDisp.download(leftDisp);
  892. CV_Assert(leftDisp.type() == CV_16SC1);
  893. leftDisp.convertTo(leftDisp, CV_32FC1, 1.0 / StereoMatcher::DISP_SCALE);
  894. calcROI.x = calcROI.y = 0;
  895. calcROI.width = leftImg.cols;
  896. calcROI.height = leftImg.rows;
  897. return 0;
  898. }
  899. };
  900. TEST(CudaStereo_StereoSGM, regression) { CV_Cuda_StereoSGMTest test; test.safe_run(); }
  901. }} // namespace
  902. #endif // HAVE_CUDA