]> rtime.felk.cvut.cz Git - opencv.git/blob - opencv/tests/cv/src/astereomatching.cpp
update threshold
[opencv.git] / opencv / tests / cv / src / astereomatching.cpp
1 /*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4 //
5 //  By downloading, copying, installing or using the software you agree to this license.
6 //  If you do not agree to this license, do not download, install,
7 //  copy or use the software.
8 //
9 //
10 //                        Intel License Agreement
11 //                For Open Source Computer Vision Library
12 //
13 // Copyright (C) 2000, Intel Corporation, all rights reserved.
14 // Third party copyrights are property of their respective owners.
15 //
16 // Redistribution and use in source and binary forms, with or without modification,
17 // are permitted provided that the following conditions are met:
18 //
19 //   * Redistribution's of source code must retain the above copyright notice,
20 //     this list of conditions and the following disclaimer.
21 //
22 //   * Redistribution's in binary form must reproduce the above copyright notice,
23 //     this list of conditions and the following disclaimer in the documentation
24 //     and/or other materials provided with the distribution.
25 //
26 //   * The name of Intel Corporation may not be used to endorse or promote products
27 //     derived from this software without specific prior written permission.
28 //
29 // This software is provided by the copyright holders and contributors "as is" and
30 // any express or implied warranties, including, but not limited to, the implied
31 // warranties of merchantability and fitness for a particular purpose are disclaimed.
32 // In no event shall the Intel Corporation or contributors be liable for any direct,
33 // indirect, incidental, special, exemplary, or consequential damages
34 // (including, but not limited to, procurement of substitute goods or services;
35 // loss of use, data, or profits; or business interruption) however caused
36 // and on any theory of liability, whether in contract, strict liability,
37 // or tort (including negligence or otherwise) arising in any way out of
38 // the use of this software, even if advised of the possibility of such damage.
39 //
40 //M*/
41
42 /*
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
46 */
47
48 #include "cvtest.h"
49 #include <limits>
50 #include <cstdio>
51
52 using namespace std;
53 using namespace cv;
54
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;
62
63 const int ERROR_KINDS_COUNT = 6;
64
65 //============================== quality measuring functions =================================================
66
67 /*
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.
70 */
71 void computeTextureBasedMasks( const Mat& _img, Mat* texturelessMask, Mat* texturedMask,
72              int texturelessWidth = EVAL_TEXTURELESS_WIDTH, float texturelessThresh = EVAL_TEXTURELESS_THRESH )
73 {
74     if( !texturelessMask && !texturedMask )
75         return;
76     if( _img.empty() )
77         CV_Error( CV_StsBadArg, "img is empty" );
78
79     Mat img = _img;
80     if( _img.channels() > 1)
81     {
82         Mat tmp; cvtColor( _img, tmp, CV_BGR2GRAY ); img = tmp;
83     }
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) );
87
88     if( texturelessMask )
89         *texturelessMask = avgDxI2 < texturelessThresh;
90     if( texturedMask )
91         *texturedMask = avgDxI2 >= texturelessThresh;
92 }
93
94 void checkSizeAndTypeOfDispMaps( const Mat& leftDispMap, const Mat& rightDispMap )
95 {
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" );
100
101     if( !rightDispMap.empty() )
102     {
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" );
107     }
108 }
109
110 void checkSizeAndTypeOfMask( const Mat& mask, Size sz )
111 {
112     if( mask.empty() )
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" );
118 }
119
120 void checkDispMapsAndUnknDispMasks( const Mat& leftDispMap, const Mat& rightDispMap,
121                                     const Mat& leftUnknDispMask, const Mat& rightUnknDispMask )
122 {
123     checkSizeAndTypeOfDispMaps( leftDispMap, rightDispMap );
124
125     if( !leftUnknDispMask.empty() )
126         checkSizeAndTypeOfMask( leftUnknDispMask, leftDispMap.size() );
127     if( !rightUnknDispMask.empty() )
128         checkSizeAndTypeOfMask( rightUnknDispMask, rightDispMap.size() );
129
130     double leftMinVal = 0, rightMinVal = 0;
131     if( leftUnknDispMask.empty() )
132         minMaxLoc( leftDispMap, &leftMinVal );
133     else
134         minMaxLoc( leftDispMap, &leftMinVal, 0, 0, 0, ~leftUnknDispMask );
135     if( !rightDispMap.empty() )
136     {
137         if( rightUnknDispMask.empty() )
138             minMaxLoc( rightDispMap, &rightMinVal );
139         else
140             minMaxLoc( rightDispMap, &rightMinVal, 0, 0, 0, ~rightUnknDispMask );
141     }
142     if( leftMinVal < 0 || rightMinVal < 0)
143         CV_Error( CV_StsBadArg, "known disparity values must be positive" );
144 }
145
146 /*
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.
149 */
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 )
154 {
155     if( !occludedMask && !nonOccludedMask )
156         return;
157     checkDispMapsAndUnknDispMasks( leftDisp, _rightDisp, leftUnknDispMask, rightUnknDispMask );
158
159     Mat rightDisp;
160     if( _rightDisp.empty() )
161     {
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++ )
167         {
168             for( int leftX = 0; leftX < leftDisp.cols; leftX++ )
169             {
170                 if( !leftUnknDispMask.empty() && leftUnknDispMask.at<uchar>(leftY,leftX) )
171                     continue;
172                 float leftDispVal = leftDisp.at<float>(leftY, leftX);
173                 int rightX = leftX - cvRound(leftDispVal), rightY = leftY;
174                 if( rightX >= 0)
175                     rightDisp.at<float>(rightY,rightX) = max(rightDisp.at<float>(rightY,rightX), leftDispVal);
176             }
177         }
178     }
179     else
180         _rightDisp.copyTo(rightDisp);
181
182     if( occludedMask )
183     {
184         occludedMask->create(leftDisp.size(), CV_8UC1);
185         occludedMask->setTo(Scalar::all(0) );
186     }
187     if( nonOccludedMask )
188     {
189         nonOccludedMask->create(leftDisp.size(), CV_8UC1);
190         nonOccludedMask->setTo(Scalar::all(0) );
191     }
192     for( int leftY = 0; leftY < leftDisp.rows; leftY++ )
193     {
194         for( int leftX = 0; leftX < leftDisp.cols; leftX++ )
195         {
196             if( !leftUnknDispMask.empty() && leftUnknDispMask.at<uchar>(leftY,leftX) )
197                 continue;
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;
202             else
203             {
204                 if( !rightUnknDispMask.empty() && rightUnknDispMask.at<uchar>(rightY,rightX) )
205                     continue;
206                 float rightDispVal = rightDisp.at<float>(rightY, rightX);
207                 if( rightDispVal > leftDispVal + dispThresh )
208                 {
209                     if( occludedMask )
210                         occludedMask->at<uchar>(leftY, leftX) = 255;
211                 }
212                 else
213                 {
214                     if( nonOccludedMask )
215                         nonOccludedMask->at<uchar>(leftY, leftX) = 255;
216                 }
217             }
218         }
219     }
220 }
221
222 /*
223   Calculate depth discontinuty regions: pixels whose neiboring disparities differ by more than
224   dispGap, dilated by window of width discontWidth.
225 */
226 void computeDepthDiscontMask( const Mat& disp, Mat& depthDiscontMask, const Mat& unknDispMask = Mat(),
227                                  float dispGap = EVAL_DISP_GAP, int discontWidth = EVAL_DISCONT_WIDTH )
228 {
229     if( disp.empty() )
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() );
235
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)) );
247 }
248
249 /*
250    Get evaluation masks excluding a border.
251 */
252 Mat getBorderedMask( Size maskSize, int border = EVAL_IGNORE_BORDER )
253 {
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;
257     if( w < 0 ||  h < 0 )
258         mask.setTo(Scalar(0));
259     else
260         mask( Rect(Point(border,border),Size(w,h)) ).setTo(Scalar(255));
261     return mask;
262 }
263
264 /*
265   Calculate root-mean-squared error between the computed disparity map (computedDisp) and ground truth map (groundTruthDisp).
266 */
267 float dispRMS( const Mat& computedDisp, const Mat& groundTruthDisp, const Mat& mask )
268 {
269     checkSizeAndTypeOfDispMaps( computedDisp, groundTruthDisp );
270     int pointsCount = computedDisp.cols*computedDisp.rows;
271     if( !mask.empty() )
272     {
273         checkSizeAndTypeOfMask( mask, computedDisp.size() );
274         pointsCount = countNonZero(mask);
275     }
276     return 1.f/sqrt((float)pointsCount) * (float)norm(computedDisp, groundTruthDisp, NORM_L2, mask);
277 }
278
279 /*
280   Calculate fraction of bad matching pixels.
281 */
282 float badMatchPxlsFraction( const Mat& computedDisp, const Mat& groundTruthDisp, const Mat& mask,
283                               int badThresh = EVAL_BAD_THRESH )
284 {
285     checkSizeAndTypeOfDispMaps( computedDisp, groundTruthDisp );
286     Mat badPxlsMap;
287     absdiff( computedDisp, groundTruthDisp, badPxlsMap );
288     badPxlsMap = badPxlsMap > badThresh;
289     int pointsCount = computedDisp.cols*computedDisp.rows;
290     if( !mask.empty() )
291     {
292         checkSizeAndTypeOfMask( mask, computedDisp.size() );
293         badPxlsMap = badPxlsMap & mask;
294         pointsCount = countNonZero(mask);
295     }
296     return 1.f/pointsCount * countNonZero(badPxlsMap);
297 }
298
299 //===================== regression test for stereo matching algorithms ==============================
300
301 const string ALGORITHMS_DIR = "stereomatching/algorithms/";
302 const string DATASETS_DIR = "stereomatching/datasets/";
303 const string DATASETS_FILE = "datasets.xml";
304
305 const string RUN_PARAMS_FILE = "_params.xml";
306 const string RESULT_FILE = "_res.xml";
307
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";
312
313 string ERROR_PREFIXES[] = { "borderedAll",
314                             "borderedNoOccl",
315                             "borderedOccl",
316                             "borderedTextured",
317                             "borderedTextureless",
318                             "borderedDepthDiscont" }; // size of ERROR_KINDS_COUNT
319
320
321 const string RMS_STR = "RMS";
322 const string BAD_PXLS_FRACTION_STR = "BadPxlsFraction";
323
324 class CV_StereoMatchingTest : public CvTest
325 {
326 public:
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 ); }
329 protected:
330     // assumed that left image is a reference image
331     virtual void runStereoMatchingAlgorithm( const Mat& leftImg, const Mat& rightImg,
332                    Mat& leftDisp, Mat& rightDisp, int caseIdx ) = 0;
333
334     int readDatasetsParams( FileStorage& fs );
335     virtual int readRunParams( FileStorage& fs );
336     void writeErrors( const string& errName, const vector<float>& errors, FileStorage* fs = 0 );
337     void readErrors( FileNode& fn, const string& errName, vector<float>& errors );
338     int compareErrors( const vector<float>& calcErrors, const vector<float>& validErrors,
339                        const vector<float>& eps, const string& errName );
340     int processStereoMatchingResults( FileStorage& fs, int caseIdx, bool isWrite,
341                   const Mat& leftImg, const Mat& rightImg,
342                   const Mat& trueLeftDisp, const Mat& trueRightDisp,
343                   const Mat& leftDisp, const Mat& rightDisp );
344     void run( int );
345
346     vector<float> rmsEps;
347     vector<float> fracEps;
348
349     struct DatasetParams
350     {
351         int dispScaleFactor;
352         int dispUnknVal;
353     };
354     map<string, DatasetParams> datasetsParams;
355
356     vector<string> caseNames;
357     vector<string> caseDatasets;
358 };
359
360 void CV_StereoMatchingTest::run(int)
361 {
362     string dataPath = ts->get_data_path();
363     string algorithmName = name;
364     assert( !algorithmName.empty() );
365     if( dataPath.empty() )
366     {
367         ts->printf( CvTS::LOG, "dataPath is empty" );
368         ts->set_failed_test_info( CvTS::FAIL_BAD_ARG_CHECK );
369         return;
370     }
371
372     FileStorage datasetsFS( dataPath + DATASETS_DIR + DATASETS_FILE, FileStorage::READ );
373     int code = readDatasetsParams( datasetsFS );
374     if( code != CvTS::OK )
375     {
376         ts->set_failed_test_info( code );
377         return;
378     }
379     FileStorage runParamsFS( dataPath + ALGORITHMS_DIR + algorithmName + RUN_PARAMS_FILE, FileStorage::READ );
380     code = readRunParams( runParamsFS );
381     if( code != CvTS::OK )
382     {
383         ts->set_failed_test_info( code );
384         return;
385     }
386     
387     string fullResultFilename = dataPath + ALGORITHMS_DIR + algorithmName + RESULT_FILE;
388     FileStorage resFS( fullResultFilename, FileStorage::READ );
389     bool isWrite = true; // write or compare results
390     if( resFS.isOpened() )
391         isWrite = false;
392     else
393     {
394         resFS.open( fullResultFilename, FileStorage::WRITE );
395         if( !resFS.isOpened() )
396         {
397             ts->printf( CvTS::LOG, "file %s can not be read or written\n", fullResultFilename.c_str() );
398             ts->set_failed_test_info( CvTS::FAIL_BAD_ARG_CHECK );
399             return;
400         }
401         resFS << "stereo_matching" << "{";
402     }
403
404     int progress = 0, caseCount = caseNames.size();
405     for( int ci = 0; ci < caseCount; ci++)
406     {
407         progress = update_progress( progress, ci, caseCount, 0 );
408
409         string datasetName = caseDatasets[ci];
410         string datasetFullDirName = dataPath + DATASETS_DIR + datasetName + "/";
411         Mat leftImg = imread(datasetFullDirName + LEFT_IMG_NAME);
412         Mat rightImg = imread(datasetFullDirName + RIGHT_IMG_NAME);
413         Mat trueLeftDisp = imread(datasetFullDirName + TRUE_LEFT_DISP_NAME, 0);
414         Mat trueRightDisp = imread(datasetFullDirName + TRUE_RIGHT_DISP_NAME, 0);
415
416         if( leftImg.empty() || rightImg.empty() || trueLeftDisp.empty() )
417         {
418             ts->printf( CvTS::LOG, "images or left ground-truth disparities of dataset %s can not be read", datasetName.c_str() );
419             code = CvTS::FAIL_INVALID_TEST_DATA;
420             continue;
421         }
422         int dispScaleFactor = datasetsParams[datasetName].dispScaleFactor;
423         Mat tmp; trueLeftDisp.convertTo( tmp, CV_32FC1, 1.f/dispScaleFactor ); trueLeftDisp = tmp; tmp.release();
424         if( !trueRightDisp.empty() )
425             trueRightDisp.convertTo( tmp, CV_32FC1, 1.f/dispScaleFactor ); trueRightDisp = tmp; tmp.release();
426
427         Mat leftDisp, rightDisp;
428         runStereoMatchingAlgorithm( leftImg, rightImg, leftDisp, rightDisp, ci );
429         leftDisp.convertTo( tmp, CV_32FC1 ); leftDisp = tmp; tmp.release();
430         rightDisp.convertTo( tmp, CV_32FC1 ); rightDisp = tmp; tmp.release();
431
432         int tempCode = processStereoMatchingResults( resFS, ci, isWrite,
433                    leftImg, rightImg, trueLeftDisp, trueRightDisp, leftDisp, rightDisp);
434         code = tempCode==CvTS::OK ? code : tempCode;
435     }
436
437     if( isWrite )
438         resFS << "}"; // "stereo_matching"
439
440     ts->set_failed_test_info( code );
441 }
442
443 void calcErrors( const Mat& leftImg, const Mat& rightImg,
444                  const Mat& trueLeftDisp, const Mat& trueRightDisp,
445                  const Mat& trueLeftUnknDispMask, const Mat& trueRightUnknDispMask,
446                  const Mat& calcLeftDisp, const Mat& calcRightDisp,
447                  vector<float>& rms, vector<float>& badPxlsFractions )
448 {
449     Mat texturelessMask, texturedMask;
450     computeTextureBasedMasks( leftImg, &texturelessMask, &texturedMask );
451     Mat occludedMask, nonOccludedMask;
452     computeOcclusionBasedMasks( trueLeftDisp, trueRightDisp, &occludedMask, &nonOccludedMask,
453                                 trueLeftUnknDispMask, trueRightUnknDispMask);
454     Mat depthDiscontMask;
455     computeDepthDiscontMask( trueLeftDisp, depthDiscontMask, trueLeftUnknDispMask);
456
457     Mat borderedKnownMask = getBorderedMask( leftImg.size() ) & ~trueLeftUnknDispMask;
458
459     nonOccludedMask &= borderedKnownMask;
460     occludedMask &= borderedKnownMask;
461     texturedMask &= nonOccludedMask; // & borderedKnownMask
462     texturelessMask &= nonOccludedMask; // & borderedKnownMask
463     depthDiscontMask &= nonOccludedMask; // & borderedKnownMask
464
465     rms.resize(ERROR_KINDS_COUNT);
466     rms[0] = dispRMS( calcLeftDisp, trueLeftDisp, borderedKnownMask );
467     rms[1] = dispRMS( calcLeftDisp, trueLeftDisp, nonOccludedMask );
468     rms[2] = dispRMS( calcLeftDisp, trueLeftDisp, occludedMask );
469     rms[3] = dispRMS( calcLeftDisp, trueLeftDisp, texturedMask );
470     rms[4] = dispRMS( calcLeftDisp, trueLeftDisp, texturelessMask );
471     rms[5] = dispRMS( calcLeftDisp, trueLeftDisp, depthDiscontMask );
472
473     badPxlsFractions.resize(ERROR_KINDS_COUNT);
474     badPxlsFractions[0] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, borderedKnownMask );
475     badPxlsFractions[1] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, nonOccludedMask );
476     badPxlsFractions[2] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, occludedMask );
477     badPxlsFractions[3] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, texturedMask );
478     badPxlsFractions[4] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, texturelessMask );
479     badPxlsFractions[5] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, depthDiscontMask );
480 }
481
482 int CV_StereoMatchingTest::processStereoMatchingResults( FileStorage& fs, int caseIdx, bool isWrite,
483               const Mat& leftImg, const Mat& rightImg,
484               const Mat& trueLeftDisp, const Mat& trueRightDisp,
485               const Mat& leftDisp, const Mat& rightDisp )
486 {
487     // rightDisp is not used in current test virsion
488     int code = CvTS::OK;
489     assert( fs.isOpened() );
490     assert( trueLeftDisp.type() == CV_32FC1 && trueRightDisp.type() == CV_32FC1 );
491     assert( leftDisp.type() == CV_32FC1 && rightDisp.type() == CV_32FC1 );
492
493     // get masks for unknown ground truth disparity values
494     Mat leftUnknMask, rightUnknMask;
495     DatasetParams params = datasetsParams[caseDatasets[caseIdx]];
496     absdiff( trueLeftDisp, Scalar(params.dispUnknVal), leftUnknMask );
497     leftUnknMask = leftUnknMask < numeric_limits<float>::epsilon();
498     assert(leftUnknMask.type() == CV_8UC1);
499     if( !trueRightDisp.empty() )
500     {
501         absdiff( trueRightDisp, Scalar(params.dispUnknVal), rightUnknMask );
502         rightUnknMask = rightUnknMask < numeric_limits<float>::epsilon();
503         assert(leftUnknMask.type() == CV_8UC1);
504     }
505
506     // calculate errors
507     vector<float> rmss, badPxlsFractions;
508     calcErrors( leftImg, rightImg, trueLeftDisp, trueRightDisp, leftUnknMask, rightUnknMask,
509                 leftDisp, rightDisp, rmss, badPxlsFractions );
510
511     if( isWrite )
512     {
513         fs << caseNames[caseIdx] << "{";
514         cvWriteComment( fs.fs, RMS_STR.c_str(), 0 );
515         writeErrors( RMS_STR, rmss, &fs );
516         cvWriteComment( fs.fs, BAD_PXLS_FRACTION_STR.c_str(), 0 );
517         writeErrors( BAD_PXLS_FRACTION_STR, badPxlsFractions, &fs );
518         fs << "}"; // datasetName
519     }
520     else // compare
521     {
522         ts->printf( CvTS::LOG, "\nquality of case named %s\n", caseNames[caseIdx].c_str() );
523         ts->printf( CvTS::LOG, "%s\n", RMS_STR.c_str() );
524         writeErrors( RMS_STR, rmss );
525         ts->printf( CvTS::LOG, "%s\n", BAD_PXLS_FRACTION_STR.c_str() );
526         writeErrors( BAD_PXLS_FRACTION_STR, badPxlsFractions );
527
528         FileNode fn = fs.getFirstTopLevelNode()[caseNames[caseIdx]];
529         vector<float> validRmss, validBadPxlsFractions;
530
531         readErrors( fn, RMS_STR, validRmss );
532         readErrors( fn, BAD_PXLS_FRACTION_STR, validBadPxlsFractions );
533         int tempCode = compareErrors( rmss, validRmss, rmsEps, RMS_STR );
534         code = tempCode==CvTS::OK ? code : tempCode;
535         tempCode = compareErrors( badPxlsFractions, validBadPxlsFractions, fracEps, BAD_PXLS_FRACTION_STR );
536         code = tempCode==CvTS::OK ? code : tempCode;
537     }
538     return code;
539 }
540
541 int CV_StereoMatchingTest::readDatasetsParams( FileStorage& fs )
542 {
543     if( !fs.isOpened() )
544     {
545         ts->printf( CvTS::LOG, "datasetsParams can not be read " );
546         return CvTS::FAIL_INVALID_TEST_DATA;
547     }
548     datasetsParams.clear();
549     FileNode fn = fs.getFirstTopLevelNode();
550     assert(fn.isSeq());
551     for( int i = 0; i < (int)fn.size(); i+=3 )
552     {
553         string name = fn[i];
554         DatasetParams params;
555         string sf = fn[i+1]; params.dispScaleFactor = atoi(sf.c_str());
556         string uv = fn[i+2]; params.dispUnknVal = atoi(uv.c_str());
557         datasetsParams[name] = params;
558     }
559     return CvTS::OK;
560 }
561
562 int CV_StereoMatchingTest::readRunParams( FileStorage& fs )
563 {
564     if( !fs.isOpened() )
565     {
566         ts->printf( CvTS::LOG, "runParams can not be read " );
567         return CvTS::FAIL_INVALID_TEST_DATA;
568     }
569     caseNames.clear();;
570     caseDatasets.clear();
571     return CvTS::OK;
572 }
573
574 void CV_StereoMatchingTest::writeErrors( const string& errName, const vector<float>& errors, FileStorage* fs )
575 {
576     assert( (int)errors.size() == ERROR_KINDS_COUNT );
577     vector<float>::const_iterator it = errors.begin();
578     if( fs )
579         for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++it )
580             *fs << ERROR_PREFIXES[i] + errName << *it;
581     else
582         for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++it )
583             ts->printf( CvTS::LOG, "%s = %f\n", string(ERROR_PREFIXES[i]+errName).c_str(), *it );
584 }
585
586 void CV_StereoMatchingTest::readErrors( FileNode& fn, const string& errName, vector<float>& errors )
587 {
588     errors.resize( ERROR_KINDS_COUNT );
589     vector<float>::iterator it = errors.begin();
590     for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++it )
591         fn[ERROR_PREFIXES[i]+errName] >> *it;
592 }
593
594 int CV_StereoMatchingTest::compareErrors( const vector<float>& calcErrors, const vector<float>& validErrors,
595                    const vector<float>& eps, const string& errName )
596 {
597     assert( (int)calcErrors.size() == ERROR_KINDS_COUNT );
598     assert( (int)validErrors.size() == ERROR_KINDS_COUNT );
599     assert( (int)eps.size() == ERROR_KINDS_COUNT );
600     vector<float>::const_iterator calcIt = calcErrors.begin(),
601                                   validIt = validErrors.begin(),
602                                   epsIt = eps.begin();
603     bool ok = true;
604     for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++calcIt, ++validIt, ++epsIt )
605         if( fabs(*calcIt - *validIt) > *epsIt )
606         {
607             ts->printf( CvTS::LOG, "bad accuracy of %s (valid=%f; calc=%f)\n", string(ERROR_PREFIXES[i]+errName).c_str(), *validIt, *calcIt );
608             ok = false;
609         }
610     return ok ? CvTS::OK : CvTS::FAIL_BAD_ACCURACY;
611 }
612
613 //----------------------------------- StereoBM test -----------------------------------------------------
614
615 class CV_StereoBMTest : public CV_StereoMatchingTest
616 {
617 public:
618     CV_StereoBMTest() : CV_StereoMatchingTest( "stereobm" )
619     { fill(rmsEps.begin(), rmsEps.end(), 0.4f); fill(fracEps.begin(), fracEps.end(), 0.02f); }
620
621 protected:
622     struct RunParams
623     {
624         int ndisp;
625         int winSize;
626     };
627     vector<RunParams> caseRunParams;
628
629     virtual int readRunParams( FileStorage& fs )
630     {
631         int code = CV_StereoMatchingTest::readRunParams( fs );
632         FileNode fn = fs.getFirstTopLevelNode();
633         assert(fn.isSeq());
634         for( int i = 0; i < (int)fn.size(); i+=4 )
635         {
636             string caseName = fn[i], datasetName = fn[i+1];
637             RunParams params;
638             string ndisp = fn[i+2]; params.ndisp = atoi(ndisp.c_str());
639             string winSize = fn[i+3]; params.winSize = atoi(winSize.c_str());
640             caseNames.push_back( caseName );
641             caseDatasets.push_back( datasetName );
642             caseRunParams.push_back( params );
643         }
644         return code;
645     }
646
647     virtual void runStereoMatchingAlgorithm( const Mat& _leftImg, const Mat& _rightImg,
648                    Mat& leftDisp, Mat& rightDisp, int caseIdx )
649     {
650         RunParams params = caseRunParams[caseIdx];
651         assert( params.ndisp%16 == 0 );
652         assert( _leftImg.type() == CV_8UC3 && _rightImg.type() == CV_8UC3 );
653         Mat leftImg; cvtColor( _leftImg, leftImg, CV_BGR2GRAY );
654         Mat rightImg; cvtColor( _rightImg, rightImg, CV_BGR2GRAY );
655
656         StereoBM bm( StereoBM::BASIC_PRESET, params.ndisp, params.winSize );
657         bm( leftImg, rightImg, leftDisp, CV_32F );
658     }
659 };
660
661 CV_StereoBMTest stereoBM;
662
663 //----------------------------------- StereoGC test -----------------------------------------------------
664
665 class CV_StereoGCTest : public CV_StereoMatchingTest
666 {
667 public:
668     CV_StereoGCTest() : CV_StereoMatchingTest( "stereogc" )
669     {
670         fill(rmsEps.begin(), rmsEps.end(), 3.f);
671         fracEps[0] = 0.05f; // all
672         fracEps[1] = 0.05f; // noOccl
673         fracEps[2] = 0.25f; // occl
674         fracEps[3] = 0.05f; // textured
675         fracEps[4] = 0.10f; // textureless
676         fracEps[5] = 0.10f; // borderedDepthDiscont
677     }
678 protected:
679     struct RunParams
680     {
681         int ndisp;
682         int iterCount;
683     };
684     vector<RunParams> caseRunParams;
685
686     virtual int readRunParams( FileStorage& fs )
687     {
688         int code = CV_StereoMatchingTest::readRunParams(fs);
689         FileNode fn = fs.getFirstTopLevelNode();
690         assert(fn.isSeq());
691         for( int i = 0; i < (int)fn.size(); i+=4 )
692         {
693             string caseName = fn[i], datasetName = fn[i+1];
694             RunParams params;
695             string ndisp = fn[i+2]; params.ndisp = atoi(ndisp.c_str());
696             string iterCount = fn[i+3]; params.iterCount = atoi(iterCount.c_str());
697             caseNames.push_back( caseName );
698             caseDatasets.push_back( datasetName );
699             caseRunParams.push_back( params );
700         }
701         return code;
702     }
703
704     virtual void runStereoMatchingAlgorithm( const Mat& _leftImg, const Mat& _rightImg,
705                    Mat& leftDisp, Mat& rightDisp, int caseIdx )
706     {
707         RunParams params = caseRunParams[caseIdx];
708         assert( _leftImg.type() == CV_8UC3 && _rightImg.type() == CV_8UC3 );
709         Mat leftImg, rightImg, tmp;
710         cvtColor( _leftImg, leftImg, CV_BGR2GRAY );
711         cvtColor( _rightImg, rightImg, CV_BGR2GRAY );
712
713         leftDisp.create( leftImg.size(), CV_16SC1 );
714         rightDisp.create( rightImg.size(), CV_16SC1 );
715
716         CvMat _limg = leftImg, _rimg = rightImg, _ldisp = leftDisp, _rdisp = rightDisp;
717         CvStereoGCState *state = cvCreateStereoGCState( params.ndisp, params.iterCount );
718         cvFindStereoCorrespondenceGC( &_limg, &_rimg, &_ldisp, &_rdisp, state );
719         cvReleaseStereoGCState( &state );
720
721         leftDisp = - leftDisp;
722     }
723
724 };
725
726 CV_StereoGCTest stereoGC;
727
728 //----------------------------------- StereoSGBM test -----------------------------------------------------
729
730 class CV_StereoSGBMTest : public CV_StereoMatchingTest
731 {
732 public:
733     CV_StereoSGBMTest() : CV_StereoMatchingTest( "stereosgbm" )
734     { fill(rmsEps.begin(), rmsEps.end(), 0.25f); fill(fracEps.begin(), fracEps.end(), 0.01f); }
735
736 protected:
737     struct RunParams
738     {
739         int ndisp;
740         int winSize;
741         bool fullDP;
742     };
743     vector<RunParams> caseRunParams;
744
745     virtual int readRunParams( FileStorage& fs )
746     {
747         int code = CV_StereoMatchingTest::readRunParams(fs);
748         FileNode fn = fs.getFirstTopLevelNode();
749         assert(fn.isSeq());
750         for( int i = 0; i < (int)fn.size(); i+=5 )
751         {
752             string caseName = fn[i], datasetName = fn[i+1];
753             RunParams params;
754             string ndisp = fn[i+2]; params.ndisp = atoi(ndisp.c_str());
755             string winSize = fn[i+3]; params.winSize = atoi(winSize.c_str());
756             string fullDP = fn[i+4]; params.fullDP = atoi(fullDP.c_str()) == 0 ? false : true;
757             caseNames.push_back( caseName );
758             caseDatasets.push_back( datasetName );
759             caseRunParams.push_back( params );
760         }
761         return code;
762     }
763
764     virtual void runStereoMatchingAlgorithm( const Mat& leftImg, const Mat& rightImg,
765                    Mat& leftDisp, Mat& rightDisp, int caseIdx )
766     {
767         RunParams params = caseRunParams[caseIdx];
768         assert( params.ndisp%16 == 0 );
769         StereoSGBM sgbm( 0, params.ndisp, params.winSize, 6*params.winSize*params.winSize, 15*params.winSize*params.winSize,
770                          1, 63, 10, 100, 32, params.fullDP );
771         sgbm( leftImg, rightImg, leftDisp );
772         assert( leftDisp.type() == CV_16SC1 );
773         leftDisp/=16;
774     }
775 };
776
777 CV_StereoSGBMTest stereoSGBM;
778