]> rtime.felk.cvut.cz Git - opencv.git/blob - opencv/tests/cv/src/astereomatching.cpp
made some tweaks in stereosgbm test + fixed logbuf overflow in cxts + fixed some...
[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 checkTypeAndSizeOfDisp( const Mat& dispMap, const Size* sz )
95 {
96     if( dispMap.empty() )
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" );
102 }
103
104 void checkTypeAndSizeOfMask( const Mat& mask, Size sz )
105 {
106     if( mask.empty() )
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" );
112 }
113
114 void checkDispMapsAndUnknDispMasks( const Mat& leftDispMap, const Mat& rightDispMap,
115                                     const Mat& leftUnknDispMask, const Mat& rightUnknDispMask )
116 {
117     // check type and size of disparity maps
118     checkTypeAndSizeOfDisp( leftDispMap, 0 );
119     if( !rightDispMap.empty() )
120     {
121         Size sz = leftDispMap.size();
122         checkTypeAndSizeOfDisp( rightDispMap, &sz );
123     }
124
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() );
130
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 );
135     else
136         minMaxLoc( leftDispMap, &leftMinVal, 0, 0, 0, ~leftUnknDispMask );
137     if( !rightDispMap.empty() )
138     {
139         if( rightUnknDispMask.empty() )
140             minMaxLoc( rightDispMap, &rightMinVal );
141         else
142             minMaxLoc( rightDispMap, &rightMinVal, 0, 0, 0, ~rightUnknDispMask );
143     }
144     if( leftMinVal < 0 || rightMinVal < 0)
145         CV_Error( CV_StsBadArg, "known disparity values must be positive" );
146 }
147
148 /*
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.
151 */
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 )
156 {
157     if( !occludedMask && !nonOccludedMask )
158         return;
159     checkDispMapsAndUnknDispMasks( leftDisp, _rightDisp, leftUnknDispMask, rightUnknDispMask );
160
161     Mat rightDisp;
162     if( _rightDisp.empty() )
163     {
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++ )
169         {
170             for( int leftX = 0; leftX < leftDisp.cols; leftX++ )
171             {
172                 if( !leftUnknDispMask.empty() && leftUnknDispMask.at<uchar>(leftY,leftX) )
173                     continue;
174                 float leftDispVal = leftDisp.at<float>(leftY, leftX);
175                 int rightX = leftX - cvRound(leftDispVal), rightY = leftY;
176                 if( rightX >= 0)
177                     rightDisp.at<float>(rightY,rightX) = max(rightDisp.at<float>(rightY,rightX), leftDispVal);
178             }
179         }
180     }
181     else
182         _rightDisp.copyTo(rightDisp);
183
184     if( occludedMask )
185     {
186         occludedMask->create(leftDisp.size(), CV_8UC1);
187         occludedMask->setTo(Scalar::all(0) );
188     }
189     if( nonOccludedMask )
190     {
191         nonOccludedMask->create(leftDisp.size(), CV_8UC1);
192         nonOccludedMask->setTo(Scalar::all(0) );
193     }
194     for( int leftY = 0; leftY < leftDisp.rows; leftY++ )
195     {
196         for( int leftX = 0; leftX < leftDisp.cols; leftX++ )
197         {
198             if( !leftUnknDispMask.empty() && leftUnknDispMask.at<uchar>(leftY,leftX) )
199                 continue;
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;
204             else
205             {
206                 if( !rightUnknDispMask.empty() && rightUnknDispMask.at<uchar>(rightY,rightX) )
207                     continue;
208                 float rightDispVal = rightDisp.at<float>(rightY, rightX);
209                 if( rightDispVal > leftDispVal + dispThresh )
210                 {
211                     if( occludedMask )
212                         occludedMask->at<uchar>(leftY, leftX) = 255;
213                 }
214                 else
215                 {
216                     if( nonOccludedMask )
217                         nonOccludedMask->at<uchar>(leftY, leftX) = 255;
218                 }
219             }
220         }
221     }
222 }
223
224 /*
225   Calculate depth discontinuty regions: pixels whose neiboring disparities differ by more than
226   dispGap, dilated by window of width discontWidth.
227 */
228 void computeDepthDiscontMask( const Mat& disp, Mat& depthDiscontMask, const Mat& unknDispMask = Mat(),
229                                  float dispGap = EVAL_DISP_GAP, int discontWidth = EVAL_DISCONT_WIDTH )
230 {
231     if( disp.empty() )
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() );
237
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)) );
249 }
250
251 /*
252    Get evaluation masks excluding a border.
253 */
254 Mat getBorderedMask( Size maskSize, int border = EVAL_IGNORE_BORDER )
255 {
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;
259     if( w < 0 ||  h < 0 )
260         mask.setTo(Scalar(0));
261     else
262         mask( Rect(Point(border,border),Size(w,h)) ).setTo(Scalar(255));
263     return mask;
264 }
265
266 /*
267   Calculate root-mean-squared error between the computed disparity map (computedDisp) and ground truth map (groundTruthDisp).
268 */
269 float dispRMS( const Mat& computedDisp, const Mat& groundTruthDisp, const Mat& mask )
270 {
271     checkTypeAndSizeOfDisp( groundTruthDisp, 0 );
272     Size sz = groundTruthDisp.size();
273     checkTypeAndSizeOfDisp( computedDisp, &sz );
274
275     int pointsCount = sz.height*sz.width;
276     if( !mask.empty() )
277     {
278         checkTypeAndSizeOfMask( mask, sz );
279         pointsCount = countNonZero(mask);
280     }
281     return 1.f/sqrt((float)pointsCount) * (float)norm(computedDisp, groundTruthDisp, NORM_L2, mask);
282 }
283
284 /*
285   Calculate fraction of bad matching pixels.
286 */
287 float badMatchPxlsFraction( const Mat& computedDisp, const Mat& groundTruthDisp, const Mat& mask,
288                               int badThresh = EVAL_BAD_THRESH )
289 {
290     checkTypeAndSizeOfDisp( groundTruthDisp, 0 );
291     Size sz = groundTruthDisp.size();
292     checkTypeAndSizeOfDisp( computedDisp, &sz );
293
294     Mat badPxlsMap;
295     absdiff( computedDisp, groundTruthDisp, badPxlsMap );
296     badPxlsMap = badPxlsMap > badThresh;
297     int pointsCount = sz.height*sz.width;
298     if( !mask.empty() )
299     {
300         checkTypeAndSizeOfMask( mask, sz );
301         badPxlsMap = badPxlsMap & mask;
302         pointsCount = countNonZero(mask);
303     }
304     return 1.f/pointsCount * countNonZero(badPxlsMap);
305 }
306
307 //===================== regression test for stereo matching algorithms ==============================
308
309 const string ALGORITHMS_DIR = "stereomatching/algorithms/";
310 const string DATASETS_DIR = "stereomatching/datasets/";
311 const string DATASETS_FILE = "datasets.xml";
312
313 const string RUN_PARAMS_FILE = "_params.xml";
314 const string RESULT_FILE = "_res.xml";
315
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";
320
321 string ERROR_PREFIXES[] = { "borderedAll",
322                             "borderedNoOccl",
323                             "borderedOccl",
324                             "borderedTextured",
325                             "borderedTextureless",
326                             "borderedDepthDiscont" }; // size of ERROR_KINDS_COUNT
327
328
329 const string RMS_STR = "RMS";
330 const string BAD_PXLS_FRACTION_STR = "BadPxlsFraction";
331
332 class QualityEvalParams
333 {
334 public:
335     QualityEvalParams() { setDefaults(); }
336     QualityEvalParams( int _ignoreBorder )
337     {
338         setDefaults();
339         ignoreBorder = _ignoreBorder;
340     }
341     void setDefaults()
342     {
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;
350     }
351     float badThresh;
352     int texturelessWidth;
353     float texturelessThresh;
354     float dispThresh;
355     float dispGap;
356     int discontWidth;
357     int ignoreBorder;
358 };
359
360 class CV_StereoMatchingTest : public CvTest
361 {
362 public:
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 ); }
365 protected:
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
369
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  );
381     void run( int );
382
383     vector<float> rmsEps;
384     vector<float> fracEps;
385
386     struct DatasetParams
387     {
388         int dispScaleFactor;
389         int dispUnknVal;
390     };
391     map<string, DatasetParams> datasetsParams;
392
393     vector<string> caseNames;
394     vector<string> caseDatasets;
395 };
396
397 void CV_StereoMatchingTest::run(int)
398 {
399     string dataPath = ts->get_data_path();
400     string algorithmName = name;
401     assert( !algorithmName.empty() );
402     if( dataPath.empty() )
403     {
404         ts->printf( CvTS::LOG, "dataPath is empty" );
405         ts->set_failed_test_info( CvTS::FAIL_BAD_ARG_CHECK );
406         return;
407     }
408
409     FileStorage datasetsFS( dataPath + DATASETS_DIR + DATASETS_FILE, FileStorage::READ );
410     int code = readDatasetsParams( datasetsFS );
411     if( code != CvTS::OK )
412     {
413         ts->set_failed_test_info( code );
414         return;
415     }
416     FileStorage runParamsFS( dataPath + ALGORITHMS_DIR + algorithmName + RUN_PARAMS_FILE, FileStorage::READ );
417     code = readRunParams( runParamsFS );
418     if( code != CvTS::OK )
419     {
420         ts->set_failed_test_info( code );
421         return;
422     }
423     
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() )
428         isWrite = false;
429     else
430     {
431         resFS.open( fullResultFilename, FileStorage::WRITE );
432         if( !resFS.isOpened() )
433         {
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 );
436             return;
437         }
438         resFS << "stereo_matching" << "{";
439     }
440
441     int progress = 0, caseCount = caseNames.size();
442     for( int ci = 0; ci < caseCount; ci++)
443     {
444         progress = update_progress( progress, ci, caseCount, 0 );
445
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);
452
453         if( leftImg.empty() || rightImg.empty() || trueLeftDisp.empty() )
454         {
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;
457             continue;
458         }
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();
463
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();
468
469         int tempCode = processStereoMatchingResults( resFS, ci, isWrite,
470                    leftImg, rightImg, trueLeftDisp, trueRightDisp, leftDisp, rightDisp, QualityEvalParams(ignBorder));
471         code = tempCode==CvTS::OK ? code : tempCode;
472     }
473
474     if( isWrite )
475         resFS << "}"; // "stereo_matching"
476
477     ts->set_failed_test_info( code );
478 }
479
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 )
486 {
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);
496
497     Mat borderedKnownMask = getBorderedMask( leftImg.size(), qualityEvalParams.ignoreBorder ) & ~trueLeftUnknDispMask;
498
499     nonOccludedMask &= borderedKnownMask;
500     occludedMask &= borderedKnownMask;
501     texturedMask &= nonOccludedMask; // & borderedKnownMask
502     texturelessMask &= nonOccludedMask; // & borderedKnownMask
503     depthDiscontMask &= nonOccludedMask; // & borderedKnownMask
504
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 );
512
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 );
520 }
521
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 )
527 {
528     // rightDisp is not used in current test virsion
529     int code = CvTS::OK;
530     assert( fs.isOpened() );
531     assert( trueLeftDisp.type() == CV_32FC1 && trueRightDisp.type() == CV_32FC1 );
532     assert( leftDisp.type() == CV_32FC1 && rightDisp.type() == CV_32FC1 );
533
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() )
541     {
542         absdiff( trueRightDisp, Scalar(params.dispUnknVal), rightUnknMask );
543         rightUnknMask = rightUnknMask < numeric_limits<float>::epsilon();
544         assert(leftUnknMask.type() == CV_8UC1);
545     }
546
547     // calculate errors
548     vector<float> rmss, badPxlsFractions;
549     calcErrors( leftImg, rightImg, trueLeftDisp, trueRightDisp, leftUnknMask, rightUnknMask,
550                 leftDisp, rightDisp, rmss, badPxlsFractions, qualityEvalParams );
551
552     if( isWrite )
553     {
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
560     }
561     else // compare
562     {
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 );
568
569         FileNode fn = fs.getFirstTopLevelNode()[caseNames[caseIdx]];
570         vector<float> validRmss, validBadPxlsFractions;
571
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;
578     }
579     return code;
580 }
581
582 int CV_StereoMatchingTest::readDatasetsParams( FileStorage& fs )
583 {
584     if( !fs.isOpened() )
585     {
586         ts->printf( CvTS::LOG, "datasetsParams can not be read " );
587         return CvTS::FAIL_INVALID_TEST_DATA;
588     }
589     datasetsParams.clear();
590     FileNode fn = fs.getFirstTopLevelNode();
591     assert(fn.isSeq());
592     for( int i = 0; i < (int)fn.size(); i+=3 )
593     {
594         string name = fn[i];
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;
599     }
600     return CvTS::OK;
601 }
602
603 int CV_StereoMatchingTest::readRunParams( FileStorage& fs )
604 {
605     if( !fs.isOpened() )
606     {
607         ts->printf( CvTS::LOG, "runParams can not be read " );
608         return CvTS::FAIL_INVALID_TEST_DATA;
609     }
610     caseNames.clear();;
611     caseDatasets.clear();
612     return CvTS::OK;
613 }
614
615 void CV_StereoMatchingTest::writeErrors( const string& errName, const vector<float>& errors, FileStorage* fs )
616 {
617     assert( (int)errors.size() == ERROR_KINDS_COUNT );
618     vector<float>::const_iterator it = errors.begin();
619     if( fs )
620         for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++it )
621             *fs << ERROR_PREFIXES[i] + errName << *it;
622     else
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 );
625 }
626
627 void CV_StereoMatchingTest::readErrors( FileNode& fn, const string& errName, vector<float>& errors )
628 {
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;
633 }
634
635 int CV_StereoMatchingTest::compareErrors( const vector<float>& calcErrors, const vector<float>& validErrors,
636                    const vector<float>& eps, const string& errName )
637 {
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(),
643                                   epsIt = eps.begin();
644     bool ok = true;
645     for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++calcIt, ++validIt, ++epsIt )
646         if( *calcIt - *validIt > *epsIt )
647         {
648             ts->printf( CvTS::LOG, "bad accuracy of %s (valid=%f; calc=%f)\n", string(ERROR_PREFIXES[i]+errName).c_str(), *validIt, *calcIt );
649             ok = false;
650         }
651     return ok ? CvTS::OK : CvTS::FAIL_BAD_ACCURACY;
652 }
653
654 //----------------------------------- StereoBM test -----------------------------------------------------
655
656 class CV_StereoBMTest : public CV_StereoMatchingTest
657 {
658 public:
659     CV_StereoBMTest() : CV_StereoMatchingTest( "stereobm" )
660     { fill(rmsEps.begin(), rmsEps.end(), 0.4f); fill(fracEps.begin(), fracEps.end(), 0.02f); }
661
662 protected:
663     struct RunParams
664     {
665         int ndisp;
666         int winSize;
667     };
668     vector<RunParams> caseRunParams;
669
670     virtual int readRunParams( FileStorage& fs )
671     {
672         int code = CV_StereoMatchingTest::readRunParams( fs );
673         FileNode fn = fs.getFirstTopLevelNode();
674         assert(fn.isSeq());
675         for( int i = 0; i < (int)fn.size(); i+=4 )
676         {
677             string caseName = fn[i], datasetName = fn[i+1];
678             RunParams params;
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 );
684         }
685         return code;
686     }
687
688     virtual int runStereoMatchingAlgorithm( const Mat& _leftImg, const Mat& _rightImg,
689                    Mat& leftDisp, Mat& rightDisp, int caseIdx )
690     {
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 );
696
697         StereoBM bm( StereoBM::BASIC_PRESET, params.ndisp, params.winSize );
698         bm( leftImg, rightImg, leftDisp, CV_32F );
699         return params.winSize/2;
700     }
701 };
702
703 CV_StereoBMTest stereoBM;
704
705 //----------------------------------- StereoGC test -----------------------------------------------------
706
707 class CV_StereoGCTest : public CV_StereoMatchingTest
708 {
709 public:
710     CV_StereoGCTest() : CV_StereoMatchingTest( "stereogc" )
711     {
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
719     }
720 protected:
721     struct RunParams
722     {
723         int ndisp;
724         int iterCount;
725     };
726     vector<RunParams> caseRunParams;
727
728     virtual int readRunParams( FileStorage& fs )
729     {
730         int code = CV_StereoMatchingTest::readRunParams(fs);
731         FileNode fn = fs.getFirstTopLevelNode();
732         assert(fn.isSeq());
733         for( int i = 0; i < (int)fn.size(); i+=4 )
734         {
735             string caseName = fn[i], datasetName = fn[i+1];
736             RunParams params;
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 );
742         }
743         return code;
744     }
745
746     virtual int runStereoMatchingAlgorithm( const Mat& _leftImg, const Mat& _rightImg,
747                    Mat& leftDisp, Mat& rightDisp, int caseIdx )
748     {
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 );
754
755         leftDisp.create( leftImg.size(), CV_16SC1 );
756         rightDisp.create( rightImg.size(), CV_16SC1 );
757
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 );
762
763         leftDisp = - leftDisp;
764         return 0;
765     }
766
767 };
768
769 CV_StereoGCTest stereoGC;
770
771 //----------------------------------- StereoSGBM test -----------------------------------------------------
772
773 class CV_StereoSGBMTest : public CV_StereoMatchingTest
774 {
775 public:
776     CV_StereoSGBMTest() : CV_StereoMatchingTest( "stereosgbm" )
777     { fill(rmsEps.begin(), rmsEps.end(), 0.25f); fill(fracEps.begin(), fracEps.end(), 0.01f); }
778
779 protected:
780     struct RunParams
781     {
782         int ndisp;
783         int winSize;
784         bool fullDP;
785     };
786     vector<RunParams> caseRunParams;
787
788     virtual int readRunParams( FileStorage& fs )
789     {
790         int code = CV_StereoMatchingTest::readRunParams(fs);
791         FileNode fn = fs.getFirstTopLevelNode();
792         assert(fn.isSeq());
793         for( int i = 0; i < (int)fn.size(); i+=5 )
794         {
795             string caseName = fn[i], datasetName = fn[i+1];
796             RunParams params;
797             string ndisp = fn[i+2]; params.ndisp = atoi(ndisp.c_str());
798             string winSize = fn[i+3]; params.winSize = atoi(winSize.c_str());
799             string 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 );
803         }
804         return code;
805     }
806
807     virtual int runStereoMatchingAlgorithm( const Mat& leftImg, const Mat& rightImg,
808                    Mat& leftDisp, Mat& rightDisp, int caseIdx )
809     {
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 );
816         leftDisp/=16;
817         return 0;
818     }
819 };
820
821 CV_StereoSGBMTest stereoSGBM;
822