]> rtime.felk.cvut.cz Git - opencv.git/blob - opencv/tests/cv/src/astereomatching.cpp
added test for stereobm
[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
51 using namespace std;
52 using namespace cv;
53
54 const float EVAL_BAD_THRESH = 1.f;
55 const int EVAL_TEXTURELESS_WIDTH = 9;
56 const float EVAL_TEXTURELESS_THRESH = 2.f;
57 const float EVAL_DISP_THRESH = 1.f;
58 const float EVAL_DISP_GAP = 2.f;
59 const int EVAL_DISCONT_WIDTH = 9;
60 const int EVAL_IGNORE_BORDER = 10;
61
62 const int ERROR_KINDS_COUNT = 6;
63
64 //============================== quality measuring functions =================================================
65
66 /*
67   Calculate textureless regions of image (regions where the squared horizontal intensity gradient averaged over
68   a square window of size=evalTexturelessWidth is below a threshold=evalTexturelessThresh) and textured regions.
69 */
70 void computeTextureBasedMasks( const Mat& img, Mat* texturelessMask, Mat* texturedMask,
71              int texturelessWidth = EVAL_TEXTURELESS_WIDTH, float texturelessThresh = EVAL_TEXTURELESS_THRESH )
72 {
73     if( !texturelessMask && !texturedMask )
74         return;
75     if( img.empty() )
76         CV_Error( CV_StsBadArg, "img is empty" );
77
78     Mat dxI; Sobel( img, dxI, CV_32F, 1, 0, 3 );
79     Mat dxI2; pow( dxI / 8.f/*normalize*/, 2, dxI2 );
80     if( dxI2.channels() > 1)
81     {
82         Mat tmp; cvtColor( dxI2, tmp, CV_BGR2GRAY ); dxI2 = tmp;
83     }
84     Mat avgDxI2; boxFilter( dxI2, avgDxI2, CV_32FC1, Size(texturelessWidth,texturelessWidth) );
85
86     if( texturelessMask )
87         *texturelessMask = avgDxI2 < texturelessThresh;
88     if( texturedMask )
89         *texturedMask = avgDxI2 >= texturelessThresh;
90 }
91
92 void checkSizeAndTypeOfDispMaps( const Mat& dispMap1, const Mat& dispMap2 )
93 {
94     if( dispMap1.empty() || dispMap2.empty() )
95         CV_Error( CV_StsBadArg, "dispMap1 or dispMap2 is empty" );
96     if( dispMap1.cols != dispMap2.cols || dispMap1.rows != dispMap2.rows )
97         CV_Error( CV_StsBadArg, "dispMap1 and dispMap2 must have the same size" );
98     if( dispMap1.type() != CV_32FC1 && dispMap2.type() != CV_32FC1 )
99         CV_Error( CV_StsBadArg, "dispMap1 and dispMap2 must have CV_32FC1 type" );
100 }
101
102 void checkSizeAndTypeOfMask( const Mat& mask, Size sz )
103 {
104     if( mask.empty() )
105         CV_Error( CV_StsBadArg, "mask is empty" );
106     if( mask.type() != CV_8UC1 )
107         CV_Error( CV_StsBadArg, "mask must have CV_8UC1 type" );
108     if( mask.rows != sz.height || mask.cols != sz.width )
109         CV_Error( CV_StsBadArg, "mask has incorrect size" );
110 }
111
112 void checkDispMapsAndUnknDispMasks( const Mat& dispMap1, const Mat& dispMap2,
113                                     const Mat& unknDispMask1, const Mat& unknDispMask2 )
114 {
115     checkSizeAndTypeOfDispMaps( dispMap1, dispMap2 );
116
117     if( !unknDispMask1.empty() )
118         checkSizeAndTypeOfMask( unknDispMask1, dispMap1.size() );
119     if( !unknDispMask2.empty() )
120         checkSizeAndTypeOfMask( unknDispMask2, dispMap1.size() );
121
122     double minVal1, minVal2;
123     if( unknDispMask1.empty() )
124         minMaxLoc( dispMap1, &minVal1 );
125     else
126         minMaxLoc( dispMap1, &minVal1, 0, 0, 0, ~unknDispMask1 );
127     if( unknDispMask2.empty() )
128         minMaxLoc( dispMap2, &minVal2 );
129     else
130         minMaxLoc( dispMap2, &minVal2, 0, 0, 0, ~unknDispMask2 );
131     if( minVal1 < 0 || minVal2 < 0)
132         CV_Error( CV_StsBadArg, "known disparity values must be positive" );
133 }
134
135 /*
136   Calculate occluded regions of reference image (left image) (regions that are occluded in the matching image (right image),
137   i.e., where the forward-mapped disparity lands at a location with a larger (nearer) disparity) and non occluded regions.
138 */
139 void computeOcclusionBasedMasks( const Mat& leftDisp, const Mat& rightDisp,
140                              Mat* occludedMask, Mat* nonOccludedMask,
141                              const Mat& leftUnknDispMask = Mat(), const Mat& rightUnknDispMask = Mat(),
142                              float dispThresh = EVAL_DISP_THRESH )
143 {
144     const float dispDiff = 1.f;
145     if( !occludedMask && !nonOccludedMask )
146         return;
147     checkDispMapsAndUnknDispMasks( leftDisp, rightDisp, leftUnknDispMask, rightUnknDispMask );
148
149     if( occludedMask )
150     {
151         occludedMask->create( leftDisp.size(), CV_8UC1 );
152         occludedMask->setTo(Scalar::all(0) );
153     }
154     if( nonOccludedMask )
155     {
156         nonOccludedMask->create( leftDisp.size(), CV_8UC1 );
157         occludedMask->setTo(Scalar::all(0) );
158     }
159     for( int leftY = 0; leftY < leftDisp.rows; leftY++ )
160     {
161         for( int leftX = 0; leftX < leftDisp.cols; leftX++ )
162         {
163             if( !leftUnknDispMask.empty() && leftUnknDispMask.at<uchar>(leftY,leftX) )
164                 continue;
165             float leftDispVal = leftDisp.at<float>(leftY, leftX);
166             int rightX = leftX - (int)leftDispVal, rightY = leftY;
167             if( occludedMask && rightX < 0 )
168                 occludedMask->at<uchar>(leftY, leftX) = 255;
169             else
170             {
171                 if( !rightUnknDispMask.empty() && rightUnknDispMask.at<uchar>(rightY,rightX) )
172                     continue;
173                 float rightDispVal = rightDisp.at<float>(rightY, rightX);
174                 if( rightDispVal > leftDispVal + dispDiff )
175                 {
176                     if( occludedMask )
177                         occludedMask->at<uchar>(leftY, leftX) = 255;
178                 }
179                 else
180                 {
181                     if( nonOccludedMask )
182                         nonOccludedMask->at<uchar>(leftY, leftX) = 255;
183                 }
184             }
185         }
186     }
187 }
188
189 /*
190   Calculate depth discontinuty regions: pixels whose neiboring disparities differ by more than
191   dispGap, dilated by window of width discontWidth.
192 */
193 void computeDepthDiscontMask( const Mat& disp, Mat& depthDiscontMask, const Mat& unknDispMask = Mat(),
194                                  float dispGap = EVAL_DISP_GAP, int discontWidth = EVAL_DISCONT_WIDTH )
195 {
196     if( disp.empty() )
197         CV_Error( CV_StsBadArg, "disp is empty" );
198     if( disp.type() != CV_32FC1 )
199         CV_Error( CV_StsBadArg, "disp must have CV_32FC1 type" );
200     if( !unknDispMask.empty() )
201         checkSizeAndTypeOfMask( unknDispMask, disp.size() );
202
203     Mat curDisp; disp.copyTo( curDisp );
204     if( !unknDispMask.empty() )
205         curDisp.setTo( Scalar(numeric_limits<float>::min()), unknDispMask );
206     Mat maxNeighbDisp; dilate( curDisp, maxNeighbDisp, Mat(3, 3, CV_8UC1, Scalar(1)) );
207     if( !unknDispMask.empty() )
208         curDisp.setTo( Scalar(numeric_limits<float>::max()), unknDispMask );
209     Mat minNeighbDisp; erode( curDisp, minNeighbDisp, Mat(3, 3, CV_8UC1, Scalar(1)) );
210     depthDiscontMask = max( (Mat)(maxNeighbDisp-disp), (Mat)(disp-minNeighbDisp) ) > dispGap;
211     if( !unknDispMask.empty() )
212         depthDiscontMask &= ~unknDispMask;
213     dilate( depthDiscontMask, depthDiscontMask, Mat(discontWidth, discontWidth, CV_8UC1, Scalar(1)) );
214 }
215
216 /*
217    Get evaluation masks excluding a border.
218 */
219 Mat getBorderedMask( Size maskSize, int border = EVAL_IGNORE_BORDER )
220 {
221     CV_Assert( border >= 0 );
222     Mat mask(maskSize, CV_8UC1, Scalar(0));
223     int w = maskSize.width - 2*border, h = maskSize.height - 2*border;
224     if( w < 0 ||  h < 0 )
225         mask.setTo(Scalar(0));
226     else
227         mask( Rect(Point(border,border),Size(w,h)) ).setTo(Scalar(255));
228     return mask;
229 }
230
231 /*
232   Calculate root-mean-squared error between the computed disparity map (computedDisp) and ground truth map (groundTruthDisp).
233 */
234 float dispRMS( const Mat& computedDisp, const Mat& groundTruthDisp, const Mat& mask )
235 {
236     checkSizeAndTypeOfDispMaps( computedDisp, groundTruthDisp );
237     int pointsCount = computedDisp.cols*computedDisp.rows;
238     if( !mask.empty() )
239     {
240         checkSizeAndTypeOfMask( mask, computedDisp.size() );
241         pointsCount = countNonZero(mask);
242     }
243     return 1.f/sqrt((float)pointsCount) * norm(computedDisp, groundTruthDisp, NORM_L2, mask);
244 }
245
246 /*
247   Calculate percentage of bad matching pixels.
248 */
249 float badMatchPxlsPercentage( const Mat& computedDisp, const Mat& groundTruthDisp, const Mat& mask,
250                               int badThresh = EVAL_BAD_THRESH )
251 {
252     checkSizeAndTypeOfDispMaps( computedDisp, groundTruthDisp );
253     Mat badPxlsMap;
254     absdiff( computedDisp, groundTruthDisp, badPxlsMap );
255     badPxlsMap = badPxlsMap > badThresh;
256     int pointsCount = computedDisp.cols*computedDisp.rows;
257     if( !mask.empty() )
258     {
259         checkSizeAndTypeOfMask( mask, computedDisp.size() );
260         badPxlsMap = badPxlsMap & mask;
261         pointsCount = countNonZero(mask);
262     }
263     return 1.f/pointsCount * countNonZero(badPxlsMap);
264 }
265
266 //===================== regression test for stereo matching algorithms ==============================
267
268 const string ALGORITHMS_DIR = "stereomatching/algorithms/";
269 const string DATASETS_DIR = "stereomatching/datasets/";
270 const string DATASETS_FILE = "datasets.xml";
271
272 const string RUN_PARAMS_FILE = "_params.xml";
273 const string RESULT_FILE = "_res.xml";
274
275 const string LEFT_IMG_NAME = "im2.png";
276 const string RIGHT_IMG_NAME = "im6.png";
277 const string TRUE_LEFT_DISP_NAME = "disp2.png";
278 const string TRUE_RIGHT_DISP_NAME = "disp6.png";
279
280 string ERROR_PREFIXES[] = { "borderedAll",
281                             "borderedNoOccl",
282                             "borderedOccl",
283                             "borderedTextured",
284                             "borderedTextureless",
285                             "borderedDepthDiscont" }; // size of ERROR_KINDS_COUNT
286
287
288 const string RMS_STR = "RMS";
289 const string BAD_PXLS_PERCENTAGE_STR = "BadPxlsPercentage";
290
291 class CV_StereoMatchingTest : public CvTest
292 {
293 public:
294     CV_StereoMatchingTest( const char* testName ) :
295             CvTest( testName, "stereo-matching" ) {}
296 protected:
297     // assumed that left image is a reference image
298     virtual void runStereoMatchingAlgorithm( const Mat& leftImg, const Mat& rigthImg,
299                    Mat& leftDisp, Mat& rightDisp, FileStorage& paramsFS, const string& datasetName ) = 0;
300
301     int readDatasetsInfo();
302     void readDatasetRunParams( FileStorage& fs, const string datasetName ) {}
303     void writeErrors( const string& errName, const vector<float>& errors, FileStorage* fs = 0 );
304     void readErrors( FileNode& fn, const string& errName, vector<float>& errors );
305     int compareErrors( const vector<float>& calcErrors, const vector<float>& validErrors,
306                        const vector<float>& eps, const string& errName );
307     int processStereoMatchingResults( FileStorage& fs, int datasetIdx, bool isWrite,
308                   const Mat& leftImg, const Mat& rightImg,
309                   const Mat& trueLeftDisp, const Mat& trueRightDisp,
310                   const Mat& leftDisp, const Mat& rightDisp );
311     void run( int );
312
313     vector<string> datasetsNames;
314     vector<int> dispScaleFactors;
315     vector<int> dispUnknownVal;
316 };
317
318 void CV_StereoMatchingTest::run(int)
319 {
320     string dataPath = ts->get_data_path();
321     string algoritmName = name;
322     assert( !algoritmName.empty() );
323     if( dataPath.empty() )
324     {
325         ts->printf( CvTS::LOG, "dataPath is empty" );
326         ts->set_failed_test_info( CvTS::FAIL_BAD_ARG_CHECK );
327         return;
328     }
329
330     int code = readDatasetsInfo();
331     if( code != CvTS::OK )
332     {
333         ts->set_failed_test_info( code );
334         return;
335     }
336
337     string fullResultFilename = dataPath + ALGORITHMS_DIR + algoritmName + RESULT_FILE;
338     bool isWrite = true; // write or compare results
339     FileStorage runParamsFS( dataPath + ALGORITHMS_DIR + algoritmName + RUN_PARAMS_FILE, FileStorage::READ );
340     FileStorage resFS( fullResultFilename, FileStorage::READ );
341     if( resFS.isOpened() )
342         isWrite = false;
343     else
344     {
345         resFS.open( fullResultFilename, FileStorage::WRITE );
346         if( !resFS.isOpened() )
347         {
348             ts->printf( CvTS::LOG, "file named %s can not be read or written\n", fullResultFilename.c_str() );
349             ts->set_failed_test_info( CvTS::FAIL_BAD_ARG_CHECK );
350             return;
351         }
352         resFS << "stereo_matching" << "{";
353     }
354
355     for( int dsi = 0; dsi < (int)datasetsNames.size(); dsi++)
356     {
357         string datasetFullDirName = dataPath + DATASETS_DIR + datasetsNames[dsi] + "/";
358         Mat leftImg = imread(datasetFullDirName + LEFT_IMG_NAME);
359         Mat rightImg = imread(datasetFullDirName + RIGHT_IMG_NAME);
360         Mat trueLeftDisp = imread(datasetFullDirName + TRUE_LEFT_DISP_NAME, 0);
361         Mat trueRightDisp = imread(datasetFullDirName + TRUE_RIGHT_DISP_NAME, 0);
362
363         if( leftImg.empty() || rightImg.empty() || trueLeftDisp.empty() || trueRightDisp.empty() )
364         {
365             ts->printf( CvTS::LOG, "images or ground-truth disparities of dataset %s can not be read", datasetsNames[dsi].c_str() );
366             code = CvTS::FAIL_INVALID_TEST_DATA;
367             continue;
368         }
369         Mat tmp;
370         int scaleFactor = dispScaleFactors[dsi];
371         trueLeftDisp.convertTo( tmp, CV_32FC1, 1.f/scaleFactor ); trueLeftDisp = tmp; tmp.release();
372         trueRightDisp.convertTo( tmp, CV_32FC1, 1.f/scaleFactor ); trueRightDisp = tmp; tmp.release();
373
374         Mat leftDisp, rightDisp;
375         runStereoMatchingAlgorithm( leftImg, rightImg, leftDisp, rightDisp, runParamsFS, datasetsNames[dsi] );
376         leftDisp.convertTo( tmp, CV_32FC1 ); leftDisp = tmp; tmp.release();
377         rightDisp.convertTo( tmp, CV_32FC1 ); rightDisp = tmp; tmp.release();
378
379         int tempCode = processStereoMatchingResults( resFS, dsi, isWrite,
380                    leftImg, rightImg, trueLeftDisp, trueRightDisp, leftDisp, rightDisp);
381
382         code = tempCode==CvTS::OK ? code : tempCode;
383     }
384
385     if( isWrite )
386         resFS << "}"; // "stereo_matching"
387
388     ts->set_failed_test_info( code );
389 }
390
391 void calcErrors( const Mat& leftImg, const Mat& rightImg,
392                  const Mat& trueLeftDisp, const Mat& trueRightDisp,
393                  const Mat& trueLeftUnknDispMask, const Mat& trueRightUnknDispMask,
394                  const Mat& calcLeftDisp, const Mat& calcRightDisp,
395                  vector<float>& rms, vector<float>& badPxlsPercentages )
396 {
397     Mat texturelessMask, texturedMask;
398     computeTextureBasedMasks( leftImg, &texturelessMask, &texturedMask );
399     Mat occludedMask, nonOccludedMask;
400     computeOcclusionBasedMasks( trueLeftDisp, trueRightDisp, &occludedMask, &nonOccludedMask,
401                                 trueLeftUnknDispMask, trueRightUnknDispMask);
402     Mat depthDiscontMask;
403     computeDepthDiscontMask( trueLeftDisp, depthDiscontMask, trueLeftUnknDispMask);
404
405     Mat borderedKnownMask = getBorderedMask( leftImg.size() ) & ~trueLeftUnknDispMask;
406
407     nonOccludedMask &= borderedKnownMask;
408     occludedMask &= borderedKnownMask;
409     texturedMask &= nonOccludedMask; // & borderedKnownMask
410     texturelessMask &= nonOccludedMask; // & borderedKnownMask
411     depthDiscontMask &= nonOccludedMask; // & borderedKnownMask
412
413     rms.resize(ERROR_KINDS_COUNT);
414     rms[0] = dispRMS( calcLeftDisp, trueLeftDisp, borderedKnownMask );
415     rms[1] = dispRMS( calcLeftDisp, trueLeftDisp, nonOccludedMask );
416     rms[2] = dispRMS( calcLeftDisp, trueLeftDisp, occludedMask );
417     rms[3] = dispRMS( calcLeftDisp, trueLeftDisp, texturedMask );
418     rms[4] = dispRMS( calcLeftDisp, trueLeftDisp, texturelessMask );
419     rms[5] = dispRMS( calcLeftDisp, trueLeftDisp, depthDiscontMask );
420
421     badPxlsPercentages.resize(ERROR_KINDS_COUNT);
422     badPxlsPercentages[0] = badMatchPxlsPercentage( calcLeftDisp, trueLeftDisp, borderedKnownMask );
423     badPxlsPercentages[1] = badMatchPxlsPercentage( calcLeftDisp, trueLeftDisp, nonOccludedMask );
424     badPxlsPercentages[2] = badMatchPxlsPercentage( calcLeftDisp, trueLeftDisp, occludedMask );
425     badPxlsPercentages[3] = badMatchPxlsPercentage( calcLeftDisp, trueLeftDisp, texturedMask );
426     badPxlsPercentages[4] = badMatchPxlsPercentage( calcLeftDisp, trueLeftDisp, texturelessMask );
427     badPxlsPercentages[5] = badMatchPxlsPercentage( calcLeftDisp, trueLeftDisp, depthDiscontMask );
428 }
429
430 int CV_StereoMatchingTest::processStereoMatchingResults( FileStorage& fs, int datasetIdx, bool isWrite,
431               const Mat& leftImg, const Mat& rightImg,
432               const Mat& trueLeftDisp, const Mat& trueRightDisp,
433               const Mat& leftDisp, const Mat& rightDisp )
434 {
435     // rightDisp is not used in current test virsion
436     int code = CvTS::OK;
437     assert( fs.isOpened() );
438     assert( trueLeftDisp.type() == CV_32FC1 && trueRightDisp.type() == CV_32FC1 );
439     assert( leftDisp.type() == CV_32FC1 && rightDisp.type() == CV_32FC1 );
440
441     // compute errors
442     Mat leftUnknMask, rightUnknMask;
443     absdiff( trueLeftDisp, Scalar(dispUnknownVal[datasetIdx]), leftUnknMask );
444     leftUnknMask = leftUnknMask < numeric_limits<float>::epsilon();
445     absdiff( trueRightDisp, Scalar(dispUnknownVal[datasetIdx]), rightUnknMask );
446     rightUnknMask = rightUnknMask < numeric_limits<float>::epsilon();
447     vector<float> rmss, badPxlsPercentages;
448     calcErrors( leftImg, rightImg, trueLeftDisp, trueRightDisp, leftUnknMask, rightUnknMask,
449                 leftDisp, rightDisp, rmss, badPxlsPercentages );
450
451     const string& datasetName = datasetsNames[datasetIdx];
452     if( isWrite )
453     {
454         fs << datasetName << "{";
455         cvWriteComment( fs.fs, RMS_STR.c_str(), 0 );
456         writeErrors( RMS_STR, rmss, &fs );
457         cvWriteComment( fs.fs, BAD_PXLS_PERCENTAGE_STR.c_str(), 0 );
458         writeErrors( BAD_PXLS_PERCENTAGE_STR, badPxlsPercentages, &fs );
459         fs << "}"; // datasetName
460     }
461     else // compare
462     {
463         ts->printf( CvTS::LOG, "\nerrors on dataset %s\n", datasetName.c_str() );
464         ts->printf( CvTS::LOG, "%s\n", RMS_STR.c_str() );
465         writeErrors( RMS_STR, rmss );
466         ts->printf( CvTS::LOG, "%s\n", BAD_PXLS_PERCENTAGE_STR.c_str() );
467         writeErrors( BAD_PXLS_PERCENTAGE_STR, badPxlsPercentages );
468
469         FileNode fn = fs.getFirstTopLevelNode()[datasetName];
470         vector<float> validRmss, validBadPxlsPercentages;
471
472         readErrors( fn, RMS_STR, validRmss );
473         readErrors( fn, BAD_PXLS_PERCENTAGE_STR, validBadPxlsPercentages );
474         int tempCode = compareErrors( rmss, validRmss,
475                       vector<float>(ERROR_KINDS_COUNT, 0.01f), RMS_STR );
476         code = tempCode==CvTS::OK ? code : tempCode;
477         tempCode = compareErrors( badPxlsPercentages, validBadPxlsPercentages,
478                       vector<float>(ERROR_KINDS_COUNT, 0.01f), BAD_PXLS_PERCENTAGE_STR );
479         code = tempCode==CvTS::OK ? code : tempCode;
480     }
481     return code;
482 }
483
484 int CV_StereoMatchingTest::readDatasetsInfo()
485 {
486     string datasetsFilename = string(ts->get_data_path()) + DATASETS_DIR + DATASETS_FILE;
487
488     FileStorage fs( datasetsFilename, FileStorage::READ );
489     if( !fs.isOpened() )
490     {
491         ts->printf( CvTS::LOG, "%s can not be read\n", datasetsFilename.c_str() );
492         return CvTS::FAIL_INVALID_TEST_DATA;
493     }
494     FileNode fn = fs.getFirstTopLevelNode()["names_scale_unknown"];
495     assert(fn.isSeq());
496     datasetsNames.clear();
497     dispScaleFactors.clear();
498     dispUnknownVal.clear();
499     for( int i = 0; i < (int)fn.size(); i+=3 )
500     {
501         string name = fn[i]; datasetsNames.push_back(name);
502         string scale = fn[i+1]; dispScaleFactors.push_back(atoi(scale.c_str()));
503         string unkn = fn[i+2]; dispUnknownVal.push_back(atoi(unkn.c_str()));
504     }
505
506     return CvTS::OK;
507 }
508
509 void CV_StereoMatchingTest::writeErrors( const string& errName, const vector<float>& errors, FileStorage* fs )
510 {
511     assert( (int)errors.size() == ERROR_KINDS_COUNT );
512     vector<float>::const_iterator it = errors.begin();
513     if( fs )
514         for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++it )
515             *fs << ERROR_PREFIXES[i] + errName << *it;
516     else
517         for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++it )
518             ts->printf( CvTS::LOG, "%s = %f\n", string(ERROR_PREFIXES[i]+errName).c_str(), *it );
519 }
520
521 void CV_StereoMatchingTest::readErrors( FileNode& fn, const string& errName, vector<float>& errors )
522 {
523     errors.resize( ERROR_KINDS_COUNT );
524     vector<float>::iterator it = errors.begin();
525     for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++it )
526         fn[ERROR_PREFIXES[i]+errName] >> *it;
527 }
528
529 int CV_StereoMatchingTest::compareErrors( const vector<float>& calcErrors, const vector<float>& validErrors,
530                    const vector<float>& eps, const string& errName )
531 {
532     assert( (int)calcErrors.size() == ERROR_KINDS_COUNT );
533     assert( (int)validErrors.size() == ERROR_KINDS_COUNT );
534     assert( (int)eps.size() == ERROR_KINDS_COUNT );
535     vector<float>::const_iterator calcIt = calcErrors.begin(),
536                                   validIt = validErrors.begin(),
537                                   epsIt = eps.begin();
538     for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++calcIt, ++validIt, ++epsIt )
539         if( fabs(*calcIt - *validIt) > *epsIt )
540         {
541             ts->printf( CvTS::LOG, "bad accuracy of %s\n", string(ERROR_PREFIXES[i]+errName).c_str());
542             return CvTS::FAIL_BAD_ACCURACY;
543         }
544     return CvTS::OK;
545 }
546
547 //----------------------------------- StereoBM test -----------------------------------------------------
548
549 class CV_StereoBMTest : public CV_StereoMatchingTest
550 {
551 public:
552     CV_StereoBMTest() :
553             CV_StereoMatchingTest( "stereobm" ) {}
554 protected:
555     virtual void runStereoMatchingAlgorithm( const Mat& leftImg, const Mat& rigthImg,
556               Mat& leftDisp, Mat& rightDisp, FileStorage& paramsFS, const string& datasetName );
557 };
558
559 void CV_StereoBMTest::runStereoMatchingAlgorithm( const Mat& _leftImg, const Mat& _rigthImg,
560               Mat& leftDisp, Mat& rightDisp, FileStorage& paramsFS, const string& datasetName )
561 {
562     int ndisp = 7;
563     int winSize = 21;
564     if( paramsFS.isOpened() && !datasetName.empty())
565     {
566         FileNodeIterator fni = paramsFS.getFirstTopLevelNode()[datasetName].begin();
567         fni >> ndisp >> winSize;
568     }
569
570     assert( _leftImg.type() == CV_8UC3 && _rigthImg.type() == CV_8UC3 );
571     Mat leftImg; cvtColor( _leftImg, leftImg, CV_BGR2GRAY );
572     Mat rigthImg; cvtColor( _rigthImg, rigthImg, CV_BGR2GRAY );
573
574     StereoBM bm( StereoBM::BASIC_PRESET, ndisp*16 );
575     bm( leftImg, rigthImg, leftDisp, CV_32F );
576 }
577
578 CV_StereoBMTest stereoBM;