Commit 2b64abdb authored by Vadim Pisarevsky's avatar Vadim Pisarevsky

Merge pull request #326 from MMp131316:MatchingOperations

parents bf0c8712 12b530c6
......@@ -52,4 +52,4 @@ $ cmake -D OPENCV_EXTRA_MODULES_PATH=<opencv_contrib>/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.
......@@ -47,24 +47,23 @@
#include "opencv2/core.hpp"
#include "opencv2/features2d.hpp"
#include "opencv2/core/affine.hpp"
#include "../../stereo/src/descriptor.hpp"
#include "../../stereo/src/matching.hpp"
/**
@defgroup stereo Stereo Correspondance Algorithms
*/
*/
namespace cv
{
namespace stereo
{
//! @addtogroup stereo
//! @{
// void correctMatches( InputArray F, InputArray points1, InputArray points2,
//! @addtogroup stereo
//! @{
// void correctMatches( InputArray F, InputArray points1, InputArray points2,
// OutputArray newPoints1, OutputArray newPoints2 );
/** @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
@param maxSpeckleSize The maximum speckle size to consider it a speckle. Larger blobs are not
......@@ -112,9 +111,16 @@ namespace cv
virtual int getDisp12MaxDiff() const = 0;
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.
*/
......@@ -143,12 +149,20 @@ namespace cv
virtual int getSmallerBlockSize() const = 0;
virtual void setSmallerBlockSize(int blockSize) = 0;
virtual Rect getROI1() const = 0;
virtual void setROI1(Rect roi1) = 0;
virtual int getScalleFactor() const = 0 ;
virtual void setScalleFactor(int factor) = 0;
virtual int getSpekleRemovalTechnique() const = 0 ;
virtual void setSpekleRemovalTechnique(int factor) = 0;
virtual bool getUsePrefilter() const = 0 ;
virtual void setUsePrefilter(bool factor) = 0;
virtual Rect getROI2() const = 0;
virtual void setROI2(Rect roi2) = 0;
virtual int getBinaryKernelType() const = 0;
virtual void setBinaryKernelType(int value) = 0;
virtual int getAgregationWindowSize() const = 0;
virtual void setAgregationWindowSize(int value) = 0;
/** @brief Creates StereoBM object
@param numDisparities the disparity search range. For each pixel algorithm will find the best
......@@ -162,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
......@@ -207,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
......@@ -215,9 +238,9 @@ namespace cv
zero. In the current implementation, this parameter must be divisible by 16.
@param blockSize Matched block size. It must be an odd number \>=1 . Normally, it should be
somewhere in the 3..11 range.
@param P1 The first parameter controlling the disparity smoothness. See below.
@param P2 The second parameter controlling the disparity smoothness. The larger the values are,
the smoother the disparity is. P1 is the penalty on the disparity change by plus or minus 1
@param P1 The first parameter controlling the disparity smoothness.This parameter is used for the case of slanted surfaces (not fronto parallel).
@param P2 The second parameter controlling the disparity smoothness.This parameter is used for "solving" the depth discontinuities problem.
The larger the values are, the smoother the disparity is. P1 is the penalty on the disparity change by plus or minus 1
between neighbor pixels. P2 is the penalty on the disparity change by more than 1 between neighbor
pixels. The algorithm requires P2 \> P1 . See stereo_match.cpp sample where some reasonably good
P1 and P2 values are shown (like 8\*number_of_image_channels\*SADWindowSize\*SADWindowSize and
......@@ -245,9 +268,9 @@ namespace cv
to a custom value.
*/
CV_EXPORTS static Ptr<cv::stereo::StereoBinarySGBM> create(int minDisparity, int numDisparities, int blockSize,
int P1 = 0, int P2 = 0, 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);
};
//! @}
......
/*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<Size, MatType, MatDepth> s_bm_test_t;
typedef perf::TestBaseWithParam<s_bm_test_t> 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<StereoBinarySGBM> 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<StereoBinaryBM> 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);
}
/*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<Size, MatType, MatDepth> descript_params_t;
typedef perf::TestBaseWithParam<descript_params_t> 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);
}
/*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)
#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 <iostream>
#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 <algorithm>
#include <cmath>
#ifdef GTEST_CREATE_SHARED_LIBRARY
#error no modules except ts should have GTEST_CREATE_SHARED_LIBRARY defined
#endif
#endif
#include "opencv2/stereo.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <stdio.h>
#include <string.h>
#include <iostream>
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 &success);
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;
int success;
// 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,success);
if (!parser.check() || !success)
{
parser.printMessage();
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<StereoBinaryBM> 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<StereoBinarySGBM> 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, int &success)
{
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<string>(0);
right = parser.get<string>(1);
kernel_size = parser.get<int>("kernel_size");
number_of_disparities = parser.get<int>("disparity");
aggregation_window = parser.get<int>("aggregation_window");
P1 = parser.get<int>("P1");
P2 = parser.get<int>("P2");
binary_descriptor_type = parser.get<int>("binary_descriptor");
scale = parser.get<float>("scale");
algo = parser.get<string>("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");
success = 1;
//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";
success = 0;
}
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";
success = 0;
}
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";
success = 0;
}
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";
success = 0;
}
if(number_of_disparities < 10)
{
cout << "Number of disparities should be greater than 10\n";
success = 0;
}
if(aggregation_window < 3)
{
cout << "Aggregation window should be > 3";
success = 0;
}
if(scale < 1)
{
cout << "The scale should be a positive number \n";
success = 0;
}
if(P1 != 0)
{
if(P2 / P1 < 2)
{
cout << "You should probably choose a greater P2 penalty\n";
success = 0;
}
}
else
{
cout << " Penalties should be greater than 0\n";
success = 0;
}
return parser;
}
//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
// (3-clause BSD License)
//
//Copyright (C) 2000-2015, Intel Corporation, all rights reserved.
//Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved.
//Copyright (C) 2009-2015, NVIDIA Corporation, all rights reserved.
//Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved.
//Copyright (C) 2015, OpenCV Foundation, all rights reserved.
//Copyright (C) 2015, Itseez 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:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistributions 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.
//
// * Neither the names of the copyright holders nor the names of the contributors
// may 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 copyright holders 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.
/*****************************************************************************************************************\
* The file contains the implemented descriptors *
\******************************************************************************************************************/
#include "precomp.hpp"
#include "descriptor.hpp"
namespace cv
{
namespace stereo
{
//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
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);
CV_Assert(image1.type() == CV_8UC1 && image2.type() == CV_8UC1);
CV_Assert(type != CV_DENSE_CENSUS || type != CV_SPARSE_CENSUS);
CV_Assert(kernelSize <= ((type == 0) ? 5 : 11));
int n2 = (kernelSize) / 2;
uint8_t *images[] = {image1.data, image2.data};
int *costs[] = {(int *)dist1.data,(int *)dist2.data};
int stride = (int)image1.step;
if(type == CV_DENSE_CENSUS)
{
parallel_for_( Range(n2, image1.rows - n2),
CombinedDescriptor<1,1,1,2,CensusKernel<2> >(image1.cols, image1.rows,stride,n2,costs,CensusKernel<2>(images),n2));
}
else if(type == CV_SPARSE_CENSUS)
{
parallel_for_( Range(n2, image1.rows - n2),
CombinedDescriptor<2,2,1,2,CensusKernel<2> >(image1.cols, image1.rows, stride,n2,costs,CensusKernel<2>(images),n2));
}
}
//function that performs census on one image
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);
CV_Assert(image1.type() == CV_8UC1);
CV_Assert(type != CV_DENSE_CENSUS || type != CV_SPARSE_CENSUS);
CV_Assert(kernelSize <= ((type == 0) ? 5 : 11));
int n2 = (kernelSize) / 2;
uint8_t *images[] = {image1.data};
int *costs[] = {(int *)dist1.data};
int stride = (int)image1.step;
if(type == CV_DENSE_CENSUS)
{
parallel_for_( Range(n2, image1.rows - n2),
CombinedDescriptor<1,1,1,1,CensusKernel<1> >(image1.cols, image1.rows,stride,n2,costs,CensusKernel<1>(images),n2));
}
else if(type == CV_SPARSE_CENSUS)
{
parallel_for_( Range(n2, image1.rows - n2),
CombinedDescriptor<2,2,1,1,CensusKernel<1> >(image1.cols, image1.rows,stride,n2,costs,CensusKernel<1>(images),n2));
}
}
//in a 9x9 kernel only certain positions are choosen for comparison
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);
CV_Assert(img1.type() == CV_8UC1 && img2.type() == CV_8UC1);
CV_Assert(kernelSize >= 7);
int n2 = (kernelSize) >> 1;
Mat images[] = {img1, img2};
int *date[] = { (int *)dist1.data, (int *)dist2.data};
parallel_for_(Range(n2, img1.rows - n2), StarKernelCensus<2>(images, n2,date));
}
//single version of star census
CV_EXPORTS void starCensusTransform(const Mat &img1, int kernelSize, Mat &dist)
{
CV_Assert(img1.size() == dist.size());
CV_Assert(kernelSize % 2 != 0);
CV_Assert(img1.type() == CV_8UC1);
CV_Assert(kernelSize >= 7);
int n2 = (kernelSize) >> 1;
Mat images[] = {img1};
int *date[] = { (int *)dist.data};
parallel_for_(Range(n2, img1.rows - n2), StarKernelCensus<1>(images, n2,date));
}
//Modified census transforms
//the first one deals with small illumination changes
//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
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);
CV_Assert(img1.type() == CV_8UC1 && img2.type() == CV_8UC1);
CV_Assert(type != CV_MODIFIED_CENSUS_TRANSFORM || type != CV_MEAN_VARIATION);
CV_Assert(kernelSize <= 9);
int n2 = (kernelSize - 1) >> 1;
uint8_t *images[] = {img1.data, img2.data};
int *date[] = { (int *)dist1.data, (int *)dist2.data};
int stride = (int)img1.cols;
if(type == CV_MODIFIED_CENSUS_TRANSFORM)
{
//MCT
parallel_for_( Range(n2, img1.rows - n2),
CombinedDescriptor<2,4,2, 2,MCTKernel<2> >(img1.cols, img1.rows,stride,n2,date,MCTKernel<2>(images,t),n2));
}
else if(type == CV_MEAN_VARIATION)
{
//MV
int *integral[2];
integral[0] = (int *)IntegralImage1.data;
integral[1] = (int *)IntegralImage2.data;
parallel_for_( Range(n2, img1.rows - n2),
CombinedDescriptor<2,3,2,2, MVKernel<2> >(img1.cols, img1.rows,stride,n2,date,MVKernel<2>(images,integral),n2));
}
}
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);
CV_Assert(img1.type() == CV_8UC1);
CV_Assert(type != CV_MODIFIED_CENSUS_TRANSFORM || type != CV_MEAN_VARIATION);
CV_Assert(kernelSize <= 9);
int n2 = (kernelSize - 1) >> 1;
uint8_t *images[] = {img1.data};
int *date[] = { (int *)dist.data};
int stride = (int)img1.step;
if(type == CV_MODIFIED_CENSUS_TRANSFORM)
{
//MCT
parallel_for_(Range(n2, img1.rows - 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)
{
//MV
int *integral[] = { (int *)IntegralImage.data};
parallel_for_(Range(n2, img1.rows - n2),
CombinedDescriptor<2,3,2,1, MVKernel<1> >(img1.cols, img1.rows,stride,n2,date,MVKernel<1>(images,integral),n2));
}
}
//different versions of simetric census
//These variants since they do not compare with the center they are invariant to noise
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);
CV_Assert(img1.type() == CV_8UC1 && img2.type() == CV_8UC1);
CV_Assert(type != CV_CS_CENSUS || type != CV_MODIFIED_CS_CENSUS);
CV_Assert(kernelSize <= 7);
int n2 = kernelSize >> 1;
uint8_t *images[] = {img1.data, img2.data};
Mat imag[] = {img1, img2};
int *date[] = { (int *)dist1.data, (int *)dist2.data};
int stride = (int)img1.step;
if(type == CV_CS_CENSUS)
{
parallel_for_(Range(n2, img1.rows - n2), SymetricCensus<2>(imag, n2,date));
}
else if(type == CV_MODIFIED_CS_CENSUS)
{
parallel_for_(Range(n2, img1.rows - n2),
CombinedDescriptor<1,1,1,2,ModifiedCsCensus<2> >(img1.cols, img1.rows,stride,n2,date,ModifiedCsCensus<2>(images,n2),1));
}
}
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);
CV_Assert(img1.type() == CV_8UC1);
CV_Assert(type != CV_MODIFIED_CS_CENSUS || type != CV_CS_CENSUS);
CV_Assert(kernelSize <= 7);
int n2 = kernelSize >> 1;
uint8_t *images[] = {img1.data};
Mat imag[] = {img1};
int *date[] = { (int *)dist1.data};
int stride = (int)img1.step;
if(type == CV_CS_CENSUS)
{
parallel_for_( Range(n2, img1.rows - n2), SymetricCensus<1>(imag, n2,date));
}
else if(type == CV_MODIFIED_CS_CENSUS)
{
parallel_for_( Range(n2, img1.rows - n2),
CombinedDescriptor<1,1,1,1,ModifiedCsCensus<1> >(img1.cols, img1.rows,stride,n2,date,ModifiedCsCensus<1>(images,n2),1));
}
}
//integral image computation used in the Mean Variation Census Transform
void imageMeanKernelSize(const Mat &image, int windowSize, Mat &cost)
{
CV_Assert(image.size > 0);
CV_Assert(cost.size > 0);
CV_Assert(windowSize % 2 != 0);
int win = windowSize / 2;
float scalling = ((float) 1) / (windowSize * windowSize);
int height = image.rows;
cost.setTo(0);
int *c = (int *)cost.data;
parallel_for_(Range(win + 1, height - win - 1),MeanKernelIntegralImage(image,win,scalling,c));
}
}
}
//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
// (3-clause BSD License)
//
//Copyright (C) 2000-2015, Intel Corporation, all rights reserved.
//Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved.
//Copyright (C) 2009-2015, NVIDIA Corporation, all rights reserved.
//Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved.
//Copyright (C) 2015, OpenCV Foundation, all rights reserved.
//Copyright (C) 2015, Itseez 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:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistributions 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.
//
// * Neither the names of the copyright holders nor the names of the contributors
// may 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 copyright holders 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.
/*****************************************************************************************************************\
* The interface contains the main descriptors that will be implemented in the descriptor class *
\*****************************************************************************************************************/
#include "precomp.hpp"
#include <stdint.h>
#ifndef _OPENCV_DESCRIPTOR_HPP_
#define _OPENCV_DESCRIPTOR_HPP_
#ifdef __cplusplus
namespace cv
{
namespace stereo
{
//types of supported kernels
enum {
CV_DENSE_CENSUS, CV_SPARSE_CENSUS,
CV_CS_CENSUS, CV_MODIFIED_CS_CENSUS, CV_MODIFIED_CENSUS_TRANSFORM,
CV_MEAN_VARIATION, CV_STAR_KERNEL
};
//!Mean Variation is a robust kernel that compares a pixel
//!not just with the center but also with the mean of the window
template<int num_images>
struct MVKernel
{
uint8_t *image[num_images];
int *integralImage[num_images];
int stop;
MVKernel(){}
MVKernel(uint8_t **images, int **integral)
{
for(int i = 0; i < num_images; i++)
{
image[i] = images[i];
integralImage[i] = integral[i];
}
stop = num_images;
}
void operator()(int rrWidth,int w2, int rWidth, int jj, int j, int c[num_images]) const
{
(void)w2;
for (int i = 0; i < stop; i++)
{
if (image[i][rrWidth + jj] > image[i][rWidth + j])
{
c[i] = c[i] + 1;
}
c[i] = c[i] << 1;
if (integralImage[i][rrWidth + jj] > image[i][rWidth + j])
{
c[i] = c[i] + 1;
}
c[i] = c[i] << 1;
}
}
};
//!Compares pixels from a patch giving high weights to pixels in which
//!the intensity is higher. The other pixels receive a lower weight
template <int num_images>
struct MCTKernel
{
uint8_t *image[num_images];
int t,imageStop;
MCTKernel(){}
MCTKernel(uint8_t ** images, int threshold)
{
for(int i = 0; i < num_images; i++)
{
image[i] = images[i];
}
imageStop = num_images;
t = threshold;
}
void operator()(int rrWidth,int w2, int rWidth, int jj, int j, int c[num_images]) const
{
(void)w2;
for(int i = 0; i < imageStop; i++)
{
if (image[i][rrWidth + jj] > image[i][rWidth + j] - t)
{
c[i] = c[i] << 1;
c[i] = c[i] + 1;
c[i] = c[i] << 1;
c[i] = c[i] + 1;
}
else if (image[i][rWidth + j] - t < image[i][rrWidth + jj] && image[i][rWidth + j] + t >= image[i][rrWidth + jj])
{
c[i] = c[i] << 2;
c[i] = c[i] + 1;
}
else
{
c[i] <<= 2;
}
}
}
};
//!A madified cs census that compares a pixel with the imediat neightbour starting
//!from the center
template<int num_images>
struct ModifiedCsCensus
{
uint8_t *image[num_images];
int n2;
int imageStop;
ModifiedCsCensus(){}
ModifiedCsCensus(uint8_t **images, int ker)
{
for(int i = 0; i < num_images; i++)
image[i] = images[i];
imageStop = num_images;
n2 = ker;
}
void operator()(int rrWidth,int w2, int rWidth, int jj, int j, int c[num_images]) const
{
(void)j;
(void)rWidth;
for(int i = 0; i < imageStop; i++)
{
if (image[i][(rrWidth + jj)] > image[i][(w2 + (jj + n2))])
{
c[i] = c[i] + 1;
}
c[i] = c[i] * 2;
}
}
};
//!A kernel in which a pixel is compared with the center of the window
template<int num_images>
struct CensusKernel
{
uint8_t *image[num_images];
int imageStop;
CensusKernel(){}
CensusKernel(uint8_t **images)
{
for(int i = 0; i < num_images; i++)
image[i] = images[i];
imageStop = num_images;
}
void operator()(int rrWidth,int w2, int rWidth, int jj, int j, int c[num_images]) const
{
(void)w2;
for(int i = 0; i < imageStop; i++)
{
////compare a pixel with the center from the kernel
if (image[i][rrWidth + jj] > image[i][rWidth + j])
{
c[i] += 1;
}
c[i] <<= 1;
}
}
};
//template clas which efficiently combines the descriptors
template <int step_start, int step_end, int step_inc,int nr_img, typename Kernel>
class CombinedDescriptor:public ParallelLoopBody
{
private:
int width, height,n2;
int stride_;
int *dst[nr_img];
Kernel kernel_;
int n2_stop;
public:
CombinedDescriptor(int w, int h,int stride, int k2, int **distance, Kernel kernel,int k2Stop)
{
width = w;
height = h;
n2 = k2;
stride_ = stride;
for(int i = 0; i < nr_img; i++)
dst[i] = distance[i];
kernel_ = kernel;
n2_stop = k2Stop;
}
void operator()(const cv::Range &r) const {
for (int i = r.start; i <= r.end ; i++)
{
int rWidth = i * stride_;
for (int j = n2 + 2; j <= width - n2 - 2; j++)
{
int c[nr_img];
memset(c,0,nr_img);
for(int step = step_start; step <= step_end; step += step_inc)
{
for (int ii = - n2; ii <= + n2_stop; ii += step)
{
int rrWidth = (ii + i) * stride_;
int rrWidthC = (ii + i + n2) * stride_;
for (int jj = j - n2; jj <= j + n2; jj += step)
{
if (ii != i || jj != j)
{
kernel_(rrWidth,rrWidthC, rWidth, jj, j,c);
}
}
}
}
for(int l = 0; l < nr_img; l++)
dst[l][rWidth + j] = c[l];
}
}
}
};
//!calculate the mean of every windowSizexWindwoSize block from the integral Image
//!this is a preprocessing for MV kernel
class MeanKernelIntegralImage : public ParallelLoopBody
{
private:
int *img;
int windowSize,width;
float scalling;
int *c;
public:
MeanKernelIntegralImage(const cv::Mat &image, int window,float scale, int *cost):
img((int *)image.data),windowSize(window) ,width(image.cols) ,scalling(scale) , c(cost){};
void operator()(const cv::Range &r) const{
for (int i = r.start; i <= r.end; i++)
{
int iw = i * width;
for (int j = windowSize + 1; j <= width - windowSize - 1; j++)
{
c[iw + j] = (int)((img[(i + windowSize - 1) * width + j + windowSize - 1] + img[(i - windowSize - 1) * width + j - windowSize - 1]
- img[(i + windowSize) * width + j - windowSize] - img[(i - windowSize) * width + j + windowSize]) * scalling);
}
}
}
};
//!implementation for the star kernel descriptor
template<int num_images>
class StarKernelCensus:public ParallelLoopBody
{
private:
uint8_t *image[num_images];
int *dst[num_images];
int n2, width, height, im_num,stride_;
public:
StarKernelCensus(const cv::Mat *img, int k2, int **distance)
{
for(int i = 0; i < num_images; i++)
{
image[i] = img[i].data;
dst[i] = distance[i];
}
n2 = k2;
width = img[0].cols;
height = img[0].rows;
im_num = num_images;
stride_ = (int)img[0].step;
}
void operator()(const cv::Range &r) const {
for (int i = r.start; i <= r.end ; i++)
{
int rWidth = i * stride_;
for (int j = n2; j <= width - n2; j++)
{
for(int d = 0 ; d < im_num; d++)
{
int c = 0;
for(int step = 4; step > 0; step--)
{
for (int ii = i - step; ii <= i + step; ii += step)
{
int rrWidth = ii * stride_;
for (int jj = j - step; jj <= j + step; jj += step)
{
if (image[d][rrWidth + jj] > image[d][rWidth + j])
{
c = c + 1;
}
c = c * 2;
}
}
}
for (int ii = -1; ii <= +1; ii++)
{
int rrWidth = (ii + i) * stride_;
if (i == -1)
{
if (ii + i != i)
{
if (image[d][rrWidth + j] > image[d][rWidth + j])
{
c = c + 1;
}
c = c * 2;
}
}
else if (i == 0)
{
for (int j2 = -1; j2 <= 1; j2 += 2)
{
if (ii + i != i)
{
if (image[d][rrWidth + j + j2] > image[d][rWidth + j])
{
c = c + 1;
}
c = c * 2;
}
}
}
else
{
if (ii + i != i)
{
if (image[d][rrWidth + j] > image[d][rWidth + j])
{
c = c + 1;
}
c = c * 2;
}
}
}
dst[d][rWidth + j] = c;
}
}
}
}
};
//!paralel implementation of the center symetric census
template <int num_images>
class SymetricCensus:public ParallelLoopBody
{
private:
uint8_t *image[num_images];
int *dst[num_images];
int n2, width, height, im_num,stride_;
public:
SymetricCensus(const cv::Mat *img, int k2, int **distance)
{
for(int i = 0; i < num_images; i++)
{
image[i] = img[i].data;
dst[i] = distance[i];
}
n2 = k2;
width = img[0].cols;
height = img[0].rows;
im_num = num_images;
stride_ = (int)img[0].step;
}
void operator()(const cv::Range &r) const {
for (int i = r.start; i <= r.end ; i++)
{
int distV = i*stride_;
for (int j = n2; j <= width - n2; j++)
{
for(int d = 0; d < im_num; d++)
{
int c = 0;
//the classic center symetric census which compares the curent pixel with its symetric not its center.
for (int ii = -n2; ii <= 0; ii++)
{
int rrWidth = (ii + i) * stride_;
for (int jj = -n2; jj <= +n2; jj++)
{
if (image[d][(rrWidth + (jj + j))] > image[d][((ii * (-1) + i) * width + (-1 * jj) + j)])
{
c = c + 1;
}
c = c * 2;
if(ii == 0 && jj < 0)
{
if (image[d][(i * width + (jj + j))] > image[d][(i * width + (-1 * jj) + j)])
{
c = c + 1;
}
c = c * 2;
}
}
}
dst[d][(distV + j)] = c;
}
}
}
}
};
/**
Two variations of census applied on input images
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);
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
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
**/
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
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
**/
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
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
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
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);
}
}
#endif
#endif
/*End of file*/
//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
// (3-clause BSD License)
//
//Copyright (C) 2000-2015, Intel Corporation, all rights reserved.
//Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved.
//Copyright (C) 2009-2015, NVIDIA Corporation, all rights reserved.
//Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved.
//Copyright (C) 2015, OpenCV Foundation, all rights reserved.
//Copyright (C) 2015, Itseez 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:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistributions 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.
//
// * Neither the names of the copyright holders nor the names of the contributors
// may 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 copyright holders 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.
/*****************************************************************************************************************\
* The interface contains the main methods for computing the matching between the left and right images *
* *
\******************************************************************************************************************/
#include "precomp.hpp"
#include <stdint.h>
#ifndef _OPENCV_MATCHING_HPP_
#define _OPENCV_MATCHING_HPP_
#ifdef __cplusplus
namespace cv
{
namespace stereo
{
class Matching
{
private:
//!The maximum disparity
int maxDisparity;
//!the factor by which we are multiplying the disparity
int scallingFactor;
//!the confidence to which a min disparity found is good or not
double confidenceCheck;
//!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(short *c, int iwpj, int widthDisp,const double confidence, const int search_region)
{
double mini, mini2, mini3;
mini = mini2 = mini3 = DBL_MAX;
int index = 0;
int iw = iwpj;
int widthDisp2;
widthDisp2 = widthDisp;
widthDisp -= 1;
for (int i = 0; i <= widthDisp; i++)
{
if (c[(iw + i * search_region) * widthDisp2 + i] < mini)
{
mini3 = mini2;
mini2 = mini;
mini = c[(iw + i * search_region) * widthDisp2 + i];
index = i;
}
else if (c[(iw + i * search_region) * widthDisp2 + i] < mini2)
{
mini3 = mini2;
mini2 = c[(iw + i * search_region) * widthDisp2 + i];
}
else if (c[(iw + i * search_region) * widthDisp2 + i] < mini3)
{
mini3 = c[(iw + i * search_region) * widthDisp2 + i];
}
}
if(mini != 0)
{
if (mini3 / mini <= confidence)
return index;
}
return -1;
}
//!Interpolate in order to obtain better results
//!function for refining the disparity at sub pixel using simetric v
static double symetricVInterpolation(short *c, int iwjp, int widthDisp, int winDisp,const int search_region)
{
if (winDisp == 0 || winDisp == widthDisp - 1)
return winDisp;
double m2m1, m3m1, m3, m2, m1;
m2 = c[(iwjp + (winDisp - 1) * search_region) * widthDisp + winDisp - 1];
m3 = c[(iwjp + (winDisp + 1) * search_region)* widthDisp + winDisp + 1];
m1 = c[(iwjp + winDisp * search_region) * widthDisp + winDisp];
m2m1 = m2 - m1;
m3m1 = m3 - m1;
if (m2m1 == 0 || m3m1 == 0) return winDisp;
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)
p = winDisp + p;
return p;
}
//!a pre processing function that generates the Hamming LUT in case the algorithm will ever be used on platform where SSE is not available
void hammingLut()
{
for (int i = 0; i <= 65536; i++)
{
int dist = 0;
int j = i;
//we number the bits from our number
while (j)
{
dist = dist + 1;
j = j & (j - 1);
}
hamLut[i] = dist;
}
}
//!the class used in computing the hamming distance
class hammingDistance : public ParallelLoopBody
{
private:
int *left, *right;
short *c;
int v,kernelSize, width, height;
int MASK;
int *hammLut;
public :
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++)
{
int iw = i * width;
for (int j = kernelSize; j < width - kernelSize; j++)
{
int j2;
int xorul;
int iwj;
iwj = iw + j;
for (int d = 0; d <= v; d++)
{
j2 = (0 > j - d) ? (0) : (j - d);
xorul = left[(iwj)] ^ right[(iw + j2)];
#if CV_SSE4_1
c[(iwj)* (v + 1) + d] = (short)_mm_popcnt_u32(xorul);
#else
c[(iwj)* (v + 1) + d] = (short)(hammLut[xorul & MASK] + hammLut[(xorul >> 16) & MASK]);
#endif
}
}
}
}
};
//!cost aggregation
class agregateCost:public ParallelLoopBody
{
private:
int win;
short *c, *parSum;
int maxDisp,width, height;
public:
agregateCost(const Mat &partialSums, int windowSize, int maxDispa, Mat &cost)
{
win = windowSize / 2;
c = (short *)cost.data;
maxDisp = maxDispa;
width = cost.cols / ( maxDisp + 1) - 1;
height = cost.rows - 1;
parSum = (short *)partialSums.data;
}
void operator()(const cv::Range &r) const {
for (int i = r.start; i <= r.end; i++)
{
int iwi = (i - 1) * width;
for (int j = win + 1; j <= width - win - 1; j++)
{
int w1 = ((i + win + 1) * width + j + win) * (maxDisp + 1);
int w2 = ((i - win) * width + j - win - 1) * (maxDisp + 1);
int w3 = ((i + win + 1) * width + j - win - 1) * (maxDisp + 1);
int w4 = ((i - win) * width + j + win) * (maxDisp + 1);
int w = (iwi + j - 1) * (maxDisp + 1);
for (int d = 0; d <= maxDisp; d++)
{
c[w + d] = parSum[w1 + d] + parSum[w2 + d]
- parSum[w3 + d] - parSum[w4 + d];
}
}
}
}
};
//!class that is responsable for generating the disparity map
class makeMap:public ParallelLoopBody
{
private:
//enum used to notify wether we are searching on the vertical ie (lr) or diagonal (rl)
enum {CV_VERTICAL_SEARCH, CV_DIAGONAL_SEARCH};
int width,disparity,scallingFact,th;
double confCheck;
uint8_t *map;
short *c;
public:
makeMap(const Mat &costVolume, int threshold, int maxDisp, double confidence,int scale, Mat &mapFinal)
{
c = (short *)costVolume.data;
map = mapFinal.data;
disparity = maxDisp;
width = costVolume.cols / ( disparity + 1) - 1;
th = threshold;
scallingFact = scale;
confCheck = confidence;
}
void operator()(const cv::Range &r) const {
for (int i = r.start; i <= r.end ; i++)
{
int lr;
int v = -1;
double p1, p2;
int iw = i * width;
for (int j = 0; j < width; j++)
{
lr = Matching:: minim(c, iw + j, disparity + 1, confCheck,CV_VERTICAL_SEARCH);
if (lr != -1)
{
v = Matching::minim(c, iw + j - lr, disparity + 1, confCheck,CV_DIAGONAL_SEARCH);
if (v != -1)
{
p1 = Matching::symetricVInterpolation(c, iw + j - lr, disparity + 1, v,CV_DIAGONAL_SEARCH);
p2 = Matching::symetricVInterpolation(c, iw + j, disparity + 1, lr,CV_VERTICAL_SEARCH);
if (abs(p1 - p2) <= th)
map[iw + j] = (uint8_t)((p2)* scallingFact);
else
{
map[iw + j] = 0;
}
}
else
{
if (width - j <= disparity)
{
p2 = Matching::symetricVInterpolation(c, iw + j, disparity + 1, lr,CV_VERTICAL_SEARCH);
map[iw + j] = (uint8_t)(p2* scallingFact);
}
}
}
else
{
map[iw + j] = 0;
}
}
}
}
};
//!median 1x9 paralelized filter
template <typename T>
class Median1x9:public ParallelLoopBody
{
private:
T *original;
T *filtered;
int height, width;
public:
Median1x9(const Mat &originalImage, Mat &filteredImage)
{
original = (T *)originalImage.data;
filtered = (T *)filteredImage.data;
height = originalImage.rows;
width = originalImage.cols;
}
void operator()(const cv::Range &r) const{
for (int m = r.start; m <= r.end; m++)
{
for (int n = 4; n < width - 4; ++n)
{
int k = 0;
T window[9];
for (int i = n - 4; i <= n + 4; ++i)
window[k++] = original[m * width + i];
for (int j = 0; j < 5; ++j)
{
int min = j;
for (int l = j + 1; l < 9; ++l)
if (window[l] < window[min])
min = l;
const T temp = window[j];
window[j] = window[min];
window[min] = temp;
}
filtered[m * width + n] = window[4];
}
}
}
};
//!median 9x1 paralelized filter
template <typename T>
class Median9x1:public ParallelLoopBody
{
private:
T *original;
T *filtered;
int height, width;
public:
Median9x1(const Mat &originalImage, Mat &filteredImage)
{
original = (T *)originalImage.data;
filtered = (T *)filteredImage.data;
height = originalImage.rows;
width = originalImage.cols;
}
void operator()(const Range &r) const{
for (int n = r.start; n <= r.end; ++n)
{
for (int m = 4; m < height - 4; ++m)
{
int k = 0;
T window[9];
for (int i = m - 4; i <= m + 4; ++i)
window[k++] = original[i * width + n];
for (int j = 0; j < 5; j++)
{
int min = j;
for (int l = j + 1; l < 9; ++l)
if (window[l] < window[min])
min = l;
const T temp = window[j];
window[j] = window[min];
window[min] = temp;
}
filtered[m * width + n] = window[4];
}
}
}
};
protected:
//arrays used in the region removal
Mat speckleY;
Mat speckleX;
Mat puss;
//int *specklePointX;
//int *specklePointY;
//long long *pus;
int previous_size;
//!method for setting the maximum disparity
void setMaxDisparity(int val)
{
CV_Assert(val > 10);
this->maxDisparity = val;
}
//!method for getting the disparity
int getMaxDisparity()
{
return this->maxDisparity;
}
//! a number by which the disparity will be multiplied for better display
void setScallingFactor(int val)
{
CV_Assert(val > 0);
this->scallingFactor = val;
}
//!method for getting the scalling factor
int getScallingFactor()
{
return scallingFactor;
}
//!setter for the confidence check
void setConfidence(double val)
{
CV_Assert(val >= 1);
this->confidenceCheck = val;
}
//getter for confidence check
double getConfidence()
{
return confidenceCheck;
}
//! Hamming distance computation method
//! leftImage and rightImage are the two transformed images
//! the cost is the resulted cost volume and kernel Size is the size of the matching window
void hammingDistanceBlockMatching(const Mat &leftImage, const Mat &rightImage, Mat &cost, const int kernelSize= 9)
{
CV_Assert(leftImage.cols == rightImage.cols);
CV_Assert(leftImage.rows == rightImage.rows);
CV_Assert(kernelSize % 2 != 0);
CV_Assert(cost.rows == leftImage.rows);
CV_Assert(cost.cols / (maxDisparity + 1) == leftImage.cols);
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,(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_16S);
CV_Assert(cost.type() == CV_16S);
int maxDisp = maxDisparity;
int width = cost.cols / ( maxDisp + 1) - 1;
int height = cost.rows - 1;
short *c = (short *)cost.data;
short *ham = (short *)hammingDistanceCost.data;
memset(c, 0, sizeof(c[0]) * (width + 1) * (height + 1) * (maxDisp + 1));
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++)
{
int iwj = (i * width + j) * (maxDisp + 1);
int iwjmu = ((i - 1) * width + j) * (maxDisp + 1);
for (int d = 0; d <= maxDisp; d++)
{
c[iwj + d] += c[iwjmu + d];
}
}
}
}
//!The aggregation on the cost volume
void blockAgregation(const Mat &partialSums, int windowSize, Mat &cost)
{
CV_Assert(windowSize % 2 != 0);
CV_Assert(partialSums.rows == cost.rows);
CV_Assert(partialSums.cols == cost.cols);
int win = windowSize / 2;
short *c = (short *)cost.data;
int maxDisp = maxDisparity;
int width = cost.cols / ( maxDisp + 1) - 1;
int height = cost.rows - 1;
memset(c, 0, sizeof(c[0]) * width * height * (maxDisp + 1));
parallel_for_(cv::Range(win + 1,height - win - 1), agregateCost(partialSums,windowSize,maxDisp,cost));
}
//!remove small regions that have an area smaller than t, we fill the region with the average of the good pixels around it
template <typename T>
void smallRegionRemoval(const Mat &currentMap, int t, Mat &out)
{
CV_Assert(currentMap.cols == out.cols);
CV_Assert(currentMap.rows == out.rows);
CV_Assert(t >= 0);
int *pus = (int *)puss.data;
int *specklePointX = (int *)speckleX.data;
int *specklePointY = (int *)speckleY.data;
memset(pus, 0, previous_size * sizeof(pus[0]));
T *map = (T *)currentMap.data;
T *outputMap = (T *)out.data;
int height = currentMap.rows;
int width = currentMap.cols;
T k = 1;
int st, dr;
int di[] = { -1, -1, -1, 0, 1, 1, 1, 0 },
dj[] = { -1, 0, 1, 1, 1, 0, -1, -1 };
int speckle_size = 0;
st = 0;
dr = 0;
for (int i = 1; i < height - 1; i++)
{
int iw = i * width;
for (int j = 1; j < width - 1; j++)
{
if (map[iw + j] != 0)
{
outputMap[iw + j] = map[iw + j];
}
else if (map[iw + j] == 0)
{
T nr = 1;
T avg = 0;
speckle_size = dr;
specklePointX[dr] = i;
specklePointY[dr] = j;
pus[i * width + j] = 1;
dr++;
map[iw + j] = k;
while (st < dr)
{
int ii = specklePointX[st];
int jj = specklePointY[st];
//going on 8 directions
for (int d = 0; d < 8; d++)
{//if insisde
if (ii + di[d] >= 0 && ii + di[d] < height && jj + dj[d] >= 0 && jj + dj[d] < width &&
pus[(ii + di[d]) * width + jj + dj[d]] == 0)
{
T val = map[(ii + di[d]) * width + jj + dj[d]];
if (val == 0)
{
map[(ii + di[d]) * width + jj + dj[d]] = k;
specklePointX[dr] = (ii + di[d]);
specklePointY[dr] = (jj + dj[d]);
dr++;
pus[(ii + di[d]) * width + jj + dj[d]] = 1;
}//this means that my point is a good point to be used in computing the final filling value
else if (val >= 1 && val < 250)
{
avg += val;
nr++;
}
}
}
st++;
}//if hole size is smaller than a specified threshold we fill the respective hole with the average of the good neighbours
if (st - speckle_size <= t)
{
T fillValue = (T)(avg / nr);
while (speckle_size < st)
{
int ii = specklePointX[speckle_size];
int jj = specklePointY[speckle_size];
outputMap[ii * width + jj] = fillValue;
speckle_size++;
}
}
}
}
}
}
//!Method responsible for generating the disparity map
//!function for generating disparity maps at sub pixel level
/* costVolume - represents the cost volume
* width, height - represent the width and height of the iage
*disparity - represents the maximum disparity
*map - is the disparity map that will result
*th - is the LR threshold
*/
void dispartyMapFormation(const Mat &costVolume, Mat &mapFinal, int th)
{
uint8_t *map = mapFinal.data;
int disparity = maxDisparity;
int width = costVolume.cols / ( disparity + 1) - 1;
int height = costVolume.rows - 1;
memset(map, 0, sizeof(map[0]) * width * height);
parallel_for_(Range(0,height - 1), makeMap(costVolume,th,disparity,confidenceCheck,scallingFactor,mapFinal));
}
public:
//!a median filter of 1x9 and 9x1
//!1x9 median filter
template<typename T>
void Median1x9Filter(const Mat &originalImage, Mat &filteredImage)
{
CV_Assert(originalImage.rows == filteredImage.rows);
CV_Assert(originalImage.cols == filteredImage.cols);
parallel_for_(Range(1,originalImage.rows - 2), Median1x9<T>(originalImage,filteredImage));
}
//!9x1 median filter
template<typename T>
void Median9x1Filter(const Mat &originalImage, Mat &filteredImage)
{
CV_Assert(originalImage.cols == filteredImage.cols);
CV_Assert(originalImage.cols == filteredImage.cols);
parallel_for_(Range(1,originalImage.cols - 2), Median9x1<T>(originalImage,filteredImage));
}
//!constructor for the matching class
//!maxDisp - represents the maximum disparity
Matching(void)
{
hammingLut();
}
~Matching(void)
{
}
//constructor for the matching class
//maxDisp - represents the maximum disparity
//confidence - represents the confidence check
Matching(int maxDisp, int scalling = 4, int confidence = 6)
{
//set the maximum disparity
setMaxDisparity(maxDisp);
//set scalling factor
setScallingFactor(scalling);
//set the value for the confidence
setConfidence(confidence);
//generate the hamming lut in case SSE is not available
hammingLut();
}
};
}
}
#endif
#endif
/*End of file*/
\ No newline at end of file
......@@ -56,4 +56,3 @@
#include <cmath>
#endif
......@@ -46,6 +46,8 @@
\****************************************************************************************/
#include "precomp.hpp"
#include "descriptor.hpp"
#include "matching.hpp"
#include <stdio.h>
#include <limits>
......@@ -53,37 +55,46 @@ namespace cv
{
namespace stereo
{
struct StereoBinaryBMParams
{
StereoBinaryBMParams(int _numDisparities = 64, int _SADWindowSize = 9)
StereoBinaryBMParams(int _numDisparities = 64, int _kernelSize = 9)
{
preFilterType = StereoBinaryBM::PREFILTER_XSOBEL;
preFilterSize = 9;
preFilterCap = 31;
SADWindowSize = _SADWindowSize;
kernelSize = _kernelSize;
minDisparity = 0;
numDisparities = _numDisparities > 0 ? _numDisparities : 64;
textureThreshold = 10;
uniquenessRatio = 15;
speckleRange = speckleWindowSize = 0;
roi1 = roi2 = Rect(0, 0, 0, 0);
disp12MaxDiff = -1;
dispType = CV_16S;
usePrefilter = false;
regionRemoval = 1;
scalling = 4;
kernelType = CV_MODIFIED_CENSUS_TRANSFORM;
agregationWindowSize = 9;
}
int preFilterType;
int preFilterSize;
int preFilterCap;
int SADWindowSize;
int kernelSize;
int minDisparity;
int numDisparities;
int textureThreshold;
int uniquenessRatio;
int speckleRange;
int speckleWindowSize;
Rect roi1, roi2;
int disp12MaxDiff;
int dispType;
int scalling;
bool usePrefilter;
int regionRemoval;
int kernelType;
int agregationWindowSize;
};
static void prefilterNorm(const Mat& src, Mat& dst, int winsize, int ftzero, uchar* buf)
......@@ -104,13 +115,11 @@ namespace cv
for (x = 0; x < size.width; x++)
vsum[x] = (ushort)(sptr[x] * (wsz2 + 2));
for (y = 1; y < wsz2; y++)
{
for (x = 0; x < size.width; x++)
vsum[x] = (ushort)(vsum[x] + sptr[srcstep*y + x]);
}
for (y = 0; y < size.height; y++)
{
const uchar* top = sptr + srcstep*MAX(y - wsz2 - 1, 0);
......@@ -119,7 +128,6 @@ namespace cv
const uchar* curr = sptr + srcstep*y;
const uchar* next = sptr + srcstep*MIN(y + 1, size.height - 1);
uchar* dptr = dst.ptr<uchar>(y);
for (x = 0; x < size.width; x++)
vsum[x] = (ushort)(vsum[x] + bottom[x] - top[x]);
......@@ -132,10 +140,8 @@ namespace cv
int sum = vsum[0] * (wsz2 + 1);
for (x = 1; x <= wsz2; x++)
sum += vsum[x];
int val = ((curr[0] * 5 + curr[1] + prev[0] + next[0])*scale_g - sum*scale_s) >> 10;
dptr[0] = tab[val + OFS];
for (x = 1; x < size.width - 1; x++)
{
sum += vsum[x + wsz2] - vsum[x - wsz2 - 1];
......@@ -232,179 +238,6 @@ namespace cv
static const int DISPARITY_SHIFT = 4;
static void
findStereoCorrespondenceBM(const Mat& left, const Mat& right,
Mat& disp, Mat& cost, const StereoBinaryBMParams& state,
uchar* buf, int _dy0, int _dy1)
{
const int ALIGN = 16;
int x, y, d;
int wsz = state.SADWindowSize, wsz2 = wsz / 2;
int dy0 = MIN(_dy0, wsz2 + 1), dy1 = MIN(_dy1, wsz2 + 1);
int ndisp = state.numDisparities;
int mindisp = state.minDisparity;
int lofs = MAX(ndisp - 1 + mindisp, 0);
int rofs = -MIN(ndisp - 1 + mindisp, 0);
int width = left.cols, height = left.rows;
int width1 = width - rofs - ndisp + 1;
int ftzero = state.preFilterCap;
int textureThreshold = state.textureThreshold;
int uniquenessRatio = state.uniquenessRatio;
short FILTERED = (short)((mindisp - 1) << DISPARITY_SHIFT);
int *sad, *hsad0, *hsad, *hsad_sub, *htext;
uchar *cbuf0, *cbuf;
const uchar* lptr0 = left.ptr() + lofs;
const uchar* rptr0 = right.ptr() + rofs;
const uchar *lptr, *lptr_sub, *rptr;
short* dptr = disp.ptr<short>();
int sstep = (int)left.step;
int dstep = (int)(disp.step / sizeof(dptr[0]));
int cstep = (height + dy0 + dy1)*ndisp;
int costbuf = 0;
int coststep = cost.data ? (int)(cost.step / sizeof(costbuf)) : 0;
const int TABSZ = 256;
uchar tab[TABSZ];
sad = (int*)alignPtr(buf + sizeof(sad[0]), ALIGN);
hsad0 = (int*)alignPtr(sad + ndisp + 1 + dy0*ndisp, ALIGN);
htext = (int*)alignPtr((int*)(hsad0 + (height + dy1)*ndisp) + wsz2 + 2, ALIGN);
cbuf0 = (uchar*)alignPtr((uchar*)(htext + height + wsz2 + 2) + dy0*ndisp, ALIGN);
for (x = 0; x < TABSZ; x++)
tab[x] = (uchar)std::abs(x - ftzero);
// initialize buffers
memset(hsad0 - dy0*ndisp, 0, (height + dy0 + dy1)*ndisp*sizeof(hsad0[0]));
memset(htext - wsz2 - 1, 0, (height + wsz + 1)*sizeof(htext[0]));
for (x = -wsz2 - 1; x < wsz2; x++)
{
hsad = hsad0 - dy0*ndisp; cbuf = cbuf0 + (x + wsz2 + 1)*cstep - dy0*ndisp;
lptr = lptr0 + std::min(std::max(x, -lofs), width - lofs - 1) - dy0*sstep;
rptr = rptr0 + std::min(std::max(x, -rofs), width - rofs - 1) - dy0*sstep;
for (y = -dy0; y < height + dy1; y++, hsad += ndisp, cbuf += ndisp, lptr += sstep, rptr += sstep)
{
int lval = lptr[0];
for (d = 0; d < ndisp; d++)
{
int diff = std::abs(lval - rptr[d]);
cbuf[d] = (uchar)diff;
hsad[d] = (int)(hsad[d] + diff);
}
htext[y] += tab[lval];
}
}
// initialize the left and right borders of the disparity map
for (y = 0; y < height; y++)
{
for (x = 0; x < lofs; x++)
dptr[y*dstep + x] = FILTERED;
for (x = lofs + width1; x < width; x++)
dptr[y*dstep + x] = FILTERED;
}
dptr += lofs;
for (x = 0; x < width1; x++, dptr++)
{
int* costptr = cost.data ? cost.ptr<int>() + lofs + x : &costbuf;
int x0 = x - wsz2 - 1, x1 = x + wsz2;
const uchar* cbuf_sub = cbuf0 + ((x0 + wsz2 + 1) % (wsz + 1))*cstep - dy0*ndisp;
cbuf = cbuf0 + ((x1 + wsz2 + 1) % (wsz + 1))*cstep - dy0*ndisp;
hsad = hsad0 - dy0*ndisp;
lptr_sub = lptr0 + MIN(MAX(x0, -lofs), width - 1 - lofs) - dy0*sstep;
lptr = lptr0 + MIN(MAX(x1, -lofs), width - 1 - lofs) - dy0*sstep;
rptr = rptr0 + MIN(MAX(x1, -rofs), width - 1 - rofs) - dy0*sstep;
for (y = -dy0; y < height + dy1; y++, cbuf += ndisp, cbuf_sub += ndisp,
hsad += ndisp, lptr += sstep, lptr_sub += sstep, rptr += sstep)
{
int lval = lptr[0];
for (d = 0; d < ndisp; d++)
{
int diff = std::abs(lval - rptr[d]);
cbuf[d] = (uchar)diff;
hsad[d] = hsad[d] + diff - cbuf_sub[d];
}
htext[y] += tab[lval] - tab[lptr_sub[0]];
}
// fill borders
for (y = dy1; y <= wsz2; y++)
htext[height + y] = htext[height + dy1 - 1];
for (y = -wsz2 - 1; y < -dy0; y++)
htext[y] = htext[-dy0];
// initialize sums
int tsum = 0;
{
for (d = 0; d < ndisp; d++)
sad[d] = (int)(hsad0[d - ndisp*dy0] * (wsz2 + 2 - dy0));
hsad = hsad0 + (1 - dy0)*ndisp;
for (y = 1 - dy0; y < wsz2; y++, hsad += ndisp)
for (d = 0; d < ndisp; d++)
sad[d] = (int)(sad[d] + hsad[d]);
for (y = -wsz2 - 1; y < wsz2; y++)
tsum += htext[y];
}
// finally, start the real processing
{
for (y = 0; y < height; y++)
{
int minsad = INT_MAX, mind = -1;
hsad = hsad0 + MIN(y + wsz2, height + dy1 - 1)*ndisp;
hsad_sub = hsad0 + MAX(y - wsz2 - 1, -dy0)*ndisp;
for (d = 0; d < ndisp; d++)
{
int currsad = sad[d] + hsad[d] - hsad_sub[d];
sad[d] = currsad;
if (currsad < minsad)
{
minsad = currsad;
mind = d;
}
}
tsum += htext[y + wsz2] - htext[y - wsz2 - 1];
if (tsum < textureThreshold)
{
dptr[y*dstep] = FILTERED;
continue;
}
if (uniquenessRatio > 0)
{
int thresh = minsad + (minsad * uniquenessRatio / 100);
for (d = 0; d < ndisp; d++)
{
if ((d < mind - 1 || d > mind + 1) && sad[d] <= thresh)
break;
}
if (d < ndisp)
{
dptr[y*dstep] = FILTERED;
continue;
}
}
{
sad[-1] = sad[1];
sad[ndisp] = sad[ndisp - 2];
int p = sad[mind + 1], n = sad[mind - 1];
d = p + n - 2 * sad[mind] + std::abs(p - n);
dptr[y*dstep] = (short)(((ndisp - mind - 1 + mindisp) * 256 + (d != 0 ? (p - n) * 256 / d : 0) + 15) >> 4);
costptr[y*coststep] = sad[mind];
}
}
}
}
}
struct PrefilterInvoker : public ParallelLoopBody
{
PrefilterInvoker(const Mat& left0, const Mat& right0, Mat& left, Mat& right,
......@@ -433,93 +266,18 @@ namespace cv
StereoBinaryBMParams* state;
};
struct FindStereoCorrespInvoker : public ParallelLoopBody
{
FindStereoCorrespInvoker(const Mat& _left, const Mat& _right,
Mat& _disp, StereoBinaryBMParams* _state,
int _nstripes, size_t _stripeBufSize,
bool _useShorts, Rect _validDisparityRect,
Mat& _slidingSumBuf, Mat& _cost)
{
left = &_left; right = &_right;
disp = &_disp; state = _state;
nstripes = _nstripes; stripeBufSize = _stripeBufSize;
useShorts = _useShorts;
validDisparityRect = _validDisparityRect;
slidingSumBuf = &_slidingSumBuf;
cost = &_cost;
}
void operator()(const Range& range) const
{
int cols = left->cols, rows = left->rows;
int _row0 = std::min(cvRound(range.start * rows / nstripes), rows);
int _row1 = std::min(cvRound(range.end * rows / nstripes), rows);
uchar *ptr = slidingSumBuf->ptr() + range.start * stripeBufSize;
int FILTERED = (state->minDisparity - 1) * 16;
Rect roi = validDisparityRect & Rect(0, _row0, cols, _row1 - _row0);
if (roi.height == 0)
return;
int row0 = roi.y;
int row1 = roi.y + roi.height;
Mat part;
if (row0 > _row0)
{
part = disp->rowRange(_row0, row0);
part = Scalar::all(FILTERED);
}
if (_row1 > row1)
{
part = disp->rowRange(row1, _row1);
part = Scalar::all(FILTERED);
}
Mat left_i = left->rowRange(row0, row1);
Mat right_i = right->rowRange(row0, row1);
Mat disp_i = disp->rowRange(row0, row1);
Mat cost_i = state->disp12MaxDiff >= 0 ? cost->rowRange(row0, row1) : Mat();
findStereoCorrespondenceBM(left_i, right_i, disp_i, cost_i, *state, ptr, row0, rows - row1);
if (state->disp12MaxDiff >= 0)
validateDisparity(disp_i, cost_i, state->minDisparity, state->numDisparities, state->disp12MaxDiff);
if (roi.x > 0)
{
part = disp_i.colRange(0, roi.x);
part = Scalar::all(FILTERED);
}
if (roi.x + roi.width < cols)
{
part = disp_i.colRange(roi.x + roi.width, cols);
part = Scalar::all(FILTERED);
}
}
protected:
const Mat *left, *right;
Mat* disp, *slidingSumBuf, *cost;
StereoBinaryBMParams *state;
int nstripes;
size_t stripeBufSize;
bool useShorts;
Rect validDisparityRect;
};
class StereoBinaryBMImpl : public StereoBinaryBM
class StereoBinaryBMImpl : public StereoBinaryBM, public Matching
{
public:
StereoBinaryBMImpl()
StereoBinaryBMImpl(): Matching(64)
{
params = StereoBinaryBMParams();
}
StereoBinaryBMImpl(int _numDisparities, int _SADWindowSize)
StereoBinaryBMImpl(int _numDisparities, int _kernelSize) : Matching(_numDisparities)
{
params = StereoBinaryBMParams(_numDisparities, _SADWindowSize);
params = StereoBinaryBMParams(_numDisparities, _kernelSize);
previous_size = 0;
}
void compute(InputArray leftarr, InputArray rightarr, OutputArray disparr)
......@@ -546,9 +304,9 @@ namespace cv
if (params.preFilterCap < 1 || params.preFilterCap > 63)
CV_Error(Error::StsOutOfRange, "preFilterCap must be within 1..63");
if (params.SADWindowSize < 5 || params.SADWindowSize > 255 || params.SADWindowSize % 2 == 0 ||
params.SADWindowSize >= std::min(leftsize.width, leftsize.height))
CV_Error(Error::StsOutOfRange, "SADWindowSize must be odd, be within 5..255 and be not larger than image width or height");
if (params.kernelSize < 5 || params.kernelSize > 255 || params.kernelSize % 2 == 0 ||
params.kernelSize >= std::min(leftsize.width, leftsize.height))
CV_Error(Error::StsOutOfRange, "kernelSize must be odd, be within 5..255 and be not larger than image width or height");
if (params.numDisparities <= 0 || params.numDisparities % 16 != 0)
CV_Error(Error::StsOutOfRange, "numDisparities must be positive and divisble by 16");
......@@ -561,135 +319,163 @@ namespace cv
int FILTERED = (params.minDisparity - 1) << DISPARITY_SHIFT;
Mat left0 = leftarr.getMat(), right0 = rightarr.getMat();
disparr.create(left0.size(), dtype);
Mat disp0 = disparr.getMat();
preFilteredImg0.create(left0.size(), CV_8U);
preFilteredImg1.create(left0.size(), CV_8U);
cost.create(left0.size(), CV_16S);
int width = left0.cols;
int height = left0.rows;
if(previous_size != width * height)
{
previous_size = width * height;
speckleX.create(height,width,CV_32SC4);
speckleY.create(height,width,CV_32SC4);
puss.create(height,width,CV_32SC4);
Mat left = preFilteredImg0, right = preFilteredImg1;
censusImage[0].create(left0.rows,left0.cols,CV_32SC4);
censusImage[1].create(left0.rows,left0.cols,CV_32SC4);
int mindisp = params.minDisparity;
int ndisp = params.numDisparities;
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);
int width = left0.cols;
int height = left0.rows;
int lofs = std::max(ndisp - 1 + mindisp, 0);
int rofs = -std::min(ndisp - 1 + mindisp, 0);
int width1 = width - rofs - ndisp + 1;
preFilteredImg0.create(left0.size(), CV_8U);
preFilteredImg1.create(left0.size(), CV_8U);
if (lofs >= width || rofs >= width || width1 < 1)
{
disp0 = Scalar::all(FILTERED * (disp0.type() < CV_32F ? 1 : 1. / (1 << DISPARITY_SHIFT)));
return;
aux.create(height,width,CV_8UC1);
}
Mat disp = disp0;
if (dtype == CV_32F)
{
dispbuf.create(disp0.size(), CV_16S);
disp = dispbuf;
}
Mat left = preFilteredImg0, right = preFilteredImg1;
int ndisp = params.numDisparities;
int wsz = params.SADWindowSize;
int wsz = params.kernelSize;
int bufSize0 = (int)((ndisp + 2)*sizeof(int));
bufSize0 += (int)((height + wsz + 2)*ndisp*sizeof(int));
bufSize0 += (int)((height + wsz + 2)*sizeof(int));
bufSize0 += (int)((height + wsz + 2)*ndisp*(wsz + 2)*sizeof(uchar) + 256);
int bufSize1 = (int)((width + params.preFilterSize + 2) * sizeof(int) + 256);
int bufSize2 = 0;
if (params.speckleRange >= 0 && params.speckleWindowSize > 0)
bufSize2 = width*height*(sizeof(Point_<short>) + sizeof(int) + sizeof(uchar));
#if CV_SSE2
bool useShorts = params.preFilterCap <= 31 && params.SADWindowSize <= 21 && checkHardwareSupport(CV_CPU_SSE2);
#else
const bool useShorts = false;
#endif
const double SAD_overhead_coeff = 10.0;
double N0 = 8000000 / (useShorts ? 1 : 4); // approx tbb's min number instructions reasonable for one thread
double maxStripeSize = std::min(std::max(N0 / (width * ndisp), (wsz - 1) * SAD_overhead_coeff), (double)height);
int nstripes = cvCeil(height / maxStripeSize);
int bufSize = std::max(bufSize0 * nstripes, std::max(bufSize1 * 2, bufSize2));
if (slidingSumBuf.cols < bufSize)
slidingSumBuf.create(1, bufSize, CV_8U);
if(params.usePrefilter == true)
{
uchar *_buf = slidingSumBuf.ptr();
parallel_for_(Range(0, 2), PrefilterInvoker(left0, right0, left, right, _buf, _buf + bufSize1, &params), 1);
}
else if(params.usePrefilter == false)
{
left = left0;
right = right0;
}
if(params.kernelType == CV_SPARSE_CENSUS)
{
censusTransform(left,right,params.kernelSize,censusImage[0],censusImage[1],CV_SPARSE_CENSUS);
}
else if(params.kernelType == CV_DENSE_CENSUS)
{
censusTransform(left,right,params.kernelSize,censusImage[0],censusImage[1],CV_SPARSE_CENSUS);
}
else if(params.kernelType == CV_CS_CENSUS)
{
symetricCensusTransform(left,right,params.kernelSize,censusImage[0],censusImage[1],CV_CS_CENSUS);
}
else if(params.kernelType == CV_MODIFIED_CS_CENSUS)
{
symetricCensusTransform(left,right,params.kernelSize,censusImage[0],censusImage[1],CV_MODIFIED_CS_CENSUS);
}
else if(params.kernelType == CV_MODIFIED_CENSUS_TRANSFORM)
{
modifiedCensusTransform(left,right,params.kernelSize,censusImage[0],censusImage[1],CV_MODIFIED_CENSUS_TRANSFORM,0);
}
else if(params.kernelType == CV_MEAN_VARIATION)
{
parSumsIntensityImage[0].create(left0.rows, left0.cols,CV_32SC4);
parSumsIntensityImage[1].create(left0.rows, left0.cols,CV_32SC4);
Integral[0].create(left0.rows,left0.cols,CV_32SC4);
Integral[1].create(left0.rows,left0.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,censusImage[0],censusImage[1],CV_MEAN_VARIATION,0,Integral[0], Integral[1]);
}
else if(params.kernelType == CV_STAR_KERNEL)
{
starCensusTransform(left,right,params.kernelSize,censusImage[0],censusImage[1]);
}
hammingDistanceBlockMatching(censusImage[0], censusImage[1], hammingDistance);
costGathering(hammingDistance, partialSumsLR);
blockAgregation(partialSumsLR, params.agregationWindowSize, agregatedHammingLRCost);
dispartyMapFormation(agregatedHammingLRCost, disp0, 3);
Median1x9Filter<uint8_t>(disp0, aux);
Median9x1Filter<uint8_t>(aux,disp0);
Rect validDisparityRect(0, 0, width, height), R1 = params.roi1, R2 = params.roi2;
validDisparityRect = getValidDisparityROI(R1.area() > 0 ? Rect(0, 0, width, height) : validDisparityRect,
R2.area() > 0 ? Rect(0, 0, width, height) : validDisparityRect,
params.minDisparity, params.numDisparities,
params.SADWindowSize);
if(params.regionRemoval == CV_SPECKLE_REMOVAL_AVG_ALGORITHM)
{
smallRegionRemoval<uint8_t>(disp0,params.speckleWindowSize,disp0);
}
else if(params.regionRemoval == CV_SPECKLE_REMOVAL_ALGORITHM)
{
if (params.speckleRange >= 0 && params.speckleWindowSize > 0)
filterSpeckles(disp0, FILTERED, params.speckleWindowSize, params.speckleRange, slidingSumBuf);
}
}
int getAgregationWindowSize() const { return params.agregationWindowSize;}
void setAgregationWindowSize(int value = 9) { CV_Assert(value % 2 != 0); params.agregationWindowSize = value;}
parallel_for_(Range(0, nstripes),
FindStereoCorrespInvoker(left, right, disp, &params, nstripes,
bufSize0, useShorts, validDisparityRect,
slidingSumBuf, cost));
int getBinaryKernelType() const { return params.kernelType;}
void setBinaryKernelType(int value = CV_MODIFIED_CENSUS_TRANSFORM) { CV_Assert(value < 7); params.kernelType = value; }
if (params.speckleRange >= 0 && params.speckleWindowSize > 0)
filterSpeckles(disp, FILTERED, params.speckleWindowSize, params.speckleRange, slidingSumBuf);
int getSpekleRemovalTechnique() const { return params.regionRemoval;}
void setSpekleRemovalTechnique(int factor = CV_SPECKLE_REMOVAL_AVG_ALGORITHM) {CV_Assert(factor < 2); params.regionRemoval = factor; }
if (disp0.data != disp.data)
disp.convertTo(disp0, disp0.type(), 1. / (1 << DISPARITY_SHIFT), 0);
}
bool getUsePrefilter() const { return params.usePrefilter;}
void setUsePrefilter(bool value = false) { params.usePrefilter = value;}
int getScalleFactor() const { return params.scalling;}
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.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 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) {}
Rect getROI1() const { return params.roi1; }
void setROI1(Rect roi1) { params.roi1 = roi1; }
Rect getROI2() const { return params.roi2; }
void setROI2(Rect roi2) { params.roi2 = roi2; }
void write(FileStorage& fs) const
{
fs << "name" << name_
<< "minDisparity" << params.minDisparity
<< "numDisparities" << params.numDisparities
<< "blockSize" << params.SADWindowSize
<< "blockSize" << params.kernelSize
<< "speckleWindowSize" << params.speckleWindowSize
<< "speckleRange" << params.speckleRange
<< "disp12MaxDiff" << params.disp12MaxDiff
......@@ -706,7 +492,7 @@ namespace cv
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"];
......@@ -715,24 +501,27 @@ namespace cv
params.preFilterCap = (int)fn["preFilterCap"];
params.textureThreshold = (int)fn["textureThreshold"];
params.uniquenessRatio = (int)fn["uniquenessRatio"];
params.roi1 = params.roi2 = Rect();
}
StereoBinaryBMParams params;
Mat preFilteredImg0, preFilteredImg1, cost, dispbuf;
Mat slidingSumBuf;
Mat parSumsIntensityImage[2];
Mat Integral[2];
Mat censusImage[2];
Mat hammingDistance;
Mat partialSumsLR;
Mat agregatedHammingLRCost;
Mat aux;
static const char* name_;
};
const char* StereoBinaryBMImpl::name_ = "StereoMatcher.BM";
const char* StereoBinaryBMImpl::name_ = "StereoBinaryMatcher.BM";
Ptr<StereoBinaryBM> StereoBinaryBM::create(int _numDisparities, int _SADWindowSize)
Ptr<StereoBinaryBM> StereoBinaryBM::create(int _numDisparities, int _kernelSize)
{
return makePtr<StereoBinaryBMImpl>(_numDisparities, _SADWindowSize);
return makePtr<StereoBinaryBMImpl>(_numDisparities, _kernelSize);
}
}
}
/* End of file. */
......@@ -40,18 +40,18 @@
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
/*
This is a variation of
"Stereo Processing by Semiglobal Matching and Mutual Information"
by Heiko Hirschmuller.
We match blocks rather than individual pixels, thus the algorithm is called
SGBM (Semi-global block matching)
*/
#include "precomp.hpp"
#include <limits.h>
#include <descriptor.hpp>
#include <matching.hpp>
namespace cv
{
......@@ -60,16 +60,14 @@ namespace cv
typedef uchar PixType;
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;
......@@ -78,7 +76,6 @@ namespace cv
speckleRange = 0;
mode = StereoBinarySGBM::MODE_SGBM;
}
StereoBinarySGBMParams( int _minDisparity, int _numDisparities, int _SADWindowSize,
int _P1, int _P2, int _disp12MaxDiff, int _preFilterCap,
int _uniquenessRatio, int _speckleWindowSize, int _speckleRange,
......@@ -86,7 +83,7 @@ namespace cv
{
minDisparity = _minDisparity;
numDisparities = _numDisparities;
SADWindowSize = _SADWindowSize;
kernelSize = _SADWindowSize;
P1 = _P1;
P2 = _P2;
disp12MaxDiff = _disp12MaxDiff;
......@@ -95,11 +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;
......@@ -108,209 +107,31 @@ 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<maxD the function
computes the cost (cost[(x-minX)*(maxD - minD) + (d - minD)]), depending on the difference between
row1[x] and row2[x-d]. The subpixel algorithm from
"Depth Discontinuities by Pixel-to-Pixel Stereo" by Stan Birchfield and C. Tomasi
is used, hence the suffix BT.
the temporary buffer should contain width2*2 elements
*/
static void calcPixelCostBT( const Mat& img1, const Mat& img2, int y,
int minD, int maxD, CostType* cost,
PixType* buffer, const PixType* tab,
int tabOfs, int )
{
int x, c, width = img1.cols, cn = img1.channels();
int minX1 = std::max(-maxD, 0), maxX1 = width + std::min(minD, 0);
int minX2 = std::max(minX1 - maxD, 0), maxX2 = std::min(maxX1 - minD, width);
int D = maxD - minD, width1 = maxX1 - minX1, width2 = maxX2 - minX2;
const PixType *row1 = img1.ptr<PixType>(y), *row2 = img2.ptr<PixType>(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).
minD <= d < maxD.
disp2full is the reverse disparity map, that is:
disp2full(x+roi.x,y+roi.y)=d means that img2(x+roi.x, y+roi.y) ~ img1(x+roi.x+d, y+roi.y)
note that disp1buf will have the same size as the roi and
disp2full will have the same size as img1 (or img2).
On exit disp2buf is not the final disparity, it is an intermediate result that becomes
final after all the tiles are processed.
the disparity in disp1buf is written with sub-pixel accuracy
(4 fractional bits, see StereoSGBM::DISP_SCALE),
using quadratic interpolation, while the disparity in disp2buf
is written as is, without interpolation.
disp2cost also has the same size as img1 (or img2).
It contains the minimum current cost, used to find the best disparity, corresponding to the minimal cost.
*/
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[] =
......@@ -332,11 +153,9 @@ namespace cv
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);
......@@ -344,33 +163,27 @@ 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);
return;
}
CV_Assert( D % 16 == 0 );
// NR - the number of directions. the loop on x below that computes Lr assumes that NR == 8.
// if you change NR, please, modify the loop as well.
int D2 = D+16, NRD2 = NR2*D2;
// the number of L_r(.,.) and min_k L_r(.,.) lines in the buffer:
// for 8-way dynamic programming we need the current row and
// 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)
......@@ -383,29 +196,23 @@ namespace cv
CSBufSize*2*sizeof(CostType) + // C, S
width*16*img1.channels()*sizeof(PixType) + // temp buffer for computing per-pixel cost
width*(sizeof(CostType) + sizeof(DispType)) + 1024; // disp2cost + disp2
if( buffer.empty() || !buffer.isContinuous() ||
buffer.cols*buffer.rows*buffer.elemSize() < totalBufSize )
buffer.create(1, (int)totalBufSize, CV_8U);
// summary cost over different (nDirs) directions
CostType* Cbuf = (CostType*)alignPtr(buffer.ptr(), ALIGN);
CostType* Sbuf = Cbuf + CSBufSize;
CostType* hsumBuf = Sbuf + CSBufSize;
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;
for( int pass = 1; pass <= npasses; pass++ )
{
int x1, y1, x2, y2, dx, dy;
if( pass == 1 )
{
y1 = 0; y2 = height; dy = 1;
......@@ -416,9 +223,7 @@ namespace cv
y1 = height-1; y2 = -1; dy = -1;
x1 = width1-1; x2 = -1; dx = -1;
}
CostType *Lr[NLR]={0}, *minLr[NLR]={0};
for( k = 0; k < NLR; k++ )
{
// shift Lr[k] and minLr[k] pointers, because we allocated them with the borders,
......@@ -431,26 +236,27 @@ namespace cv
minLr[k] = pixDiff + costBufSize + LrSize*NLR + minLrSize*k + NR2*LrBorder;
memset( minLr[k] - LrBorder*NR2, 0, minLrSize*sizeof(CostType) );
}
for( int y = y1; y != y2; y += dy )
{
int x, d;
DispType* disp1ptr = disp1.ptr<DispType>(y);
CostType* C = Cbuf + (!fullDP ? 0 : y*costBufSize);
CostType* S = Sbuf + (!fullDP ? 0 : y*costBufSize);
if( pass == 1 ) // compute C on the first pass, and reuse it on the second pass, if any.
{
int dy1 = y == 0 ? 0 : y + SH2, dy2 = y == 0 ? SH2 : dy1;
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 )
{
......@@ -503,13 +309,11 @@ namespace cv
{
const CostType* pixAdd = pixDiff + std::min(x + SW2*D, (width1-1)*D);
const CostType* pixSub = pixDiff + std::max(x - (SW2+1)*D, 0);
for( d = 0; d < D; d++ )
hsumAdd[x + d] = (CostType)(hsumAdd[x - D + d] + pixAdd[d] - pixSub[d]);
}
}
}
if( y == 0 )
{
int scale = k == 0 ? SH2 + 1 : 1;
......@@ -517,18 +321,15 @@ namespace cv
C[x] = (CostType)(C[x] + hsumAdd[x]*scale);
}
}
// also, clear the S buffer
for( k = 0; k < width1*D; k++ )
S[k] = 0;
}
// clear the left and the right borders
memset( Lr[0] - NRD2*LrBorder - 8, 0, NRD2*LrBorder*sizeof(CostType) );
memset( Lr[0] + width1*NRD2 - 8, 0, NRD2*LrBorder*sizeof(CostType) );
memset( minLr[0] - NR2*LrBorder, 0, NR2*LrBorder*sizeof(CostType) );
memset( minLr[0] + width1*NR2, 0, NR2*LrBorder*sizeof(CostType) );
/*
[formula 13 in the paper]
compute L_r(p, d) = C(p, d) +
......@@ -550,87 +351,65 @@ namespace cv
for( x = x1; x != x2; x += dx )
{
int xm = x*NR2, xd = xm*D2;
int delta0 = minLr[0][xm - dx*NR2] + P2, delta1 = minLr[1][xm - NR2 + 1] + P2;
int delta2 = minLr[1][xm + 2] + P2, delta3 = minLr[1][xm + NR2 + 3] + P2;
CostType* Lr_p0 = Lr[0] + xd - dx*NRD2;
CostType* Lr_p1 = Lr[1] + xd - NRD2 + D2;
CostType* Lr_p2 = Lr[1] + xd + D2*2;
CostType* Lr_p3 = Lr[1] + xd + NRD2 + D2*3;
Lr_p0[-1] = Lr_p0[D] = Lr_p1[-1] = Lr_p1[D] =
Lr_p2[-1] = Lr_p2[D] = Lr_p3[-1] = Lr_p3[D] = MAX_COST;
CostType* Lr_p = Lr[0] + xd;
const CostType* Cp = C + x*D;
CostType* Sp = S + x*D;
#if CV_SSE2
if( useSIMD )
{
__m128i _P1 = _mm_set1_epi16((short)P1);
__m128i _delta0 = _mm_set1_epi16((short)delta0);
__m128i _delta1 = _mm_set1_epi16((short)delta1);
__m128i _delta2 = _mm_set1_epi16((short)delta2);
__m128i _delta3 = _mm_set1_epi16((short)delta3);
__m128i _minL0 = _mm_set1_epi16((short)MAX_COST);
for( d = 0; d < D; d += 8 )
{
__m128i Cpd = _mm_load_si128((const __m128i*)(Cp + d));
__m128i L0, L1, L2, L3;
L0 = _mm_load_si128((const __m128i*)(Lr_p0 + d));
L1 = _mm_load_si128((const __m128i*)(Lr_p1 + d));
L2 = _mm_load_si128((const __m128i*)(Lr_p2 + d));
L3 = _mm_load_si128((const __m128i*)(Lr_p3 + d));
L0 = _mm_min_epi16(L0, _mm_adds_epi16(_mm_loadu_si128((const __m128i*)(Lr_p0 + d - 1)), _P1));
L0 = _mm_min_epi16(L0, _mm_adds_epi16(_mm_loadu_si128((const __m128i*)(Lr_p0 + d + 1)), _P1));
L1 = _mm_min_epi16(L1, _mm_adds_epi16(_mm_loadu_si128((const __m128i*)(Lr_p1 + d - 1)), _P1));
L1 = _mm_min_epi16(L1, _mm_adds_epi16(_mm_loadu_si128((const __m128i*)(Lr_p1 + d + 1)), _P1));
L2 = _mm_min_epi16(L2, _mm_adds_epi16(_mm_loadu_si128((const __m128i*)(Lr_p2 + d - 1)), _P1));
L2 = _mm_min_epi16(L2, _mm_adds_epi16(_mm_loadu_si128((const __m128i*)(Lr_p2 + d + 1)), _P1));
L3 = _mm_min_epi16(L3, _mm_adds_epi16(_mm_loadu_si128((const __m128i*)(Lr_p3 + d - 1)), _P1));
L3 = _mm_min_epi16(L3, _mm_adds_epi16(_mm_loadu_si128((const __m128i*)(Lr_p3 + d + 1)), _P1));
L0 = _mm_min_epi16(L0, _delta0);
L0 = _mm_adds_epi16(_mm_subs_epi16(L0, _delta0), Cpd);
L1 = _mm_min_epi16(L1, _delta1);
L1 = _mm_adds_epi16(_mm_subs_epi16(L1, _delta1), Cpd);
L2 = _mm_min_epi16(L2, _delta2);
L2 = _mm_adds_epi16(_mm_subs_epi16(L2, _delta2), Cpd);
L3 = _mm_min_epi16(L3, _delta3);
L3 = _mm_adds_epi16(_mm_subs_epi16(L3, _delta3), Cpd);
_mm_store_si128( (__m128i*)(Lr_p + d), L0);
_mm_store_si128( (__m128i*)(Lr_p + d + D2), L1);
_mm_store_si128( (__m128i*)(Lr_p + d + D2*2), L2);
_mm_store_si128( (__m128i*)(Lr_p + d + D2*3), L3);
__m128i t0 = _mm_min_epi16(_mm_unpacklo_epi16(L0, L2), _mm_unpackhi_epi16(L0, L2));
__m128i t1 = _mm_min_epi16(_mm_unpacklo_epi16(L1, L3), _mm_unpackhi_epi16(L1, L3));
t0 = _mm_min_epi16(_mm_unpacklo_epi16(t0, t1), _mm_unpackhi_epi16(t0, t1));
_minL0 = _mm_min_epi16(_minL0, t0);
__m128i Sval = _mm_load_si128((const __m128i*)(Sp + d));
L0 = _mm_adds_epi16(L0, L1);
L2 = _mm_adds_epi16(L2, L3);
Sval = _mm_adds_epi16(Sval, L0);
Sval = _mm_adds_epi16(Sval, L2);
_mm_store_si128((__m128i*)(Sp + d), Sval);
}
_minL0 = _mm_min_epi16(_minL0, _mm_srli_si128(_minL0, 8));
_mm_storel_epi64((__m128i*)&minLr[0][xm], _minL0);
}
......@@ -691,58 +470,45 @@ namespace cv
CostType* Lr_p0 = Lr[0] + xd + NRD2;
Lr_p0[-1] = Lr_p0[D] = MAX_COST;
CostType* Lr_p = Lr[0] + xd;
const CostType* Cp = C + x*D;
#if CV_SSE2
if( useSIMD )
{
__m128i _P1 = _mm_set1_epi16((short)P1);
__m128i _delta0 = _mm_set1_epi16((short)delta0);
__m128i _minL0 = _mm_set1_epi16((short)minL0);
__m128i _minS = _mm_set1_epi16(MAX_COST), _bestDisp = _mm_set1_epi16(-1);
__m128i _d8 = _mm_setr_epi16(0, 1, 2, 3, 4, 5, 6, 7), _8 = _mm_set1_epi16(8);
for( d = 0; d < D; d += 8 )
{
__m128i Cpd = _mm_load_si128((const __m128i*)(Cp + d)), L0;
L0 = _mm_load_si128((const __m128i*)(Lr_p0 + d));
L0 = _mm_min_epi16(L0, _mm_adds_epi16(_mm_loadu_si128((const __m128i*)(Lr_p0 + d - 1)), _P1));
L0 = _mm_min_epi16(L0, _mm_adds_epi16(_mm_loadu_si128((const __m128i*)(Lr_p0 + d + 1)), _P1));
L0 = _mm_min_epi16(L0, _delta0);
L0 = _mm_adds_epi16(_mm_subs_epi16(L0, _delta0), Cpd);
_mm_store_si128((__m128i*)(Lr_p + d), L0);
_minL0 = _mm_min_epi16(_minL0, L0);
L0 = _mm_adds_epi16(L0, *(__m128i*)(Sp + d));
_mm_store_si128((__m128i*)(Sp + d), L0);
__m128i mask = _mm_cmpgt_epi16(_minS, L0);
_minS = _mm_min_epi16(_minS, L0);
_bestDisp = _mm_xor_si128(_bestDisp, _mm_and_si128(_mm_xor_si128(_bestDisp,_d8), mask));
_d8 = _mm_adds_epi16(_d8, _8);
}
short CV_DECL_ALIGNED(16) bestDispBuf[8];
_mm_store_si128((__m128i*)bestDispBuf, _bestDisp);
_minL0 = _mm_min_epi16(_minL0, _mm_srli_si128(_minL0, 8));
_minL0 = _mm_min_epi16(_minL0, _mm_srli_si128(_minL0, 4));
_minL0 = _mm_min_epi16(_minL0, _mm_srli_si128(_minL0, 2));
__m128i qS = _mm_min_epi16(_minS, _mm_srli_si128(_minS, 8));
qS = _mm_min_epi16(qS, _mm_srli_si128(qS, 4));
qS = _mm_min_epi16(qS, _mm_srli_si128(qS, 2));
minLr[0][xm] = (CostType)_mm_cvtsi128_si32(_minL0);
minS = (CostType)_mm_cvtsi128_si32(qS);
qS = _mm_shuffle_epi32(_mm_unpacklo_epi16(qS, qS), 0);
qS = _mm_cmpeq_epi16(_minS, qS);
int idx = _mm_movemask_epi8(_mm_packs_epi16(qS, qS)) & 255;
bestDisp = bestDispBuf[LSBTab[idx]];
}
else
......@@ -751,10 +517,8 @@ namespace cv
for( d = 0; d < D; d++ )
{
int L0 = Cp[d] + std::min((int)Lr_p0[d], std::min(Lr_p0[d-1] + P1, std::min(Lr_p0[d+1] + P1, delta0))) - delta0;
Lr_p[d] = (CostType)L0;
minL0 = std::min(minL0, L0);
int Sval = Sp[d] = saturate_cast<CostType>(Sp[d] + L0);
if( Sval < minS )
{
......@@ -777,7 +541,6 @@ namespace cv
}
}
}
for( d = 0; d < D; d++ )
{
if( Sp[d]*(100 - uniquenessRatio) < minS*100 && std::abs(bestDisp - d) > 1 )
......@@ -792,8 +555,37 @@ namespace cv
disp2cost[_x2] = (CostType)minS;
disp2ptr[_x2] = (DispType)(d + minD);
}
if( 0 < d && d < D-1 )
{
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])
......@@ -801,11 +593,11 @@ namespace cv
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;
disp1ptr[x + minX1] = (DispType)(d + minD*DISP_SCALE);
}
for( x = minX1; x < maxX1; x++ )
{
// we round the computed disparity both towards -inf and +inf and check
......@@ -822,79 +614,150 @@ namespace cv
disp1ptr[x] = (DispType)INVALID_DISP_SCALED;
}
}
// now shift the cyclic buffers
std::swap( Lr[0], Lr[1] );
std::swap( minLr[0], minLr[1] );
}
}
}
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,
_uniquenessRatio, _speckleWindowSize, _speckleRange,
_mode );
}
void compute( InputArray leftarr, InputArray rightarr, OutputArray disparr )
{
Mat left = leftarr.getMat(), right = rightarr.getMat();
CV_Assert( left.size() == right.size() && left.type() == right.type() &&
left.depth() == CV_8U );
disparr.create( left.size(), CV_16S );
Mat disp = disparr.getMat();
censusImageLeft.create(left.rows,left.cols,CV_32SC4);
censusImageRight.create(left.rows,left.cols,CV_32SC4);
computeDisparityBinarySGBM( left, right, disp, params, buffer );
medianBlur(disp, disp, 3);
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;
speckleX.create(height,width,CV_32SC4);
speckleY.create(height,width,CV_32SC4);
puss.create(height,width,CV_32SC4);
}
Mat aux;
aux.create(height,width,CV_16S);
Median1x9Filter<short>(disp, aux);
Median9x1Filter<short>(aux,disp);
smallRegionRemoval<short>(disp, params.speckleWindowSize, disp);
}
else if(params.regionRemoval == CV_SPECKLE_REMOVAL_ALGORITHM)
{
int width = left.cols;
int height = left.rows;
Mat aux;
aux.create(height,width,CV_16S);
Median1x9Filter<short>(disp, aux);
Median9x1Filter<short>(aux,disp);
if( params.speckleWindowSize > 0 )
filterSpeckles(disp, (params.minDisparity - 1)*StereoMatcher::DISP_SCALE, params.speckleWindowSize,
StereoMatcher::DISP_SCALE*params.speckleRange, buffer);
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; }
......@@ -904,7 +767,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
......@@ -921,7 +784,7 @@ namespace cv
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"];
......@@ -935,18 +798,25 @@ namespace cv
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";
const char* StereoBinarySGBMImpl::name_ = "StereoBinaryMatcher.SGBM";
Ptr<StereoBinarySGBM> StereoBinarySGBM::create(int minDisparity, int numDisparities, int SADWindowSize,
Ptr<StereoBinarySGBM> 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<StereoBinarySGBM>(
new StereoBinarySGBMImpl(minDisparity, numDisparities, SADWindowSize,
new StereoBinarySGBMImpl(minDisparity, numDisparities, kernelSize,
P1, P2, disp12MaxDiff,
preFilterCap, uniquenessRatio,
speckleWindowSize, speckleRange,
......
/*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 <limits.h>
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
//in order for the tests to build succesfully please replace
//ts->get_data_path() + "testdata/imL2l.bmp with the path from your disk
//for example if your images are on D:\\ , please write D:\\testdata\\imL2l.bmp
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<StereoBinaryBM> 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<StereoBinarySGBM> 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(); }
/*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 <limits.h>
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
//some test images can be found in the test data folder
//in order for the tests to build succesfully please replace
//ts->get_data_path() + "testdata/imL2l.bmp with the path from your disk
//for example if your images are on D:\\ , please write D:\\testdata\\imL2l.bmp
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<int>::min();
int max = numeric_limits<int>::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(); }
......@@ -12,8 +12,6 @@
#include <iostream>
#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 <algorithm>
#include <cmath>
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment