diff --git a/modules/README.md b/modules/README.md index 2633ce3e6..1844f2905 100644 --- a/modules/README.md +++ b/modules/README.md @@ -52,4 +52,4 @@ $ cmake -D OPENCV_EXTRA_MODULES_PATH=/modules -D BUILD_opencv_re 22. **opencv_xphoto**: Additional photo processing algorithms: Color balance / Denoising / Inpainting. -23. **opencv_stereo**: Stereo Correspondence done with different descriptors: Census / CS-Census / MCT / BRIEF / MV / RT. +23. **opencv_stereo**: Stereo Correspondence done with different descriptors: Census / CS-Census / MCT / BRIEF / MV. diff --git a/modules/stereo/include/opencv2/stereo.hpp b/modules/stereo/include/opencv2/stereo.hpp index 96661664c..fe3ec351e 100644 --- a/modules/stereo/include/opencv2/stereo.hpp +++ b/modules/stereo/include/opencv2/stereo.hpp @@ -59,14 +59,10 @@ namespace cv { namespace stereo { - //! @addtogroup stereo //! @{ // void correctMatches( InputArray F, InputArray points1, InputArray points2, // OutputArray newPoints1, OutputArray newPoints2 ); - enum { - CV_SPECKLE_REMOVAL_ALGORITHM, CV_SPECKLE_REMOVAL_AVG_ALGORITHM - }; /** @brief Filters off small noise blobs (speckles) in the disparity map @param img The input 16-bit signed disparity image @param newVal The disparity value used to paint-off the speckles @@ -117,8 +113,14 @@ namespace cv virtual void setDisp12MaxDiff(int disp12MaxDiff) = 0; }; - - + //!speckle removal algorithms. These algorithms have the purpose of removing small regions + enum { + CV_SPECKLE_REMOVAL_ALGORITHM, CV_SPECKLE_REMOVAL_AVG_ALGORITHM + }; + //!subpixel interpolationm methods for disparities. + enum{ + CV_QUADRATIC_INTERPOLATION, CV_SIMETRICV_INTERPOLATION + }; /** @brief Class for computing stereo correspondence using the block matching algorithm, introduced and contributed to OpenCV by K. Konolige. */ @@ -174,7 +176,7 @@ namespace cv The function create StereoBM object. You can then call StereoBM::compute() to compute disparity for a specific stereo pair. */ - CV_EXPORTS static Ptr< cv::stereo::StereoBinaryBM > create(int numDisparities = 0, int blockSize = 21); + CV_EXPORTS static Ptr< cv::stereo::StereoBinaryBM > create(int numDisparities = 0, int blockSize = 9); }; /** @brief The class implements the modified H. Hirschmuller algorithm @cite HH08 that differs from the original @@ -219,6 +221,15 @@ namespace cv virtual int getMode() const = 0; virtual void setMode(int mode) = 0; + virtual int getSpekleRemovalTechnique() const = 0 ; + virtual void setSpekleRemovalTechnique(int factor) = 0; + + virtual int getBinaryKernelType() const = 0; + virtual void setBinaryKernelType(int value) = 0; + + virtual int getSubPixelInterpolationMethod() const = 0; + virtual void setSubPixelInterpolationMethod(int value) = 0; + /** @brief Creates StereoSGBM object @param minDisparity Minimum possible disparity value. Normally, it is zero but sometimes @@ -257,9 +268,9 @@ namespace cv to a custom value. */ CV_EXPORTS static Ptr create(int minDisparity, int numDisparities, int blockSize, - int P1 = 100, int P2 = 1000, int disp12MaxDiff = 0, - int preFilterCap = 0, int uniquenessRatio = 0, - int speckleWindowSize = 0, int speckleRange = 0, + int P1 = 100, int P2 = 1000, int disp12MaxDiff = 1, + int preFilterCap = 0, int uniquenessRatio = 5, + int speckleWindowSize = 400, int speckleRange = 200, int mode = StereoBinarySGBM::MODE_SGBM); }; //! @} diff --git a/modules/stereo/perf/perf_bm.cpp b/modules/stereo/perf/perf_bm.cpp new file mode 100644 index 000000000..88aeb6a0d --- /dev/null +++ b/modules/stereo/perf/perf_bm.cpp @@ -0,0 +1,118 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "perf_precomp.hpp" + +using namespace std; +using namespace cv; +using namespace cv::stereo; +using namespace perf; + +typedef std::tr1::tuple s_bm_test_t; +typedef perf::TestBaseWithParam s_bm; + +PERF_TEST_P( s_bm, sgm_perf, + testing::Combine( + testing::Values( cv::Size(512, 283), cv::Size(320, 240)), + testing::Values( CV_8UC1,CV_8U ), + testing::Values( CV_8UC1,CV_8U,CV_16S ) + ) + ) +{ + Size sz = std::tr1::get<0>(GetParam()); + int matType = std::tr1::get<1>(GetParam()); + int sdepth = std::tr1::get<2>(GetParam()); + + Mat left(sz, matType); + Mat right(sz, matType); + Mat out1(sz, sdepth); + Ptr sgbm = StereoBinarySGBM::create(0, 16, 5); + sgbm->setBinaryKernelType(CV_DENSE_CENSUS); + declare.in(left, WARMUP_RNG) + .out(out1) + .time(0.1) + .iterations(20); + TEST_CYCLE() + { + sgbm->compute(left, right, out1); + } + SANITY_CHECK(out1); +} +PERF_TEST_P( s_bm, bm_perf, + testing::Combine( + testing::Values( cv::Size(512, 383), cv::Size(320, 240) ), + testing::Values( CV_8UC1,CV_8U ), + testing::Values( CV_8UC1,CV_8U ) + ) + ) +{ + Size sz = std::tr1::get<0>(GetParam()); + int matType = std::tr1::get<1>(GetParam()); + int sdepth = std::tr1::get<2>(GetParam()); + + Mat left(sz, matType); + Mat right(sz, matType); + Mat out1(sz, sdepth); + Ptr sbm = StereoBinaryBM::create(16, 9); + // we set the corresponding parameters + sbm->setPreFilterCap(31); + sbm->setMinDisparity(0); + sbm->setTextureThreshold(10); + sbm->setUniquenessRatio(0); + sbm->setSpeckleWindowSize(400); + sbm->setDisp12MaxDiff(0); + sbm->setAgregationWindowSize(11); + // the user can choose between the average speckle removal algorithm or + // the classical version that was implemented in OpenCV + sbm->setSpekleRemovalTechnique(CV_SPECKLE_REMOVAL_AVG_ALGORITHM); + sbm->setUsePrefilter(false); + + declare.in(left, WARMUP_RNG) + .out(out1) + .time(0.1) + .iterations(20); + TEST_CYCLE() + { + sbm->compute(left, right, out1); + } + SANITY_CHECK(out1); +} diff --git a/modules/stereo/perf/perf_descriptor.cpp b/modules/stereo/perf/perf_descriptor.cpp new file mode 100644 index 000000000..27d57fbd1 --- /dev/null +++ b/modules/stereo/perf/perf_descriptor.cpp @@ -0,0 +1,143 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ +#include "perf_precomp.hpp" + +using namespace std; +using namespace cv; +using namespace cv::stereo; +using namespace perf; + +typedef std::tr1::tuple descript_params_t; +typedef perf::TestBaseWithParam descript_params; + +PERF_TEST_P( descript_params, census_sparse_descriptor, + testing::Combine( + testing::Values( TYPICAL_MAT_SIZES ), + testing::Values( CV_8UC1,CV_8U ), + testing::Values( CV_32SC4,CV_32S ) + ) + ) +{ + Size sz = std::tr1::get<0>(GetParam()); + int matType = std::tr1::get<1>(GetParam()); + int sdepth = std::tr1::get<2>(GetParam()); + Mat left(sz, matType); + Mat out1(sz, sdepth); + declare.in(left, WARMUP_RNG) + .out(out1) + .time(0.01); + TEST_CYCLE() + { + censusTransform(left,9,out1,CV_SPARSE_CENSUS); + } + SANITY_CHECK(out1); +} +PERF_TEST_P( descript_params, star_census_transform, + testing::Combine( + testing::Values( TYPICAL_MAT_SIZES ), + testing::Values( CV_8UC1,CV_8U ), + testing::Values( CV_32SC4,CV_32S ) + ) + ) +{ + Size sz = std::tr1::get<0>(GetParam()); + int matType = std::tr1::get<1>(GetParam()); + int sdepth = std::tr1::get<2>(GetParam()); + Mat left(sz, matType); + Mat out1(sz, sdepth); + declare.in(left, WARMUP_RNG) + .out(out1) + .time(0.01); + TEST_CYCLE() + { + starCensusTransform(left,9,out1); + } + SANITY_CHECK(out1); +} +PERF_TEST_P( descript_params, modified_census_transform, + testing::Combine( + testing::Values( TYPICAL_MAT_SIZES ), + testing::Values( CV_8UC1,CV_8U ), + testing::Values( CV_32SC4,CV_32S ) + ) + ) +{ + Size sz = std::tr1::get<0>(GetParam()); + int matType = std::tr1::get<1>(GetParam()); + int sdepth = std::tr1::get<2>(GetParam()); + + Mat left(sz, matType); + Mat out1(sz, sdepth); + + declare.in(left, WARMUP_RNG) + .out(out1) + .time(0.01); + TEST_CYCLE() + { + modifiedCensusTransform(left,9,out1,CV_MODIFIED_CENSUS_TRANSFORM); + } + SANITY_CHECK(out1); +} +PERF_TEST_P( descript_params, center_symetric_census, + testing::Combine( + testing::Values( TYPICAL_MAT_SIZES ), + testing::Values( CV_8UC1,CV_8U ), + testing::Values( CV_32SC4,CV_32S ) + ) + ) +{ + Size sz = std::tr1::get<0>(GetParam()); + int matType = std::tr1::get<1>(GetParam()); + int sdepth = std::tr1::get<2>(GetParam()); + + Mat left(sz, matType); + Mat out1(sz, sdepth); + + declare.in(left, WARMUP_RNG) + .out(out1) + .time(0.01); + TEST_CYCLE() + { + symetricCensusTransform(left,7,out1,CV_CS_CENSUS); + } + SANITY_CHECK(out1); +} diff --git a/modules/stereo/perf/perf_main.cpp b/modules/stereo/perf/perf_main.cpp new file mode 100644 index 000000000..657393c4b --- /dev/null +++ b/modules/stereo/perf/perf_main.cpp @@ -0,0 +1,44 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ +#include "perf_precomp.hpp" + +CV_PERF_TEST_MAIN(stereo) diff --git a/modules/stereo/perf/perf_precomp.hpp b/modules/stereo/perf/perf_precomp.hpp new file mode 100644 index 000000000..23ed07281 --- /dev/null +++ b/modules/stereo/perf/perf_precomp.hpp @@ -0,0 +1,32 @@ +#ifdef __GNUC__ +# pragma GCC diagnostic ignored "-Wmissing-declarations" +# if defined __clang__ || defined __APPLE__ +# pragma GCC diagnostic ignored "-Wmissing-prototypes" +# pragma GCC diagnostic ignored "-Wextra" +# endif +#endif + +#ifndef __OPENCV_PERF_PRECOMP_HPP__ +#define __OPENCV_PERF_PRECOMP_HPP__ + +#include +#include "opencv2/ts.hpp" +#include "opencv2/imgcodecs.hpp" +#include "opencv2/stereo.hpp" +#include "opencv2/imgproc.hpp" +#include "opencv2/features2d.hpp" +#include "opencv2/core/utility.hpp" +#include "opencv2/core/private.hpp" +#include "opencv2/core/cvdef.h" +#include "opencv2/core.hpp" +#include "opencv2/highgui.hpp" +#include "opencv2/calib3d.hpp" + +#include +#include + +#ifdef GTEST_CREATE_SHARED_LIBRARY +#error no modules except ts should have GTEST_CREATE_SHARED_LIBRARY defined +#endif + +#endif diff --git a/modules/stereo/samples/Sample1.cpp b/modules/stereo/samples/Sample1.cpp deleted file mode 100644 index cf9a820ed..000000000 --- a/modules/stereo/samples/Sample1.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include -#include "opencv2/stereo.hpp" -#include "opencv2/imgproc.hpp" -#include "opencv2/highgui.hpp" -#include - -using namespace cv; -using namespace stereo; -using namespace std; - -//in this example we will load a sequence of images from a file process them and display the result on the screen -//the descriptor used is the modified_census transform - -int main(int, char**) -{ - //begin the program - cout << " Running Main function \n"; - //declare 2 images - Mat image1, image2; - - // -- 1. Call the constructor for StereoBinaryBM - int ndisparities = 32; /**< Range of disparity */ - int kernelSize = 9; /**< Size of the block window. Must be odd */ - - Ptr sbm = StereoBinaryBM::create(ndisparities, kernelSize); - - // -- 2. Set parameters - sbm->setPreFilterCap(31); - sbm->setMinDisparity(0); - sbm->setTextureThreshold(10); - sbm->setUniquenessRatio(0); - sbm->setSpeckleWindowSize(400);//speckle size - sbm->setSpeckleRange(200); - sbm->setDisp12MaxDiff(0); - sbm->setScalleFactor(4);//the scalling factor - sbm->setBinaryKernelType(CV_MODIFIED_CENSUS_TRANSFORM);//binary descriptor kernel - sbm->setAgregationWindowSize(9); - sbm->setSpekleRemovalTechnique(CV_SPECKLE_REMOVAL_AVG_ALGORITHM);//speckle removal algorithm - sbm->setUsePrefilter(false);//prefilter or not the images prior to making the transformations - - for(int i = 0 ; i < 200; i++) - { - string path = "D:\\WorkingSec"; - string left = "l.bmp"; - string right = ".bmp"; - - std::string s; - std::stringstream out; - out << i; - s = out.str(); - - string finLeft = path + "\\rezult" + s + left; - string finRigth = path + "\\rezult" + s + right; - - image1 = imread(finLeft, CV_8UC1); - image2 = imread(finRigth, CV_8UC1); - //set a certain region of interest - Rect region_of_interest = Rect(0, 20, image1.cols, (image1.rows - 20 - 110)); - - Mat imgLeft = image1(region_of_interest); - Mat imgRight = image2(region_of_interest); - - Mat imgDisparity8U = Mat(imgLeft.rows, imgLeft.cols, CV_8UC1); - if (imgLeft.empty() || imgRight.empty()) - { - std::cout << " --(!) Error reading images \n" ; return -1; - } - ////-- 3. Calculate the disparity image - sbm->compute(imgLeft, imgRight, imgDisparity8U); - - imshow("RealImage", image1); - imshow("Disparity", imgDisparity8U); - waitKey(1); - } - - waitKey(0); - return 0; -} diff --git a/modules/stereo/samples/Sample2.cpp b/modules/stereo/samples/Sample2.cpp deleted file mode 100644 index 569ac7142..000000000 --- a/modules/stereo/samples/Sample2.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include -#include "opencv2/stereo.hpp" -#include "opencv2/imgproc.hpp" -#include "opencv2/highgui.hpp" -#include - -using namespace cv; -using namespace stereo; -using namespace std; - - -int main(int, char**) -{ - //begin the program - cout << " Running Main function \n"; - //declare 2 images - Mat image1, image2; - - // -- 1. Call the constructor for StereoBinaryBM - int ndisparities = 32; /**< Range of disparity */ - int kernelSize = 9; /**< Size of the block window. Must be odd */ - - Ptr sbm = StereoBinaryBM::create(ndisparities, kernelSize); - - // -- 2. Set parameters - sbm->setPreFilterCap(31); - sbm->setMinDisparity(0); - sbm->setTextureThreshold(10); - sbm->setUniquenessRatio(0); - sbm->setSpeckleWindowSize(400);//speckle size - sbm->setSpeckleRange(200); - sbm->setDisp12MaxDiff(0); - sbm->setScalleFactor(4);//the scalling factor - sbm->setBinaryKernelType(CV_MEAN_VARIATION);//binary descriptor kernel - sbm->setAgregationWindowSize(9); - sbm->setSpekleRemovalTechnique(CV_SPECKLE_REMOVAL_AVG_ALGORITHM);//speckle removal algorithm - sbm->setUsePrefilter(false);//prefilter or not the images prior to making the transformations - - //load 2 images from disc - image1 = imread("D:\\rezult0l.bmp", CV_8UC1); - image2 = imread("D:\\rezult0.bmp", CV_8UC1); - //set a certain region of interest - Rect region_of_interest = Rect(0, 20, image1.cols, (image1.rows - 20 - 110)); - - Mat imgLeft = image1(region_of_interest); - Mat imgRight = image2(region_of_interest); - - Mat imgDisparity8U = Mat(imgLeft.rows, imgLeft.cols, CV_8UC1); - if (imgLeft.empty() || imgRight.empty()) - { - std::cout << " --(!) Error reading images \n" ; return -1; - } - ////-- 3. Calculate the disparity image - sbm->compute(imgLeft, imgRight, imgDisparity8U); - - imshow("RealImage", image1); - imshow("Disparity", imgDisparity8U); - - waitKey(0); - return 0; -} diff --git a/modules/stereo/samples/sample.cpp b/modules/stereo/samples/sample.cpp new file mode 100644 index 000000000..5f8400d43 --- /dev/null +++ b/modules/stereo/samples/sample.cpp @@ -0,0 +1,185 @@ +#include "opencv2/stereo.hpp" +#include "opencv2/imgproc.hpp" +#include "opencv2/highgui.hpp" +#include +#include +#include + +using namespace std; +using namespace cv; +using namespace cv::stereo; + +enum { STEREO_BINARY_BM, STEREO_BINARY_SGM }; +static cv::CommandLineParser parse_argument_values(int argc, char **argv, string &left, string &right, int &kernel_size, int &number_of_disparities, + int &aggregation_window, int &P1, int &P2, float &scale, int &algo, int &binary_descriptor_type); +int main(int argc, char** argv) +{ + string left, right; + int kernel_size = 0, number_of_disparities = 0, aggregation_window = 0, P1 = 0, P2 = 0; + float scale = 4; + int algo = STEREO_BINARY_BM; + int binary_descriptor_type = 0; + // here we extract the values that were added as arguments + // we also test to see if they are provided correcly + cv::CommandLineParser parser = + parse_argument_values(argc, argv, left, right, + kernel_size, + number_of_disparities, + aggregation_window, + P1, P2, + scale, + algo, binary_descriptor_type); + + if (!parser.check()) + { + parser.printMessage(); + return 1; + } + int fail = 0; + //TEST if the provided parameters are correct + if(binary_descriptor_type == CV_DENSE_CENSUS && kernel_size > 5) + { + cout << "For the dense census transform the maximum kernel size should be 5\n"; + fail = 1; + } + if((binary_descriptor_type == CV_MEAN_VARIATION || binary_descriptor_type == CV_MODIFIED_CENSUS_TRANSFORM || binary_descriptor_type == CV_STAR_KERNEL) && kernel_size != 9) + { + cout <<" For Mean variation and the modified census transform the kernel size should be equal to 9\n"; + fail = 1; + } + if((binary_descriptor_type == CV_CS_CENSUS || binary_descriptor_type == CV_MODIFIED_CS_CENSUS) && kernel_size > 7) + { + cout << " The kernel size should be smaller or equal to 7 for the CS census and modified center symetric census\n"; + fail = 1; + } + if(binary_descriptor_type == CV_SPARSE_CENSUS && kernel_size > 11) + { + cout << "The kernel size for the sparse census must be smaller or equal to 11\n"; + fail = 1; + } + + if(number_of_disparities < 10) + { + cout << "Number of disparities should be greater than 10\n"; + fail = 1; + } + if(P2 / P1 < 2) + { + cout << "You should probabilly choose a greater P2 penalty\n"; + fail = 1; + } + if(fail == 1) + { + return 1; + } + // verify if the user inputs the correct number of parameters + Mat image1, image2; + // we read a pair of images from the disk + image1 = imread(left, CV_8UC1); + image2 = imread(right, CV_8UC1); + // verify if they are loaded correctly + if (image1.empty() || image2.empty()) + { + cout << " --(!) Error reading images \n"; + + parser.printMessage(); + return 1; + } + + // we display the parsed parameters + const char *b[7] = { "CV_DENSE_CENSUS", "CV_SPARSE_CENSUS", "CV_CS_CENSUS", "CV_MODIFIED_CS_CENSUS", + "CV_MODIFIED_CENSUS_TRANSFORM", "CV_MEAN_VARIATION", "CV_STAR_KERNEL" }; + cout << "Program Name: " << argv[0]; + cout << "\nPath to left image " << left << " \n" << "Path to right image " << right << "\n"; + cout << "\nkernel size " << kernel_size << "\n" + << "numberOfDisparities " << number_of_disparities << "\n" + << "aggregationWindow " << aggregation_window << "\n" + << "scallingFactor " << scale << "\n" << "Descriptor name : " << b[binary_descriptor_type] << "\n"; + + Mat imgDisparity16S2 = Mat(image1.rows, image1.cols, CV_16S); + Mat imgDisparity8U2 = Mat(image1.rows, image1.cols, CV_8UC1); + imshow("Original Left image", image1); + + if (algo == STEREO_BINARY_BM) + { + Ptr sbm = StereoBinaryBM::create(number_of_disparities, kernel_size); + // we set the corresponding parameters + sbm->setPreFilterCap(31); + sbm->setMinDisparity(0); + sbm->setTextureThreshold(10); + sbm->setUniquenessRatio(0); + sbm->setSpeckleWindowSize(400); // speckle size + sbm->setSpeckleRange(200); + sbm->setDisp12MaxDiff(0); + sbm->setScalleFactor((int)scale); // the scaling factor + sbm->setBinaryKernelType(binary_descriptor_type); // binary descriptor kernel + sbm->setAgregationWindowSize(aggregation_window); + // the user can choose between the average speckle removal algorithm or + // the classical version that was implemented in OpenCV + sbm->setSpekleRemovalTechnique(CV_SPECKLE_REMOVAL_AVG_ALGORITHM); + sbm->setUsePrefilter(false); + //-- calculate the disparity image + sbm->compute(image1, image2, imgDisparity8U2); + imshow("Disparity", imgDisparity8U2); + } + else if (algo == STEREO_BINARY_SGM) + { + // we set the corresponding parameters + Ptr sgbm = StereoBinarySGBM::create(0, number_of_disparities, kernel_size); + // setting the penalties for sgbm + sgbm->setP1(P1); + sgbm->setP2(P2); + sgbm->setMinDisparity(0); + sgbm->setUniquenessRatio(5); + sgbm->setSpeckleWindowSize(400); + sgbm->setSpeckleRange(0); + sgbm->setDisp12MaxDiff(1); + sgbm->setBinaryKernelType(binary_descriptor_type); + sgbm->setSpekleRemovalTechnique(CV_SPECKLE_REMOVAL_AVG_ALGORITHM); + sgbm->setSubPixelInterpolationMethod(CV_SIMETRICV_INTERPOLATION); + sgbm->compute(image1, image2, imgDisparity16S2); + /*Alternative for scalling + imgDisparity16S2.convertTo(imgDisparity8U2, CV_8UC1, scale); + */ + double minVal; double maxVal; + minMaxLoc(imgDisparity16S2, &minVal, &maxVal); + imgDisparity16S2.convertTo(imgDisparity8U2, CV_8UC1, 255 / (maxVal - minVal)); + //show the disparity image + imshow("Windowsgm", imgDisparity8U2); + } + waitKey(0); + return 0; +} +static cv::CommandLineParser parse_argument_values(int argc, char **argv, string &left, string &right, int &kernel_size, int &number_of_disparities, + int &aggregation_window, int &P1, int &P2, float &scale, int &algo, int &binary_descriptor_type) +{ + static const char* keys = + "{ @left | | }" + "{ @right | | }" + "{ k kernel_size | 9 | }" + "{ d disparity | 128 | }" + "{ w aggregation_window | 9 | }" + "{ P1 | 100 | }" + "{ P2 | 1000 | }" + "{ b binary_descriptor | 4 | Index of the descriptor type:\n 0 - CV_DENSE_CENSUS,\n 1 - CV_SPARSE_CENSUS,\n 2 - CV_CS_CENSUS,\n 3 - CV_MODIFIED_CS_CENSUS,\n 4 - CV_MODIFIED_CENSUS_TRANSFORM,\n 5 - CV_MEAN_VARIATION,\n 6 - CV_STAR_KERNEL}" + "{ s scale | 1.01593 | }" + "{ a algorithm | sgm | }" + ; + + cv::CommandLineParser parser( argc, argv, keys ); + + left = parser.get(0); + right = parser.get(1); + kernel_size = parser.get("kernel_size"); + number_of_disparities = parser.get("disparity"); + aggregation_window = parser.get("aggregation_window"); + P1 = parser.get("P1"); + P2 = parser.get("P2"); + binary_descriptor_type = parser.get("binary_descriptor"); + scale = parser.get("scale"); + algo = parser.get("algorithm") == "sgm" ? STEREO_BINARY_SGM : STEREO_BINARY_BM; + + parser.about("\nDemo stereo matching converting L and R images into disparity images using BM and SGBM\n"); + + return parser; +} diff --git a/modules/stereo/src/descriptor.cpp b/modules/stereo/src/descriptor.cpp index 10ac65be5..547956d25 100644 --- a/modules/stereo/src/descriptor.cpp +++ b/modules/stereo/src/descriptor.cpp @@ -52,7 +52,7 @@ namespace cv { //function that performs the census transform on two images. //Two variants of census are offered a sparse version whcih takes every second pixel as well as dense version - void censusTransform(const Mat &image1, const Mat &image2, int kernelSize, Mat &dist1, Mat &dist2, const int type) + CV_EXPORTS void censusTransform(const Mat &image1, const Mat &image2, int kernelSize, Mat &dist1, Mat &dist2, const int type) { CV_Assert(image1.size() == image2.size()); CV_Assert(kernelSize % 2 != 0); @@ -75,7 +75,7 @@ namespace cv } } //function that performs census on one image - void censusTransform(const Mat &image1, int kernelSize, Mat &dist1, const int type) + CV_EXPORTS void censusTransform(const Mat &image1, int kernelSize, Mat &dist1, const int type) { CV_Assert(image1.size() == dist1.size()); CV_Assert(kernelSize % 2 != 0); @@ -98,7 +98,7 @@ namespace cv } } //in a 9x9 kernel only certain positions are choosen for comparison - void starCensusTransform(const Mat &img1, const Mat &img2, int kernelSize, Mat &dist1, Mat &dist2) + CV_EXPORTS void starCensusTransform(const Mat &img1, const Mat &img2, int kernelSize, Mat &dist1, Mat &dist2) { CV_Assert(img1.size() == img2.size()); CV_Assert(kernelSize % 2 != 0); @@ -110,7 +110,7 @@ namespace cv parallel_for_(Range(n2, img1.rows - n2), StarKernelCensus<2>(images, n2,date)); } //single version of star census - void starCensusTransform(const Mat &img1, int kernelSize, Mat &dist) + CV_EXPORTS void starCensusTransform(const Mat &img1, int kernelSize, Mat &dist) { CV_Assert(img1.size() == dist.size()); CV_Assert(kernelSize % 2 != 0); @@ -126,7 +126,7 @@ namespace cv //the sencond modified census transform is invariant to noise; i.e. //if the current pixel with whom we are dooing the comparison is a noise, this descriptor will provide a better result by comparing with the mean of the window //otherwise if the pixel is not noise the information is strengthend - void modifiedCensusTransform(const Mat &img1, const Mat &img2, int kernelSize, Mat &dist1,Mat &dist2, const int type, int t, const Mat &IntegralImage1, const Mat &IntegralImage2 ) + CV_EXPORTS void modifiedCensusTransform(const Mat &img1, const Mat &img2, int kernelSize, Mat &dist1,Mat &dist2, const int type, int t, const Mat &IntegralImage1, const Mat &IntegralImage2 ) { CV_Assert(img1.size() == img2.size()); CV_Assert(kernelSize % 2 != 0); @@ -153,7 +153,7 @@ namespace cv CombinedDescriptor<2,3,2,2, MVKernel<2> >(img1.cols, img1.rows,stride,n2,date,MVKernel<2>(images,integral),n2)); } } - void modifiedCensusTransform(const Mat &img1, int kernelSize, Mat &dist, const int type, int t , Mat const &IntegralImage) + CV_EXPORTS void modifiedCensusTransform(const Mat &img1, int kernelSize, Mat &dist, const int type, int t , Mat const &IntegralImage) { CV_Assert(img1.size() == dist.size()); CV_Assert(kernelSize % 2 != 0); @@ -168,7 +168,7 @@ namespace cv { //MCT parallel_for_(Range(n2, img1.rows - n2), - CombinedDescriptor<2,3,2, 1,MCTKernel<1> >(img1.cols, img1.rows,stride,n2,date,MCTKernel<1>(images,t),n2)); + CombinedDescriptor<2,4,2, 1,MCTKernel<1> >(img1.cols, img1.rows,stride,n2,date,MCTKernel<1>(images,t),n2)); } else if(type == CV_MEAN_VARIATION) { @@ -180,7 +180,7 @@ namespace cv } //different versions of simetric census //These variants since they do not compare with the center they are invariant to noise - void symetricCensusTransform(const Mat &img1, const Mat &img2, int kernelSize, Mat &dist1, Mat &dist2, const int type) + CV_EXPORTS void symetricCensusTransform(const Mat &img1, const Mat &img2, int kernelSize, Mat &dist1, Mat &dist2, const int type) { CV_Assert(img1.size() == img2.size()); CV_Assert(kernelSize % 2 != 0); @@ -202,7 +202,7 @@ namespace cv CombinedDescriptor<1,1,1,2,ModifiedCsCensus<2> >(img1.cols, img1.rows,stride,n2,date,ModifiedCsCensus<2>(images,n2),1)); } } - void symetricCensusTransform(const Mat &img1, int kernelSize, Mat &dist1, const int type) + CV_EXPORTS void symetricCensusTransform(const Mat &img1, int kernelSize, Mat &dist1, const int type) { CV_Assert(img1.size() == dist1.size()); CV_Assert(kernelSize % 2 != 0); diff --git a/modules/stereo/src/descriptor.hpp b/modules/stereo/src/descriptor.hpp index 779b63015..6cdab8f33 100644 --- a/modules/stereo/src/descriptor.hpp +++ b/modules/stereo/src/descriptor.hpp @@ -424,26 +424,26 @@ namespace cv Implementation of a census transform which is taking into account just the some pixels from the census kernel thus allowing for larger block sizes **/ //void applyCensusOnImages(const cv::Mat &im1,const cv::Mat &im2, int kernelSize, cv::Mat &dist, cv::Mat &dist2, const int type); - void censusTransform(const cv::Mat &image1, const cv::Mat &image2, int kernelSize, cv::Mat &dist1, cv::Mat &dist2, const int type); + CV_EXPORTS void censusTransform(const cv::Mat &image1, const cv::Mat &image2, int kernelSize, cv::Mat &dist1, cv::Mat &dist2, const int type); //single image census transform - void censusTransform(const cv::Mat &image1, int kernelSize, cv::Mat &dist1, const int type); + CV_EXPORTS void censusTransform(const cv::Mat &image1, int kernelSize, cv::Mat &dist1, const int type); /** STANDARD_MCT - Modified census which is memorizing for each pixel 2 bits and includes a tolerance to the pixel comparison MCT_MEAN_VARIATION - Implementation of a modified census transform which is also taking into account the variation to the mean of the window not just the center pixel **/ - void modifiedCensusTransform(const cv::Mat &img1, const cv::Mat &img2, int kernelSize, cv::Mat &dist1,cv::Mat &dist2, const int type, int t = 0 , const cv::Mat &IntegralImage1 = cv::Mat::zeros(100,100,CV_8UC1), const cv::Mat &IntegralImage2 = cv::Mat::zeros(100,100,CV_8UC1)); + CV_EXPORTS void modifiedCensusTransform(const cv::Mat &img1, const cv::Mat &img2, int kernelSize, cv::Mat &dist1,cv::Mat &dist2, const int type, int t = 0 , const cv::Mat &IntegralImage1 = cv::Mat::zeros(100,100,CV_8UC1), const cv::Mat &IntegralImage2 = cv::Mat::zeros(100,100,CV_8UC1)); //single version of modified census transform descriptor - void modifiedCensusTransform(const cv::Mat &img1, int kernelSize, cv::Mat &dist, const int type, int t = 0 ,const cv::Mat &IntegralImage = cv::Mat::zeros(100,100,CV_8UC1)); + CV_EXPORTS void modifiedCensusTransform(const cv::Mat &img1, int kernelSize, cv::Mat &dist, const int type, int t = 0 ,const cv::Mat &IntegralImage = cv::Mat::zeros(100,100,CV_8UC1)); /**The classical center symetric census A modified version of cs census which is comparing a pixel with its correspondent after the center **/ - void symetricCensusTransform(const cv::Mat &img1, const cv::Mat &img2, int kernelSize, cv::Mat &dist1, cv::Mat &dist2, const int type); + CV_EXPORTS void symetricCensusTransform(const cv::Mat &img1, const cv::Mat &img2, int kernelSize, cv::Mat &dist1, cv::Mat &dist2, const int type); //single version of census transform - void symetricCensusTransform(const cv::Mat &img1, int kernelSize, cv::Mat &dist1, const int type); + CV_EXPORTS void symetricCensusTransform(const cv::Mat &img1, int kernelSize, cv::Mat &dist1, const int type); //in a 9x9 kernel only certain positions are choosen - void starCensusTransform(const cv::Mat &img1, const cv::Mat &img2, int kernelSize, cv::Mat &dist1,cv::Mat &dist2); + CV_EXPORTS void starCensusTransform(const cv::Mat &img1, const cv::Mat &img2, int kernelSize, cv::Mat &dist1,cv::Mat &dist2); //single image version of star kernel - void starCensusTransform(const cv::Mat &img1, int kernelSize, cv::Mat &dist); + CV_EXPORTS void starCensusTransform(const cv::Mat &img1, int kernelSize, cv::Mat &dist); //integral image computation used in the Mean Variation Census Transform void imageMeanKernelSize(const cv::Mat &img, int windowSize, cv::Mat &c); } diff --git a/modules/stereo/src/matching.hpp b/modules/stereo/src/matching.hpp index 87f6ef5e1..54031c20e 100644 --- a/modules/stereo/src/matching.hpp +++ b/modules/stereo/src/matching.hpp @@ -67,7 +67,7 @@ namespace cv //!the LUT used in case SSE is not available int hamLut[65537]; //!function used for getting the minimum disparity from the cost volume" - static int minim(int *c, int iwpj, int widthDisp,const double confidence, const int search_region) + static int minim(short *c, int iwpj, int widthDisp,const double confidence, const int search_region) { double mini, mini2, mini3; mini = mini2 = mini3 = DBL_MAX; @@ -101,7 +101,7 @@ namespace cv } //!Interpolate in order to obtain better results //!function for refining the disparity at sub pixel using simetric v - static double symetricVInterpolation(int *c, int iwjp, int widthDisp, int winDisp,const int search_region) + static double symetricVInterpolation(short *c, int iwjp, int widthDisp, int winDisp,const int search_region) { if (winDisp == 0 || winDisp == widthDisp - 1) return winDisp; @@ -146,12 +146,13 @@ namespace cv class hammingDistance : public ParallelLoopBody { private: - int *left, *right, *c; + int *left, *right; + short *c; int v,kernelSize, width, height; int MASK; int *hammLut; public : - hammingDistance(const Mat &leftImage, const Mat &rightImage, int *cost, int maxDisp, int kerSize, int *hammingLUT): + hammingDistance(const Mat &leftImage, const Mat &rightImage, short *cost, int maxDisp, int kerSize, int *hammingLUT): left((int *)leftImage.data), right((int *)rightImage.data), c(cost), v(maxDisp),kernelSize(kerSize),width(leftImage.cols), height(leftImage.rows), MASK(65535), hammLut(hammingLUT){} void operator()(const cv::Range &r) const { for (int i = r.start; i <= r.end ; i++) @@ -168,63 +169,31 @@ namespace cv j2 = (0 > j - d) ? (0) : (j - d); xorul = left[(iwj)] ^ right[(iw + j2)]; #if CV_SSE4_1 - c[(iwj)* (v + 1) + d] = _mm_popcnt_u32(xorul); + c[(iwj)* (v + 1) + d] = (short)_mm_popcnt_u32(xorul); #else - c[(iwj)* (v + 1) + d] = hammLut[xorul & MASK] + hammLut[xorul >> 16]; + c[(iwj)* (v + 1) + d] = (short)(hammLut[xorul & MASK] + hammLut[xorul >> 16]); #endif } } } } }; - //!preprocessing used for agregation - class costGatheringHorizontal:public ParallelLoopBody - { - private: - int *c, *ham; - int width, maxDisp; - public: - costGatheringHorizontal(const Mat &hamimg,const int maxDispa, Mat &output) - { - ham = (int *)hamimg.data; - c = (int *)output.data; - maxDisp = maxDispa; - width = output.cols / ( maxDisp + 1) - 1; - } - void operator()(const cv::Range &r) const { - for (int i = r.start; i <= r.end; i++) - { - int iw = i * width; - int iwi = (i - 1) * width; - for (int j = 1; j <= width; j++) - { - int iwj = (iw + j) * (maxDisp + 1); - int iwjmu = (iw + j - 1) * (maxDisp + 1); - int iwijmu = (iwi + j - 1) * (maxDisp + 1); - for (int d = 0; d <= maxDisp; d++) - { - c[iwj + d] = ham[iwijmu + d] + c[iwjmu + d]; - } - } - } - } - }; //!cost aggregation class agregateCost:public ParallelLoopBody { private: int win; - int *c, *parSum; + short *c, *parSum; int maxDisp,width, height; public: agregateCost(const Mat &partialSums, int windowSize, int maxDispa, Mat &cost) { win = windowSize / 2; - c = (int *)cost.data; + c = (short *)cost.data; maxDisp = maxDispa; width = cost.cols / ( maxDisp + 1) - 1; height = cost.rows - 1; - parSum = (int *)partialSums.data; + parSum = (short *)partialSums.data; } void operator()(const cv::Range &r) const { for (int i = r.start; i <= r.end; i++) @@ -255,11 +224,11 @@ namespace cv int width,disparity,scallingFact,th; double confCheck; uint8_t *map; - int *c; + short *c; public: makeMap(const Mat &costVolume, int threshold, int maxDisp, double confidence,int scale, Mat &mapFinal) { - c = (int *)costVolume.data; + c = (short *)costVolume.data; map = mapFinal.data; disparity = maxDisp; width = costVolume.cols / ( disparity + 1) - 1; @@ -393,6 +362,7 @@ namespace cv int *specklePointX; int *specklePointY; long long *pus; + int previous_size; //!method for setting the maximum disparity void setMaxDisparity(int val) { @@ -436,22 +406,37 @@ namespace cv CV_Assert(kernelSize % 2 != 0); CV_Assert(cost.rows == leftImage.rows); CV_Assert(cost.cols / (maxDisparity + 1) == leftImage.cols); - int *c = (int *)cost.data; + short *c = (short *)cost.data; memset(c, 0, sizeof(c[0]) * leftImage.cols * leftImage.rows * (maxDisparity + 1)); - parallel_for_(cv::Range(kernelSize / 2,leftImage.rows - kernelSize / 2), hammingDistance(leftImage,rightImage,(int *)cost.data,maxDisparity,kernelSize / 2,hamLut)); + parallel_for_(cv::Range(kernelSize / 2,leftImage.rows - kernelSize / 2), hammingDistance(leftImage,rightImage,(short *)cost.data,maxDisparity,kernelSize / 2,hamLut)); } //preprocessing the cost volume in order to get it ready for aggregation void costGathering(const Mat &hammingDistanceCost, Mat &cost) { CV_Assert(hammingDistanceCost.rows == hammingDistanceCost.rows); - CV_Assert(hammingDistanceCost.type() == CV_32SC4); - CV_Assert(cost.type() == CV_32SC4); + CV_Assert(hammingDistanceCost.type() == CV_16S); + CV_Assert(cost.type() == CV_16S); int maxDisp = maxDisparity; int width = cost.cols / ( maxDisp + 1) - 1; int height = cost.rows - 1; - int *c = (int *)cost.data; + short *c = (short *)cost.data; + short *ham = (short *)hammingDistanceCost.data; memset(c, 0, sizeof(c[0]) * (width + 1) * (height + 1) * (maxDisp + 1)); - parallel_for_(cv::Range(1,height), costGatheringHorizontal(hammingDistanceCost,maxDisparity,cost)); + for (int i = 1; i <= height; i++) + { + int iw = i * width; + int iwi = (i - 1) * width; + for (int j = 1; j <= width; j++) + { + int iwj = (iw + j) * (maxDisp + 1); + int iwjmu = (iw + j - 1) * (maxDisp + 1); + int iwijmu = (iwi + j - 1) * (maxDisp + 1); + for (int d = 0; d <= maxDisp; d++) + { + c[iwj + d] = ham[iwijmu + d] + c[iwjmu + d]; + } + } + } for (int i = 1; i <= height; i++) { for (int j = 1; j <= width; j++) @@ -472,7 +457,7 @@ namespace cv CV_Assert(partialSums.rows == cost.rows); CV_Assert(partialSums.cols == cost.cols); int win = windowSize / 2; - int *c = (int *)cost.data; + short *c = (short *)cost.data; int maxDisp = maxDisparity; int width = cost.cols / ( maxDisp + 1) - 1; int height = cost.rows - 1; @@ -484,8 +469,8 @@ namespace cv { CV_Assert(currentMap.cols == out.cols); CV_Assert(currentMap.rows == out.rows); - CV_Assert(t > 0); - memset(pus, 0, sizeof(pus)); + CV_Assert(t >= 0); + memset(pus, 0, previous_size * sizeof(pus[0])); uint8_t *map = currentMap.data; uint8_t *outputMap = out.data; int height = currentMap.rows; diff --git a/modules/stereo/src/stereo_binary_bm.cpp b/modules/stereo/src/stereo_binary_bm.cpp index 98ab6d6cd..500af0e8e 100644 --- a/modules/stereo/src/stereo_binary_bm.cpp +++ b/modules/stereo/src/stereo_binary_bm.cpp @@ -269,7 +269,7 @@ namespace cv class StereoBinaryBMImpl : public StereoBinaryBM, public Matching { public: - StereoBinaryBMImpl() + StereoBinaryBMImpl(): Matching(64) { params = StereoBinaryBMParams(); } @@ -322,20 +322,6 @@ namespace cv Mat left0 = leftarr.getMat(), right0 = rightarr.getMat(); Mat disp0 = disparr.getMat(); - censusImage[0].create(left0.rows,left0.cols,CV_32SC4); - censusImage[1].create(left0.rows,left0.cols,CV_32SC4); - - partialSumsLR.create(left0.rows + 1,(left0.cols + 1) * (params.numDisparities + 1),CV_32SC4); - agregatedHammingLRCost.create(left0.rows + 1,(left0.cols + 1) * (params.numDisparities + 1),CV_32SC4); - hammingDistance.create(left0.rows, left0.cols * (params.numDisparities + 1),CV_32SC4); - - preFilteredImg0.create(left0.size(), CV_8U); - preFilteredImg1.create(left0.size(), CV_8U); - - Mat left = preFilteredImg0, right = preFilteredImg1; - - int ndisp = params.numDisparities; - int width = left0.cols; int height = left0.rows; @@ -345,8 +331,22 @@ namespace cv specklePointX = new int[width * height]; specklePointY = new int[width * height]; pus = new long long[width * height]; + + censusImage[0].create(left0.rows,left0.cols,CV_32SC4); + censusImage[1].create(left0.rows,left0.cols,CV_32SC4); + + partialSumsLR.create(left0.rows + 1,(left0.cols + 1) * (params.numDisparities + 1),CV_16S); + agregatedHammingLRCost.create(left0.rows + 1,(left0.cols + 1) * (params.numDisparities + 1),CV_16S); + hammingDistance.create(left0.rows, left0.cols * (params.numDisparities + 1),CV_16S); + + preFilteredImg0.create(left0.size(), CV_8U); + preFilteredImg1.create(left0.size(), CV_8U); } + Mat left = preFilteredImg0, right = preFilteredImg1; + + int ndisp = params.numDisparities; + int wsz = params.kernelSize; int bufSize0 = (int)((ndisp + 2)*sizeof(int)); bufSize0 += (int)((height + wsz + 2)*ndisp*sizeof(int)); @@ -419,52 +419,52 @@ namespace cv } } int getAgregationWindowSize() const { return params.agregationWindowSize;} - void setAgregationWindowSize(int value = 9) { params.agregationWindowSize = value;} + void setAgregationWindowSize(int value = 9) { CV_Assert(value % 2 != 0); params.agregationWindowSize = value;} int getBinaryKernelType() const { return params.kernelType;} - void setBinaryKernelType(int value = CV_MODIFIED_CENSUS_TRANSFORM) { params.kernelType = value; } + void setBinaryKernelType(int value = CV_MODIFIED_CENSUS_TRANSFORM) { CV_Assert(value < 7); params.kernelType = value; } int getSpekleRemovalTechnique() const { return params.regionRemoval;} - void setSpekleRemovalTechnique(int factor = CV_SPECKLE_REMOVAL_AVG_ALGORITHM) { params.regionRemoval = factor; } + void setSpekleRemovalTechnique(int factor = CV_SPECKLE_REMOVAL_AVG_ALGORITHM) {CV_Assert(factor < 2); params.regionRemoval = factor; } bool getUsePrefilter() const { return params.usePrefilter;} void setUsePrefilter(bool value = false) { params.usePrefilter = value;} int getScalleFactor() const { return params.scalling;} - void setScalleFactor(int factor) {params.scalling = factor; setScallingFactor(factor);} + void setScalleFactor(int factor = 4) {CV_Assert(factor > 0); params.scalling = factor; setScallingFactor(factor);} int getMinDisparity() const { return params.minDisparity; } - void setMinDisparity(int minDisparity) { params.minDisparity = minDisparity; } + void setMinDisparity(int minDisparity) {CV_Assert(minDisparity >= 0); params.minDisparity = minDisparity; } int getNumDisparities() const { return params.numDisparities; } - void setNumDisparities(int numDisparities) { params.numDisparities = numDisparities; } + void setNumDisparities(int numDisparities) {CV_Assert(numDisparities > 0); params.numDisparities = numDisparities; } int getBlockSize() const { return params.kernelSize; } - void setBlockSize(int blockSize) { params.kernelSize = blockSize; } + void setBlockSize(int blockSize) {CV_Assert(blockSize % 2 != 0); params.kernelSize = blockSize; } int getSpeckleWindowSize() const { return params.speckleWindowSize; } - void setSpeckleWindowSize(int speckleWindowSize) { params.speckleWindowSize = speckleWindowSize; } + void setSpeckleWindowSize(int speckleWindowSize) {CV_Assert(speckleWindowSize >= 0); params.speckleWindowSize = speckleWindowSize; } int getSpeckleRange() const { return params.speckleRange; } - void setSpeckleRange(int speckleRange) { params.speckleRange = speckleRange; } + void setSpeckleRange(int speckleRange) {CV_Assert(speckleRange >= 0); params.speckleRange = speckleRange; } int getDisp12MaxDiff() const { return params.disp12MaxDiff; } - void setDisp12MaxDiff(int disp12MaxDiff) { params.disp12MaxDiff = disp12MaxDiff; } + void setDisp12MaxDiff(int disp12MaxDiff) {CV_Assert(disp12MaxDiff >= 0); params.disp12MaxDiff = disp12MaxDiff; } int getPreFilterType() const { return params.preFilterType; } - void setPreFilterType(int preFilterType) { params.preFilterType = preFilterType; } + void setPreFilterType(int preFilterType) { CV_Assert(preFilterType >= 0); params.preFilterType = preFilterType; } int getPreFilterSize() const { return params.preFilterSize; } - void setPreFilterSize(int preFilterSize) { params.preFilterSize = preFilterSize; } + void setPreFilterSize(int preFilterSize) { CV_Assert(preFilterSize >= 0); params.preFilterSize = preFilterSize; } int getPreFilterCap() const { return params.preFilterCap; } - void setPreFilterCap(int preFilterCap) { params.preFilterCap = preFilterCap; } + void setPreFilterCap(int preFilterCap) {CV_Assert(preFilterCap >= 0); params.preFilterCap = preFilterCap; } int getTextureThreshold() const { return params.textureThreshold; } - void setTextureThreshold(int textureThreshold) { params.textureThreshold = textureThreshold; } + void setTextureThreshold(int textureThreshold) {CV_Assert(textureThreshold >= 0); params.textureThreshold = textureThreshold; } int getUniquenessRatio() const { return params.uniquenessRatio; } - void setUniquenessRatio(int uniquenessRatio) { params.uniquenessRatio = uniquenessRatio; } + void setUniquenessRatio(int uniquenessRatio) {CV_Assert(uniquenessRatio >= 0); params.uniquenessRatio = uniquenessRatio; } int getSmallerBlockSize() const { return 0; } void setSmallerBlockSize(int) {} @@ -506,12 +506,11 @@ namespace cv Mat preFilteredImg0, preFilteredImg1, cost, dispbuf; Mat slidingSumBuf; Mat parSumsIntensityImage[2]; + Mat Integral[2]; Mat censusImage[2]; Mat hammingDistance; Mat partialSumsLR; Mat agregatedHammingLRCost; - Mat Integral[2]; - int previous_size; static const char* name_; }; diff --git a/modules/stereo/src/stereo_binary_sgbm.cpp b/modules/stereo/src/stereo_binary_sgbm.cpp index fcbf53d55..01e015e98 100644 --- a/modules/stereo/src/stereo_binary_sgbm.cpp +++ b/modules/stereo/src/stereo_binary_sgbm.cpp @@ -47,8 +47,11 @@ by Heiko Hirschmuller. We match blocks rather than individual pixels, thus the algorithm is called SGBM (Semi-global block matching) */ + #include "precomp.hpp" #include +#include +#include namespace cv { @@ -58,12 +61,13 @@ namespace cv typedef short CostType; typedef short DispType; enum { NR = 16, NR2 = NR/2 }; + struct StereoBinarySGBMParams { StereoBinarySGBMParams() { minDisparity = numDisparities = 0; - SADWindowSize = 0; + kernelSize = 0; P1 = P2 = 0; disp12MaxDiff = 0; preFilterCap = 0; @@ -79,7 +83,7 @@ namespace cv { minDisparity = _minDisparity; numDisparities = _numDisparities; - SADWindowSize = _SADWindowSize; + kernelSize = _SADWindowSize; P1 = _P1; P2 = _P2; disp12MaxDiff = _disp12MaxDiff; @@ -88,10 +92,13 @@ namespace cv speckleWindowSize = _speckleWindowSize; speckleRange = _speckleRange; mode = _mode; + regionRemoval = 1; + kernelType = CV_MODIFIED_CENSUS_TRANSFORM; + subpixelInterpolationMethod = CV_QUADRATIC_INTERPOLATION; } int minDisparity; int numDisparities; - int SADWindowSize; + int kernelSize; int preFilterCap; int uniquenessRatio; int P1; @@ -100,163 +107,11 @@ namespace cv int speckleRange; int disp12MaxDiff; int mode; + int regionRemoval; + int kernelType; + int subpixelInterpolationMethod; }; - /* - For each pixel row1[x], max(-maxD, 0) <= minX <= x < maxX <= width - max(0, -minD), - and for each disparity minD<=d(y), *row2 = img2.ptr(y); - PixType *prow1 = buffer + width2*2, *prow2 = prow1 + width*cn*2; - tab += tabOfs; - for( c = 0; c < cn*2; c++ ) - { - prow1[width*c] = prow1[width*c + width-1] = - prow2[width*c] = prow2[width*c + width-1] = tab[0]; - } - int n1 = y > 0 ? -(int)img1.step : 0, s1 = y < img1.rows-1 ? (int)img1.step : 0; - int n2 = y > 0 ? -(int)img2.step : 0, s2 = y < img2.rows-1 ? (int)img2.step : 0; - if( cn == 1 ) - { - for( x = 1; x < width-1; x++ ) - { - prow1[x] = tab[(row1[x+1] - row1[x-1])*2 + row1[x+n1+1] - row1[x+n1-1] + row1[x+s1+1] - row1[x+s1-1]]; - prow2[width-1-x] = tab[(row2[x+1] - row2[x-1])*2 + row2[x+n2+1] - row2[x+n2-1] + row2[x+s2+1] - row2[x+s2-1]]; - prow1[x+width] = row1[x]; - prow2[width-1-x+width] = row2[x]; - } - } - else - { - for( x = 1; x < width-1; x++ ) - { - prow1[x] = tab[(row1[x*3+3] - row1[x*3-3])*2 + row1[x*3+n1+3] - row1[x*3+n1-3] + row1[x*3+s1+3] - row1[x*3+s1-3]]; - prow1[x+width] = tab[(row1[x*3+4] - row1[x*3-2])*2 + row1[x*3+n1+4] - row1[x*3+n1-2] + row1[x*3+s1+4] - row1[x*3+s1-2]]; - prow1[x+width*2] = tab[(row1[x*3+5] - row1[x*3-1])*2 + row1[x*3+n1+5] - row1[x*3+n1-1] + row1[x*3+s1+5] - row1[x*3+s1-1]]; - prow2[width-1-x] = tab[(row2[x*3+3] - row2[x*3-3])*2 + row2[x*3+n2+3] - row2[x*3+n2-3] + row2[x*3+s2+3] - row2[x*3+s2-3]]; - prow2[width-1-x+width] = tab[(row2[x*3+4] - row2[x*3-2])*2 + row2[x*3+n2+4] - row2[x*3+n2-2] + row2[x*3+s2+4] - row2[x*3+s2-2]]; - prow2[width-1-x+width*2] = tab[(row2[x*3+5] - row2[x*3-1])*2 + row2[x*3+n2+5] - row2[x*3+n2-1] + row2[x*3+s2+5] - row2[x*3+s2-1]]; - prow1[x+width*3] = row1[x*3]; - prow1[x+width*4] = row1[x*3+1]; - prow1[x+width*5] = row1[x*3+2]; - prow2[width-1-x+width*3] = row2[x*3]; - prow2[width-1-x+width*4] = row2[x*3+1]; - prow2[width-1-x+width*5] = row2[x*3+2]; - } - } - memset( cost, 0, width1*D*sizeof(cost[0]) ); - buffer -= minX2; - cost -= minX1*D + minD; // simplify the cost indices inside the loop -#if CV_SSE2 - volatile bool useSIMD = checkHardwareSupport(CV_CPU_SSE2); -#endif -#if 1 - for( c = 0; c < cn*2; c++, prow1 += width, prow2 += width ) - { - int diff_scale = c < cn ? 0 : 2; - // precompute - // v0 = min(row2[x-1/2], row2[x], row2[x+1/2]) and - // v1 = max(row2[x-1/2], row2[x], row2[x+1/2]) and - for( x = minX2; x < maxX2; x++ ) - { - int v = prow2[x]; - int vl = x > 0 ? (v + prow2[x-1])/2 : v; - int vr = x < width-1 ? (v + prow2[x+1])/2 : v; - int v0 = std::min(vl, vr); v0 = std::min(v0, v); - int v1 = std::max(vl, vr); v1 = std::max(v1, v); - buffer[x] = (PixType)v0; - buffer[x + width2] = (PixType)v1; - } - for( x = minX1; x < maxX1; x++ ) - { - int u = prow1[x]; - int ul = x > 0 ? (u + prow1[x-1])/2 : u; - int ur = x < width-1 ? (u + prow1[x+1])/2 : u; - int u0 = std::min(ul, ur); u0 = std::min(u0, u); - int u1 = std::max(ul, ur); u1 = std::max(u1, u); -#if CV_SSE2 - if( useSIMD ) - { - __m128i _u = _mm_set1_epi8((char)u), _u0 = _mm_set1_epi8((char)u0); - __m128i _u1 = _mm_set1_epi8((char)u1), z = _mm_setzero_si128(); - __m128i ds = _mm_cvtsi32_si128(diff_scale); - for( int d = minD; d < maxD; d += 16 ) - { - __m128i _v = _mm_loadu_si128((const __m128i*)(prow2 + width-x-1 + d)); - __m128i _v0 = _mm_loadu_si128((const __m128i*)(buffer + width-x-1 + d)); - __m128i _v1 = _mm_loadu_si128((const __m128i*)(buffer + width-x-1 + d + width2)); - __m128i c0 = _mm_max_epu8(_mm_subs_epu8(_u, _v1), _mm_subs_epu8(_v0, _u)); - __m128i c1 = _mm_max_epu8(_mm_subs_epu8(_v, _u1), _mm_subs_epu8(_u0, _v)); - __m128i diff = _mm_min_epu8(c0, c1); - c0 = _mm_load_si128((__m128i*)(cost + x*D + d)); - c1 = _mm_load_si128((__m128i*)(cost + x*D + d + 8)); - _mm_store_si128((__m128i*)(cost + x*D + d), _mm_adds_epi16(c0, _mm_srl_epi16(_mm_unpacklo_epi8(diff,z), ds))); - _mm_store_si128((__m128i*)(cost + x*D + d + 8), _mm_adds_epi16(c1, _mm_srl_epi16(_mm_unpackhi_epi8(diff,z), ds))); - } - } - else -#endif - { - for( int d = minD; d < maxD; d++ ) - { - int v = prow2[width-x-1 + d]; - int v0 = buffer[width-x-1 + d]; - int v1 = buffer[width-x-1 + d + width2]; - int c0 = std::max(0, u - v1); c0 = std::max(c0, v0 - u); - int c1 = std::max(0, v - u1); c1 = std::max(c1, u0 - v); - cost[x*D + d] = (CostType)(cost[x*D+d] + (std::min(c0, c1) >> diff_scale)); - } - } - } - } -#else - for( c = 0; c < cn*2; c++, prow1 += width, prow2 += width ) - { - for( x = minX1; x < maxX1; x++ ) - { - int u = prow1[x]; -#if CV_SSE2 - if( useSIMD ) - { - __m128i _u = _mm_set1_epi8(u), z = _mm_setzero_si128(); - - for( int d = minD; d < maxD; d += 16 ) - { - __m128i _v = _mm_loadu_si128((const __m128i*)(prow2 + width-1-x + d)); - __m128i diff = _mm_adds_epu8(_mm_subs_epu8(_u,_v), _mm_subs_epu8(_v,_u)); - __m128i c0 = _mm_load_si128((__m128i*)(cost + x*D + d)); - __m128i c1 = _mm_load_si128((__m128i*)(cost + x*D + d + 8)); - _mm_store_si128((__m128i*)(cost + x*D + d), _mm_adds_epi16(c0, _mm_unpacklo_epi8(diff,z))); - _mm_store_si128((__m128i*)(cost + x*D + d + 8), _mm_adds_epi16(c1, _mm_unpackhi_epi8(diff,z))); - } - } - else -#endif - { - for( int d = minD; d < maxD; d++ ) - { - int v = prow2[width-1-x + d]; - cost[x*D + d] = (CostType)(cost[x*D + d] + (CostType)std::abs(u - v)); - } - } - } - } -#endif - } /* computes disparity for "roi" in img1 w.r.t. img2 and write it to disp1buf. that is, disp1buf(x, y)=d means that img1(x+roi.x, y+roi.y) ~ img2(x+roi.x-d, y+roi.y). @@ -276,7 +131,7 @@ namespace cv */ static void computeDisparityBinarySGBM( const Mat& img1, const Mat& img2, Mat& disp1, const StereoBinarySGBMParams& params, - Mat& buffer ) + Mat& buffer,const Mat& hamDist) { #if CV_SSE2 static const uchar LSBTab[] = @@ -293,14 +148,14 @@ namespace cv volatile bool useSIMD = checkHardwareSupport(CV_CPU_SSE2); #endif + const int ALIGN = 16; const int DISP_SHIFT = StereoMatcher::DISP_SHIFT; const int DISP_SCALE = (1 << DISP_SHIFT); const CostType MAX_COST = SHRT_MAX; int minD = params.minDisparity, maxD = minD + params.numDisparities; - Size SADWindowSize; - SADWindowSize.width = SADWindowSize.height = params.SADWindowSize > 0 ? params.SADWindowSize : 5; - int ftzero = std::max(params.preFilterCap, 15) | 1; + Size kernelSize; + kernelSize.width = kernelSize.height = params.kernelSize > 0 ? params.kernelSize : 5; int uniquenessRatio = params.uniquenessRatio >= 0 ? params.uniquenessRatio : 10; int disp12MaxDiff = params.disp12MaxDiff > 0 ? params.disp12MaxDiff : 1; int P1 = params.P1 > 0 ? params.P1 : 2, P2 = std::max(params.P2 > 0 ? params.P2 : 5, P1+1); @@ -308,13 +163,10 @@ namespace cv int minX1 = std::max(-maxD, 0), maxX1 = width + std::min(minD, 0); int D = maxD - minD, width1 = maxX1 - minX1; int INVALID_DISP = minD - 1, INVALID_DISP_SCALED = INVALID_DISP*DISP_SCALE; - int SW2 = SADWindowSize.width/2, SH2 = SADWindowSize.height/2; + int SW2 = kernelSize.width/2, SH2 = kernelSize.height/2; bool fullDP = params.mode == StereoBinarySGBM::MODE_HH; int npasses = fullDP ? 2 : 1; - const int TAB_OFS = 256*4, TAB_SIZE = 256 + TAB_OFS*2; - PixType clipTab[TAB_SIZE]; - for( k = 0; k < TAB_SIZE; k++ ) - clipTab[k] = (PixType)(std::min(std::max(k - TAB_OFS, -ftzero), ftzero) + ftzero); + if( minX1 >= maxX1 ) { disp1 = Scalar::all(INVALID_DISP_SCALED); @@ -329,6 +181,9 @@ namespace cv // the previous row, i.e. 2 rows in total const int NLR = 2; const int LrBorder = NLR - 1; + int ww = img2.cols; + short *ham; + ham = (short *)hamDist.data; // for each possible stereo match (img1(x,y) <=> img2(x-d,y)) // we keep pixel difference cost (C) and the summary cost over NR directions (S). // we also keep all the partial costs for the previous line L_r(x,d) and also min_k L_r(x, k) @@ -351,7 +206,7 @@ namespace cv CostType* pixDiff = hsumBuf + costBufSize*hsumBufNRows; CostType* disp2cost = pixDiff + costBufSize + (LrSize + minLrSize)*NLR; DispType* disp2ptr = (DispType*)(disp2cost + width); - PixType* tempBuf = (PixType*)(disp2ptr + width); + // PixType* tempBuf = (PixType*)(disp2ptr + width); // add P2 to every C(x,y). it saves a few operations in the inner loops for( k = 0; k < width1*D; k++ ) Cbuf[k] = (CostType)P2; @@ -393,10 +248,15 @@ namespace cv for( k = dy1; k <= dy2; k++ ) { CostType* hsumAdd = hsumBuf + (std::min(k, height-1) % hsumBufNRows)*costBufSize; - if( k < height ) { - calcPixelCostBT( img1, img2, k, minD, maxD, pixDiff, tempBuf, clipTab, TAB_OFS, ftzero ); + for(int ii = 0; ii <= ww; ii++) + { + for(int dd = 0; dd <= params.numDisparities; dd++) + { + pixDiff[ii * (params.numDisparities)+ dd] = (CostType)(ham[(k * (ww) + ii) * (params.numDisparities +1) + dd]); + } + } memset(hsumAdd, 0, D*sizeof(CostType)); for( x = 0; x <= SW2*D; x += D ) { @@ -404,14 +264,17 @@ namespace cv for( d = 0; d < D; d++ ) hsumAdd[d] = (CostType)(hsumAdd[d] + pixDiff[x + d]*scale); } + if( y > 0 ) { const CostType* hsumSub = hsumBuf + (std::max(y - SH2 - 1, 0) % hsumBufNRows)*costBufSize; const CostType* Cprev = !fullDP || y == 0 ? C : C - costBufSize; + for( x = D; x < width1*D; x += D ) { const CostType* pixAdd = pixDiff + std::min(x + SW2*D, (width1-1)*D); const CostType* pixSub = pixDiff + std::max(x - (SW2+1)*D, 0); + #if CV_SSE2 if( useSIMD ) { @@ -554,21 +417,28 @@ namespace cv #endif { int minL0 = MAX_COST, minL1 = MAX_COST, minL2 = MAX_COST, minL3 = MAX_COST; + for( d = 0; d < D; d++ ) { int Cpd = Cp[d], L0, L1, L2, L3; + L0 = Cpd + std::min((int)Lr_p0[d], std::min(Lr_p0[d-1] + P1, std::min(Lr_p0[d+1] + P1, delta0))) - delta0; L1 = Cpd + std::min((int)Lr_p1[d], std::min(Lr_p1[d-1] + P1, std::min(Lr_p1[d+1] + P1, delta1))) - delta1; L2 = Cpd + std::min((int)Lr_p2[d], std::min(Lr_p2[d-1] + P1, std::min(Lr_p2[d+1] + P1, delta2))) - delta2; L3 = Cpd + std::min((int)Lr_p3[d], std::min(Lr_p3[d-1] + P1, std::min(Lr_p3[d+1] + P1, delta3))) - delta3; + Lr_p[d] = (CostType)L0; minL0 = std::min(minL0, L0); + Lr_p[d + D2] = (CostType)L1; minL1 = std::min(minL1, L1); + Lr_p[d + D2*2] = (CostType)L2; minL2 = std::min(minL2, L2); + Lr_p[d + D2*3] = (CostType)L3; minL3 = std::min(minL3, L3); + Sp[d] = saturate_cast(Sp[d] + L0 + L1 + L2 + L3); } minLr[0][xm] = (CostType)minL0; @@ -577,6 +447,7 @@ namespace cv minLr[0][xm+3] = (CostType)minL3; } } + if( pass == npasses ) { for( x = 0; x < width; x++ ) @@ -593,6 +464,7 @@ namespace cv if( npasses == 1 ) { int xm = x*NR2, xd = xm*D2; + int minL0 = MAX_COST; int delta0 = minLr[0][xm + NR2] + P2; CostType* Lr_p0 = Lr[0] + xd + NRD2; @@ -685,11 +557,42 @@ namespace cv } if( 0 < d && d < D-1 ) { - // do subpixel quadratic interpolation: - // fit parabola into (x1=d-1, y1=Sp[d-1]), (x2=d, y2=Sp[d]), (x3=d+1, y3=Sp[d+1]) - // then find minimum of the parabola. - int denom2 = std::max(Sp[d-1] + Sp[d+1] - 2*Sp[d], 1); - d = d*DISP_SCALE + ((Sp[d-1] - Sp[d+1])*DISP_SCALE + denom2)/(denom2*2); + if(params.subpixelInterpolationMethod == CV_SIMETRICV_INTERPOLATION) + { + double m2m1, m3m1, m3, m2, m1; + m2 = Sp[d - 1]; + m3 = Sp[d + 1]; + m1 = Sp[d]; + m2m1 = m2 - m1; + m3m1 = m3 - m1; + if (!(m2m1 == 0 || m3m1 == 0)) + { + double p; + p = 0; + if (m2 > m3) + { + p = (0.5 - 0.25 * ((m3m1 * m3m1) / (m2m1 * m2m1) + (m3m1 / m2m1))); + } + else + { + p = -1 * (0.5 - 0.25 * ((m2m1 * m2m1) / (m3m1 * m3m1) + (m2m1 / m3m1))); + } + if (p >= -0.5 && p <= 0.5) + d = (int)(d * DISP_SCALE + p * DISP_SCALE ); + } + else + { + d *= DISP_SCALE; + } + } + else if(params.subpixelInterpolationMethod == CV_QUADRATIC_INTERPOLATION) + { + // do subpixel quadratic interpolation: + // fit parabola into (x1=d-1, y1=Sp[d-1]), (x2=d, y2=Sp[d]), (x3=d+1, y3=Sp[d+1]) + // then find minimum of the parabola. + int denom2 = std::max(Sp[d-1] + Sp[d+1] - 2*Sp[d], 1); + d = d*DISP_SCALE + ((Sp[d-1] - Sp[d+1])*DISP_SCALE + denom2)/(denom2*2); + } } else d *= DISP_SCALE; @@ -717,17 +620,17 @@ namespace cv } } } - class StereoBinarySGBMImpl : public StereoBinarySGBM + class StereoBinarySGBMImpl : public StereoBinarySGBM, public Matching { public: - StereoBinarySGBMImpl() + StereoBinarySGBMImpl():Matching() { params = StereoBinarySGBMParams(); } StereoBinarySGBMImpl( int _minDisparity, int _numDisparities, int _SADWindowSize, int _P1, int _P2, int _disp12MaxDiff, int _preFilterCap, int _uniquenessRatio, int _speckleWindowSize, int _speckleRange, - int _mode ) + int _mode ):Matching(_numDisparities) { params = StereoBinarySGBMParams( _minDisparity, _numDisparities, _SADWindowSize, _P1, _P2, _disp12MaxDiff, _preFilterCap, @@ -741,41 +644,132 @@ namespace cv left.depth() == CV_8U ); disparr.create( left.size(), CV_16S ); Mat disp = disparr.getMat(); - computeDisparityBinarySGBM( left, right, disp, params, buffer ); - // medianBlur(disp, disp, 3); - if( params.speckleWindowSize > 0 ) - filterSpeckles(disp, (params.minDisparity - 1)*StereoMatcher::DISP_SCALE, params.speckleWindowSize, - StereoMatcher::DISP_SCALE*params.speckleRange, buffer); + censusImageLeft.create(left.rows,left.cols,CV_32SC4); + censusImageRight.create(left.rows,left.cols,CV_32SC4); + + hamDist.create(left.rows, left.cols * (params.numDisparities + 1),CV_16S); + + if(params.kernelType == CV_SPARSE_CENSUS) + { + censusTransform(left,right,params.kernelSize,censusImageLeft,censusImageRight,CV_SPARSE_CENSUS); + } + else if(params.kernelType == CV_DENSE_CENSUS) + { + censusTransform(left,right,params.kernelSize,censusImageLeft,censusImageRight,CV_SPARSE_CENSUS); + } + else if(params.kernelType == CV_CS_CENSUS) + { + symetricCensusTransform(left,right,params.kernelSize,censusImageLeft,censusImageRight,CV_CS_CENSUS); + } + else if(params.kernelType == CV_MODIFIED_CS_CENSUS) + { + symetricCensusTransform(left,right,params.kernelSize,censusImageLeft,censusImageRight,CV_MODIFIED_CS_CENSUS); + } + else if(params.kernelType == CV_MODIFIED_CENSUS_TRANSFORM) + { + modifiedCensusTransform(left,right,params.kernelSize,censusImageLeft,censusImageRight,CV_MODIFIED_CENSUS_TRANSFORM,0); + } + else if(params.kernelType == CV_MEAN_VARIATION) + { + parSumsIntensityImage[0].create(left.rows, left.cols,CV_32SC4); + parSumsIntensityImage[1].create(left.rows, left.cols,CV_32SC4); + Integral[0].create(left.rows,left.cols,CV_32SC4); + Integral[1].create(left.rows,left.cols,CV_32SC4); + integral(left, parSumsIntensityImage[0],CV_32S); + integral(right, parSumsIntensityImage[1],CV_32S); + imageMeanKernelSize(parSumsIntensityImage[0], params.kernelSize,Integral[0]); + imageMeanKernelSize(parSumsIntensityImage[1], params.kernelSize, Integral[1]); + modifiedCensusTransform(left,right,params.kernelSize,censusImageLeft,censusImageRight,CV_MEAN_VARIATION,0,Integral[0], Integral[1]); + } + else if(params.kernelType == CV_STAR_KERNEL) + { + starCensusTransform(left,right,params.kernelSize,censusImageLeft,censusImageRight); + } + + hammingDistanceBlockMatching(censusImageLeft, censusImageRight, hamDist); + + computeDisparityBinarySGBM( left, right, disp, params, buffer,hamDist); + + if(params.regionRemoval == CV_SPECKLE_REMOVAL_AVG_ALGORITHM) + { + int width = left.cols; + int height = left.rows; + if(previous_size != width * height) + { + previous_size = width * height; + specklePointX = new int[width * height]; + specklePointY = new int[width * height]; + pus = new long long[width * height]; + } + double minVal; double maxVal; + Mat imgDisparity8U2; + imgDisparity8U2.create(height,width,CV_8UC1); + Mat aux; + aux.create(height,width,CV_8UC1); + minMaxLoc(disp, &minVal, &maxVal); + disp.convertTo(imgDisparity8U2, CV_8UC1, 255 / (maxVal - minVal)); + Median1x9Filter(imgDisparity8U2, aux); + Median9x1Filter(aux,imgDisparity8U2); + smallRegionRemoval(imgDisparity8U2,params.speckleWindowSize,imgDisparity8U2); + imgDisparity8U2.convertTo(disp, CV_16S); + } + else if(params.regionRemoval == CV_SPECKLE_REMOVAL_ALGORITHM) + { + int width = left.cols; + int height = left.rows; + double minVal; double maxVal; + Mat imgDisparity8U2; + imgDisparity8U2.create(height,width,CV_8UC1); + Mat aux; + aux.create(height,width,CV_8UC1); + minMaxLoc(disp, &minVal, &maxVal); + disp.convertTo(imgDisparity8U2, CV_8UC1, 255 / (maxVal - minVal)); + Median1x9Filter(imgDisparity8U2, aux); + Median9x1Filter(aux,imgDisparity8U2); + imgDisparity8U2.convertTo(disp, CV_16S); + if( params.speckleWindowSize > 0 ) + filterSpeckles(disp, (params.minDisparity - 1) * StereoMatcher::DISP_SCALE, params.speckleWindowSize, + StereoMatcher::DISP_SCALE * params.speckleRange, buffer); + } } + int getSubPixelInterpolationMethod() const { return params.subpixelInterpolationMethod;} + void setSubPixelInterpolationMethod(int value = CV_QUADRATIC_INTERPOLATION) { CV_Assert(value < 2); params.subpixelInterpolationMethod = value;} + + int getBinaryKernelType() const { return params.kernelType;} + void setBinaryKernelType(int value = CV_MODIFIED_CENSUS_TRANSFORM) { CV_Assert(value < 7); params.kernelType = value; } + + int getSpekleRemovalTechnique() const { return params.regionRemoval;} + void setSpekleRemovalTechnique(int factor = CV_SPECKLE_REMOVAL_AVG_ALGORITHM) { CV_Assert(factor < 2); params.regionRemoval = factor; } + int getMinDisparity() const { return params.minDisparity; } - void setMinDisparity(int minDisparity) { params.minDisparity = minDisparity; } + void setMinDisparity(int minDisparity) {CV_Assert(minDisparity >= 0); params.minDisparity = minDisparity; } int getNumDisparities() const { return params.numDisparities; } - void setNumDisparities(int numDisparities) { params.numDisparities = numDisparities; } + void setNumDisparities(int numDisparities) { CV_Assert(numDisparities > 0); params.numDisparities = numDisparities; } - int getBlockSize() const { return params.SADWindowSize; } - void setBlockSize(int blockSize) { params.SADWindowSize = blockSize; } + int getBlockSize() const { return params.kernelSize; } + void setBlockSize(int blockSize) {CV_Assert(blockSize % 2 != 0); params.kernelSize = blockSize; } int getSpeckleWindowSize() const { return params.speckleWindowSize; } - void setSpeckleWindowSize(int speckleWindowSize) { params.speckleWindowSize = speckleWindowSize; } + void setSpeckleWindowSize(int speckleWindowSize) {CV_Assert(speckleWindowSize >= 0); params.speckleWindowSize = speckleWindowSize; } int getSpeckleRange() const { return params.speckleRange; } - void setSpeckleRange(int speckleRange) { params.speckleRange = speckleRange; } + void setSpeckleRange(int speckleRange) { CV_Assert(speckleRange >= 0); params.speckleRange = speckleRange; } int getDisp12MaxDiff() const { return params.disp12MaxDiff; } - void setDisp12MaxDiff(int disp12MaxDiff) { params.disp12MaxDiff = disp12MaxDiff; } + void setDisp12MaxDiff(int disp12MaxDiff) {CV_Assert(disp12MaxDiff > 0); params.disp12MaxDiff = disp12MaxDiff; } int getPreFilterCap() const { return params.preFilterCap; } - void setPreFilterCap(int preFilterCap) { params.preFilterCap = preFilterCap; } + void setPreFilterCap(int preFilterCap) { CV_Assert(preFilterCap > 0); params.preFilterCap = preFilterCap; } int getUniquenessRatio() const { return params.uniquenessRatio; } - void setUniquenessRatio(int uniquenessRatio) { params.uniquenessRatio = uniquenessRatio; } + void setUniquenessRatio(int uniquenessRatio) { CV_Assert(uniquenessRatio >= 0); params.uniquenessRatio = uniquenessRatio; } int getP1() const { return params.P1; } - void setP1(int P1) { params.P1 = P1; } + void setP1(int P1) { CV_Assert(P1 > 0); params.P1 = P1; } int getP2() const { return params.P2; } - void setP2(int P2) { params.P2 = P2; } + void setP2(int P2) {CV_Assert(P2 > 0); CV_Assert(P2 >= 2 * params.P1); params.P2 = P2; } int getMode() const { return params.mode; } void setMode(int mode) { params.mode = mode; } @@ -785,7 +779,7 @@ namespace cv fs << "name" << name_ << "minDisparity" << params.minDisparity << "numDisparities" << params.numDisparities - << "blockSize" << params.SADWindowSize + << "blockSize" << params.kernelSize << "speckleWindowSize" << params.speckleWindowSize << "speckleRange" << params.speckleRange << "disp12MaxDiff" << params.disp12MaxDiff @@ -795,13 +789,14 @@ namespace cv << "P2" << params.P2 << "mode" << params.mode; } + void read(const FileNode& fn) { FileNode n = fn["name"]; CV_Assert( n.isString() && String(n) == name_ ); params.minDisparity = (int)fn["minDisparity"]; params.numDisparities = (int)fn["numDisparities"]; - params.SADWindowSize = (int)fn["blockSize"]; + params.kernelSize = (int)fn["blockSize"]; params.speckleWindowSize = (int)fn["speckleWindowSize"]; params.speckleRange = (int)fn["speckleRange"]; params.disp12MaxDiff = (int)fn["disp12MaxDiff"]; @@ -811,24 +806,35 @@ namespace cv params.P2 = (int)fn["P2"]; params.mode = (int)fn["mode"]; } + StereoBinarySGBMParams params; Mat buffer; static const char* name_; + Mat censusImageLeft; + Mat censusImageRight; + Mat partialSumsLR; + Mat agregatedHammingLRCost; + Mat hamDist; + Mat parSumsIntensityImage[2]; + Mat Integral[2]; }; + const char* StereoBinarySGBMImpl::name_ = "StereoMatcher.SGBM"; - Ptr StereoBinarySGBM::create(int minDisparity, int numDisparities, int SADWindowSize, + + Ptr StereoBinarySGBM::create(int minDisparity, int numDisparities, int kernelSize, int P1, int P2, int disp12MaxDiff, int preFilterCap, int uniquenessRatio, int speckleWindowSize, int speckleRange, int mode) { return Ptr( - new StereoBinarySGBMImpl(minDisparity, numDisparities, SADWindowSize, + new StereoBinarySGBMImpl(minDisparity, numDisparities, kernelSize, P1, P2, disp12MaxDiff, preFilterCap, uniquenessRatio, speckleWindowSize, speckleRange, mode)); } + typedef cv::Point_ Point2s; } } diff --git a/modules/stereo/test/test_block_matching.cpp b/modules/stereo/test/test_block_matching.cpp new file mode 100644 index 000000000..207fd3a7a --- /dev/null +++ b/modules/stereo/test/test_block_matching.cpp @@ -0,0 +1,238 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// Intel License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of Intel Corporation may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "test_precomp.hpp" +#include + +using namespace cv; +using namespace cv::stereo; +using namespace std; + +class CV_BlockMatchingTest : public cvtest::BaseTest +{ +public: + CV_BlockMatchingTest(); + ~CV_BlockMatchingTest(); +protected: + void run(int /* idx */); +}; + +CV_BlockMatchingTest::CV_BlockMatchingTest(){} +CV_BlockMatchingTest::~CV_BlockMatchingTest(){} + +static double errorLevel(const Mat &ideal, Mat &actual) +{ + uint8_t *date, *harta; + harta = actual.data; + date = ideal.data; + int stride, h; + stride = (int)ideal.step; + h = ideal.rows; + int error = 0; + for (int i = 0; i < ideal.rows; i++) + { + for (int j = 0; j < ideal.cols; j++) + { + if (date[i * stride + j] != 0) + if (abs(date[i * stride + j] - harta[i * stride + j]) > 2 * 16) + { + error += 1; + } + } + } + return ((double)((error * 100) * 1.0) / (stride * h)); +} +void CV_BlockMatchingTest::run(int ) +{ + Mat image1, image2, gt; + //some test images can be found in the test data folder + image1 = imread(ts->get_data_path() + "testdata/imL2l.bmp", CV_8UC1); + image2 = imread(ts->get_data_path() + "testdata/imL2.bmp", CV_8UC1); + gt = imread(ts->get_data_path() + "testdata/groundtruth.bmp", CV_8UC1); + + if(image1.empty() || image2.empty() || gt.empty()) + { + ts->printf(cvtest::TS::LOG, "Wrong input data \n"); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + return; + } + if(image1.rows != image2.rows || image1.cols != image2.cols || gt.cols != gt.cols || gt.rows != gt.rows) + { + ts->printf(cvtest::TS::LOG, "Wrong input / output dimension \n"); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + return; + } + + RNG range; + //set the parameters + int binary_descriptor_type = range.uniform(0,8); + int kernel_size, aggregation_window; + if(binary_descriptor_type == 0) + kernel_size = 5; + else if(binary_descriptor_type == 2 || binary_descriptor_type == 3) + kernel_size = 7; + else if(binary_descriptor_type == 1) + kernel_size = 11; + else + kernel_size = 9; + if(binary_descriptor_type == 3) + aggregation_window = 13; + else + aggregation_window = 11; + Mat test = Mat(image1.rows, image1.cols, CV_8UC1); + Ptr sbm = StereoBinaryBM::create(16, kernel_size); + //we set the corresponding parameters + sbm->setPreFilterCap(31); + sbm->setMinDisparity(0); + sbm->setTextureThreshold(10); + sbm->setUniquenessRatio(0); + sbm->setSpeckleWindowSize(400);//speckle size + sbm->setSpeckleRange(200); + sbm->setDisp12MaxDiff(0); + sbm->setScalleFactor(16);//the scaling factor + sbm->setBinaryKernelType(binary_descriptor_type);//binary descriptor kernel + sbm->setAgregationWindowSize(aggregation_window); + //speckle removal algorithm the user can choose between the average speckle removal algorithm + //or the classical version that was implemented in open cv + sbm->setSpekleRemovalTechnique(CV_SPECKLE_REMOVAL_AVG_ALGORITHM); + sbm->setUsePrefilter(false);//pre-filter or not the images prior to making the transformations + //-- calculate the disparity image + sbm->compute(image1, image2, test); + if(test.empty()) + { + ts->printf(cvtest::TS::LOG, "Wrong input / output dimension \n"); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT); + return; + } + if(errorLevel(gt,test) > 20) + { + ts->printf( cvtest::TS::LOG, + "Too big error\n"); + ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); + return; + } +} + +class CV_SGBlockMatchingTest : public cvtest::BaseTest +{ +public: + CV_SGBlockMatchingTest(); + ~CV_SGBlockMatchingTest(); +protected: + void run(int /* idx */); +}; + +CV_SGBlockMatchingTest::CV_SGBlockMatchingTest(){} +CV_SGBlockMatchingTest::~CV_SGBlockMatchingTest(){} + +void CV_SGBlockMatchingTest::run(int ) +{ + Mat image1, image2, gt; + //some test images can be found in the test data folder + image1 = imread(ts->get_data_path() + "testdata/imL2l.bmp", CV_8UC1); + image2 = imread(ts->get_data_path() + "testdata/imL2.bmp", CV_8UC1); + gt = imread(ts->get_data_path() + "testdata/groundtruth.bmp", CV_8UC1); + + + if(image1.empty() || image2.empty() || gt.empty()) + { + ts->printf(cvtest::TS::LOG, "Wrong input data \n"); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + return; + } + if(image1.rows != image2.rows || image1.cols != image2.cols || gt.cols != gt.cols || gt.rows != gt.rows) + { + ts->printf(cvtest::TS::LOG, "Wrong input / output dimension \n"); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + return; + } + + RNG range; + //set the parameters + int binary_descriptor_type = range.uniform(0,8); + int kernel_size; + if(binary_descriptor_type == 0) + kernel_size = 5; + else if(binary_descriptor_type == 2 || binary_descriptor_type == 3) + kernel_size = 7; + else if(binary_descriptor_type == 1) + kernel_size = 11; + else + kernel_size = 9; + + Mat test = Mat(image1.rows, image1.cols, CV_8UC1); + Mat imgDisparity16S2 = Mat(image1.rows, image1.cols, CV_16S); + Ptr sgbm = StereoBinarySGBM::create(0, 16, kernel_size); + //setting the penalties for sgbm + sgbm->setP1(10); + sgbm->setP2(100); + sgbm->setMinDisparity(0); + sgbm->setNumDisparities(16);//set disparity number + sgbm->setUniquenessRatio(1); + sgbm->setSpeckleWindowSize(400); + sgbm->setSpeckleRange(200); + sgbm->setDisp12MaxDiff(1); + sgbm->setBinaryKernelType(binary_descriptor_type);//set the binary descriptor + sgbm->setSpekleRemovalTechnique(CV_SPECKLE_REMOVAL_AVG_ALGORITHM); //the avg speckle removal algorithm + sgbm->setSubPixelInterpolationMethod(CV_SIMETRICV_INTERPOLATION);// the SIMETRIC V interpolation method + sgbm->compute(image1, image2, imgDisparity16S2); + double minVal; double maxVal; + minMaxLoc(imgDisparity16S2, &minVal, &maxVal); + + imgDisparity16S2.convertTo(test, CV_8UC1, 255 / (maxVal - minVal)); + + if(test.empty()) + { + ts->printf(cvtest::TS::LOG, "Wrong input / output dimension \n"); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT); + return; + } + double error = errorLevel(gt,test); + if(error > 10) + { + ts->printf( cvtest::TS::LOG, + "Too big error\n"); + ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); + return; + } +} +TEST(block_matching_simple_test, accuracy) { CV_BlockMatchingTest test; test.safe_run(); } +TEST(SG_block_matching_simple_test, accuracy) { CV_SGBlockMatchingTest test; test.safe_run(); } diff --git a/modules/stereo/test/test_descriptors.cpp b/modules/stereo/test/test_descriptors.cpp new file mode 100644 index 000000000..13219afce --- /dev/null +++ b/modules/stereo/test/test_descriptors.cpp @@ -0,0 +1,466 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// Intel License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of Intel Corporation may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "test_precomp.hpp" +#include + +using namespace cv; +using namespace cv::stereo; +using namespace std; + +class CV_DescriptorBaseTest : public cvtest::BaseTest +{ +public: + CV_DescriptorBaseTest(); + ~CV_DescriptorBaseTest(); +protected: + virtual void imageTransformation(const Mat &img1, const Mat &img2, Mat &out1, Mat &out2) = 0; + virtual void imageTransformation(const Mat &img1, Mat &out1) = 0; + void testROI(const Mat &img); + void testMonotonicity(const Mat &img, Mat &out); + void run(int ); + Mat censusImage[2]; + Mat censusImageSingle[2]; + Mat left; + Mat right; + int kernel_size, descriptor_type; +}; +//we test to see if the descriptor applied on a roi +//has the same value with the descriptor from the original image +//tested at the roi boundaries +void CV_DescriptorBaseTest::testROI(const Mat &img) +{ + int pt, pb,w,h; + //initialize random values for the roi top and bottom + pt = rand() % 100; + pb = rand() % 100; + //calculate the new width and height + w = img.cols; + h = img.rows - pt - pb; + int start = pt + kernel_size / 2 + 1; + int stop = h - kernel_size/2 - 1; + //set the region of interest according to above values + Rect region_of_interest = Rect(0, pt, w, h); + Mat image_roi1 = img(region_of_interest); + Mat p1,p2; + //create 2 images where to put our output + p1.create(image_roi1.rows, image_roi1.cols, CV_32SC4); + p2.create(img.rows, img.cols, CV_32SC4); + imageTransformation(image_roi1,p1); + imageTransformation(img,p2); + int *roi_data = (int *)p1.data; + int *img_data = (int *)p2.data; + //verify result + for(int i = start; i < stop; i++) + { + for(int j = 0; j < w ; j++) + { + if(roi_data[(i - pt) * w + j] != img_data[(i) * w + j]) + { + ts->printf(cvtest::TS::LOG, "Something wrong with ROI \n"); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT); + return; + } + } + + } +} +CV_DescriptorBaseTest::~CV_DescriptorBaseTest() +{ + left.release(); + right.release(); + censusImage[0].release(); + censusImage[1].release(); + censusImageSingle[0].release(); + censusImageSingle[1].release(); +} +CV_DescriptorBaseTest::CV_DescriptorBaseTest() +{ + //read 2 images from file + //two test images can be found in the test data folder + left = imread(ts->get_data_path() + "testdata/imL2l.bmp", CV_8UC1); + right = imread(ts->get_data_path() + "testdata/imL2.bmp", CV_8UC1); + + if(left.empty() || right.empty()) + { + ts->printf(cvtest::TS::LOG, "Wrong input data \n"); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + return; + } + ts->printf(cvtest::TS::LOG, "Data loaded \n"); +} +//verify if we don't have an image with all pixels the same( except when all input pixels are equal) +void CV_DescriptorBaseTest::testMonotonicity(const Mat &img, Mat &out) +{ + //verify if input data is correct + if(img.rows != out.rows || img.cols != out.cols || img.empty() || out.empty()) + { + ts->printf(cvtest::TS::LOG, "Wrong input / output dimension \n"); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + return; + } + //verify that for an input image with different pxels the values of the + //output pixels are not the same + int same = 0; + uint8_t *data = img.data; + uint8_t val = data[1]; + int stride = (int)img.step; + for(int i = 0 ; i < img.rows && !same; i++) + { + for(int j = 0; j < img.cols; j++) + { + if(val != data[i * stride + j]) + { + same = 1; + break; + } + } + } + int value_descript = out.data[1]; + int accept = 0; + uint8_t *outData = out.data; + for(int i = 0 ; i < img.rows && !accept; i++) + { + for(int j = 0; j < img.cols; j++) + { + //we verify for the output image if the iage pixels are not all the same of an input + //image with different pixels + if(value_descript != outData[i * stride + j] && same) + { + //if we found a value that is different we accept + accept = 1; + break; + } + } + } + if(accept == 1 && same == 0) + { + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT); + ts->printf(cvtest::TS::LOG, "The image has all values the same \n"); + return; + } + if(accept == 0 && same == 1) + { + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT); + ts->printf(cvtest::TS::LOG, "For correct image we get all descriptor values the same \n"); + return; + } + ts->set_failed_test_info(cvtest::TS::OK); +} + +/////////////////////////////////// +//census transform + +class CV_CensusTransformTest: public CV_DescriptorBaseTest +{ +public: + CV_CensusTransformTest(); +protected: + void imageTransformation(const Mat &img1, const Mat &img2, Mat &out1, Mat &out2); + void imageTransformation(const Mat &img1, Mat &out1); +}; + +CV_CensusTransformTest::CV_CensusTransformTest() +{ + kernel_size = 11; + descriptor_type = CV_SPARSE_CENSUS; +} +void CV_CensusTransformTest::imageTransformation(const Mat &img1, const Mat &img2, Mat &out1, Mat &out2) +{ + //verify if input data is correct + if(img1.rows != out1.rows || img1.cols != out1.cols || img1.empty() || out1.empty() + || img2.rows != out2.rows || img2.cols != out2.cols || img2.empty() || out2.empty()) + { + ts->printf(cvtest::TS::LOG, "Wrong input / output data \n"); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + return; + } + if(kernel_size % 2 == 0) + { + ts->printf(cvtest::TS::LOG, "Wrong kernel size;Kernel should be odd \n"); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + return; + } + censusTransform(img1,img2,kernel_size,out1,out2,descriptor_type); + +} +void CV_CensusTransformTest::imageTransformation(const Mat &img1, Mat &out1) +{ + //verify if input data is correct + if(img1.rows != out1.rows || img1.cols != out1.cols || img1.empty() || out1.empty()) + { + ts->printf(cvtest::TS::LOG, "Wrong input / output data \n"); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + return; + } + if(kernel_size % 2 == 0) + { + ts->printf(cvtest::TS::LOG, "Wrong kernel size;Kernel should be odd \n"); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + return; + } + censusTransform(img1,kernel_size,out1,descriptor_type); +} +////////////////////////////////// +//symetric census + +class CV_SymetricCensusTest: public CV_DescriptorBaseTest +{ +public: + CV_SymetricCensusTest(); +protected: + void imageTransformation(const Mat &img1, const Mat &img2, Mat &out1, Mat &out2); + void imageTransformation(const Mat &img1, Mat &out1); +}; +CV_SymetricCensusTest::CV_SymetricCensusTest() +{ + kernel_size = 7; + descriptor_type = CV_CS_CENSUS; +} +void CV_SymetricCensusTest::imageTransformation(const Mat &img1, const Mat &img2, Mat &out1, Mat &out2) +{ + //verify if input data is correct + if(img1.rows != out1.rows || img1.cols != out1.cols || img1.empty() || out1.empty() + || img2.rows != out2.rows || img2.cols != out2.cols || img2.empty() || out2.empty()) + { + ts->printf(cvtest::TS::LOG, "Wrong input / output data \n"); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + return; + } + if(kernel_size % 2 == 0) + { + ts->printf(cvtest::TS::LOG, "Wrong kernel size;Kernel should be odd \n"); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + return; + } + symetricCensusTransform(img1,img2,kernel_size,out1,out2,descriptor_type); +} +void CV_SymetricCensusTest::imageTransformation(const Mat &img1, Mat &out1) +{ + //verify if input data is correct + if(img1.rows != out1.rows || img1.cols != out1.cols || img1.empty() || out1.empty()) + { + ts->printf(cvtest::TS::LOG, "Wrong input / output data \n"); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + return; + } + if(kernel_size % 2 == 0) + { + ts->printf(cvtest::TS::LOG, "Wrong kernel size;Kernel should be odd \n"); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + return; + } + symetricCensusTransform(img1,kernel_size,out1,descriptor_type); +} +////////////////////////////////// +//modified census transform +class CV_ModifiedCensusTransformTest: public CV_DescriptorBaseTest +{ +public: + CV_ModifiedCensusTransformTest(); +protected: + void imageTransformation(const Mat &img1, const Mat &img2, Mat &out1, Mat &out2); + void imageTransformation(const Mat &img1, Mat &out1); +}; +CV_ModifiedCensusTransformTest::CV_ModifiedCensusTransformTest() +{ + kernel_size = 9; + descriptor_type = CV_MODIFIED_CENSUS_TRANSFORM; +} +void CV_ModifiedCensusTransformTest::imageTransformation(const Mat &img1, const Mat &img2, Mat &out1, Mat &out2) +{ + //verify if input data is correct + if(img1.rows != out1.rows || img1.cols != out1.cols || img1.empty() || out1.empty() + || img2.rows != out2.rows || img2.cols != out2.cols || img2.empty() || out2.empty()) + { + ts->printf(cvtest::TS::LOG, "Wrong input / output data \n"); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + return; + } + if(kernel_size % 2 == 0) + { + ts->printf(cvtest::TS::LOG, "Wrong kernel size;Kernel should be odd \n"); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + return; + } + modifiedCensusTransform(img1,img2,kernel_size,out1,out2,descriptor_type); +} +void CV_ModifiedCensusTransformTest::imageTransformation(const Mat &img1, Mat &out1) +{ + if(img1.rows != out1.rows || img1.cols != out1.cols || img1.empty() || out1.empty()) + { + ts->printf(cvtest::TS::LOG, "Wrong input / output data \n"); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + return; + } + if(kernel_size % 2 == 0) + { + ts->printf(cvtest::TS::LOG, "Wrong kernel size;Kernel should be odd \n"); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + return; + } + modifiedCensusTransform(img1,kernel_size,out1,descriptor_type); +} +////////////////////////////////// +//star kernel census +class CV_StarKernelCensusTest: public CV_DescriptorBaseTest +{ +public: + CV_StarKernelCensusTest(); +protected: + void imageTransformation(const Mat &img1, const Mat &img2, Mat &out1, Mat &out2); + void imageTransformation(const Mat &img1, Mat &out1); +}; +CV_StarKernelCensusTest :: CV_StarKernelCensusTest() +{ + kernel_size = 9; + descriptor_type = CV_STAR_KERNEL; +} +void CV_StarKernelCensusTest :: imageTransformation(const Mat &img1, const Mat &img2, Mat &out1, Mat &out2) +{ + //verify if input data is correct + if(img1.rows != out1.rows || img1.cols != out1.cols || img1.empty() || out1.empty() + || img2.rows != out2.rows || img2.cols != out2.cols || img2.empty() || out2.empty()) + { + ts->printf(cvtest::TS::LOG, "Wrong input / output data \n"); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + return; + } + if(kernel_size % 2 == 0) + { + ts->printf(cvtest::TS::LOG, "Wrong kernel size;Kernel should be odd \n"); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + return; + } + starCensusTransform(img1,img2,kernel_size,out1,out2); +} +void CV_StarKernelCensusTest::imageTransformation(const Mat &img1, Mat &out1) +{ + if(img1.rows != out1.rows || img1.cols != out1.cols || img1.empty() || out1.empty()) + { + ts->printf(cvtest::TS::LOG, "Wrong input / output data \n"); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + return; + } + if(kernel_size % 2 == 0) + { + ts->printf(cvtest::TS::LOG, "Wrong kernel size;Kernel should be odd \n"); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + return; + } + starCensusTransform(img1,kernel_size,out1); +} + +void CV_DescriptorBaseTest::run(int ) +{ + if (left.empty() || right.empty()) + { + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + ts->printf(cvtest::TS::LOG, "No input images detected\n"); + return; + } + testROI(left); + + censusImage[0].create(left.rows, left.cols, CV_32SC4); + censusImage[1].create(left.rows, left.cols, CV_32SC4); + censusImageSingle[0].create(left.rows, left.cols, CV_32SC4); + censusImageSingle[1].create(left.rows, left.cols, CV_32SC4); + censusImage[0].setTo(0); + censusImage[1].setTo(0); + censusImageSingle[0].setTo(0); + censusImageSingle[1].setTo(0); + + imageTransformation(left, right, censusImage[0], censusImage[1]); + imageTransformation(left, censusImageSingle[0]); + imageTransformation(right, censusImageSingle[1]); + testMonotonicity(left,censusImage[0]); + testMonotonicity(right,censusImage[1]); + testMonotonicity(left,censusImageSingle[0]); + testMonotonicity(right,censusImageSingle[1]); + + if (censusImage[0].empty() || censusImage[1].empty() || censusImageSingle[0].empty() || censusImageSingle[1].empty()) + { + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT); + ts->printf(cvtest::TS::LOG, "The descriptor images are empty \n"); + return; + } + int *datl1 = (int *)censusImage[0].data; + int *datr1 = (int *)censusImage[1].data; + int *datl2 = (int *)censusImageSingle[0].data; + int *datr2 = (int *)censusImageSingle[1].data; + for(int i = 0; i < censusImage[0].rows - kernel_size/ 2; i++) + { + for(int j = 0; j < censusImage[0].cols; j++) + { + if(datl1[i * censusImage[0].cols + j] != datl2[i * censusImage[0].cols + j]) + { + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT); + ts->printf(cvtest::TS::LOG, "Mismatch for left images %d \n",descriptor_type); + return; + } + if(datr1[i * censusImage[0].cols + j] != datr2[i * censusImage[0].cols + j]) + { + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT); + ts->printf(cvtest::TS::LOG, "Mismatch for right images %d \n",descriptor_type); + return; + } + } + } + int min = numeric_limits::min(); + int max = numeric_limits::max(); + //check if all values are between int min and int max and not NAN + if (0 != cvtest::check(censusImage[0], min, max, 0)) + { + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + return; + } + //check if all values are between int min and int max and not NAN + if (0 != cvtest::check(censusImage[1], min, max, 0)) + { + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA); + return ; + } +} + +TEST(census_transform_testing, accuracy) { CV_CensusTransformTest test; test.safe_run(); } +TEST(symetric_census_testing, accuracy) { CV_SymetricCensusTest test; test.safe_run(); } +TEST(modified_census_testing, accuracy) { CV_ModifiedCensusTransformTest test; test.safe_run(); } +TEST(star_kernel_testing, accuracy) { CV_StarKernelCensusTest test; test.safe_run(); } diff --git a/modules/stereo/test/test_precomp.hpp b/modules/stereo/test/test_precomp.hpp index 6b659c327..1735d935a 100644 --- a/modules/stereo/test/test_precomp.hpp +++ b/modules/stereo/test/test_precomp.hpp @@ -12,8 +12,6 @@ #include #include "opencv2/ts.hpp" #include "opencv2/imgcodecs.hpp" - - #include "opencv2/stereo.hpp" #include "opencv2/imgproc.hpp" #include "opencv2/features2d.hpp" @@ -22,10 +20,9 @@ #include "opencv2/core/cvdef.h" #include "opencv2/core.hpp" #include "opencv2/highgui.hpp" +#include "opencv2/calib3d.hpp" #include #include - - #endif diff --git a/modules/stereo/testdata/groundtruth.bmp b/modules/stereo/testdata/groundtruth.bmp new file mode 100644 index 000000000..5615ed088 Binary files /dev/null and b/modules/stereo/testdata/groundtruth.bmp differ diff --git a/modules/stereo/testdata/imL2.bmp b/modules/stereo/testdata/imL2.bmp new file mode 100644 index 000000000..0db173f8c Binary files /dev/null and b/modules/stereo/testdata/imL2.bmp differ diff --git a/modules/stereo/testdata/imL2l.bmp b/modules/stereo/testdata/imL2l.bmp new file mode 100644 index 000000000..9cac9508d Binary files /dev/null and b/modules/stereo/testdata/imL2l.bmp differ diff --git a/modules/stereo/testdata/imgKitty.bmp b/modules/stereo/testdata/imgKitty.bmp new file mode 100644 index 000000000..4f89bf904 Binary files /dev/null and b/modules/stereo/testdata/imgKitty.bmp differ diff --git a/modules/stereo/testdata/imgKittyl.bmp b/modules/stereo/testdata/imgKittyl.bmp new file mode 100644 index 000000000..66afef03d Binary files /dev/null and b/modules/stereo/testdata/imgKittyl.bmp differ diff --git a/modules/stereo/testdata/rezult0.bmp b/modules/stereo/testdata/rezult0.bmp new file mode 100644 index 000000000..4d5d67b72 Binary files /dev/null and b/modules/stereo/testdata/rezult0.bmp differ diff --git a/modules/stereo/testdata/rezult0l.bmp b/modules/stereo/testdata/rezult0l.bmp new file mode 100644 index 000000000..d65538bbf Binary files /dev/null and b/modules/stereo/testdata/rezult0l.bmp differ