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 checkTypeAndSizeOfDisp( const Mat& dispMap, const Size* sz )
97 CV_Error( CV_StsBadArg, "dispMap is empty" );
98 if( dispMap.type() != CV_32FC1 )
99 CV_Error( CV_StsBadArg, "dispMap must have CV_32FC1 type" );
100 if( sz && (dispMap.rows != sz->height || dispMap.cols != sz->width) )
101 CV_Error( CV_StsBadArg, "dispMap has incorrect size" );
104 void checkTypeAndSizeOfMask( const Mat& mask, Size sz )
107 CV_Error( CV_StsBadArg, "mask is empty" );
108 if( mask.type() != CV_8UC1 )
109 CV_Error( CV_StsBadArg, "mask must have CV_8UC1 type" );
110 if( mask.rows != sz.height || mask.cols != sz.width )
111 CV_Error( CV_StsBadArg, "mask has incorrect size" );
114 void checkDispMapsAndUnknDispMasks( const Mat& leftDispMap, const Mat& rightDispMap,
115 const Mat& leftUnknDispMask, const Mat& rightUnknDispMask )
117 // check type and size of disparity maps
118 checkTypeAndSizeOfDisp( leftDispMap, 0 );
119 if( !rightDispMap.empty() )
121 Size sz = leftDispMap.size();
122 checkTypeAndSizeOfDisp( rightDispMap, &sz );
125 // check size and type of unknown disparity maps
126 if( !leftUnknDispMask.empty() )
127 checkTypeAndSizeOfMask( leftUnknDispMask, leftDispMap.size() );
128 if( !rightUnknDispMask.empty() )
129 checkTypeAndSizeOfMask( rightUnknDispMask, rightDispMap.size() );
131 // check values of disparity maps (known disparity values musy be positive)
132 double leftMinVal = 0, rightMinVal = 0;
133 if( leftUnknDispMask.empty() )
134 minMaxLoc( leftDispMap, &leftMinVal );
136 minMaxLoc( leftDispMap, &leftMinVal, 0, 0, 0, ~leftUnknDispMask );
137 if( !rightDispMap.empty() )
139 if( rightUnknDispMask.empty() )
140 minMaxLoc( rightDispMap, &rightMinVal );
142 minMaxLoc( rightDispMap, &rightMinVal, 0, 0, 0, ~rightUnknDispMask );
144 if( leftMinVal < 0 || rightMinVal < 0)
145 CV_Error( CV_StsBadArg, "known disparity values must be positive" );
149 Calculate occluded regions of reference image (left image) (regions that are occluded in the matching image (right image),
150 i.e., where the forward-mapped disparity lands at a location with a larger (nearer) disparity) and non occluded regions.
152 void computeOcclusionBasedMasks( const Mat& leftDisp, const Mat& _rightDisp,
153 Mat* occludedMask, Mat* nonOccludedMask,
154 const Mat& leftUnknDispMask = Mat(), const Mat& rightUnknDispMask = Mat(),
155 float dispThresh = EVAL_DISP_THRESH )
157 if( !occludedMask && !nonOccludedMask )
159 checkDispMapsAndUnknDispMasks( leftDisp, _rightDisp, leftUnknDispMask, rightUnknDispMask );
162 if( _rightDisp.empty() )
164 if( !rightUnknDispMask.empty() )
165 CV_Error( CV_StsBadArg, "rightUnknDispMask must be empty if _rightDisp is empty" );
166 rightDisp.create(leftDisp.size(), CV_32FC1);
167 rightDisp.setTo(Scalar::all(0) );
168 for( int leftY = 0; leftY < leftDisp.rows; leftY++ )
170 for( int leftX = 0; leftX < leftDisp.cols; leftX++ )
172 if( !leftUnknDispMask.empty() && leftUnknDispMask.at<uchar>(leftY,leftX) )
174 float leftDispVal = leftDisp.at<float>(leftY, leftX);
175 int rightX = leftX - cvRound(leftDispVal), rightY = leftY;
177 rightDisp.at<float>(rightY,rightX) = max(rightDisp.at<float>(rightY,rightX), leftDispVal);
182 _rightDisp.copyTo(rightDisp);
186 occludedMask->create(leftDisp.size(), CV_8UC1);
187 occludedMask->setTo(Scalar::all(0) );
189 if( nonOccludedMask )
191 nonOccludedMask->create(leftDisp.size(), CV_8UC1);
192 nonOccludedMask->setTo(Scalar::all(0) );
194 for( int leftY = 0; leftY < leftDisp.rows; leftY++ )
196 for( int leftX = 0; leftX < leftDisp.cols; leftX++ )
198 if( !leftUnknDispMask.empty() && leftUnknDispMask.at<uchar>(leftY,leftX) )
200 float leftDispVal = leftDisp.at<float>(leftY, leftX);
201 int rightX = leftX - cvRound(leftDispVal), rightY = leftY;
202 if( rightX < 0 && occludedMask )
203 occludedMask->at<uchar>(leftY, leftX) = 255;
206 if( !rightUnknDispMask.empty() && rightUnknDispMask.at<uchar>(rightY,rightX) )
208 float rightDispVal = rightDisp.at<float>(rightY, rightX);
209 if( rightDispVal > leftDispVal + dispThresh )
212 occludedMask->at<uchar>(leftY, leftX) = 255;
216 if( nonOccludedMask )
217 nonOccludedMask->at<uchar>(leftY, leftX) = 255;
225 Calculate depth discontinuty regions: pixels whose neiboring disparities differ by more than
226 dispGap, dilated by window of width discontWidth.
228 void computeDepthDiscontMask( const Mat& disp, Mat& depthDiscontMask, const Mat& unknDispMask = Mat(),
229 float dispGap = EVAL_DISP_GAP, int discontWidth = EVAL_DISCONT_WIDTH )
232 CV_Error( CV_StsBadArg, "disp is empty" );
233 if( disp.type() != CV_32FC1 )
234 CV_Error( CV_StsBadArg, "disp must have CV_32FC1 type" );
235 if( !unknDispMask.empty() )
236 checkTypeAndSizeOfMask( unknDispMask, disp.size() );
238 Mat curDisp; disp.copyTo( curDisp );
239 if( !unknDispMask.empty() )
240 curDisp.setTo( Scalar(numeric_limits<float>::min()), unknDispMask );
241 Mat maxNeighbDisp; dilate( curDisp, maxNeighbDisp, Mat(3, 3, CV_8UC1, Scalar(1)) );
242 if( !unknDispMask.empty() )
243 curDisp.setTo( Scalar(numeric_limits<float>::max()), unknDispMask );
244 Mat minNeighbDisp; erode( curDisp, minNeighbDisp, Mat(3, 3, CV_8UC1, Scalar(1)) );
245 depthDiscontMask = max( (Mat)(maxNeighbDisp-disp), (Mat)(disp-minNeighbDisp) ) > dispGap;
246 if( !unknDispMask.empty() )
247 depthDiscontMask &= ~unknDispMask;
248 dilate( depthDiscontMask, depthDiscontMask, Mat(discontWidth, discontWidth, CV_8UC1, Scalar(1)) );
252 Get evaluation masks excluding a border.
254 Mat getBorderedMask( Size maskSize, int border = EVAL_IGNORE_BORDER )
256 CV_Assert( border >= 0 );
257 Mat mask(maskSize, CV_8UC1, Scalar(0));
258 int w = maskSize.width - 2*border, h = maskSize.height - 2*border;
260 mask.setTo(Scalar(0));
262 mask( Rect(Point(border,border),Size(w,h)) ).setTo(Scalar(255));
267 Calculate root-mean-squared error between the computed disparity map (computedDisp) and ground truth map (groundTruthDisp).
269 float dispRMS( const Mat& computedDisp, const Mat& groundTruthDisp, const Mat& mask )
271 checkTypeAndSizeOfDisp( groundTruthDisp, 0 );
272 Size sz = groundTruthDisp.size();
273 checkTypeAndSizeOfDisp( computedDisp, &sz );
275 int pointsCount = sz.height*sz.width;
278 checkTypeAndSizeOfMask( mask, sz );
279 pointsCount = countNonZero(mask);
281 return 1.f/sqrt((float)pointsCount) * (float)norm(computedDisp, groundTruthDisp, NORM_L2, mask);
285 Calculate fraction of bad matching pixels.
287 float badMatchPxlsFraction( const Mat& computedDisp, const Mat& groundTruthDisp, const Mat& mask,
288 int badThresh = EVAL_BAD_THRESH )
290 checkTypeAndSizeOfDisp( groundTruthDisp, 0 );
291 Size sz = groundTruthDisp.size();
292 checkTypeAndSizeOfDisp( computedDisp, &sz );
295 absdiff( computedDisp, groundTruthDisp, badPxlsMap );
296 badPxlsMap = badPxlsMap > badThresh;
297 int pointsCount = sz.height*sz.width;
300 checkTypeAndSizeOfMask( mask, sz );
301 badPxlsMap = badPxlsMap & mask;
302 pointsCount = countNonZero(mask);
304 return 1.f/pointsCount * countNonZero(badPxlsMap);
307 //===================== regression test for stereo matching algorithms ==============================
309 const string ALGORITHMS_DIR = "stereomatching/algorithms/";
310 const string DATASETS_DIR = "stereomatching/datasets/";
311 const string DATASETS_FILE = "datasets.xml";
313 const string RUN_PARAMS_FILE = "_params.xml";
314 const string RESULT_FILE = "_res.xml";
316 const string LEFT_IMG_NAME = "im2.png";
317 const string RIGHT_IMG_NAME = "im6.png";
318 const string TRUE_LEFT_DISP_NAME = "disp2.png";
319 const string TRUE_RIGHT_DISP_NAME = "disp6.png";
321 string ERROR_PREFIXES[] = { "borderedAll",
325 "borderedTextureless",
326 "borderedDepthDiscont" }; // size of ERROR_KINDS_COUNT
329 const string RMS_STR = "RMS";
330 const string BAD_PXLS_FRACTION_STR = "BadPxlsFraction";
332 class QualityEvalParams
335 QualityEvalParams() { setDefaults(); }
336 QualityEvalParams( int _ignoreBorder )
339 ignoreBorder = _ignoreBorder;
343 badThresh = EVAL_BAD_THRESH;
344 texturelessWidth = EVAL_TEXTURELESS_WIDTH;
345 texturelessThresh = EVAL_TEXTURELESS_THRESH;
346 dispThresh = EVAL_DISP_THRESH;
347 dispGap = EVAL_DISP_GAP;
348 discontWidth = EVAL_DISCONT_WIDTH;
349 ignoreBorder = EVAL_IGNORE_BORDER;
352 int texturelessWidth;
353 float texturelessThresh;
360 class CV_StereoMatchingTest : public CvTest
363 CV_StereoMatchingTest( const char* testName ) : CvTest( testName, "stereo-matching" )
364 { rmsEps.resize( ERROR_KINDS_COUNT, 0.01f ); fracEps.resize( ERROR_KINDS_COUNT, 1.e-6f ); }
366 // assumed that left image is a reference image
367 virtual int runStereoMatchingAlgorithm( const Mat& leftImg, const Mat& rightImg,
368 Mat& leftDisp, Mat& rightDisp, int caseIdx ) = 0; // return ignored border width
370 int readDatasetsParams( FileStorage& fs );
371 virtual int readRunParams( FileStorage& fs );
372 void writeErrors( const string& errName, const vector<float>& errors, FileStorage* fs = 0 );
373 void readErrors( FileNode& fn, const string& errName, vector<float>& errors );
374 int compareErrors( const vector<float>& calcErrors, const vector<float>& validErrors,
375 const vector<float>& eps, const string& errName );
376 int processStereoMatchingResults( FileStorage& fs, int caseIdx, bool isWrite,
377 const Mat& leftImg, const Mat& rightImg,
378 const Mat& trueLeftDisp, const Mat& trueRightDisp,
379 const Mat& leftDisp, const Mat& rightDisp,
380 const QualityEvalParams& qualityEvalParams );
383 vector<float> rmsEps;
384 vector<float> fracEps;
391 map<string, DatasetParams> datasetsParams;
393 vector<string> caseNames;
394 vector<string> caseDatasets;
397 void CV_StereoMatchingTest::run(int)
399 string dataPath = ts->get_data_path();
400 string algorithmName = name;
401 assert( !algorithmName.empty() );
402 if( dataPath.empty() )
404 ts->printf( CvTS::LOG, "dataPath is empty" );
405 ts->set_failed_test_info( CvTS::FAIL_BAD_ARG_CHECK );
409 FileStorage datasetsFS( dataPath + DATASETS_DIR + DATASETS_FILE, FileStorage::READ );
410 int code = readDatasetsParams( datasetsFS );
411 if( code != CvTS::OK )
413 ts->set_failed_test_info( code );
416 FileStorage runParamsFS( dataPath + ALGORITHMS_DIR + algorithmName + RUN_PARAMS_FILE, FileStorage::READ );
417 code = readRunParams( runParamsFS );
418 if( code != CvTS::OK )
420 ts->set_failed_test_info( code );
424 string fullResultFilename = dataPath + ALGORITHMS_DIR + algorithmName + RESULT_FILE;
425 FileStorage resFS( fullResultFilename, FileStorage::READ );
426 bool isWrite = true; // write or compare results
427 if( resFS.isOpened() )
431 resFS.open( fullResultFilename, FileStorage::WRITE );
432 if( !resFS.isOpened() )
434 ts->printf( CvTS::LOG, "file %s can not be read or written\n", fullResultFilename.c_str() );
435 ts->set_failed_test_info( CvTS::FAIL_BAD_ARG_CHECK );
438 resFS << "stereo_matching" << "{";
441 int progress = 0, caseCount = caseNames.size();
442 for( int ci = 0; ci < caseCount; ci++)
444 progress = update_progress( progress, ci, caseCount, 0 );
446 string datasetName = caseDatasets[ci];
447 string datasetFullDirName = dataPath + DATASETS_DIR + datasetName + "/";
448 Mat leftImg = imread(datasetFullDirName + LEFT_IMG_NAME);
449 Mat rightImg = imread(datasetFullDirName + RIGHT_IMG_NAME);
450 Mat trueLeftDisp = imread(datasetFullDirName + TRUE_LEFT_DISP_NAME, 0);
451 Mat trueRightDisp = imread(datasetFullDirName + TRUE_RIGHT_DISP_NAME, 0);
453 if( leftImg.empty() || rightImg.empty() || trueLeftDisp.empty() )
455 ts->printf( CvTS::LOG, "images or left ground-truth disparities of dataset %s can not be read", datasetName.c_str() );
456 code = CvTS::FAIL_INVALID_TEST_DATA;
459 int dispScaleFactor = datasetsParams[datasetName].dispScaleFactor;
460 Mat tmp; trueLeftDisp.convertTo( tmp, CV_32FC1, 1.f/dispScaleFactor ); trueLeftDisp = tmp; tmp.release();
461 if( !trueRightDisp.empty() )
462 trueRightDisp.convertTo( tmp, CV_32FC1, 1.f/dispScaleFactor ); trueRightDisp = tmp; tmp.release();
464 Mat leftDisp, rightDisp;
465 int ignBorder = max(runStereoMatchingAlgorithm(leftImg, rightImg, leftDisp, rightDisp, ci), EVAL_IGNORE_BORDER);
466 leftDisp.convertTo( tmp, CV_32FC1 ); leftDisp = tmp; tmp.release();
467 rightDisp.convertTo( tmp, CV_32FC1 ); rightDisp = tmp; tmp.release();
469 int tempCode = processStereoMatchingResults( resFS, ci, isWrite,
470 leftImg, rightImg, trueLeftDisp, trueRightDisp, leftDisp, rightDisp, QualityEvalParams(ignBorder));
471 code = tempCode==CvTS::OK ? code : tempCode;
475 resFS << "}"; // "stereo_matching"
477 ts->set_failed_test_info( code );
480 void calcErrors( const Mat& leftImg, const Mat& rightImg,
481 const Mat& trueLeftDisp, const Mat& trueRightDisp,
482 const Mat& trueLeftUnknDispMask, const Mat& trueRightUnknDispMask,
483 const Mat& calcLeftDisp, const Mat& calcRightDisp,
484 vector<float>& rms, vector<float>& badPxlsFractions,
485 const QualityEvalParams& qualityEvalParams )
487 Mat texturelessMask, texturedMask;
488 computeTextureBasedMasks( leftImg, &texturelessMask, &texturedMask,
489 qualityEvalParams.texturelessWidth, qualityEvalParams.texturelessThresh );
490 Mat occludedMask, nonOccludedMask;
491 computeOcclusionBasedMasks( trueLeftDisp, trueRightDisp, &occludedMask, &nonOccludedMask,
492 trueLeftUnknDispMask, trueRightUnknDispMask, qualityEvalParams.dispThresh);
493 Mat depthDiscontMask;
494 computeDepthDiscontMask( trueLeftDisp, depthDiscontMask, trueLeftUnknDispMask,
495 qualityEvalParams.dispGap, qualityEvalParams.discontWidth);
497 Mat borderedKnownMask = getBorderedMask( leftImg.size(), qualityEvalParams.ignoreBorder ) & ~trueLeftUnknDispMask;
499 nonOccludedMask &= borderedKnownMask;
500 occludedMask &= borderedKnownMask;
501 texturedMask &= nonOccludedMask; // & borderedKnownMask
502 texturelessMask &= nonOccludedMask; // & borderedKnownMask
503 depthDiscontMask &= nonOccludedMask; // & borderedKnownMask
505 rms.resize(ERROR_KINDS_COUNT);
506 rms[0] = dispRMS( calcLeftDisp, trueLeftDisp, borderedKnownMask );
507 rms[1] = dispRMS( calcLeftDisp, trueLeftDisp, nonOccludedMask );
508 rms[2] = dispRMS( calcLeftDisp, trueLeftDisp, occludedMask );
509 rms[3] = dispRMS( calcLeftDisp, trueLeftDisp, texturedMask );
510 rms[4] = dispRMS( calcLeftDisp, trueLeftDisp, texturelessMask );
511 rms[5] = dispRMS( calcLeftDisp, trueLeftDisp, depthDiscontMask );
513 badPxlsFractions.resize(ERROR_KINDS_COUNT);
514 badPxlsFractions[0] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, borderedKnownMask, qualityEvalParams.badThresh );
515 badPxlsFractions[1] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, nonOccludedMask, qualityEvalParams.badThresh );
516 badPxlsFractions[2] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, occludedMask, qualityEvalParams.badThresh );
517 badPxlsFractions[3] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, texturedMask, qualityEvalParams.badThresh );
518 badPxlsFractions[4] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, texturelessMask, qualityEvalParams.badThresh );
519 badPxlsFractions[5] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, depthDiscontMask, qualityEvalParams.badThresh );
522 int CV_StereoMatchingTest::processStereoMatchingResults( FileStorage& fs, int caseIdx, bool isWrite,
523 const Mat& leftImg, const Mat& rightImg,
524 const Mat& trueLeftDisp, const Mat& trueRightDisp,
525 const Mat& leftDisp, const Mat& rightDisp,
526 const QualityEvalParams& qualityEvalParams )
528 // rightDisp is not used in current test virsion
530 assert( fs.isOpened() );
531 assert( trueLeftDisp.type() == CV_32FC1 && trueRightDisp.type() == CV_32FC1 );
532 assert( leftDisp.type() == CV_32FC1 && rightDisp.type() == CV_32FC1 );
534 // get masks for unknown ground truth disparity values
535 Mat leftUnknMask, rightUnknMask;
536 DatasetParams params = datasetsParams[caseDatasets[caseIdx]];
537 absdiff( trueLeftDisp, Scalar(params.dispUnknVal), leftUnknMask );
538 leftUnknMask = leftUnknMask < numeric_limits<float>::epsilon();
539 assert(leftUnknMask.type() == CV_8UC1);
540 if( !trueRightDisp.empty() )
542 absdiff( trueRightDisp, Scalar(params.dispUnknVal), rightUnknMask );
543 rightUnknMask = rightUnknMask < numeric_limits<float>::epsilon();
544 assert(leftUnknMask.type() == CV_8UC1);
548 vector<float> rmss, badPxlsFractions;
549 calcErrors( leftImg, rightImg, trueLeftDisp, trueRightDisp, leftUnknMask, rightUnknMask,
550 leftDisp, rightDisp, rmss, badPxlsFractions, qualityEvalParams );
554 fs << caseNames[caseIdx] << "{";
555 cvWriteComment( fs.fs, RMS_STR.c_str(), 0 );
556 writeErrors( RMS_STR, rmss, &fs );
557 cvWriteComment( fs.fs, BAD_PXLS_FRACTION_STR.c_str(), 0 );
558 writeErrors( BAD_PXLS_FRACTION_STR, badPxlsFractions, &fs );
559 fs << "}"; // datasetName
563 ts->printf( CvTS::LOG, "\nquality of case named %s\n", caseNames[caseIdx].c_str() );
564 ts->printf( CvTS::LOG, "%s\n", RMS_STR.c_str() );
565 writeErrors( RMS_STR, rmss );
566 ts->printf( CvTS::LOG, "%s\n", BAD_PXLS_FRACTION_STR.c_str() );
567 writeErrors( BAD_PXLS_FRACTION_STR, badPxlsFractions );
569 FileNode fn = fs.getFirstTopLevelNode()[caseNames[caseIdx]];
570 vector<float> validRmss, validBadPxlsFractions;
572 readErrors( fn, RMS_STR, validRmss );
573 readErrors( fn, BAD_PXLS_FRACTION_STR, validBadPxlsFractions );
574 int tempCode = compareErrors( rmss, validRmss, rmsEps, RMS_STR );
575 code = tempCode==CvTS::OK ? code : tempCode;
576 tempCode = compareErrors( badPxlsFractions, validBadPxlsFractions, fracEps, BAD_PXLS_FRACTION_STR );
577 code = tempCode==CvTS::OK ? code : tempCode;
582 int CV_StereoMatchingTest::readDatasetsParams( FileStorage& fs )
586 ts->printf( CvTS::LOG, "datasetsParams can not be read " );
587 return CvTS::FAIL_INVALID_TEST_DATA;
589 datasetsParams.clear();
590 FileNode fn = fs.getFirstTopLevelNode();
592 for( int i = 0; i < (int)fn.size(); i+=3 )
595 DatasetParams params;
596 string sf = fn[i+1]; params.dispScaleFactor = atoi(sf.c_str());
597 string uv = fn[i+2]; params.dispUnknVal = atoi(uv.c_str());
598 datasetsParams[name] = params;
603 int CV_StereoMatchingTest::readRunParams( FileStorage& fs )
607 ts->printf( CvTS::LOG, "runParams can not be read " );
608 return CvTS::FAIL_INVALID_TEST_DATA;
611 caseDatasets.clear();
615 void CV_StereoMatchingTest::writeErrors( const string& errName, const vector<float>& errors, FileStorage* fs )
617 assert( (int)errors.size() == ERROR_KINDS_COUNT );
618 vector<float>::const_iterator it = errors.begin();
620 for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++it )
621 *fs << ERROR_PREFIXES[i] + errName << *it;
623 for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++it )
624 ts->printf( CvTS::LOG, "%s = %f\n", string(ERROR_PREFIXES[i]+errName).c_str(), *it );
627 void CV_StereoMatchingTest::readErrors( FileNode& fn, const string& errName, vector<float>& errors )
629 errors.resize( ERROR_KINDS_COUNT );
630 vector<float>::iterator it = errors.begin();
631 for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++it )
632 fn[ERROR_PREFIXES[i]+errName] >> *it;
635 int CV_StereoMatchingTest::compareErrors( const vector<float>& calcErrors, const vector<float>& validErrors,
636 const vector<float>& eps, const string& errName )
638 assert( (int)calcErrors.size() == ERROR_KINDS_COUNT );
639 assert( (int)validErrors.size() == ERROR_KINDS_COUNT );
640 assert( (int)eps.size() == ERROR_KINDS_COUNT );
641 vector<float>::const_iterator calcIt = calcErrors.begin(),
642 validIt = validErrors.begin(),
645 for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++calcIt, ++validIt, ++epsIt )
646 if( *calcIt - *validIt > *epsIt )
648 ts->printf( CvTS::LOG, "bad accuracy of %s (valid=%f; calc=%f)\n", string(ERROR_PREFIXES[i]+errName).c_str(), *validIt, *calcIt );
651 return ok ? CvTS::OK : CvTS::FAIL_BAD_ACCURACY;
654 //----------------------------------- StereoBM test -----------------------------------------------------
656 class CV_StereoBMTest : public CV_StereoMatchingTest
659 CV_StereoBMTest() : CV_StereoMatchingTest( "stereobm" )
660 { fill(rmsEps.begin(), rmsEps.end(), 0.4f); fill(fracEps.begin(), fracEps.end(), 0.02f); }
668 vector<RunParams> caseRunParams;
670 virtual int readRunParams( FileStorage& fs )
672 int code = CV_StereoMatchingTest::readRunParams( fs );
673 FileNode fn = fs.getFirstTopLevelNode();
675 for( int i = 0; i < (int)fn.size(); i+=4 )
677 string caseName = fn[i], datasetName = fn[i+1];
679 string ndisp = fn[i+2]; params.ndisp = atoi(ndisp.c_str());
680 string winSize = fn[i+3]; params.winSize = atoi(winSize.c_str());
681 caseNames.push_back( caseName );
682 caseDatasets.push_back( datasetName );
683 caseRunParams.push_back( params );
688 virtual int runStereoMatchingAlgorithm( const Mat& _leftImg, const Mat& _rightImg,
689 Mat& leftDisp, Mat& rightDisp, int caseIdx )
691 RunParams params = caseRunParams[caseIdx];
692 assert( params.ndisp%16 == 0 );
693 assert( _leftImg.type() == CV_8UC3 && _rightImg.type() == CV_8UC3 );
694 Mat leftImg; cvtColor( _leftImg, leftImg, CV_BGR2GRAY );
695 Mat rightImg; cvtColor( _rightImg, rightImg, CV_BGR2GRAY );
697 StereoBM bm( StereoBM::BASIC_PRESET, params.ndisp, params.winSize );
698 bm( leftImg, rightImg, leftDisp, CV_32F );
699 return params.winSize/2;
703 CV_StereoBMTest stereoBM;
705 //----------------------------------- StereoGC test -----------------------------------------------------
707 class CV_StereoGCTest : public CV_StereoMatchingTest
710 CV_StereoGCTest() : CV_StereoMatchingTest( "stereogc" )
712 fill(rmsEps.begin(), rmsEps.end(), 3.f);
713 fracEps[0] = 0.05f; // all
714 fracEps[1] = 0.05f; // noOccl
715 fracEps[2] = 0.25f; // occl
716 fracEps[3] = 0.05f; // textured
717 fracEps[4] = 0.10f; // textureless
718 fracEps[5] = 0.10f; // borderedDepthDiscont
726 vector<RunParams> caseRunParams;
728 virtual int readRunParams( FileStorage& fs )
730 int code = CV_StereoMatchingTest::readRunParams(fs);
731 FileNode fn = fs.getFirstTopLevelNode();
733 for( int i = 0; i < (int)fn.size(); i+=4 )
735 string caseName = fn[i], datasetName = fn[i+1];
737 string ndisp = fn[i+2]; params.ndisp = atoi(ndisp.c_str());
738 string iterCount = fn[i+3]; params.iterCount = atoi(iterCount.c_str());
739 caseNames.push_back( caseName );
740 caseDatasets.push_back( datasetName );
741 caseRunParams.push_back( params );
746 virtual int runStereoMatchingAlgorithm( const Mat& _leftImg, const Mat& _rightImg,
747 Mat& leftDisp, Mat& rightDisp, int caseIdx )
749 RunParams params = caseRunParams[caseIdx];
750 assert( _leftImg.type() == CV_8UC3 && _rightImg.type() == CV_8UC3 );
751 Mat leftImg, rightImg, tmp;
752 cvtColor( _leftImg, leftImg, CV_BGR2GRAY );
753 cvtColor( _rightImg, rightImg, CV_BGR2GRAY );
755 leftDisp.create( leftImg.size(), CV_16SC1 );
756 rightDisp.create( rightImg.size(), CV_16SC1 );
758 CvMat _limg = leftImg, _rimg = rightImg, _ldisp = leftDisp, _rdisp = rightDisp;
759 CvStereoGCState *state = cvCreateStereoGCState( params.ndisp, params.iterCount );
760 cvFindStereoCorrespondenceGC( &_limg, &_rimg, &_ldisp, &_rdisp, state );
761 cvReleaseStereoGCState( &state );
763 leftDisp = - leftDisp;
769 CV_StereoGCTest stereoGC;
771 //----------------------------------- StereoSGBM test -----------------------------------------------------
773 class CV_StereoSGBMTest : public CV_StereoMatchingTest
776 CV_StereoSGBMTest() : CV_StereoMatchingTest( "stereosgbm" )
777 { fill(rmsEps.begin(), rmsEps.end(), 0.25f); fill(fracEps.begin(), fracEps.end(), 0.01f); }
786 vector<RunParams> caseRunParams;
788 virtual int readRunParams( FileStorage& fs )
790 int code = CV_StereoMatchingTest::readRunParams(fs);
791 FileNode fn = fs.getFirstTopLevelNode();
793 for( int i = 0; i < (int)fn.size(); i+=5 )
795 string caseName = fn[i], datasetName = fn[i+1];
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 fullDP = fn[i+4]; params.fullDP = atoi(fullDP.c_str()) == 0 ? false : true;
800 caseNames.push_back( caseName );
801 caseDatasets.push_back( datasetName );
802 caseRunParams.push_back( params );
807 virtual int runStereoMatchingAlgorithm( const Mat& leftImg, const Mat& rightImg,
808 Mat& leftDisp, Mat& rightDisp, int caseIdx )
810 RunParams params = caseRunParams[caseIdx];
811 assert( params.ndisp%16 == 0 );
812 StereoSGBM sgbm( 0, params.ndisp, params.winSize, 10*params.winSize*params.winSize, 40*params.winSize*params.winSize,
813 1, 63, 10, 100, 32, params.fullDP );
814 sgbm( leftImg, rightImg, leftDisp );
815 assert( leftDisp.type() == CV_16SC1 );
821 CV_StereoSGBMTest stereoSGBM;