Home | History | Annotate | Download | only in test
      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 "test_precomp.hpp"
     49 #include <limits>
     50 #include <cstdio>
     51 #include <map>
     52 
     53 using namespace std;
     54 using namespace cv;
     55 
     56 const float EVAL_BAD_THRESH = 1.f;
     57 const int EVAL_TEXTURELESS_WIDTH = 3;
     58 const float EVAL_TEXTURELESS_THRESH = 4.f;
     59 const float EVAL_DISP_THRESH = 1.f;
     60 const float EVAL_DISP_GAP = 2.f;
     61 const int EVAL_DISCONT_WIDTH = 9;
     62 const int EVAL_IGNORE_BORDER = 10;
     63 
     64 const int ERROR_KINDS_COUNT = 6;
     65 
     66 //============================== quality measuring functions =================================================
     67 
     68 /*
     69   Calculate textureless regions of image (regions where the squared horizontal intensity gradient averaged over
     70   a square window of size=evalTexturelessWidth is below a threshold=evalTexturelessThresh) and textured regions.
     71 */
     72 void computeTextureBasedMasks( const Mat& _img, Mat* texturelessMask, Mat* texturedMask,
     73              int texturelessWidth = EVAL_TEXTURELESS_WIDTH, float texturelessThresh = EVAL_TEXTURELESS_THRESH )
     74 {
     75     if( !texturelessMask && !texturedMask )
     76         return;
     77     if( _img.empty() )
     78         CV_Error( Error::StsBadArg, "img is empty" );
     79 
     80     Mat img = _img;
     81     if( _img.channels() > 1)
     82     {
     83         Mat tmp; cvtColor( _img, tmp, COLOR_BGR2GRAY ); img = tmp;
     84     }
     85     Mat dxI; Sobel( img, dxI, CV_32FC1, 1, 0, 3 );
     86     Mat dxI2; pow( dxI / 8.f/*normalize*/, 2, dxI2 );
     87     Mat avgDxI2; boxFilter( dxI2, avgDxI2, CV_32FC1, Size(texturelessWidth,texturelessWidth) );
     88 
     89     if( texturelessMask )
     90         *texturelessMask = avgDxI2 < texturelessThresh;
     91     if( texturedMask )
     92         *texturedMask = avgDxI2 >= texturelessThresh;
     93 }
     94 
     95 void checkTypeAndSizeOfDisp( const Mat& dispMap, const Size* sz )
     96 {
     97     if( dispMap.empty() )
     98         CV_Error( Error::StsBadArg, "dispMap is empty" );
     99     if( dispMap.type() != CV_32FC1 )
    100         CV_Error( Error::StsBadArg, "dispMap must have CV_32FC1 type" );
    101     if( sz && (dispMap.rows != sz->height || dispMap.cols != sz->width) )
    102         CV_Error( Error::StsBadArg, "dispMap has incorrect size" );
    103 }
    104 
    105 void checkTypeAndSizeOfMask( const Mat& mask, Size sz )
    106 {
    107     if( mask.empty() )
    108         CV_Error( Error::StsBadArg, "mask is empty" );
    109     if( mask.type() != CV_8UC1 )
    110         CV_Error( Error::StsBadArg, "mask must have CV_8UC1 type" );
    111     if( mask.rows != sz.height || mask.cols != sz.width )
    112         CV_Error( Error::StsBadArg, "mask has incorrect size" );
    113 }
    114 
    115 void checkDispMapsAndUnknDispMasks( const Mat& leftDispMap, const Mat& rightDispMap,
    116                                     const Mat& leftUnknDispMask, const Mat& rightUnknDispMask )
    117 {
    118     // check type and size of disparity maps
    119     checkTypeAndSizeOfDisp( leftDispMap, 0 );
    120     if( !rightDispMap.empty() )
    121     {
    122         Size sz = leftDispMap.size();
    123         checkTypeAndSizeOfDisp( rightDispMap, &sz );
    124     }
    125 
    126     // check size and type of unknown disparity maps
    127     if( !leftUnknDispMask.empty() )
    128         checkTypeAndSizeOfMask( leftUnknDispMask, leftDispMap.size() );
    129     if( !rightUnknDispMask.empty() )
    130         checkTypeAndSizeOfMask( rightUnknDispMask, rightDispMap.size() );
    131 
    132     // check values of disparity maps (known disparity values musy be positive)
    133     double leftMinVal = 0, rightMinVal = 0;
    134     if( leftUnknDispMask.empty() )
    135         minMaxLoc( leftDispMap, &leftMinVal );
    136     else
    137         minMaxLoc( leftDispMap, &leftMinVal, 0, 0, 0, ~leftUnknDispMask );
    138     if( !rightDispMap.empty() )
    139     {
    140         if( rightUnknDispMask.empty() )
    141             minMaxLoc( rightDispMap, &rightMinVal );
    142         else
    143             minMaxLoc( rightDispMap, &rightMinVal, 0, 0, 0, ~rightUnknDispMask );
    144     }
    145     if( leftMinVal < 0 || rightMinVal < 0)
    146         CV_Error( Error::StsBadArg, "known disparity values must be positive" );
    147 }
    148 
    149 /*
    150   Calculate occluded regions of reference image (left image) (regions that are occluded in the matching image (right image),
    151   i.e., where the forward-mapped disparity lands at a location with a larger (nearer) disparity) and non occluded regions.
    152 */
    153 void computeOcclusionBasedMasks( const Mat& leftDisp, const Mat& _rightDisp,
    154                              Mat* occludedMask, Mat* nonOccludedMask,
    155                              const Mat& leftUnknDispMask = Mat(), const Mat& rightUnknDispMask = Mat(),
    156                              float dispThresh = EVAL_DISP_THRESH )
    157 {
    158     if( !occludedMask && !nonOccludedMask )
    159         return;
    160     checkDispMapsAndUnknDispMasks( leftDisp, _rightDisp, leftUnknDispMask, rightUnknDispMask );
    161 
    162     Mat rightDisp;
    163     if( _rightDisp.empty() )
    164     {
    165         if( !rightUnknDispMask.empty() )
    166            CV_Error( Error::StsBadArg, "rightUnknDispMask must be empty if _rightDisp is empty" );
    167         rightDisp.create(leftDisp.size(), CV_32FC1);
    168         rightDisp.setTo(Scalar::all(0) );
    169         for( int leftY = 0; leftY < leftDisp.rows; leftY++ )
    170         {
    171             for( int leftX = 0; leftX < leftDisp.cols; leftX++ )
    172             {
    173                 if( !leftUnknDispMask.empty() && leftUnknDispMask.at<uchar>(leftY,leftX) )
    174                     continue;
    175                 float leftDispVal = leftDisp.at<float>(leftY, leftX);
    176                 int rightX = leftX - cvRound(leftDispVal), rightY = leftY;
    177                 if( rightX >= 0)
    178                     rightDisp.at<float>(rightY,rightX) = max(rightDisp.at<float>(rightY,rightX), leftDispVal);
    179             }
    180         }
    181     }
    182     else
    183         _rightDisp.copyTo(rightDisp);
    184 
    185     if( occludedMask )
    186     {
    187         occludedMask->create(leftDisp.size(), CV_8UC1);
    188         occludedMask->setTo(Scalar::all(0) );
    189     }
    190     if( nonOccludedMask )
    191     {
    192         nonOccludedMask->create(leftDisp.size(), CV_8UC1);
    193         nonOccludedMask->setTo(Scalar::all(0) );
    194     }
    195     for( int leftY = 0; leftY < leftDisp.rows; leftY++ )
    196     {
    197         for( int leftX = 0; leftX < leftDisp.cols; leftX++ )
    198         {
    199             if( !leftUnknDispMask.empty() && leftUnknDispMask.at<uchar>(leftY,leftX) )
    200                 continue;
    201             float leftDispVal = leftDisp.at<float>(leftY, leftX);
    202             int rightX = leftX - cvRound(leftDispVal), rightY = leftY;
    203             if( rightX < 0 && occludedMask )
    204                 occludedMask->at<uchar>(leftY, leftX) = 255;
    205             else
    206             {
    207                 if( !rightUnknDispMask.empty() && rightUnknDispMask.at<uchar>(rightY,rightX) )
    208                     continue;
    209                 float rightDispVal = rightDisp.at<float>(rightY, rightX);
    210                 if( rightDispVal > leftDispVal + dispThresh )
    211                 {
    212                     if( occludedMask )
    213                         occludedMask->at<uchar>(leftY, leftX) = 255;
    214                 }
    215                 else
    216                 {
    217                     if( nonOccludedMask )
    218                         nonOccludedMask->at<uchar>(leftY, leftX) = 255;
    219                 }
    220             }
    221         }
    222     }
    223 }
    224 
    225 /*
    226   Calculate depth discontinuty regions: pixels whose neiboring disparities differ by more than
    227   dispGap, dilated by window of width discontWidth.
    228 */
    229 void computeDepthDiscontMask( const Mat& disp, Mat& depthDiscontMask, const Mat& unknDispMask = Mat(),
    230                                  float dispGap = EVAL_DISP_GAP, int discontWidth = EVAL_DISCONT_WIDTH )
    231 {
    232     if( disp.empty() )
    233         CV_Error( Error::StsBadArg, "disp is empty" );
    234     if( disp.type() != CV_32FC1 )
    235         CV_Error( Error::StsBadArg, "disp must have CV_32FC1 type" );
    236     if( !unknDispMask.empty() )
    237         checkTypeAndSizeOfMask( unknDispMask, disp.size() );
    238 
    239     Mat curDisp; disp.copyTo( curDisp );
    240     if( !unknDispMask.empty() )
    241         curDisp.setTo( Scalar(numeric_limits<float>::min()), unknDispMask );
    242     Mat maxNeighbDisp; dilate( curDisp, maxNeighbDisp, Mat(3, 3, CV_8UC1, Scalar(1)) );
    243     if( !unknDispMask.empty() )
    244         curDisp.setTo( Scalar(numeric_limits<float>::max()), unknDispMask );
    245     Mat minNeighbDisp; erode( curDisp, minNeighbDisp, Mat(3, 3, CV_8UC1, Scalar(1)) );
    246     depthDiscontMask = max( (Mat)(maxNeighbDisp-disp), (Mat)(disp-minNeighbDisp) ) > dispGap;
    247     if( !unknDispMask.empty() )
    248         depthDiscontMask &= ~unknDispMask;
    249     dilate( depthDiscontMask, depthDiscontMask, Mat(discontWidth, discontWidth, CV_8UC1, Scalar(1)) );
    250 }
    251 
    252 /*
    253    Get evaluation masks excluding a border.
    254 */
    255 Mat getBorderedMask( Size maskSize, int border = EVAL_IGNORE_BORDER )
    256 {
    257     CV_Assert( border >= 0 );
    258     Mat mask(maskSize, CV_8UC1, Scalar(0));
    259     int w = maskSize.width - 2*border, h = maskSize.height - 2*border;
    260     if( w < 0 ||  h < 0 )
    261         mask.setTo(Scalar(0));
    262     else
    263         mask( Rect(Point(border,border),Size(w,h)) ).setTo(Scalar(255));
    264     return mask;
    265 }
    266 
    267 /*
    268   Calculate root-mean-squared error between the computed disparity map (computedDisp) and ground truth map (groundTruthDisp).
    269 */
    270 float dispRMS( const Mat& computedDisp, const Mat& groundTruthDisp, const Mat& mask )
    271 {
    272     checkTypeAndSizeOfDisp( groundTruthDisp, 0 );
    273     Size sz = groundTruthDisp.size();
    274     checkTypeAndSizeOfDisp( computedDisp, &sz );
    275 
    276     int pointsCount = sz.height*sz.width;
    277     if( !mask.empty() )
    278     {
    279         checkTypeAndSizeOfMask( mask, sz );
    280         pointsCount = countNonZero(mask);
    281     }
    282     return 1.f/sqrt((float)pointsCount) * (float)cvtest::norm(computedDisp, groundTruthDisp, NORM_L2, mask);
    283 }
    284 
    285 /*
    286   Calculate fraction of bad matching pixels.
    287 */
    288 float badMatchPxlsFraction( const Mat& computedDisp, const Mat& groundTruthDisp, const Mat& mask,
    289                             float _badThresh = EVAL_BAD_THRESH )
    290 {
    291     int badThresh = cvRound(_badThresh);
    292     checkTypeAndSizeOfDisp( groundTruthDisp, 0 );
    293     Size sz = groundTruthDisp.size();
    294     checkTypeAndSizeOfDisp( computedDisp, &sz );
    295 
    296     Mat badPxlsMap;
    297     absdiff( computedDisp, groundTruthDisp, badPxlsMap );
    298     badPxlsMap = badPxlsMap > badThresh;
    299     int pointsCount = sz.height*sz.width;
    300     if( !mask.empty() )
    301     {
    302         checkTypeAndSizeOfMask( mask, sz );
    303         badPxlsMap = badPxlsMap & mask;
    304         pointsCount = countNonZero(mask);
    305     }
    306     return 1.f/pointsCount * countNonZero(badPxlsMap);
    307 }
    308 
    309 //===================== regression test for stereo matching algorithms ==============================
    310 
    311 const string ALGORITHMS_DIR = "stereomatching/algorithms/";
    312 const string DATASETS_DIR = "stereomatching/datasets/";
    313 const string DATASETS_FILE = "datasets.xml";
    314 
    315 const string RUN_PARAMS_FILE = "_params.xml";
    316 const string RESULT_FILE = "_res.xml";
    317 
    318 const string LEFT_IMG_NAME = "im2.png";
    319 const string RIGHT_IMG_NAME = "im6.png";
    320 const string TRUE_LEFT_DISP_NAME = "disp2.png";
    321 const string TRUE_RIGHT_DISP_NAME = "disp6.png";
    322 
    323 string ERROR_PREFIXES[] = { "borderedAll",
    324                             "borderedNoOccl",
    325                             "borderedOccl",
    326                             "borderedTextured",
    327                             "borderedTextureless",
    328                             "borderedDepthDiscont" }; // size of ERROR_KINDS_COUNT
    329 
    330 
    331 const string RMS_STR = "RMS";
    332 const string BAD_PXLS_FRACTION_STR = "BadPxlsFraction";
    333 
    334 class QualityEvalParams
    335 {
    336 public:
    337     QualityEvalParams() { setDefaults(); }
    338     QualityEvalParams( int _ignoreBorder )
    339     {
    340         setDefaults();
    341         ignoreBorder = _ignoreBorder;
    342     }
    343     void setDefaults()
    344     {
    345         badThresh = EVAL_BAD_THRESH;
    346         texturelessWidth = EVAL_TEXTURELESS_WIDTH;
    347         texturelessThresh = EVAL_TEXTURELESS_THRESH;
    348         dispThresh = EVAL_DISP_THRESH;
    349         dispGap = EVAL_DISP_GAP;
    350         discontWidth = EVAL_DISCONT_WIDTH;
    351         ignoreBorder = EVAL_IGNORE_BORDER;
    352     }
    353     float badThresh;
    354     int texturelessWidth;
    355     float texturelessThresh;
    356     float dispThresh;
    357     float dispGap;
    358     int discontWidth;
    359     int ignoreBorder;
    360 };
    361 
    362 class CV_StereoMatchingTest : public cvtest::BaseTest
    363 {
    364 public:
    365     CV_StereoMatchingTest()
    366     { rmsEps.resize( ERROR_KINDS_COUNT, 0.01f );  fracEps.resize( ERROR_KINDS_COUNT, 1.e-6f ); }
    367 protected:
    368     // assumed that left image is a reference image
    369     virtual int runStereoMatchingAlgorithm( const Mat& leftImg, const Mat& rightImg,
    370                    Mat& leftDisp, Mat& rightDisp, int caseIdx ) = 0; // return ignored border width
    371 
    372     int readDatasetsParams( FileStorage& fs );
    373     virtual int readRunParams( FileStorage& fs );
    374     void writeErrors( const string& errName, const vector<float>& errors, FileStorage* fs = 0 );
    375     void readErrors( FileNode& fn, const string& errName, vector<float>& errors );
    376     int compareErrors( const vector<float>& calcErrors, const vector<float>& validErrors,
    377                        const vector<float>& eps, const string& errName );
    378     int processStereoMatchingResults( FileStorage& fs, int caseIdx, bool isWrite,
    379                   const Mat& leftImg, const Mat& rightImg,
    380                   const Mat& trueLeftDisp, const Mat& trueRightDisp,
    381                   const Mat& leftDisp, const Mat& rightDisp,
    382                   const QualityEvalParams& qualityEvalParams  );
    383     void run( int );
    384 
    385     vector<float> rmsEps;
    386     vector<float> fracEps;
    387 
    388     struct DatasetParams
    389     {
    390         int dispScaleFactor;
    391         int dispUnknVal;
    392     };
    393     map<string, DatasetParams> datasetsParams;
    394 
    395     vector<string> caseNames;
    396     vector<string> caseDatasets;
    397 };
    398 
    399 void CV_StereoMatchingTest::run(int)
    400 {
    401     string dataPath = ts->get_data_path() + "cv/";
    402     string algorithmName = name;
    403     assert( !algorithmName.empty() );
    404     if( dataPath.empty() )
    405     {
    406         ts->printf( cvtest::TS::LOG, "dataPath is empty" );
    407         ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ARG_CHECK );
    408         return;
    409     }
    410 
    411     FileStorage datasetsFS( dataPath + DATASETS_DIR + DATASETS_FILE, FileStorage::READ );
    412     int code = readDatasetsParams( datasetsFS );
    413     if( code != cvtest::TS::OK )
    414     {
    415         ts->set_failed_test_info( code );
    416         return;
    417     }
    418     FileStorage runParamsFS( dataPath + ALGORITHMS_DIR + algorithmName + RUN_PARAMS_FILE, FileStorage::READ );
    419     code = readRunParams( runParamsFS );
    420     if( code != cvtest::TS::OK )
    421     {
    422         ts->set_failed_test_info( code );
    423         return;
    424     }
    425 
    426     string fullResultFilename = dataPath + ALGORITHMS_DIR + algorithmName + RESULT_FILE;
    427     FileStorage resFS( fullResultFilename, FileStorage::READ );
    428     bool isWrite = true; // write or compare results
    429     if( resFS.isOpened() )
    430         isWrite = false;
    431     else
    432     {
    433         resFS.open( fullResultFilename, FileStorage::WRITE );
    434         if( !resFS.isOpened() )
    435         {
    436             ts->printf( cvtest::TS::LOG, "file %s can not be read or written\n", fullResultFilename.c_str() );
    437             ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ARG_CHECK );
    438             return;
    439         }
    440         resFS << "stereo_matching" << "{";
    441     }
    442 
    443     int progress = 0, caseCount = (int)caseNames.size();
    444     for( int ci = 0; ci < caseCount; ci++)
    445     {
    446         progress = update_progress( progress, ci, caseCount, 0 );
    447         printf("progress: %d%%\n", progress);
    448         fflush(stdout);
    449         string datasetName = caseDatasets[ci];
    450         string datasetFullDirName = dataPath + DATASETS_DIR + datasetName + "/";
    451         Mat leftImg = imread(datasetFullDirName + LEFT_IMG_NAME);
    452         Mat rightImg = imread(datasetFullDirName + RIGHT_IMG_NAME);
    453         Mat trueLeftDisp = imread(datasetFullDirName + TRUE_LEFT_DISP_NAME, 0);
    454         Mat trueRightDisp = imread(datasetFullDirName + TRUE_RIGHT_DISP_NAME, 0);
    455 
    456         if( leftImg.empty() || rightImg.empty() || trueLeftDisp.empty() )
    457         {
    458             ts->printf( cvtest::TS::LOG, "images or left ground-truth disparities of dataset %s can not be read", datasetName.c_str() );
    459             code = cvtest::TS::FAIL_INVALID_TEST_DATA;
    460             continue;
    461         }
    462         int dispScaleFactor = datasetsParams[datasetName].dispScaleFactor;
    463         Mat tmp;
    464 
    465         trueLeftDisp.convertTo( tmp, CV_32FC1, 1.f/dispScaleFactor );
    466         trueLeftDisp = tmp;
    467         tmp.release();
    468 
    469         if( !trueRightDisp.empty() )
    470         {
    471             trueRightDisp.convertTo( tmp, CV_32FC1, 1.f/dispScaleFactor );
    472             trueRightDisp = tmp;
    473             tmp.release();
    474         }
    475 
    476         Mat leftDisp, rightDisp;
    477         int ignBorder = max(runStereoMatchingAlgorithm(leftImg, rightImg, leftDisp, rightDisp, ci), EVAL_IGNORE_BORDER);
    478 
    479         leftDisp.convertTo( tmp, CV_32FC1 );
    480         leftDisp = tmp;
    481         tmp.release();
    482 
    483         rightDisp.convertTo( tmp, CV_32FC1 );
    484         rightDisp = tmp;
    485         tmp.release();
    486 
    487         int tempCode = processStereoMatchingResults( resFS, ci, isWrite,
    488                    leftImg, rightImg, trueLeftDisp, trueRightDisp, leftDisp, rightDisp, QualityEvalParams(ignBorder));
    489         code = tempCode==cvtest::TS::OK ? code : tempCode;
    490     }
    491 
    492     if( isWrite )
    493         resFS << "}"; // "stereo_matching"
    494 
    495     ts->set_failed_test_info( code );
    496 }
    497 
    498 void calcErrors( const Mat& leftImg, const Mat& /*rightImg*/,
    499                  const Mat& trueLeftDisp, const Mat& trueRightDisp,
    500                  const Mat& trueLeftUnknDispMask, const Mat& trueRightUnknDispMask,
    501                  const Mat& calcLeftDisp, const Mat& /*calcRightDisp*/,
    502                  vector<float>& rms, vector<float>& badPxlsFractions,
    503                  const QualityEvalParams& qualityEvalParams )
    504 {
    505     Mat texturelessMask, texturedMask;
    506     computeTextureBasedMasks( leftImg, &texturelessMask, &texturedMask,
    507                               qualityEvalParams.texturelessWidth, qualityEvalParams.texturelessThresh );
    508     Mat occludedMask, nonOccludedMask;
    509     computeOcclusionBasedMasks( trueLeftDisp, trueRightDisp, &occludedMask, &nonOccludedMask,
    510                                 trueLeftUnknDispMask, trueRightUnknDispMask, qualityEvalParams.dispThresh);
    511     Mat depthDiscontMask;
    512     computeDepthDiscontMask( trueLeftDisp, depthDiscontMask, trueLeftUnknDispMask,
    513                              qualityEvalParams.dispGap, qualityEvalParams.discontWidth);
    514 
    515     Mat borderedKnownMask = getBorderedMask( leftImg.size(), qualityEvalParams.ignoreBorder ) & ~trueLeftUnknDispMask;
    516 
    517     nonOccludedMask &= borderedKnownMask;
    518     occludedMask &= borderedKnownMask;
    519     texturedMask &= nonOccludedMask; // & borderedKnownMask
    520     texturelessMask &= nonOccludedMask; // & borderedKnownMask
    521     depthDiscontMask &= nonOccludedMask; // & borderedKnownMask
    522 
    523     rms.resize(ERROR_KINDS_COUNT);
    524     rms[0] = dispRMS( calcLeftDisp, trueLeftDisp, borderedKnownMask );
    525     rms[1] = dispRMS( calcLeftDisp, trueLeftDisp, nonOccludedMask );
    526     rms[2] = dispRMS( calcLeftDisp, trueLeftDisp, occludedMask );
    527     rms[3] = dispRMS( calcLeftDisp, trueLeftDisp, texturedMask );
    528     rms[4] = dispRMS( calcLeftDisp, trueLeftDisp, texturelessMask );
    529     rms[5] = dispRMS( calcLeftDisp, trueLeftDisp, depthDiscontMask );
    530 
    531     badPxlsFractions.resize(ERROR_KINDS_COUNT);
    532     badPxlsFractions[0] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, borderedKnownMask, qualityEvalParams.badThresh );
    533     badPxlsFractions[1] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, nonOccludedMask, qualityEvalParams.badThresh );
    534     badPxlsFractions[2] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, occludedMask, qualityEvalParams.badThresh );
    535     badPxlsFractions[3] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, texturedMask, qualityEvalParams.badThresh );
    536     badPxlsFractions[4] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, texturelessMask, qualityEvalParams.badThresh );
    537     badPxlsFractions[5] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, depthDiscontMask, qualityEvalParams.badThresh );
    538 }
    539 
    540 int CV_StereoMatchingTest::processStereoMatchingResults( FileStorage& fs, int caseIdx, bool isWrite,
    541               const Mat& leftImg, const Mat& rightImg,
    542               const Mat& trueLeftDisp, const Mat& trueRightDisp,
    543               const Mat& leftDisp, const Mat& rightDisp,
    544               const QualityEvalParams& qualityEvalParams )
    545 {
    546     // rightDisp is not used in current test virsion
    547     int code = cvtest::TS::OK;
    548     assert( fs.isOpened() );
    549     assert( trueLeftDisp.type() == CV_32FC1 );
    550     assert( trueRightDisp.empty() || trueRightDisp.type() == CV_32FC1 );
    551     assert( leftDisp.type() == CV_32FC1 && rightDisp.type() == CV_32FC1 );
    552 
    553     // get masks for unknown ground truth disparity values
    554     Mat leftUnknMask, rightUnknMask;
    555     DatasetParams params = datasetsParams[caseDatasets[caseIdx]];
    556     absdiff( trueLeftDisp, Scalar(params.dispUnknVal), leftUnknMask );
    557     leftUnknMask = leftUnknMask < numeric_limits<float>::epsilon();
    558     assert(leftUnknMask.type() == CV_8UC1);
    559     if( !trueRightDisp.empty() )
    560     {
    561         absdiff( trueRightDisp, Scalar(params.dispUnknVal), rightUnknMask );
    562         rightUnknMask = rightUnknMask < numeric_limits<float>::epsilon();
    563         assert(leftUnknMask.type() == CV_8UC1);
    564     }
    565 
    566     // calculate errors
    567     vector<float> rmss, badPxlsFractions;
    568     calcErrors( leftImg, rightImg, trueLeftDisp, trueRightDisp, leftUnknMask, rightUnknMask,
    569                 leftDisp, rightDisp, rmss, badPxlsFractions, qualityEvalParams );
    570 
    571     if( isWrite )
    572     {
    573         fs << caseNames[caseIdx] << "{";
    574         //cvWriteComment( fs.fs, RMS_STR.c_str(), 0 );
    575         writeErrors( RMS_STR, rmss, &fs );
    576         //cvWriteComment( fs.fs, BAD_PXLS_FRACTION_STR.c_str(), 0 );
    577         writeErrors( BAD_PXLS_FRACTION_STR, badPxlsFractions, &fs );
    578         fs << "}"; // datasetName
    579     }
    580     else // compare
    581     {
    582         ts->printf( cvtest::TS::LOG, "\nquality of case named %s\n", caseNames[caseIdx].c_str() );
    583         ts->printf( cvtest::TS::LOG, "%s\n", RMS_STR.c_str() );
    584         writeErrors( RMS_STR, rmss );
    585         ts->printf( cvtest::TS::LOG, "%s\n", BAD_PXLS_FRACTION_STR.c_str() );
    586         writeErrors( BAD_PXLS_FRACTION_STR, badPxlsFractions );
    587 
    588         FileNode fn = fs.getFirstTopLevelNode()[caseNames[caseIdx]];
    589         vector<float> validRmss, validBadPxlsFractions;
    590 
    591         readErrors( fn, RMS_STR, validRmss );
    592         readErrors( fn, BAD_PXLS_FRACTION_STR, validBadPxlsFractions );
    593         int tempCode = compareErrors( rmss, validRmss, rmsEps, RMS_STR );
    594         code = tempCode==cvtest::TS::OK ? code : tempCode;
    595         tempCode = compareErrors( badPxlsFractions, validBadPxlsFractions, fracEps, BAD_PXLS_FRACTION_STR );
    596         code = tempCode==cvtest::TS::OK ? code : tempCode;
    597     }
    598     return code;
    599 }
    600 
    601 int CV_StereoMatchingTest::readDatasetsParams( FileStorage& fs )
    602 {
    603     if( !fs.isOpened() )
    604     {
    605         ts->printf( cvtest::TS::LOG, "datasetsParams can not be read " );
    606         return cvtest::TS::FAIL_INVALID_TEST_DATA;
    607     }
    608     datasetsParams.clear();
    609     FileNode fn = fs.getFirstTopLevelNode();
    610     assert(fn.isSeq());
    611     for( int i = 0; i < (int)fn.size(); i+=3 )
    612     {
    613         String _name = fn[i];
    614         DatasetParams params;
    615         String sf = fn[i+1]; params.dispScaleFactor = atoi(sf.c_str());
    616         String uv = fn[i+2]; params.dispUnknVal = atoi(uv.c_str());
    617         datasetsParams[_name] = params;
    618     }
    619     return cvtest::TS::OK;
    620 }
    621 
    622 int CV_StereoMatchingTest::readRunParams( FileStorage& fs )
    623 {
    624     if( !fs.isOpened() )
    625     {
    626         ts->printf( cvtest::TS::LOG, "runParams can not be read " );
    627         return cvtest::TS::FAIL_INVALID_TEST_DATA;
    628     }
    629     caseNames.clear();;
    630     caseDatasets.clear();
    631     return cvtest::TS::OK;
    632 }
    633 
    634 void CV_StereoMatchingTest::writeErrors( const string& errName, const vector<float>& errors, FileStorage* fs )
    635 {
    636     assert( (int)errors.size() == ERROR_KINDS_COUNT );
    637     vector<float>::const_iterator it = errors.begin();
    638     if( fs )
    639         for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++it )
    640             *fs << ERROR_PREFIXES[i] + errName << *it;
    641     else
    642         for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++it )
    643             ts->printf( cvtest::TS::LOG, "%s = %f\n", string(ERROR_PREFIXES[i]+errName).c_str(), *it );
    644 }
    645 
    646 void CV_StereoMatchingTest::readErrors( FileNode& fn, const string& errName, vector<float>& errors )
    647 {
    648     errors.resize( ERROR_KINDS_COUNT );
    649     vector<float>::iterator it = errors.begin();
    650     for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++it )
    651         fn[ERROR_PREFIXES[i]+errName] >> *it;
    652 }
    653 
    654 int CV_StereoMatchingTest::compareErrors( const vector<float>& calcErrors, const vector<float>& validErrors,
    655                    const vector<float>& eps, const string& errName )
    656 {
    657     assert( (int)calcErrors.size() == ERROR_KINDS_COUNT );
    658     assert( (int)validErrors.size() == ERROR_KINDS_COUNT );
    659     assert( (int)eps.size() == ERROR_KINDS_COUNT );
    660     vector<float>::const_iterator calcIt = calcErrors.begin(),
    661                                   validIt = validErrors.begin(),
    662                                   epsIt = eps.begin();
    663     bool ok = true;
    664     for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++calcIt, ++validIt, ++epsIt )
    665         if( *calcIt - *validIt > *epsIt )
    666         {
    667             ts->printf( cvtest::TS::LOG, "bad accuracy of %s (valid=%f; calc=%f)\n", string(ERROR_PREFIXES[i]+errName).c_str(), *validIt, *calcIt );
    668             ok = false;
    669         }
    670     return ok ? cvtest::TS::OK : cvtest::TS::FAIL_BAD_ACCURACY;
    671 }
    672 
    673 //----------------------------------- StereoBM test -----------------------------------------------------
    674 
    675 class CV_StereoBMTest : public CV_StereoMatchingTest
    676 {
    677 public:
    678     CV_StereoBMTest()
    679     {
    680         name = "stereobm";
    681         fill(rmsEps.begin(), rmsEps.end(), 0.4f);
    682         fill(fracEps.begin(), fracEps.end(), 0.022f);
    683     }
    684 
    685 protected:
    686     struct RunParams
    687     {
    688         int ndisp;
    689         int winSize;
    690     };
    691     vector<RunParams> caseRunParams;
    692 
    693     virtual int readRunParams( FileStorage& fs )
    694     {
    695         int code = CV_StereoMatchingTest::readRunParams( fs );
    696         FileNode fn = fs.getFirstTopLevelNode();
    697         assert(fn.isSeq());
    698         for( int i = 0; i < (int)fn.size(); i+=4 )
    699         {
    700             String caseName = fn[i], datasetName = fn[i+1];
    701             RunParams params;
    702             String ndisp = fn[i+2]; params.ndisp = atoi(ndisp.c_str());
    703             String winSize = fn[i+3]; params.winSize = atoi(winSize.c_str());
    704             caseNames.push_back( caseName );
    705             caseDatasets.push_back( datasetName );
    706             caseRunParams.push_back( params );
    707         }
    708         return code;
    709     }
    710 
    711     virtual int runStereoMatchingAlgorithm( const Mat& _leftImg, const Mat& _rightImg,
    712                    Mat& leftDisp, Mat& /*rightDisp*/, int caseIdx )
    713     {
    714         RunParams params = caseRunParams[caseIdx];
    715         assert( params.ndisp%16 == 0 );
    716         assert( _leftImg.type() == CV_8UC3 && _rightImg.type() == CV_8UC3 );
    717         Mat leftImg; cvtColor( _leftImg, leftImg, COLOR_BGR2GRAY );
    718         Mat rightImg; cvtColor( _rightImg, rightImg, COLOR_BGR2GRAY );
    719 
    720         Ptr<StereoBM> bm = StereoBM::create( params.ndisp, params.winSize );
    721         Mat tempDisp;
    722         bm->compute( leftImg, rightImg, tempDisp );
    723         tempDisp.convertTo(leftDisp, CV_32F, 1./StereoMatcher::DISP_SCALE);
    724         return params.winSize/2;
    725     }
    726 };
    727 
    728 //----------------------------------- StereoSGBM test -----------------------------------------------------
    729 
    730 class CV_StereoSGBMTest : public CV_StereoMatchingTest
    731 {
    732 public:
    733     CV_StereoSGBMTest()
    734     {
    735         name = "stereosgbm";
    736         fill(rmsEps.begin(), rmsEps.end(), 0.25f);
    737         fill(fracEps.begin(), fracEps.end(), 0.01f);
    738     }
    739 
    740 protected:
    741     struct RunParams
    742     {
    743         int ndisp;
    744         int winSize;
    745         bool fullDP;
    746     };
    747     vector<RunParams> caseRunParams;
    748 
    749     virtual int readRunParams( FileStorage& fs )
    750     {
    751         int code = CV_StereoMatchingTest::readRunParams(fs);
    752         FileNode fn = fs.getFirstTopLevelNode();
    753         assert(fn.isSeq());
    754         for( int i = 0; i < (int)fn.size(); i+=5 )
    755         {
    756             String caseName = fn[i], datasetName = fn[i+1];
    757             RunParams params;
    758             String ndisp = fn[i+2]; params.ndisp = atoi(ndisp.c_str());
    759             String winSize = fn[i+3]; params.winSize = atoi(winSize.c_str());
    760             String fullDP = fn[i+4]; params.fullDP = atoi(fullDP.c_str()) == 0 ? false : true;
    761             caseNames.push_back( caseName );
    762             caseDatasets.push_back( datasetName );
    763             caseRunParams.push_back( params );
    764         }
    765         return code;
    766     }
    767 
    768     virtual int runStereoMatchingAlgorithm( const Mat& leftImg, const Mat& rightImg,
    769                    Mat& leftDisp, Mat& /*rightDisp*/, int caseIdx )
    770     {
    771         RunParams params = caseRunParams[caseIdx];
    772         assert( params.ndisp%16 == 0 );
    773         Ptr<StereoSGBM> sgbm = StereoSGBM::create( 0, params.ndisp, params.winSize,
    774                                                  10*params.winSize*params.winSize,
    775                                                  40*params.winSize*params.winSize,
    776                                                  1, 63, 10, 100, 32, params.fullDP ?
    777                                                  StereoSGBM::MODE_HH : StereoSGBM::MODE_SGBM );
    778         sgbm->compute( leftImg, rightImg, leftDisp );
    779         CV_Assert( leftDisp.type() == CV_16SC1 );
    780         leftDisp/=16;
    781         return 0;
    782     }
    783 };
    784 
    785 
    786 TEST(Calib3d_StereoBM, regression) { CV_StereoBMTest test; test.safe_run(); }
    787 TEST(Calib3d_StereoSGBM, regression) { CV_StereoSGBMTest test; test.safe_run(); }
    788