1 /*M///////////////////////////////////////////////////////////////////////////////////////
3 // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
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.
10 // Intel License Agreement
11 // For Open Source Computer Vision Library
13 // Copyright (C) 2000, Intel Corporation, all rights reserved.
14 // Third party copyrights are property of their respective owners.
16 // Redistribution and use in source and binary forms, with or without modification,
17 // are permitted provided that the following conditions are met:
19 // * Redistribution's of source code must retain the above copyright notice,
20 // this list of conditions and the following disclaimer.
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.
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.
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.
43 This is a regression test for stereo matching algorithms. This test gets some quality metrics
44 discribed in "A Taxonomy and Evaluation of Dense Two-Frame Stereo Correspondence Algorithms".
45 Daniel Scharstein, Richard Szeliski
55 const float EVAL_BAD_THRESH = 1.f;
56 const int EVAL_TEXTURELESS_WIDTH = 3;
57 const float EVAL_TEXTURELESS_THRESH = 4.f;
58 const float EVAL_DISP_THRESH = 1.f;
59 const float EVAL_DISP_GAP = 2.f;
60 const int EVAL_DISCONT_WIDTH = 9;
61 const int EVAL_IGNORE_BORDER = 10;
63 const int ERROR_KINDS_COUNT = 6;
65 //============================== quality measuring functions =================================================
68 Calculate textureless regions of image (regions where the squared horizontal intensity gradient averaged over
69 a square window of size=evalTexturelessWidth is below a threshold=evalTexturelessThresh) and textured regions.
71 void computeTextureBasedMasks( const Mat& _img, Mat* texturelessMask, Mat* texturedMask,
72 int texturelessWidth = EVAL_TEXTURELESS_WIDTH, float texturelessThresh = EVAL_TEXTURELESS_THRESH )
74 if( !texturelessMask && !texturedMask )
77 CV_Error( CV_StsBadArg, "img is empty" );
80 if( _img.channels() > 1)
82 Mat tmp; cvtColor( _img, tmp, CV_BGR2GRAY ); img = tmp;
84 Mat dxI; Sobel( img, dxI, CV_32FC1, 1, 0, 3 );
85 Mat dxI2; pow( dxI / 8.f/*normalize*/, 2, dxI2 );
86 Mat avgDxI2; boxFilter( dxI2, avgDxI2, CV_32FC1, Size(texturelessWidth,texturelessWidth) );
89 *texturelessMask = avgDxI2 < texturelessThresh;
91 *texturedMask = avgDxI2 >= texturelessThresh;
94 void checkSizeAndTypeOfDispMaps( const Mat& leftDispMap, const Mat& rightDispMap )
96 if( leftDispMap.empty() )
97 CV_Error( CV_StsBadArg, "leftDispMap is empty" );
98 if( leftDispMap.type() != CV_32FC1 )
99 CV_Error( CV_StsBadArg, "leftDispMap must have CV_32FC1 type" );
101 if( !rightDispMap.empty() )
103 if( rightDispMap.type() != CV_32FC1 )
104 CV_Error( CV_StsBadArg, "rightDispMap must have CV_32FC1 type" );
105 if( rightDispMap.cols != leftDispMap.cols || rightDispMap.rows != leftDispMap.rows )
106 CV_Error( CV_StsBadArg, "leftDispMap and rightDispMap must have the same size" );
110 void checkSizeAndTypeOfMask( const Mat& mask, Size sz )
113 CV_Error( CV_StsBadArg, "mask is empty" );
114 if( mask.type() != CV_8UC1 )
115 CV_Error( CV_StsBadArg, "mask must have CV_8UC1 type" );
116 if( mask.rows != sz.height || mask.cols != sz.width )
117 CV_Error( CV_StsBadArg, "mask has incorrect size" );
120 void checkDispMapsAndUnknDispMasks( const Mat& leftDispMap, const Mat& rightDispMap,
121 const Mat& leftUnknDispMask, const Mat& rightUnknDispMask )
123 checkSizeAndTypeOfDispMaps( leftDispMap, rightDispMap );
125 if( !leftUnknDispMask.empty() )
126 checkSizeAndTypeOfMask( leftUnknDispMask, leftDispMap.size() );
127 if( !rightUnknDispMask.empty() )
128 checkSizeAndTypeOfMask( rightUnknDispMask, rightDispMap.size() );
130 double leftMinVal = 0, rightMinVal = 0;
131 if( leftUnknDispMask.empty() )
132 minMaxLoc( leftDispMap, &leftMinVal );
134 minMaxLoc( leftDispMap, &leftMinVal, 0, 0, 0, ~leftUnknDispMask );
135 if( !rightDispMap.empty() )
137 if( rightUnknDispMask.empty() )
138 minMaxLoc( rightDispMap, &rightMinVal );
140 minMaxLoc( rightDispMap, &rightMinVal, 0, 0, 0, ~rightUnknDispMask );
142 if( leftMinVal < 0 || rightMinVal < 0)
143 CV_Error( CV_StsBadArg, "known disparity values must be positive" );
147 Calculate occluded regions of reference image (left image) (regions that are occluded in the matching image (right image),
148 i.e., where the forward-mapped disparity lands at a location with a larger (nearer) disparity) and non occluded regions.
150 void computeOcclusionBasedMasks( const Mat& leftDisp, const Mat& _rightDisp,
151 Mat* occludedMask, Mat* nonOccludedMask,
152 const Mat& leftUnknDispMask = Mat(), const Mat& rightUnknDispMask = Mat(),
153 float dispThresh = EVAL_DISP_THRESH )
155 if( !occludedMask && !nonOccludedMask )
157 checkDispMapsAndUnknDispMasks( leftDisp, _rightDisp, leftUnknDispMask, rightUnknDispMask );
160 if( _rightDisp.empty() )
162 if( !rightUnknDispMask.empty() )
163 CV_Error( CV_StsBadArg, "rightUnknDispMask must be empty if _rightDisp is empty" );
164 rightDisp.create(leftDisp.size(), CV_32FC1);
165 rightDisp.setTo(Scalar::all(0) );
166 for( int leftY = 0; leftY < leftDisp.rows; leftY++ )
168 for( int leftX = 0; leftX < leftDisp.cols; leftX++ )
170 if( !leftUnknDispMask.empty() && leftUnknDispMask.at<uchar>(leftY,leftX) )
172 float leftDispVal = leftDisp.at<float>(leftY, leftX);
173 int rightX = leftX - cvRound(leftDispVal), rightY = leftY;
175 rightDisp.at<float>(rightY,rightX) = max(rightDisp.at<float>(rightY,rightX), leftDispVal);
180 _rightDisp.copyTo(rightDisp);
184 occludedMask->create(leftDisp.size(), CV_8UC1);
185 occludedMask->setTo(Scalar::all(0) );
187 if( nonOccludedMask )
189 nonOccludedMask->create(leftDisp.size(), CV_8UC1);
190 nonOccludedMask->setTo(Scalar::all(0) );
192 for( int leftY = 0; leftY < leftDisp.rows; leftY++ )
194 for( int leftX = 0; leftX < leftDisp.cols; leftX++ )
196 if( !leftUnknDispMask.empty() && leftUnknDispMask.at<uchar>(leftY,leftX) )
198 float leftDispVal = leftDisp.at<float>(leftY, leftX);
199 int rightX = leftX - cvRound(leftDispVal), rightY = leftY;
200 if( rightX < 0 && occludedMask )
201 occludedMask->at<uchar>(leftY, leftX) = 255;
204 if( !rightUnknDispMask.empty() && rightUnknDispMask.at<uchar>(rightY,rightX) )
206 float rightDispVal = rightDisp.at<float>(rightY, rightX);
207 if( rightDispVal > leftDispVal + dispThresh )
210 occludedMask->at<uchar>(leftY, leftX) = 255;
214 if( nonOccludedMask )
215 nonOccludedMask->at<uchar>(leftY, leftX) = 255;
223 Calculate depth discontinuty regions: pixels whose neiboring disparities differ by more than
224 dispGap, dilated by window of width discontWidth.
226 void computeDepthDiscontMask( const Mat& disp, Mat& depthDiscontMask, const Mat& unknDispMask = Mat(),
227 float dispGap = EVAL_DISP_GAP, int discontWidth = EVAL_DISCONT_WIDTH )
230 CV_Error( CV_StsBadArg, "disp is empty" );
231 if( disp.type() != CV_32FC1 )
232 CV_Error( CV_StsBadArg, "disp must have CV_32FC1 type" );
233 if( !unknDispMask.empty() )
234 checkSizeAndTypeOfMask( unknDispMask, disp.size() );
236 Mat curDisp; disp.copyTo( curDisp );
237 if( !unknDispMask.empty() )
238 curDisp.setTo( Scalar(numeric_limits<float>::min()), unknDispMask );
239 Mat maxNeighbDisp; dilate( curDisp, maxNeighbDisp, Mat(3, 3, CV_8UC1, Scalar(1)) );
240 if( !unknDispMask.empty() )
241 curDisp.setTo( Scalar(numeric_limits<float>::max()), unknDispMask );
242 Mat minNeighbDisp; erode( curDisp, minNeighbDisp, Mat(3, 3, CV_8UC1, Scalar(1)) );
243 depthDiscontMask = max( (Mat)(maxNeighbDisp-disp), (Mat)(disp-minNeighbDisp) ) > dispGap;
244 if( !unknDispMask.empty() )
245 depthDiscontMask &= ~unknDispMask;
246 dilate( depthDiscontMask, depthDiscontMask, Mat(discontWidth, discontWidth, CV_8UC1, Scalar(1)) );
250 Get evaluation masks excluding a border.
252 Mat getBorderedMask( Size maskSize, int border = EVAL_IGNORE_BORDER )
254 CV_Assert( border >= 0 );
255 Mat mask(maskSize, CV_8UC1, Scalar(0));
256 int w = maskSize.width - 2*border, h = maskSize.height - 2*border;
258 mask.setTo(Scalar(0));
260 mask( Rect(Point(border,border),Size(w,h)) ).setTo(Scalar(255));
265 Calculate root-mean-squared error between the computed disparity map (computedDisp) and ground truth map (groundTruthDisp).
267 float dispRMS( const Mat& computedDisp, const Mat& groundTruthDisp, const Mat& mask )
269 checkSizeAndTypeOfDispMaps( computedDisp, groundTruthDisp );
270 int pointsCount = computedDisp.cols*computedDisp.rows;
273 checkSizeAndTypeOfMask( mask, computedDisp.size() );
274 pointsCount = countNonZero(mask);
276 return 1.f/sqrt((float)pointsCount) * (float)norm(computedDisp, groundTruthDisp, NORM_L2, mask);
280 Calculate fraction of bad matching pixels.
282 float badMatchPxlsFraction( const Mat& computedDisp, const Mat& groundTruthDisp, const Mat& mask,
283 int badThresh = EVAL_BAD_THRESH )
285 checkSizeAndTypeOfDispMaps( computedDisp, groundTruthDisp );
287 absdiff( computedDisp, groundTruthDisp, badPxlsMap );
288 badPxlsMap = badPxlsMap > badThresh;
289 int pointsCount = computedDisp.cols*computedDisp.rows;
292 checkSizeAndTypeOfMask( mask, computedDisp.size() );
293 badPxlsMap = badPxlsMap & mask;
294 pointsCount = countNonZero(mask);
296 return 1.f/pointsCount * countNonZero(badPxlsMap);
299 //===================== regression test for stereo matching algorithms ==============================
301 const string ALGORITHMS_DIR = "stereomatching/algorithms/";
302 const string DATASETS_DIR = "stereomatching/datasets/";
303 const string DATASETS_FILE = "datasets.xml";
305 const string RUN_PARAMS_FILE = "_params.xml";
306 const string RESULT_FILE = "_res.xml";
308 const string LEFT_IMG_NAME = "im2.png";
309 const string RIGHT_IMG_NAME = "im6.png";
310 const string TRUE_LEFT_DISP_NAME = "disp2.png";
311 const string TRUE_RIGHT_DISP_NAME = "disp6.png";
313 string ERROR_PREFIXES[] = { "borderedAll",
317 "borderedTextureless",
318 "borderedDepthDiscont" }; // size of ERROR_KINDS_COUNT
321 const string RMS_STR = "RMS";
322 const string BAD_PXLS_FRACTION_STR = "BadPxlsFraction";
324 class CV_StereoMatchingTest : public CvTest
327 CV_StereoMatchingTest( const char* testName ) : CvTest( testName, "stereo-matching" )
328 { rmsEps.resize( ERROR_KINDS_COUNT, 0.01f ); fracEps.resize( ERROR_KINDS_COUNT, 1.e-6f ); }
330 // assumed that left image is a reference image
331 virtual void runStereoMatchingAlgorithm( const Mat& leftImg, const Mat& rightImg,
332 Mat& leftDisp, Mat& rightDisp, FileStorage& paramsFS, const string& datasetName ) = 0;
334 int readDatasetsInfo();
335 void writeErrors( const string& errName, const vector<float>& errors, FileStorage* fs = 0 );
336 void readErrors( FileNode& fn, const string& errName, vector<float>& errors );
337 int compareErrors( const vector<float>& calcErrors, const vector<float>& validErrors,
338 const vector<float>& eps, const string& errName );
339 int processStereoMatchingResults( FileStorage& fs, int datasetIdx, bool isWrite,
340 const Mat& leftImg, const Mat& rightImg,
341 const Mat& trueLeftDisp, const Mat& trueRightDisp,
342 const Mat& leftDisp, const Mat& rightDisp );
345 vector<string> datasetsNames;
346 vector<int> dispScaleFactors;
347 vector<int> dispUnknownVal;
349 vector<float> rmsEps;
350 vector<float> fracEps;
353 void CV_StereoMatchingTest::run(int)
355 string dataPath = ts->get_data_path();
356 string algoritmName = name;
357 assert( !algoritmName.empty() );
358 if( dataPath.empty() )
360 ts->printf( CvTS::LOG, "dataPath is empty" );
361 ts->set_failed_test_info( CvTS::FAIL_BAD_ARG_CHECK );
365 int code = readDatasetsInfo();
366 if( code != CvTS::OK )
368 ts->set_failed_test_info( code );
372 string fullResultFilename = dataPath + ALGORITHMS_DIR + algoritmName + RESULT_FILE;
373 bool isWrite = true; // write or compare results
374 FileStorage runParamsFS( dataPath + ALGORITHMS_DIR + algoritmName + RUN_PARAMS_FILE, FileStorage::READ );
375 FileStorage resFS( fullResultFilename, FileStorage::READ );
376 if( resFS.isOpened() )
380 resFS.open( fullResultFilename, FileStorage::WRITE );
381 if( !resFS.isOpened() )
383 ts->printf( CvTS::LOG, "file named %s can not be read or written\n", fullResultFilename.c_str() );
384 ts->set_failed_test_info( CvTS::FAIL_BAD_ARG_CHECK );
387 resFS << "stereo_matching" << "{";
391 for( int dsi = 0; dsi < (int)datasetsNames.size(); dsi++)
393 progress = update_progress( progress, dsi, (int)datasetsNames.size(), 0 );
394 string datasetFullDirName = dataPath + DATASETS_DIR + datasetsNames[dsi] + "/";
395 Mat leftImg = imread(datasetFullDirName + LEFT_IMG_NAME);
396 Mat rightImg = imread(datasetFullDirName + RIGHT_IMG_NAME);
397 Mat trueLeftDisp = imread(datasetFullDirName + TRUE_LEFT_DISP_NAME, 0);
398 Mat trueRightDisp = imread(datasetFullDirName + TRUE_RIGHT_DISP_NAME, 0);
400 if( leftImg.empty() || rightImg.empty() || trueLeftDisp.empty()/* || trueRightDisp.empty()*/ )
402 ts->printf( CvTS::LOG, "images or left ground-truth disparities of dataset %s can not be read", datasetsNames[dsi].c_str() );
403 code = CvTS::FAIL_INVALID_TEST_DATA;
407 int dispScaleFactor = dispScaleFactors[dsi];
408 trueLeftDisp.convertTo( tmp, CV_32FC1, 1.f/dispScaleFactor ); trueLeftDisp = tmp; tmp.release();
409 if( !trueRightDisp.empty() )
410 trueRightDisp.convertTo( tmp, CV_32FC1, 1.f/dispScaleFactor ); trueRightDisp = tmp; tmp.release();
412 Mat leftDisp, rightDisp;
413 runStereoMatchingAlgorithm( leftImg, rightImg, leftDisp, rightDisp, runParamsFS, datasetsNames[dsi] );
414 leftDisp.convertTo( tmp, CV_32FC1 ); leftDisp = tmp; tmp.release();
415 rightDisp.convertTo( tmp, CV_32FC1 ); rightDisp = tmp; tmp.release();
417 int tempCode = processStereoMatchingResults( resFS, dsi, isWrite,
418 leftImg, rightImg, trueLeftDisp, trueRightDisp, leftDisp, rightDisp);
419 code = tempCode==CvTS::OK ? code : tempCode;
423 resFS << "}"; // "stereo_matching"
425 ts->set_failed_test_info( code );
428 void calcErrors( const Mat& leftImg, const Mat& rightImg,
429 const Mat& trueLeftDisp, const Mat& trueRightDisp,
430 const Mat& trueLeftUnknDispMask, const Mat& trueRightUnknDispMask,
431 const Mat& calcLeftDisp, const Mat& calcRightDisp,
432 vector<float>& rms, vector<float>& badPxlsFractions )
434 Mat texturelessMask, texturedMask;
435 computeTextureBasedMasks( leftImg, &texturelessMask, &texturedMask );
436 Mat occludedMask, nonOccludedMask;
437 computeOcclusionBasedMasks( trueLeftDisp, trueRightDisp, &occludedMask, &nonOccludedMask,
438 trueLeftUnknDispMask, trueRightUnknDispMask);
439 Mat depthDiscontMask;
440 computeDepthDiscontMask( trueLeftDisp, depthDiscontMask, trueLeftUnknDispMask);
442 Mat borderedKnownMask = getBorderedMask( leftImg.size() ) & ~trueLeftUnknDispMask;
444 nonOccludedMask &= borderedKnownMask;
445 occludedMask &= borderedKnownMask;
446 texturedMask &= nonOccludedMask; // & borderedKnownMask
447 texturelessMask &= nonOccludedMask; // & borderedKnownMask
448 depthDiscontMask &= nonOccludedMask; // & borderedKnownMask
450 rms.resize(ERROR_KINDS_COUNT);
451 rms[0] = dispRMS( calcLeftDisp, trueLeftDisp, borderedKnownMask );
452 rms[1] = dispRMS( calcLeftDisp, trueLeftDisp, nonOccludedMask );
453 rms[2] = dispRMS( calcLeftDisp, trueLeftDisp, occludedMask );
454 rms[3] = dispRMS( calcLeftDisp, trueLeftDisp, texturedMask );
455 rms[4] = dispRMS( calcLeftDisp, trueLeftDisp, texturelessMask );
456 rms[5] = dispRMS( calcLeftDisp, trueLeftDisp, depthDiscontMask );
458 badPxlsFractions.resize(ERROR_KINDS_COUNT);
459 badPxlsFractions[0] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, borderedKnownMask );
460 badPxlsFractions[1] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, nonOccludedMask );
461 badPxlsFractions[2] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, occludedMask );
462 badPxlsFractions[3] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, texturedMask );
463 badPxlsFractions[4] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, texturelessMask );
464 badPxlsFractions[5] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, depthDiscontMask );
467 int CV_StereoMatchingTest::processStereoMatchingResults( FileStorage& fs, int datasetIdx, bool isWrite,
468 const Mat& leftImg, const Mat& rightImg,
469 const Mat& trueLeftDisp, const Mat& trueRightDisp,
470 const Mat& leftDisp, const Mat& rightDisp )
472 // rightDisp is not used in current test virsion
474 assert( fs.isOpened() );
475 assert( trueLeftDisp.type() == CV_32FC1 && trueRightDisp.type() == CV_32FC1 );
476 assert( leftDisp.type() == CV_32FC1 && rightDisp.type() == CV_32FC1 );
478 // get masks for unknown ground truth disparity values
479 Mat leftUnknMask, rightUnknMask;
480 absdiff( trueLeftDisp, Scalar(dispUnknownVal[datasetIdx]), leftUnknMask );
481 leftUnknMask = leftUnknMask < numeric_limits<float>::epsilon();
482 assert(leftUnknMask.type() == CV_8UC1);
483 if( !trueRightDisp.empty() )
485 absdiff( trueRightDisp, Scalar(dispUnknownVal[datasetIdx]), rightUnknMask );
486 rightUnknMask = rightUnknMask < numeric_limits<float>::epsilon();
487 assert(leftUnknMask.type() == CV_8UC1);
491 vector<float> rmss, badPxlsFractions;
492 calcErrors( leftImg, rightImg, trueLeftDisp, trueRightDisp, leftUnknMask, rightUnknMask,
493 leftDisp, rightDisp, rmss, badPxlsFractions );
495 const string& datasetName = datasetsNames[datasetIdx];
498 fs << datasetName << "{";
499 cvWriteComment( fs.fs, RMS_STR.c_str(), 0 );
500 writeErrors( RMS_STR, rmss, &fs );
501 cvWriteComment( fs.fs, BAD_PXLS_FRACTION_STR.c_str(), 0 );
502 writeErrors( BAD_PXLS_FRACTION_STR, badPxlsFractions, &fs );
503 fs << "}"; // datasetName
507 ts->printf( CvTS::LOG, "\nquality on dataset %s\n", datasetName.c_str() );
508 ts->printf( CvTS::LOG, "%s\n", RMS_STR.c_str() );
509 writeErrors( RMS_STR, rmss );
510 ts->printf( CvTS::LOG, "%s\n", BAD_PXLS_FRACTION_STR.c_str() );
511 writeErrors( BAD_PXLS_FRACTION_STR, badPxlsFractions );
513 FileNode fn = fs.getFirstTopLevelNode()[datasetName];
514 vector<float> validRmss, validBadPxlsFractions;
516 readErrors( fn, RMS_STR, validRmss );
517 readErrors( fn, BAD_PXLS_FRACTION_STR, validBadPxlsFractions );
518 int tempCode = compareErrors( rmss, validRmss, rmsEps, RMS_STR );
519 code = tempCode==CvTS::OK ? code : tempCode;
520 tempCode = compareErrors( badPxlsFractions, validBadPxlsFractions, fracEps, BAD_PXLS_FRACTION_STR );
521 code = tempCode==CvTS::OK ? code : tempCode;
526 int CV_StereoMatchingTest::readDatasetsInfo()
528 string datasetsFilename = string(ts->get_data_path()) + DATASETS_DIR + DATASETS_FILE;
530 FileStorage fs( datasetsFilename, FileStorage::READ );
533 ts->printf( CvTS::LOG, "%s can not be read\n", datasetsFilename.c_str() );
534 return CvTS::FAIL_INVALID_TEST_DATA;
536 FileNode fn = fs.getFirstTopLevelNode()["names_scale_unknown"];
538 datasetsNames.clear();
539 dispScaleFactors.clear();
540 dispUnknownVal.clear();
541 for( int i = 0; i < (int)fn.size(); i+=3 )
543 string name = fn[i]; datasetsNames.push_back(name);
544 string scale = fn[i+1]; dispScaleFactors.push_back(atoi(scale.c_str()));
545 string unkn = fn[i+2]; dispUnknownVal.push_back(atoi(unkn.c_str()));
551 void CV_StereoMatchingTest::writeErrors( const string& errName, const vector<float>& errors, FileStorage* fs )
553 assert( (int)errors.size() == ERROR_KINDS_COUNT );
554 vector<float>::const_iterator it = errors.begin();
556 for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++it )
557 *fs << ERROR_PREFIXES[i] + errName << *it;
559 for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++it )
560 ts->printf( CvTS::LOG, "%s = %f\n", string(ERROR_PREFIXES[i]+errName).c_str(), *it );
563 void CV_StereoMatchingTest::readErrors( FileNode& fn, const string& errName, vector<float>& errors )
565 errors.resize( ERROR_KINDS_COUNT );
566 vector<float>::iterator it = errors.begin();
567 for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++it )
568 fn[ERROR_PREFIXES[i]+errName] >> *it;
571 int CV_StereoMatchingTest::compareErrors( const vector<float>& calcErrors, const vector<float>& validErrors,
572 const vector<float>& eps, const string& errName )
574 assert( (int)calcErrors.size() == ERROR_KINDS_COUNT );
575 assert( (int)validErrors.size() == ERROR_KINDS_COUNT );
576 assert( (int)eps.size() == ERROR_KINDS_COUNT );
577 vector<float>::const_iterator calcIt = calcErrors.begin(),
578 validIt = validErrors.begin(),
581 for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++calcIt, ++validIt, ++epsIt )
582 if( fabs(*calcIt - *validIt) > *epsIt )
584 ts->printf( CvTS::LOG, "bad accuracy of %s (valid=%f; calc=%f)\n", string(ERROR_PREFIXES[i]+errName).c_str(), *validIt, *calcIt );
587 return ok ? CvTS::OK : CvTS::FAIL_BAD_ACCURACY;
590 //----------------------------------- StereoBM test -----------------------------------------------------
592 class CV_StereoBMTest : public CV_StereoMatchingTest
595 CV_StereoBMTest() : CV_StereoMatchingTest( "stereobm" )
596 { fill(rmsEps.begin(), rmsEps.end(), 0.001f); fill(fracEps.begin(), fracEps.end(), 0.00001f); }
598 virtual void runStereoMatchingAlgorithm( const Mat& _leftImg, const Mat& _rightImg,
599 Mat& leftDisp, Mat& rightDisp, FileStorage& paramsFS, const string& datasetName )
603 assert( !datasetName.empty() );
604 if( paramsFS.isOpened() )
606 FileNodeIterator fni = paramsFS.getFirstTopLevelNode()[datasetName].begin();
607 fni >> ndisp >> winSize;
610 ts->printf( CvTS::LOG, "%s was tested with default params "
611 "(ndisp = 16, winSize = 21)\n", datasetName.c_str());
612 assert( ndisp%16 == 0 );
613 assert( _leftImg.type() == CV_8UC3 && _rightImg.type() == CV_8UC3 );
614 Mat leftImg; cvtColor( _leftImg, leftImg, CV_BGR2GRAY );
615 Mat rightImg; cvtColor( _rightImg, rightImg, CV_BGR2GRAY );
617 StereoBM bm( StereoBM::BASIC_PRESET, ndisp, winSize );
618 bm( leftImg, rightImg, leftDisp, CV_32F );
622 CV_StereoBMTest stereoBM;
624 //----------------------------------- StereoGC test -----------------------------------------------------
626 class CV_StereoGCTest : public CV_StereoMatchingTest
629 CV_StereoGCTest() : CV_StereoMatchingTest( "stereogc" )
631 fill(rmsEps.begin(), rmsEps.end(), 3.f);
632 fracEps[0] = 0.05f; // all
633 fracEps[1] = 0.05f; // noOccl
634 fracEps[2] = 0.25f; // occl
635 fracEps[3] = 0.05f; // textured
636 fracEps[4] = 0.10f; // textureless
637 fracEps[5] = 0.10f; // borderedDepthDiscont
640 virtual void runStereoMatchingAlgorithm( const Mat& _leftImg, const Mat& _rightImg,
641 Mat& leftDisp, Mat& rightDisp, FileStorage& paramsFS, const string& datasetName )
645 assert( !datasetName.empty() );
646 if( paramsFS.isOpened() )
648 FileNodeIterator fni = paramsFS.getFirstTopLevelNode()[datasetName].begin();
649 fni >> ndisp >> icount;
652 ts->printf( CvTS::LOG, "%s was tested with default params "
653 "(ndisp = 20, icount = 2)\n", datasetName.c_str());
655 assert( _leftImg.type() == CV_8UC3 && _rightImg.type() == CV_8UC3 );
656 Mat leftImg, rightImg, tmp;
657 cvtColor( _leftImg, leftImg, CV_BGR2GRAY );
658 cvtColor( _rightImg, rightImg, CV_BGR2GRAY );
660 leftDisp.create( leftImg.size(), CV_16SC1 );
661 rightDisp.create( rightImg.size(), CV_16SC1 );
663 CvMat _limg = leftImg, _rimg = rightImg, _ldisp = leftDisp, _rdisp = rightDisp;
664 CvStereoGCState *state = cvCreateStereoGCState( ndisp, icount );
665 cvFindStereoCorrespondenceGC( &_limg, &_rimg, &_ldisp, &_rdisp, state );
666 cvReleaseStereoGCState( &state );
668 leftDisp = - leftDisp;
673 CV_StereoGCTest stereoGC;