Path: blob/master/modules/calib3d/test/test_stereomatching.cpp
16337 views
/*M///////////////////////////////////////////////////////////////////////////////////////1//2// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.3//4// By downloading, copying, installing or using the software you agree to this license.5// If you do not agree to this license, do not download, install,6// copy or use the software.7//8//9// Intel License Agreement10// For Open Source Computer Vision Library11//12// Copyright (C) 2000, Intel Corporation, all rights reserved.13// Third party copyrights are property of their respective owners.14//15// Redistribution and use in source and binary forms, with or without modification,16// are permitted provided that the following conditions are met:17//18// * Redistribution's of source code must retain the above copyright notice,19// this list of conditions and the following disclaimer.20//21// * Redistribution's in binary form must reproduce the above copyright notice,22// this list of conditions and the following disclaimer in the documentation23// and/or other materials provided with the distribution.24//25// * The name of Intel Corporation may not be used to endorse or promote products26// derived from this software without specific prior written permission.27//28// This software is provided by the copyright holders and contributors "as is" and29// any express or implied warranties, including, but not limited to, the implied30// warranties of merchantability and fitness for a particular purpose are disclaimed.31// In no event shall the Intel Corporation or contributors be liable for any direct,32// indirect, incidental, special, exemplary, or consequential damages33// (including, but not limited to, procurement of substitute goods or services;34// loss of use, data, or profits; or business interruption) however caused35// and on any theory of liability, whether in contract, strict liability,36// or tort (including negligence or otherwise) arising in any way out of37// the use of this software, even if advised of the possibility of such damage.38//39//M*/4041/*42This is a regression test for stereo matching algorithms. This test gets some quality metrics43described in "A Taxonomy and Evaluation of Dense Two-Frame Stereo Correspondence Algorithms".44Daniel Scharstein, Richard Szeliski45*/4647#include "test_precomp.hpp"4849namespace opencv_test { namespace {5051const float EVAL_BAD_THRESH = 1.f;52const int EVAL_TEXTURELESS_WIDTH = 3;53const float EVAL_TEXTURELESS_THRESH = 4.f;54const float EVAL_DISP_THRESH = 1.f;55const float EVAL_DISP_GAP = 2.f;56const int EVAL_DISCONT_WIDTH = 9;57const int EVAL_IGNORE_BORDER = 10;5859const int ERROR_KINDS_COUNT = 6;6061//============================== quality measuring functions =================================================6263/*64Calculate textureless regions of image (regions where the squared horizontal intensity gradient averaged over65a square window of size=evalTexturelessWidth is below a threshold=evalTexturelessThresh) and textured regions.66*/67void computeTextureBasedMasks( const Mat& _img, Mat* texturelessMask, Mat* texturedMask,68int texturelessWidth = EVAL_TEXTURELESS_WIDTH, float texturelessThresh = EVAL_TEXTURELESS_THRESH )69{70if( !texturelessMask && !texturedMask )71return;72if( _img.empty() )73CV_Error( Error::StsBadArg, "img is empty" );7475Mat img = _img;76if( _img.channels() > 1)77{78Mat tmp; cvtColor( _img, tmp, COLOR_BGR2GRAY ); img = tmp;79}80Mat dxI; Sobel( img, dxI, CV_32FC1, 1, 0, 3 );81Mat dxI2; pow( dxI / 8.f/*normalize*/, 2, dxI2 );82Mat avgDxI2; boxFilter( dxI2, avgDxI2, CV_32FC1, Size(texturelessWidth,texturelessWidth) );8384if( texturelessMask )85*texturelessMask = avgDxI2 < texturelessThresh;86if( texturedMask )87*texturedMask = avgDxI2 >= texturelessThresh;88}8990void checkTypeAndSizeOfDisp( const Mat& dispMap, const Size* sz )91{92if( dispMap.empty() )93CV_Error( Error::StsBadArg, "dispMap is empty" );94if( dispMap.type() != CV_32FC1 )95CV_Error( Error::StsBadArg, "dispMap must have CV_32FC1 type" );96if( sz && (dispMap.rows != sz->height || dispMap.cols != sz->width) )97CV_Error( Error::StsBadArg, "dispMap has incorrect size" );98}99100void checkTypeAndSizeOfMask( const Mat& mask, Size sz )101{102if( mask.empty() )103CV_Error( Error::StsBadArg, "mask is empty" );104if( mask.type() != CV_8UC1 )105CV_Error( Error::StsBadArg, "mask must have CV_8UC1 type" );106if( mask.rows != sz.height || mask.cols != sz.width )107CV_Error( Error::StsBadArg, "mask has incorrect size" );108}109110void checkDispMapsAndUnknDispMasks( const Mat& leftDispMap, const Mat& rightDispMap,111const Mat& leftUnknDispMask, const Mat& rightUnknDispMask )112{113// check type and size of disparity maps114checkTypeAndSizeOfDisp( leftDispMap, 0 );115if( !rightDispMap.empty() )116{117Size sz = leftDispMap.size();118checkTypeAndSizeOfDisp( rightDispMap, &sz );119}120121// check size and type of unknown disparity maps122if( !leftUnknDispMask.empty() )123checkTypeAndSizeOfMask( leftUnknDispMask, leftDispMap.size() );124if( !rightUnknDispMask.empty() )125checkTypeAndSizeOfMask( rightUnknDispMask, rightDispMap.size() );126127// check values of disparity maps (known disparity values musy be positive)128double leftMinVal = 0, rightMinVal = 0;129if( leftUnknDispMask.empty() )130minMaxLoc( leftDispMap, &leftMinVal );131else132minMaxLoc( leftDispMap, &leftMinVal, 0, 0, 0, ~leftUnknDispMask );133if( !rightDispMap.empty() )134{135if( rightUnknDispMask.empty() )136minMaxLoc( rightDispMap, &rightMinVal );137else138minMaxLoc( rightDispMap, &rightMinVal, 0, 0, 0, ~rightUnknDispMask );139}140if( leftMinVal < 0 || rightMinVal < 0)141CV_Error( Error::StsBadArg, "known disparity values must be positive" );142}143144/*145Calculate occluded regions of reference image (left image) (regions that are occluded in the matching image (right image),146i.e., where the forward-mapped disparity lands at a location with a larger (nearer) disparity) and non occluded regions.147*/148void computeOcclusionBasedMasks( const Mat& leftDisp, const Mat& _rightDisp,149Mat* occludedMask, Mat* nonOccludedMask,150const Mat& leftUnknDispMask = Mat(), const Mat& rightUnknDispMask = Mat(),151float dispThresh = EVAL_DISP_THRESH )152{153if( !occludedMask && !nonOccludedMask )154return;155checkDispMapsAndUnknDispMasks( leftDisp, _rightDisp, leftUnknDispMask, rightUnknDispMask );156157Mat rightDisp;158if( _rightDisp.empty() )159{160if( !rightUnknDispMask.empty() )161CV_Error( Error::StsBadArg, "rightUnknDispMask must be empty if _rightDisp is empty" );162rightDisp.create(leftDisp.size(), CV_32FC1);163rightDisp.setTo(Scalar::all(0) );164for( int leftY = 0; leftY < leftDisp.rows; leftY++ )165{166for( int leftX = 0; leftX < leftDisp.cols; leftX++ )167{168if( !leftUnknDispMask.empty() && leftUnknDispMask.at<uchar>(leftY,leftX) )169continue;170float leftDispVal = leftDisp.at<float>(leftY, leftX);171int rightX = leftX - cvRound(leftDispVal), rightY = leftY;172if( rightX >= 0)173rightDisp.at<float>(rightY,rightX) = max(rightDisp.at<float>(rightY,rightX), leftDispVal);174}175}176}177else178_rightDisp.copyTo(rightDisp);179180if( occludedMask )181{182occludedMask->create(leftDisp.size(), CV_8UC1);183occludedMask->setTo(Scalar::all(0) );184}185if( nonOccludedMask )186{187nonOccludedMask->create(leftDisp.size(), CV_8UC1);188nonOccludedMask->setTo(Scalar::all(0) );189}190for( int leftY = 0; leftY < leftDisp.rows; leftY++ )191{192for( int leftX = 0; leftX < leftDisp.cols; leftX++ )193{194if( !leftUnknDispMask.empty() && leftUnknDispMask.at<uchar>(leftY,leftX) )195continue;196float leftDispVal = leftDisp.at<float>(leftY, leftX);197int rightX = leftX - cvRound(leftDispVal), rightY = leftY;198if( rightX < 0 && occludedMask )199occludedMask->at<uchar>(leftY, leftX) = 255;200else201{202if( !rightUnknDispMask.empty() && rightUnknDispMask.at<uchar>(rightY,rightX) )203continue;204float rightDispVal = rightDisp.at<float>(rightY, rightX);205if( rightDispVal > leftDispVal + dispThresh )206{207if( occludedMask )208occludedMask->at<uchar>(leftY, leftX) = 255;209}210else211{212if( nonOccludedMask )213nonOccludedMask->at<uchar>(leftY, leftX) = 255;214}215}216}217}218}219220/*221Calculate depth discontinuty regions: pixels whose neiboring disparities differ by more than222dispGap, dilated by window of width discontWidth.223*/224void computeDepthDiscontMask( const Mat& disp, Mat& depthDiscontMask, const Mat& unknDispMask = Mat(),225float dispGap = EVAL_DISP_GAP, int discontWidth = EVAL_DISCONT_WIDTH )226{227if( disp.empty() )228CV_Error( Error::StsBadArg, "disp is empty" );229if( disp.type() != CV_32FC1 )230CV_Error( Error::StsBadArg, "disp must have CV_32FC1 type" );231if( !unknDispMask.empty() )232checkTypeAndSizeOfMask( unknDispMask, disp.size() );233234Mat curDisp; disp.copyTo( curDisp );235if( !unknDispMask.empty() )236curDisp.setTo( Scalar(std::numeric_limits<float>::min()), unknDispMask );237Mat maxNeighbDisp; dilate( curDisp, maxNeighbDisp, Mat(3, 3, CV_8UC1, Scalar(1)) );238if( !unknDispMask.empty() )239curDisp.setTo( Scalar(std::numeric_limits<float>::max()), unknDispMask );240Mat minNeighbDisp; erode( curDisp, minNeighbDisp, Mat(3, 3, CV_8UC1, Scalar(1)) );241depthDiscontMask = max( (Mat)(maxNeighbDisp-disp), (Mat)(disp-minNeighbDisp) ) > dispGap;242if( !unknDispMask.empty() )243depthDiscontMask &= ~unknDispMask;244dilate( depthDiscontMask, depthDiscontMask, Mat(discontWidth, discontWidth, CV_8UC1, Scalar(1)) );245}246247/*248Get evaluation masks excluding a border.249*/250Mat getBorderedMask( Size maskSize, int border = EVAL_IGNORE_BORDER )251{252CV_Assert( border >= 0 );253Mat mask(maskSize, CV_8UC1, Scalar(0));254int w = maskSize.width - 2*border, h = maskSize.height - 2*border;255if( w < 0 || h < 0 )256mask.setTo(Scalar(0));257else258mask( Rect(Point(border,border),Size(w,h)) ).setTo(Scalar(255));259return mask;260}261262/*263Calculate root-mean-squared error between the computed disparity map (computedDisp) and ground truth map (groundTruthDisp).264*/265float dispRMS( const Mat& computedDisp, const Mat& groundTruthDisp, const Mat& mask )266{267checkTypeAndSizeOfDisp( groundTruthDisp, 0 );268Size sz = groundTruthDisp.size();269checkTypeAndSizeOfDisp( computedDisp, &sz );270271int pointsCount = sz.height*sz.width;272if( !mask.empty() )273{274checkTypeAndSizeOfMask( mask, sz );275pointsCount = countNonZero(mask);276}277return 1.f/sqrt((float)pointsCount) * (float)cvtest::norm(computedDisp, groundTruthDisp, NORM_L2, mask);278}279280/*281Calculate fraction of bad matching pixels.282*/283float badMatchPxlsFraction( const Mat& computedDisp, const Mat& groundTruthDisp, const Mat& mask,284float _badThresh = EVAL_BAD_THRESH )285{286int badThresh = cvRound(_badThresh);287checkTypeAndSizeOfDisp( groundTruthDisp, 0 );288Size sz = groundTruthDisp.size();289checkTypeAndSizeOfDisp( computedDisp, &sz );290291Mat badPxlsMap;292absdiff( computedDisp, groundTruthDisp, badPxlsMap );293badPxlsMap = badPxlsMap > badThresh;294int pointsCount = sz.height*sz.width;295if( !mask.empty() )296{297checkTypeAndSizeOfMask( mask, sz );298badPxlsMap = badPxlsMap & mask;299pointsCount = countNonZero(mask);300}301return 1.f/pointsCount * countNonZero(badPxlsMap);302}303304//===================== regression test for stereo matching algorithms ==============================305306const string ALGORITHMS_DIR = "stereomatching/algorithms/";307const string DATASETS_DIR = "stereomatching/datasets/";308const string DATASETS_FILE = "datasets.xml";309310const string RUN_PARAMS_FILE = "_params.xml";311const string RESULT_FILE = "_res.xml";312313const string LEFT_IMG_NAME = "im2.png";314const string RIGHT_IMG_NAME = "im6.png";315const string TRUE_LEFT_DISP_NAME = "disp2.png";316const string TRUE_RIGHT_DISP_NAME = "disp6.png";317318string ERROR_PREFIXES[] = { "borderedAll",319"borderedNoOccl",320"borderedOccl",321"borderedTextured",322"borderedTextureless",323"borderedDepthDiscont" }; // size of ERROR_KINDS_COUNT324325string ROI_PREFIXES[] = { "roiX",326"roiY",327"roiWidth",328"roiHeight" };329330331const string RMS_STR = "RMS";332const string BAD_PXLS_FRACTION_STR = "BadPxlsFraction";333const string ROI_STR = "ValidDisparityROI";334335class QualityEvalParams336{337public:338QualityEvalParams() { setDefaults(); }339QualityEvalParams( int _ignoreBorder )340{341setDefaults();342ignoreBorder = _ignoreBorder;343}344void setDefaults()345{346badThresh = EVAL_BAD_THRESH;347texturelessWidth = EVAL_TEXTURELESS_WIDTH;348texturelessThresh = EVAL_TEXTURELESS_THRESH;349dispThresh = EVAL_DISP_THRESH;350dispGap = EVAL_DISP_GAP;351discontWidth = EVAL_DISCONT_WIDTH;352ignoreBorder = EVAL_IGNORE_BORDER;353}354float badThresh;355int texturelessWidth;356float texturelessThresh;357float dispThresh;358float dispGap;359int discontWidth;360int ignoreBorder;361};362363class CV_StereoMatchingTest : public cvtest::BaseTest364{365public:366CV_StereoMatchingTest()367{ rmsEps.resize( ERROR_KINDS_COUNT, 0.01f ); fracEps.resize( ERROR_KINDS_COUNT, 1.e-6f ); }368protected:369// assumed that left image is a reference image370virtual int runStereoMatchingAlgorithm( const Mat& leftImg, const Mat& rightImg,371Rect& calcROI, Mat& leftDisp, Mat& rightDisp, int caseIdx ) = 0; // return ignored border width372373int readDatasetsParams( FileStorage& fs );374virtual int readRunParams( FileStorage& fs );375void writeErrors( const string& errName, const vector<float>& errors, FileStorage* fs = 0 );376void writeROI( const Rect& calcROI, FileStorage* fs = 0 );377void readErrors( FileNode& fn, const string& errName, vector<float>& errors );378void readROI( FileNode& fn, Rect& trueROI );379int compareErrors( const vector<float>& calcErrors, const vector<float>& validErrors,380const vector<float>& eps, const string& errName );381int compareROI( const Rect& calcROI, const Rect& validROI );382int processStereoMatchingResults( FileStorage& fs, int caseIdx, bool isWrite,383const Mat& leftImg, const Mat& rightImg,384const Rect& calcROI,385const Mat& trueLeftDisp, const Mat& trueRightDisp,386const Mat& leftDisp, const Mat& rightDisp,387const QualityEvalParams& qualityEvalParams );388void run( int );389390vector<float> rmsEps;391vector<float> fracEps;392393struct DatasetParams394{395int dispScaleFactor;396int dispUnknVal;397};398map<string, DatasetParams> datasetsParams;399400vector<string> caseNames;401vector<string> caseDatasets;402};403404void CV_StereoMatchingTest::run(int)405{406string dataPath = ts->get_data_path() + "cv/";407string algorithmName = name;408assert( !algorithmName.empty() );409if( dataPath.empty() )410{411ts->printf( cvtest::TS::LOG, "dataPath is empty" );412ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ARG_CHECK );413return;414}415416FileStorage datasetsFS( dataPath + DATASETS_DIR + DATASETS_FILE, FileStorage::READ );417int code = readDatasetsParams( datasetsFS );418if( code != cvtest::TS::OK )419{420ts->set_failed_test_info( code );421return;422}423FileStorage runParamsFS( dataPath + ALGORITHMS_DIR + algorithmName + RUN_PARAMS_FILE, FileStorage::READ );424code = readRunParams( runParamsFS );425if( code != cvtest::TS::OK )426{427ts->set_failed_test_info( code );428return;429}430431string fullResultFilename = dataPath + ALGORITHMS_DIR + algorithmName + RESULT_FILE;432FileStorage resFS( fullResultFilename, FileStorage::READ );433bool isWrite = true; // write or compare results434if( resFS.isOpened() )435isWrite = false;436else437{438resFS.open( fullResultFilename, FileStorage::WRITE );439if( !resFS.isOpened() )440{441ts->printf( cvtest::TS::LOG, "file %s can not be read or written\n", fullResultFilename.c_str() );442ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ARG_CHECK );443return;444}445resFS << "stereo_matching" << "{";446}447448int progress = 0, caseCount = (int)caseNames.size();449for( int ci = 0; ci < caseCount; ci++)450{451progress = update_progress( progress, ci, caseCount, 0 );452printf("progress: %d%%\n", progress);453fflush(stdout);454string datasetName = caseDatasets[ci];455string datasetFullDirName = dataPath + DATASETS_DIR + datasetName + "/";456Mat leftImg = imread(datasetFullDirName + LEFT_IMG_NAME);457Mat rightImg = imread(datasetFullDirName + RIGHT_IMG_NAME);458Mat trueLeftDisp = imread(datasetFullDirName + TRUE_LEFT_DISP_NAME, 0);459Mat trueRightDisp = imread(datasetFullDirName + TRUE_RIGHT_DISP_NAME, 0);460Rect calcROI;461462if( leftImg.empty() || rightImg.empty() || trueLeftDisp.empty() )463{464ts->printf( cvtest::TS::LOG, "images or left ground-truth disparities of dataset %s can not be read", datasetName.c_str() );465code = cvtest::TS::FAIL_INVALID_TEST_DATA;466continue;467}468int dispScaleFactor = datasetsParams[datasetName].dispScaleFactor;469Mat tmp;470471trueLeftDisp.convertTo( tmp, CV_32FC1, 1.f/dispScaleFactor );472trueLeftDisp = tmp;473tmp.release();474475if( !trueRightDisp.empty() )476{477trueRightDisp.convertTo( tmp, CV_32FC1, 1.f/dispScaleFactor );478trueRightDisp = tmp;479tmp.release();480}481482Mat leftDisp, rightDisp;483int ignBorder = max(runStereoMatchingAlgorithm(leftImg, rightImg, calcROI, leftDisp, rightDisp, ci), EVAL_IGNORE_BORDER);484485leftDisp.convertTo( tmp, CV_32FC1 );486leftDisp = tmp;487tmp.release();488489rightDisp.convertTo( tmp, CV_32FC1 );490rightDisp = tmp;491tmp.release();492493int tempCode = processStereoMatchingResults( resFS, ci, isWrite,494leftImg, rightImg, calcROI, trueLeftDisp, trueRightDisp, leftDisp, rightDisp, QualityEvalParams(ignBorder));495code = tempCode==cvtest::TS::OK ? code : tempCode;496}497498if( isWrite )499resFS << "}"; // "stereo_matching"500501ts->set_failed_test_info( code );502}503504void calcErrors( const Mat& leftImg, const Mat& /*rightImg*/,505const Mat& trueLeftDisp, const Mat& trueRightDisp,506const Mat& trueLeftUnknDispMask, const Mat& trueRightUnknDispMask,507const Mat& calcLeftDisp, const Mat& /*calcRightDisp*/,508vector<float>& rms, vector<float>& badPxlsFractions,509const QualityEvalParams& qualityEvalParams )510{511Mat texturelessMask, texturedMask;512computeTextureBasedMasks( leftImg, &texturelessMask, &texturedMask,513qualityEvalParams.texturelessWidth, qualityEvalParams.texturelessThresh );514Mat occludedMask, nonOccludedMask;515computeOcclusionBasedMasks( trueLeftDisp, trueRightDisp, &occludedMask, &nonOccludedMask,516trueLeftUnknDispMask, trueRightUnknDispMask, qualityEvalParams.dispThresh);517Mat depthDiscontMask;518computeDepthDiscontMask( trueLeftDisp, depthDiscontMask, trueLeftUnknDispMask,519qualityEvalParams.dispGap, qualityEvalParams.discontWidth);520521Mat borderedKnownMask = getBorderedMask( leftImg.size(), qualityEvalParams.ignoreBorder ) & ~trueLeftUnknDispMask;522523nonOccludedMask &= borderedKnownMask;524occludedMask &= borderedKnownMask;525texturedMask &= nonOccludedMask; // & borderedKnownMask526texturelessMask &= nonOccludedMask; // & borderedKnownMask527depthDiscontMask &= nonOccludedMask; // & borderedKnownMask528529rms.resize(ERROR_KINDS_COUNT);530rms[0] = dispRMS( calcLeftDisp, trueLeftDisp, borderedKnownMask );531rms[1] = dispRMS( calcLeftDisp, trueLeftDisp, nonOccludedMask );532rms[2] = dispRMS( calcLeftDisp, trueLeftDisp, occludedMask );533rms[3] = dispRMS( calcLeftDisp, trueLeftDisp, texturedMask );534rms[4] = dispRMS( calcLeftDisp, trueLeftDisp, texturelessMask );535rms[5] = dispRMS( calcLeftDisp, trueLeftDisp, depthDiscontMask );536537badPxlsFractions.resize(ERROR_KINDS_COUNT);538badPxlsFractions[0] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, borderedKnownMask, qualityEvalParams.badThresh );539badPxlsFractions[1] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, nonOccludedMask, qualityEvalParams.badThresh );540badPxlsFractions[2] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, occludedMask, qualityEvalParams.badThresh );541badPxlsFractions[3] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, texturedMask, qualityEvalParams.badThresh );542badPxlsFractions[4] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, texturelessMask, qualityEvalParams.badThresh );543badPxlsFractions[5] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, depthDiscontMask, qualityEvalParams.badThresh );544}545546int CV_StereoMatchingTest::processStereoMatchingResults( FileStorage& fs, int caseIdx, bool isWrite,547const Mat& leftImg, const Mat& rightImg,548const Rect& calcROI,549const Mat& trueLeftDisp, const Mat& trueRightDisp,550const Mat& leftDisp, const Mat& rightDisp,551const QualityEvalParams& qualityEvalParams )552{553// rightDisp is not used in current test virsion554int code = cvtest::TS::OK;555assert( fs.isOpened() );556assert( trueLeftDisp.type() == CV_32FC1 );557assert( trueRightDisp.empty() || trueRightDisp.type() == CV_32FC1 );558assert( leftDisp.type() == CV_32FC1 && (rightDisp.empty() || rightDisp.type() == CV_32FC1) );559560// get masks for unknown ground truth disparity values561Mat leftUnknMask, rightUnknMask;562DatasetParams params = datasetsParams[caseDatasets[caseIdx]];563absdiff( trueLeftDisp, Scalar(params.dispUnknVal), leftUnknMask );564leftUnknMask = leftUnknMask < std::numeric_limits<float>::epsilon();565assert(leftUnknMask.type() == CV_8UC1);566if( !trueRightDisp.empty() )567{568absdiff( trueRightDisp, Scalar(params.dispUnknVal), rightUnknMask );569rightUnknMask = rightUnknMask < std::numeric_limits<float>::epsilon();570assert(rightUnknMask.type() == CV_8UC1);571}572573// calculate errors574vector<float> rmss, badPxlsFractions;575calcErrors( leftImg, rightImg, trueLeftDisp, trueRightDisp, leftUnknMask, rightUnknMask,576leftDisp, rightDisp, rmss, badPxlsFractions, qualityEvalParams );577578if( isWrite )579{580fs << caseNames[caseIdx] << "{";581fs.writeComment( RMS_STR, 0 );582writeErrors( RMS_STR, rmss, &fs );583fs.writeComment( BAD_PXLS_FRACTION_STR, 0 );584writeErrors( BAD_PXLS_FRACTION_STR, badPxlsFractions, &fs );585fs.writeComment( ROI_STR, 0 );586writeROI( calcROI, &fs );587fs << "}"; // datasetName588}589else // compare590{591ts->printf( cvtest::TS::LOG, "\nquality of case named %s\n", caseNames[caseIdx].c_str() );592ts->printf( cvtest::TS::LOG, "%s\n", RMS_STR.c_str() );593writeErrors( RMS_STR, rmss );594ts->printf( cvtest::TS::LOG, "%s\n", BAD_PXLS_FRACTION_STR.c_str() );595writeErrors( BAD_PXLS_FRACTION_STR, badPxlsFractions );596ts->printf( cvtest::TS::LOG, "%s\n", ROI_STR.c_str() );597writeROI( calcROI );598599FileNode fn = fs.getFirstTopLevelNode()[caseNames[caseIdx]];600vector<float> validRmss, validBadPxlsFractions;601Rect validROI;602603readErrors( fn, RMS_STR, validRmss );604readErrors( fn, BAD_PXLS_FRACTION_STR, validBadPxlsFractions );605readROI( fn, validROI );606int tempCode = compareErrors( rmss, validRmss, rmsEps, RMS_STR );607code = tempCode==cvtest::TS::OK ? code : tempCode;608tempCode = compareErrors( badPxlsFractions, validBadPxlsFractions, fracEps, BAD_PXLS_FRACTION_STR );609code = tempCode==cvtest::TS::OK ? code : tempCode;610tempCode = compareROI( calcROI, validROI );611code = tempCode==cvtest::TS::OK ? code : tempCode;612}613return code;614}615616int CV_StereoMatchingTest::readDatasetsParams( FileStorage& fs )617{618if( !fs.isOpened() )619{620ts->printf( cvtest::TS::LOG, "datasetsParams can not be read " );621return cvtest::TS::FAIL_INVALID_TEST_DATA;622}623datasetsParams.clear();624FileNode fn = fs.getFirstTopLevelNode();625assert(fn.isSeq());626for( int i = 0; i < (int)fn.size(); i+=3 )627{628String _name = fn[i];629DatasetParams params;630String sf = fn[i+1]; params.dispScaleFactor = atoi(sf.c_str());631String uv = fn[i+2]; params.dispUnknVal = atoi(uv.c_str());632datasetsParams[_name] = params;633}634return cvtest::TS::OK;635}636637int CV_StereoMatchingTest::readRunParams( FileStorage& fs )638{639if( !fs.isOpened() )640{641ts->printf( cvtest::TS::LOG, "runParams can not be read " );642return cvtest::TS::FAIL_INVALID_TEST_DATA;643}644caseNames.clear();;645caseDatasets.clear();646return cvtest::TS::OK;647}648649void CV_StereoMatchingTest::writeErrors( const string& errName, const vector<float>& errors, FileStorage* fs )650{651assert( (int)errors.size() == ERROR_KINDS_COUNT );652vector<float>::const_iterator it = errors.begin();653if( fs )654for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++it )655*fs << ERROR_PREFIXES[i] + errName << *it;656else657for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++it )658ts->printf( cvtest::TS::LOG, "%s = %f\n", string(ERROR_PREFIXES[i]+errName).c_str(), *it );659}660661void CV_StereoMatchingTest::writeROI( const Rect& calcROI, FileStorage* fs )662{663if( fs )664{665*fs << ROI_PREFIXES[0] << calcROI.x;666*fs << ROI_PREFIXES[1] << calcROI.y;667*fs << ROI_PREFIXES[2] << calcROI.width;668*fs << ROI_PREFIXES[3] << calcROI.height;669}670else671{672ts->printf( cvtest::TS::LOG, "%s = %d\n", ROI_PREFIXES[0].c_str(), calcROI.x );673ts->printf( cvtest::TS::LOG, "%s = %d\n", ROI_PREFIXES[1].c_str(), calcROI.y );674ts->printf( cvtest::TS::LOG, "%s = %d\n", ROI_PREFIXES[2].c_str(), calcROI.width );675ts->printf( cvtest::TS::LOG, "%s = %d\n", ROI_PREFIXES[3].c_str(), calcROI.height );676}677}678679void CV_StereoMatchingTest::readErrors( FileNode& fn, const string& errName, vector<float>& errors )680{681errors.resize( ERROR_KINDS_COUNT );682vector<float>::iterator it = errors.begin();683for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++it )684fn[ERROR_PREFIXES[i]+errName] >> *it;685}686687void CV_StereoMatchingTest::readROI( FileNode& fn, Rect& validROI )688{689fn[ROI_PREFIXES[0]] >> validROI.x;690fn[ROI_PREFIXES[1]] >> validROI.y;691fn[ROI_PREFIXES[2]] >> validROI.width;692fn[ROI_PREFIXES[3]] >> validROI.height;693}694695int CV_StereoMatchingTest::compareErrors( const vector<float>& calcErrors, const vector<float>& validErrors,696const vector<float>& eps, const string& errName )697{698assert( (int)calcErrors.size() == ERROR_KINDS_COUNT );699assert( (int)validErrors.size() == ERROR_KINDS_COUNT );700assert( (int)eps.size() == ERROR_KINDS_COUNT );701vector<float>::const_iterator calcIt = calcErrors.begin(),702validIt = validErrors.begin(),703epsIt = eps.begin();704bool ok = true;705for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++calcIt, ++validIt, ++epsIt )706if( *calcIt - *validIt > *epsIt )707{708ts->printf( cvtest::TS::LOG, "bad accuracy of %s (valid=%f; calc=%f)\n", string(ERROR_PREFIXES[i]+errName).c_str(), *validIt, *calcIt );709ok = false;710}711return ok ? cvtest::TS::OK : cvtest::TS::FAIL_BAD_ACCURACY;712}713714int CV_StereoMatchingTest::compareROI( const Rect& calcROI, const Rect& validROI )715{716int compare[4][2] = {717{ calcROI.x, validROI.x },718{ calcROI.y, validROI.y },719{ calcROI.width, validROI.width },720{ calcROI.height, validROI.height },721};722bool ok = true;723for (int i = 0; i < 4; i++)724{725if (compare[i][0] != compare[i][1])726{727ts->printf( cvtest::TS::LOG, "bad accuracy of %s (valid=%d; calc=%d)\n", ROI_PREFIXES[i].c_str(), compare[i][1], compare[i][0] );728ok = false;729}730}731return ok ? cvtest::TS::OK : cvtest::TS::FAIL_BAD_ACCURACY;732}733734//----------------------------------- StereoBM test -----------------------------------------------------735736class CV_StereoBMTest : public CV_StereoMatchingTest737{738public:739CV_StereoBMTest()740{741name = "stereobm";742fill(rmsEps.begin(), rmsEps.end(), 0.4f);743fill(fracEps.begin(), fracEps.end(), 0.022f);744}745746protected:747struct RunParams748{749int ndisp;750int mindisp;751int winSize;752};753vector<RunParams> caseRunParams;754755virtual int readRunParams( FileStorage& fs )756{757int code = CV_StereoMatchingTest::readRunParams( fs );758FileNode fn = fs.getFirstTopLevelNode();759assert(fn.isSeq());760for( int i = 0; i < (int)fn.size(); i+=5 )761{762String caseName = fn[i], datasetName = fn[i+1];763RunParams params;764String ndisp = fn[i+2]; params.ndisp = atoi(ndisp.c_str());765String mindisp = fn[i+3]; params.mindisp = atoi(mindisp.c_str());766String winSize = fn[i+4]; params.winSize = atoi(winSize.c_str());767caseNames.push_back( caseName );768caseDatasets.push_back( datasetName );769caseRunParams.push_back( params );770}771return code;772}773774virtual int runStereoMatchingAlgorithm( const Mat& _leftImg, const Mat& _rightImg,775Rect& calcROI, Mat& leftDisp, Mat& /*rightDisp*/, int caseIdx )776{777RunParams params = caseRunParams[caseIdx];778assert( params.ndisp%16 == 0 );779assert( _leftImg.type() == CV_8UC3 && _rightImg.type() == CV_8UC3 );780Mat leftImg; cvtColor( _leftImg, leftImg, COLOR_BGR2GRAY );781Mat rightImg; cvtColor( _rightImg, rightImg, COLOR_BGR2GRAY );782783Ptr<StereoBM> bm = StereoBM::create( params.ndisp, params.winSize );784Mat tempDisp;785bm->setMinDisparity(params.mindisp);786787Rect cROI(0, 0, _leftImg.cols, _leftImg.rows);788calcROI = getValidDisparityROI(cROI, cROI, params.mindisp, params.ndisp, params.winSize);789790bm->compute( leftImg, rightImg, tempDisp );791tempDisp.convertTo(leftDisp, CV_32F, 1./StereoMatcher::DISP_SCALE);792793//check for fixed-type disparity data type794Mat_<float> fixedFloatDisp;795bm->compute( leftImg, rightImg, fixedFloatDisp );796EXPECT_LT(cvtest::norm(fixedFloatDisp, leftDisp, cv::NORM_L2 | cv::NORM_RELATIVE),7970.005 + DBL_EPSILON);798799if (params.mindisp != 0)800for (int y = 0; y < leftDisp.rows; y++)801for (int x = 0; x < leftDisp.cols; x++)802{803if (leftDisp.at<float>(y, x) < params.mindisp)804leftDisp.at<float>(y, x) = -1./StereoMatcher::DISP_SCALE; // treat disparity < mindisp as no disparity805}806807return params.winSize/2;808}809};810811//----------------------------------- StereoSGBM test -----------------------------------------------------812813class CV_StereoSGBMTest : public CV_StereoMatchingTest814{815public:816CV_StereoSGBMTest()817{818name = "stereosgbm";819fill(rmsEps.begin(), rmsEps.end(), 0.25f);820fill(fracEps.begin(), fracEps.end(), 0.01f);821}822823protected:824struct RunParams825{826int ndisp;827int winSize;828int mode;829};830vector<RunParams> caseRunParams;831832virtual int readRunParams( FileStorage& fs )833{834int code = CV_StereoMatchingTest::readRunParams(fs);835FileNode fn = fs.getFirstTopLevelNode();836assert(fn.isSeq());837for( int i = 0; i < (int)fn.size(); i+=5 )838{839String caseName = fn[i], datasetName = fn[i+1];840RunParams params;841String ndisp = fn[i+2]; params.ndisp = atoi(ndisp.c_str());842String winSize = fn[i+3]; params.winSize = atoi(winSize.c_str());843String mode = fn[i+4]; params.mode = atoi(mode.c_str());844caseNames.push_back( caseName );845caseDatasets.push_back( datasetName );846caseRunParams.push_back( params );847}848return code;849}850851virtual int runStereoMatchingAlgorithm( const Mat& leftImg, const Mat& rightImg,852Rect& calcROI, Mat& leftDisp, Mat& /*rightDisp*/, int caseIdx )853{854RunParams params = caseRunParams[caseIdx];855assert( params.ndisp%16 == 0 );856Ptr<StereoSGBM> sgbm = StereoSGBM::create( 0, params.ndisp, params.winSize,85710*params.winSize*params.winSize,85840*params.winSize*params.winSize,8591, 63, 10, 100, 32, params.mode );860861Rect cROI(0, 0, leftImg.cols, leftImg.rows);862calcROI = getValidDisparityROI(cROI, cROI, 0, params.ndisp, params.winSize);863864sgbm->compute( leftImg, rightImg, leftDisp );865CV_Assert( leftDisp.type() == CV_16SC1 );866leftDisp/=16;867return 0;868}869};870871872TEST(Calib3d_StereoBM, regression) { CV_StereoBMTest test; test.safe_run(); }873TEST(Calib3d_StereoSGBM, regression) { CV_StereoSGBMTest test; test.safe_run(); }874875TEST(Calib3d_StereoSGBM_HH4, regression)876{877String path = cvtest::TS::ptr()->get_data_path() + "cv/stereomatching/datasets/teddy/";878Mat leftImg = imread(path + "im2.png", 0);879ASSERT_FALSE(leftImg.empty());880Mat rightImg = imread(path + "im6.png", 0);881ASSERT_FALSE(rightImg.empty());882Mat testData = imread(path + "disp2_hh4.png",-1);883ASSERT_FALSE(testData.empty());884Mat leftDisp;885Mat toCheck;886{887Ptr<StereoSGBM> sgbm = StereoSGBM::create( 0, 48, 3, 90, 360, 1, 63, 10, 100, 32, StereoSGBM::MODE_HH4);888sgbm->compute( leftImg, rightImg, leftDisp);889CV_Assert( leftDisp.type() == CV_16SC1 );890leftDisp.convertTo(toCheck, CV_16UC1,1,16);891}892Mat diff;893absdiff(toCheck, testData,diff);894CV_Assert( countNonZero(diff)==0);895}896897}} // namespace898899900