vendor: OpenCV 5.0.0 snapshot at 755e50675d97db9b7d449d8bd6b09888646f6c6e

This commit is contained in:
Gitea Mirror Bot
2026-08-22 00:11:13 +08:00
commit 12022378a3
3872 changed files with 2513409 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
set(the_description "Stereo Correspondence")
ocv_define_module(xstereo opencv_imgproc opencv_features opencv_core opencv_stereo opencv_tracking WRAP python)
+11
View File
@@ -0,0 +1,11 @@
Stereo Correspondence with different descriptors
================================================
Stereo matching done with different descriptors: Census / CS-Census / MCT / BRIEF / MV.
Quasi Dense Stereo
======================
Quasi Dense Stereo is method for performing dense stereo matching.
The code uses pyramidal Lucas-Kanade with Shi-Tomasi features to get the initial seed correspondences.
Then these seeds are propagated by using mentioned growing scheme.
+33
View File
@@ -0,0 +1,33 @@
@InProceedings{Stoyanov2010,
author="Stoyanov, Danail
and Scarzanella, Marco Visentini
and Pratt, Philip
and Yang, Guang-Zhong",
editor="Jiang, Tianzi
and Navab, Nassir
and Pluim, Josien P. W.
and Viergever, Max A.",
title="Real-Time Stereo Reconstruction in Robotically Assisted Minimally Invasive Surgery",
booktitle="Medical Image Computing and Computer-Assisted Intervention (MICCAI 2010)",
year="2010",
publisher="Springer Berlin Heidelberg",
address="Berlin, Heidelberg",
pages="275--282",
abstract="The recovery of 3D tissue structure and morphology during robotic assisted surgery is an important step towards accurate deployment of surgical guidance and control techniques in minimally invasive therapies. In this article, we present a novel stereo reconstruction algorithm that propagates disparity information around a set of candidate feature matches. This has the advantage of avoiding problems with specular highlights, occlusions from instruments and view dependent illumination bias. Furthermore, the algorithm can be used with any feature matching strategy allowing the propagation of depth in very disparate views. Validation is provided for a phantom model with known geometry and this data is available online in order to establish a structured validation scheme in the field. The practical value of the proposed method is further demonstrated by reconstructions on various in vivo images of robotic assisted procedures, which are also available to the community.",
isbn="978-3-642-15705-9"
}
@article{Lhuillier2000,
abstract = {A new robust dense matching algorithm is introduced. The algorithm$\backslash$nstarts from matching the most textured points, then a match propagation$\backslash$nalgorithm is developed with the best first strategy to dense matching.$\backslash$nNext, the matching map is regularised by using the local geometric$\backslash$nconstraints encoded by planar affine applications and by using the$\backslash$nglobal geometric constraint encoded by the fundamental matrix. Two most$\backslash$ndistinctive features are a match propagation strategy developed by$\backslash$nanalogy to region growing and a successive regularisation by local and$\backslash$nglobal geometric constraints. The algorithm is efficient, robust and can$\backslash$ncope with wide disparity. The algorithm is demonstrated on many real$\backslash$nimage pairs, and applications on image interpolation and a creation of$\backslash$nnovel views are also presented},
author = {Lhuillier, Maxime and Quan, Long},
doi = {10.1109/ICPR.2000.905620},
file = {:home/dimitrisps/Desktop/ucl/papers/quasiDenseMatching.pdf:pdf},
isbn = {0-7695-0750-6},
issn = {10514651},
journal = {Proceedings-International Conference on Pattern Recognition},
number = {1},
pages = {968--972},
title = {{Robust dense matching using local and global geometric constraints}},
volume = {15},
year = {2000}
}
+228
View File
@@ -0,0 +1,228 @@
/*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.
// Copyright (C) 2013, OpenCV Foundation, 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*/
#ifndef __OPENCV_XSTEREO_HPP__
#define __OPENCV_XSTEREO_HPP__
#include "opencv2/core.hpp"
#include "opencv2/stereo.hpp"
#include "opencv2/xstereo/descriptor.hpp"
#include <opencv2/xstereo/quasi_dense_stereo.hpp>
/**
@defgroup xstereo Extra Stereo Correspondance Algorithms
*/
namespace cv
{
namespace stereo
{
//! @ingroup xstereo
//! @{
//!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.
*/
class StereoBinaryBM : public StereoMatcher
{
public:
enum { PREFILTER_NORMALIZED_RESPONSE = 0,
PREFILTER_XSOBEL = 1
};
virtual int getPreFilterType() const = 0;
virtual void setPreFilterType(int preFilterType) = 0;
virtual int getPreFilterSize() const = 0;
virtual void setPreFilterSize(int preFilterSize) = 0;
virtual int getPreFilterCap() const = 0;
virtual void setPreFilterCap(int preFilterCap) = 0;
virtual int getTextureThreshold() const = 0;
virtual void setTextureThreshold(int textureThreshold) = 0;
virtual int getUniquenessRatio() const = 0;
virtual void setUniquenessRatio(int uniquenessRatio) = 0;
virtual int getSmallerBlockSize() const = 0;
virtual void setSmallerBlockSize(int blockSize) = 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 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
disparity from 0 (default minimum disparity) to numDisparities. The search range can then be
shifted by changing the minimum disparity.
@param blockSize the linear size of the blocks compared by the algorithm. The size should be odd
(as the block is centered at the current pixel). Larger block size implies smoother, though less
accurate disparity map. Smaller block size gives more detailed disparity map, but there is higher
chance for algorithm to find a wrong correspondence.
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 = 9);
};
/** @brief The class implements the modified H. Hirschmuller algorithm @cite HH08 that differs from the original
one as follows:
- By default, the algorithm is single-pass, which means that you consider only 5 directions
instead of 8. Set mode=StereoSGBM::MODE_HH in createStereoSGBM to run the full variant of the
algorithm but beware that it may consume a lot of memory.
- The algorithm matches blocks, not individual pixels. Though, setting blockSize=1 reduces the
blocks to single pixels.
- Mutual information cost function is not implemented. Instead, a simpler Birchfield-Tomasi
sub-pixel metric from @cite BT98 is used. Though, the color images are supported as well.
- Some pre- and post- processing steps from K. Konolige algorithm StereoBM are included, for
example: pre-filtering (StereoBM::PREFILTER_XSOBEL type) and post-filtering (uniqueness
check, quadratic interpolation and speckle filtering).
@note
- (Python) An example illustrating the use of the StereoSGBM matching algorithm can be found
at opencv_source_code/samples/python2/stereo_match.py
*/
class StereoBinarySGBM : public StereoMatcher
{
public:
enum
{
MODE_SGBM = 0,
MODE_HH = 1
};
virtual int getPreFilterCap() const = 0;
virtual void setPreFilterCap(int preFilterCap) = 0;
virtual int getUniquenessRatio() const = 0;
virtual void setUniquenessRatio(int uniquenessRatio) = 0;
virtual int getP1() const = 0;
virtual void setP1(int P1) = 0;
virtual int getP2() const = 0;
virtual void setP2(int P2) = 0;
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
rectification algorithms can shift images, so this parameter needs to be adjusted accordingly.
@param numDisparities Maximum disparity minus minimum disparity. The value is always greater than
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.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
32\*number_of_image_channels\*SADWindowSize\*SADWindowSize , respectively).
@param disp12MaxDiff Maximum allowed difference (in integer pixel units) in the left-right
disparity check. Set it to a non-positive value to disable the check.
@param preFilterCap Truncation value for the prefiltered image pixels. The algorithm first
computes x-derivative at each pixel and clips its value by [-preFilterCap, preFilterCap] interval.
The result values are passed to the Birchfield-Tomasi pixel cost function.
@param uniquenessRatio Margin in percentage by which the best (minimum) computed cost function
value should "win" the second best value to consider the found match correct. Normally, a value
within the 5-15 range is good enough.
@param speckleWindowSize Maximum size of smooth disparity regions to consider their noise speckles
and invalidate. Set it to 0 to disable speckle filtering. Otherwise, set it somewhere in the
50-200 range.
@param speckleRange Maximum disparity variation within each connected component. If you do speckle
filtering, set the parameter to a positive value, it will be implicitly multiplied by 16.
Normally, 1 or 2 is good enough.
@param mode Set it to StereoSGBM::MODE_HH to run the full-scale two-pass dynamic programming
algorithm. It will consume O(W\*H\*numDisparities) bytes, which is large for 640x480 stereo and
huge for HD-size pictures. By default, it is set to false .
The first constructor initializes StereoSGBM with all the default parameters. So, you only have to
set StereoSGBM::numDisparities at minimum. The second constructor enables you to set each parameter
to a custom value.
*/
CV_EXPORTS static Ptr<StereoBinarySGBM> create(int minDisparity, int numDisparities, int blockSize,
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);
};
//! @}
}//stereo
} // cv
#endif
@@ -0,0 +1,44 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#ifndef _OPENCV_STEREO_DESCRIPTOR_HPP_
#define _OPENCV_STEREO_DESCRIPTOR_HPP_
namespace cv { namespace stereo {
// FIXIT deprecate and remove CV_ prefix
/// 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
};
/**
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
**/
CV_EXPORTS void censusTransform(const Mat &image1, const Mat &image2, int kernelSize, Mat &dist1, Mat &dist2, const int type);
/// single image census transform
CV_EXPORTS void censusTransform(const Mat &image1, int kernelSize, 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 Mat &img1, const Mat &img2, int kernelSize, Mat &dist1, Mat &dist2, const int type, int t = 0, const Mat &integralImage1 = Mat(), const Mat &integralImage2 = Mat());
///single version of modified census transform descriptor
CV_EXPORTS void modifiedCensusTransform(const Mat &img1, int kernelSize, Mat &dist, const int type, int t = 0, const Mat &integralImage = Mat());
/**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 Mat &img1, const Mat &img2, int kernelSize, Mat &dist1, Mat &dist2, const int type);
///single version of census transform
CV_EXPORTS void symetricCensusTransform(const Mat &img1, int kernelSize, Mat &dist1, const int type);
///in a 9x9 kernel only certain positions are choosen
CV_EXPORTS void starCensusTransform(const Mat &img1, const Mat &img2, int kernelSize, Mat &dist1, Mat &dist2);
///single image version of star kernel
CV_EXPORTS void starCensusTransform(const Mat &img1, int kernelSize, Mat &dist);
}} // namespace
#endif
@@ -0,0 +1,197 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//authors: Danail Stoyanov, Evangelos Mazomenos, Dimitrios Psychogyios
//__OPENCV_QUASI_DENSE_STEREO_H__
#ifndef __OPENCV_QUASI_DENSE_STEREO_H__
#define __OPENCV_QUASI_DENSE_STEREO_H__
#include <opencv2/core.hpp>
namespace cv
{
namespace stereo
{
/** \addtogroup xstereo
* @{
*/
// A basic match structure
struct CV_EXPORTS_W_SIMPLE MatchQuasiDense
{
CV_PROP_RW cv::Point2i p0;
CV_PROP_RW cv::Point2i p1;
CV_PROP_RW float corr;
CV_WRAP MatchQuasiDense() { corr = 0; }
CV_WRAP_AS(apply) bool operator < (const MatchQuasiDense & rhs) const//fixme may be used uninitialized in this function
{
return this->corr < rhs.corr;
}
};
struct CV_EXPORTS_W_SIMPLE PropagationParameters
{
CV_PROP_RW int corrWinSizeX; // similarity window
CV_PROP_RW int corrWinSizeY;
CV_PROP_RW int borderX; // border to ignore
CV_PROP_RW int borderY;
//matching
CV_PROP_RW float correlationThreshold; // correlation threshold
CV_PROP_RW float textrureThreshold; // texture threshold
CV_PROP_RW int neighborhoodSize; // neighborhood size
CV_PROP_RW int disparityGradient; // disparity gradient threshold
// Parameters for LK flow algorithm
CV_PROP_RW int lkTemplateSize;
CV_PROP_RW int lkPyrLvl;
CV_PROP_RW int lkTermParam1;
CV_PROP_RW float lkTermParam2;
// Parameters for GFT algorithm.
CV_PROP_RW float gftQualityThres;
CV_PROP_RW int gftMinSeperationDist;
CV_PROP_RW int gftMaxNumFeatures;
};
/**
* @brief Class containing the methods needed for Quasi Dense Stereo computation.
*
* This module contains the code to perform quasi dense stereo matching.
* The method initially starts with a sparse 3D reconstruction based on feature matching across a
* stereo image pair and subsequently propagates the structure into neighboring image regions.
* To obtain initial seed correspondences, the algorithm locates Shi and Tomashi features in the
* left image of the stereo pair and then tracks them using pyramidal Lucas-Kanade in the right image.
* To densify the sparse correspondences, the algorithm computes the zero-mean normalized
* cross-correlation (ZNCC) in small patches around every seed pair and uses it as a quality metric
* for each match. In this code, we introduce a custom structure to store the location and ZNCC value
* of correspondences called "Match". Seed Matches are stored in a priority queue sorted according to
* their ZNCC value, allowing for the best quality Match to be readily available. The algorithm pops
* Matches and uses them to extract new matches around them. This is done by considering a small
* neighboring area around each Seed and retrieving correspondences above a certain texture threshold
* that are not previously computed. New matches are stored in the seed priority queue and used as seeds.
* The propagation process ends when no additional matches can be retrieved.
*
*
* @sa This code represents the work presented in @cite Stoyanov2010.
* If this code is useful for your work please cite @cite Stoyanov2010.
*
* Also the original growing scheme idea is described in @cite Lhuillier2000
*
*/
class CV_EXPORTS_W QuasiDenseStereo
{
public:
/**
* @brief destructor
* Method to free all the memory allocated by matrices and vectors in this class.
*/
CV_WRAP virtual ~QuasiDenseStereo() = 0;
/**
* @brief Load a file containing the configuration parameters of the class.
* @param[in] filepath The location of the .YAML file containing the configuration parameters.
* @note default value is an empty string in which case the default parameters will be loaded.
* @retval 1: If the path is not empty and the program loaded the parameters successfully.
* @retval 0: If the path is empty and the program loaded default parameters.
* @retval -1: If the file location is not valid or the program could not open the file and
* loaded default parameters from defaults.hpp.
* @note The method is automatically called in the constructor and configures the class.
* @note Loading different parameters will have an effect on the output. This is useful for tuning
* in case of video processing.
* @sa loadParameters
*/
CV_WRAP virtual int loadParameters(cv::String filepath) = 0;
/**
* @brief Save a file containing all the configuration parameters the class is currently set to.
* @param[in] filepath The location to store the parameters file.
* @note Calling this method with no arguments will result in storing class parameters to a file
* names "qds_parameters.yaml" in the root project folder.
* @note This method can be used to generate a template file for tuning the class.
* @sa loadParameters
*/
CV_WRAP virtual int saveParameters(cv::String filepath) = 0;
/**
* @brief Get The sparse corresponding points.
* @param[out] sMatches A vector containing all sparse correspondences.
* @note The method clears the sMatches vector.
* @note The returned Match elements inside the sMatches vector, do not use corr member.
*/
CV_WRAP virtual void getSparseMatches(CV_OUT std::vector<MatchQuasiDense> &sMatches) = 0;
/**
* @brief Get The dense corresponding points.
* @param[out] denseMatches A vector containing all dense matches.
* @note The method clears the denseMatches vector.
* @note The returned Match elements inside the sMatches vector, do not use corr member.
*/
CV_WRAP virtual void getDenseMatches(CV_OUT std::vector<MatchQuasiDense> &denseMatches) = 0;
/**
* @brief Main process of the algorithm. This method computes the sparse seeds and then densifies them.
*
* Initially input images are converted to gray-scale and then the sparseMatching method
* is called to obtain the sparse stereo. Finally quasiDenseMatching is called to densify the corresponding
* points.
* @param[in] imgLeft The left Channel of a stereo image pair.
* @param[in] imgRight The right Channel of a stereo image pair.
* @note If input images are in color, the method assumes that are BGR and converts them to grayscale.
* @sa sparseMatching
* @sa quasiDenseMatching
*/
CV_WRAP virtual void process(const cv::Mat &imgLeft ,const cv::Mat &imgRight) = 0;
/**
* @brief Specify pixel coordinates in the left image and get its corresponding location in the right image.
* @param[in] x The x pixel coordinate in the left image channel.
* @param[in] y The y pixel coordinate in the left image channel.
* @retval cv::Point(x, y) The location of the corresponding pixel in the right image.
* @retval cv::Point(0, 0) (NO_MATCH) if no match is found in the right image for the specified pixel location in the left image.
* @note This method should be always called after process, otherwise the matches will not be correct.
*/
CV_WRAP virtual cv::Point2f getMatch(const int x, const int y) = 0;
/**
* @brief Compute and return the disparity map based on the correspondences found in the "process" method.
* @note Default level is 50
* @return cv::Mat containing a the disparity image in grayscale.
* @sa computeDisparity
* @sa quantizeDisparity
*/
CV_WRAP virtual cv::Mat getDisparity() = 0;
CV_WRAP static cv::Ptr<QuasiDenseStereo> create(cv::Size monoImgSize, cv::String paramFilepath = cv::String());
CV_PROP_RW PropagationParameters Param;
};
/** @}*/
} //namespace cv
} //namespace stereo
#endif // __OPENCV_QUASI_DENSE_STEREO_H__
@@ -0,0 +1,17 @@
#ifdef HAVE_OPENCV_STEREO
typedef std::vector<stereo::MatchQuasiDense> vector_MatchQuasiDense;
template<> struct pyopencvVecConverter<stereo::MatchQuasiDense>
{
static bool to(PyObject* obj, std::vector<stereo::MatchQuasiDense>& value, const ArgInfo& info)
{
return pyopencv_to_generic_vec(obj, value, info);
}
static PyObject* from(const std::vector<stereo::MatchQuasiDense>& value)
{
return pyopencv_from_generic_vec(value);
}
};
#endif
@@ -0,0 +1,20 @@
#!/usr/bin/env python
import cv2 as cv
from tests_common import NewOpenCVTests
class quasi_dense_stereo_test(NewOpenCVTests):
def test_simple(self):
stereo = cv.stereo.QuasiDenseStereo_create((100, 100))
self.assertIsNotNone(stereo)
dense_matches = cv.stereo_MatchQuasiDense()
self.assertIsNotNone(dense_matches)
parameters = cv.stereo_PropagationParameters()
self.assertIsNotNone(parameters)
if __name__ == '__main__':
NewOpenCVTests.bootstrap()
+122
View File
@@ -0,0 +1,122 @@
/*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"
namespace opencv_test { namespace {
typedef 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_8U ),
testing::Values( CV_8U,CV_16S )
)
)
{
Size sz = get<0>(GetParam());
int matType = get<1>(GetParam());
int sdepth = 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)
.in(right, WARMUP_RNG)
.out(out1)
.time(0.1)
.iterations(20);
TEST_CYCLE()
{
sgbm->compute(left, right, out1);
}
SANITY_CHECK_NOTHING();
}
PERF_TEST_P( s_bm, bm_perf,
testing::Combine(
testing::Values( cv::Size(512, 383), cv::Size(320, 240) ),
testing::Values( CV_8U ),
testing::Values( CV_8U )
)
)
{
Size sz = get<0>(GetParam());
int matType = get<1>(GetParam());
int sdepth = 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)
.in(right, WARMUP_RNG)
.out(out1)
.time(0.1)
.iterations(20);
TEST_CYCLE()
{
sbm->compute(left, right, out1);
}
SANITY_CHECK_NOTHING();
}
}} // namespace
+143
View File
@@ -0,0 +1,143 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#include "perf_precomp.hpp"
namespace opencv_test { namespace {
typedef 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_8U ),
testing::Values( CV_32SC4,CV_32S )
)
)
{
Size sz = get<0>(GetParam());
int matType = get<1>(GetParam());
int sdepth = 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_NOTHING();
}
PERF_TEST_P( descript_params, star_census_transform,
testing::Combine(
testing::Values( TYPICAL_MAT_SIZES ),
testing::Values( CV_8U ),
testing::Values( CV_32SC4,CV_32S )
)
)
{
Size sz = get<0>(GetParam());
int matType = get<1>(GetParam());
int sdepth = 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_NOTHING();
}
PERF_TEST_P( descript_params, modified_census_transform,
testing::Combine(
testing::Values( TYPICAL_MAT_SIZES ),
testing::Values( CV_8U ),
testing::Values( CV_32SC4,CV_32S )
)
)
{
Size sz = get<0>(GetParam());
int matType = get<1>(GetParam());
int sdepth = 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_NOTHING();
}
PERF_TEST_P( descript_params, center_symetric_census,
testing::Combine(
testing::Values( TYPICAL_MAT_SIZES ),
testing::Values( CV_8U ),
testing::Values( CV_32SC4,CV_32S )
)
)
{
Size sz = get<0>(GetParam());
int matType = get<1>(GetParam());
int sdepth = 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_NOTHING();
}
}} // namespace
+44
View File
@@ -0,0 +1,44 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#include "perf_precomp.hpp"
CV_PERF_TEST_MAIN(stereo)
+15
View File
@@ -0,0 +1,15 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#ifndef __OPENCV_PERF_PRECOMP_HPP__
#define __OPENCV_PERF_PRECOMP_HPP__
#include "opencv2/ts.hpp"
#include "opencv2/xstereo.hpp"
namespace opencv_test {
using namespace cv::stereo;
using namespace perf;
}
#endif
@@ -0,0 +1,63 @@
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <fstream>
#include <opencv2/xstereo.hpp>
using namespace cv;
using namespace std;
int main()
{
//! [load]
cv::Mat rightImg, leftImg;
leftImg = imread("./imgLeft.png", IMREAD_COLOR);
rightImg = imread("./imgRight.png", IMREAD_COLOR);
//! [load]
//! [create]
cv::Size frameSize = leftImg.size();
Ptr<stereo::QuasiDenseStereo> stereo = stereo::QuasiDenseStereo::create(frameSize);
//! [create]
//! [process]
stereo->process(leftImg, rightImg);
//! [process]
//! [disp]
cv::Mat disp;
disp = stereo->getDisparity();
cv::namedWindow("disparity map");
cv::imshow("disparity map", disp);
//! [disp]
cv::namedWindow("right channel");
cv::namedWindow("left channel");
cv::imshow("left channel", leftImg);
cv::imshow("right channel", rightImg);
//! [export]
vector<stereo::MatchQuasiDense> matches;
stereo->getDenseMatches(matches);
std::ofstream dense("./dense.txt", std::ios::out);
for (uint i=0; i< matches.size(); i++)
{
dense << matches[i].p0 << matches[i].p1 << endl;
}
dense.close();
//! [export]
cv::waitKey(0);
return 0;
}
@@ -0,0 +1,22 @@
#include <opencv2/core.hpp>
#include <opencv2/xstereo.hpp>
using namespace cv;
using namespace std;
int main(int argc, char* argv[])
{
//! [create]
Ptr<stereo::QuasiDenseStereo> stereo = stereo::QuasiDenseStereo::create(cv::Size(5,5));
//! [create]
//! [write]
std::string parameterFileLocation = "./parameters.yaml";
if (argc > 1)
parameterFileLocation = argv[1];
stereo->saveParameters(parameterFileLocation);
//! [write]
return 0;
}
+196
View File
@@ -0,0 +1,196 @@
#include "opencv2/xstereo.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 bool 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
if (!parse_argument_values(argc, argv, left, right,
kernel_size,
number_of_disparities,
aggregation_window,
P1, P2,
scale,
algo, binary_descriptor_type,success))
{
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";
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 bool 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;
}
if (!parser.check() || !success)
{
parser.printMessage();
return false;
}
return true;
}
@@ -0,0 +1,21 @@
import numpy as np
import cv2 as cv
left_img = cv.imread(cv.samples.findFile("aloeL.jpg"), cv.IMREAD_COLOR)
right_img = cv.imread(cv.samples.findFile("aloeR.jpg"), cv.IMREAD_COLOR)
frame_size = left_img.shape[0:2];
stereo = cv.stereo.QuasiDenseStereo_create(frame_size[::-1])
stereo.process(left_img, right_img)
disp = stereo.getDisparity()
cv.imshow("disparity", disp)
cv.waitKey()
dense_matches = stereo.getDenseMatches()
try:
with open("dense.txt", "wt") as f:
# if you want all matches use for idx in len(dense_matches): It can be a big file
for idx in range(0, min(10, len(dense_matches))):
nb = f.write(str(dense_matches[idx].p0) + "\t" + str(dense_matches[idx].p1) + "\t" + str(dense_matches[idx].corr) + "\n")
except:
print("Cannot open file")
+243
View File
@@ -0,0 +1,243 @@
//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"
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(0, image1.rows),
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(0, image1.rows),
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(0, image1.rows),
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(0, image1.rows),
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(0, img1.rows), 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(0, img1.rows), 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(0, img1.rows),
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
CV_Assert(!integralImage1.empty());
CV_Assert(!integralImage1.isContinuous());
CV_CheckTypeEQ(integralImage1.type(), CV_32SC1, "");
CV_CheckGE(integralImage1.cols, img1.cols, "");
CV_CheckGE(integralImage1.rows, img1.rows, "");
CV_Assert(!integralImage2.empty());
CV_Assert(!integralImage2.isContinuous());
CV_CheckTypeEQ(integralImage2.type(), CV_32SC1, "");
CV_CheckGE(integralImage2.cols, img2.cols, "");
CV_CheckGE(integralImage2.rows, img2.rows, "");
int *integral[2] = {
(int *)integralImage1.data,
(int *)integralImage2.data
};
parallel_for_(Range(0, img1.rows),
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(0, img1.rows),
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
CV_Assert(!integralImage.empty());
CV_Assert(!integralImage.isContinuous());
CV_CheckTypeEQ(integralImage.type(), CV_32SC1, "");
CV_CheckGE(integralImage.cols, img1.cols, "");
CV_CheckGE(integralImage.rows, img1.rows, "");
int *integral[] = { (int *)integralImage.data};
parallel_for_(Range(0, img1.rows),
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(0, img1.rows), SymetricCensus<2>(imag, n2,date));
}
else if(type == CV_MODIFIED_CS_CENSUS)
{
parallel_for_(Range(0, img1.rows),
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(0, img1.rows), SymetricCensus<1>(imag, n2,date));
}
else if(type == CV_MODIFIED_CS_CENSUS)
{
parallel_for_( Range(0, img1.rows),
CombinedDescriptor<1,1,1,1,ModifiedCsCensus<1> >(img1.cols, img1.rows,stride,n2,date,ModifiedCsCensus<1>(images,n2),1));
}
}
}
}
+414
View File
@@ -0,0 +1,414 @@
//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 <stdint.h>
#ifndef _OPENCV_DESCRIPTOR_HPP_
#define _OPENCV_DESCRIPTOR_HPP_
#ifdef __cplusplus
namespace cv
{
namespace stereo
{
//!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
{
CV_UNUSED(w2);
for (int i = 0; i < stop; i++)
{
if (image[i][rrWidth + jj] > image[i][rWidth + j])
{
c[i] += 1;
}
c[i] <<= 1;
if (integralImage[i][rrWidth + jj] > image[i][rWidth + j])
{
c[i] += 1;
}
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
{
CV_UNUSED(w2);
for(int i = 0; i < imageStop; i++)
{
c[i] <<= 2;
if (image[i][rrWidth + jj] > image[i][rWidth + j] + t)
c[i] += 3;
else if (image[i][rrWidth + jj] > image[i][rWidth + j] - t)
c[i] += 1;
}
}
};
//!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
{
CV_UNUSED(j);
CV_UNUSED(rWidth);
for(int i = 0; i < imageStop; i++)
{
if (image[i][(rrWidth + jj)] > image[i][(w2 + (jj + n2))])
{
c[i] += 1;
}
c[i] <<= 1;
}
}
};
//!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
{
CV_UNUSED(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 CV_OVERRIDE {
for (int i = r.start; i < r.end ; i++)
{
int rWidth = i * stride_;
for (int j = 0; j < width; j++)
{
if (i < n2 || i >= height - n2 || j < n2 + 2 || j >= width - n2 - 2)
{
for(int l = 0; l < nr_img; l++)
dst[l][rWidth + j] = 0; // TODO out of range value?
continue;
}
int c[nr_img];
memset(c, 0, sizeof(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];
}
}
}
};
//!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 CV_OVERRIDE {
for (int i = r.start; i < r.end; i++)
{
int rWidth = i * stride_;
for (int j = 0; j < width; j++)
{
for(int d = 0 ; d < im_num; d++)
{
if (i < n2 || i >= height - n2 || j < n2 || j >= width - n2)
{
dst[d][rWidth + j] = 0; // TODO out of range value?
continue;
}
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 CV_OVERRIDE {
for (int i = r.start; i < r.end ; i++)
{
int distV = i*stride_;
for (int j = 0; j < width; j++)
{
for(int d = 0; d < im_num; d++)
{
if (i < n2 || i >= height - n2 || j < n2 || j >= width - n2)
{
dst[d][distV + j] = 0; // TODO out of range value?
continue;
}
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;
}
}
}
}
};
}
}
#endif
#endif
/*End of file*/
+645
View File
@@ -0,0 +1,645 @@
//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 *
* *
\******************************************************************************************************************/
#ifndef _OPENCV_MATCHING_HPP_
#define _OPENCV_MATCHING_HPP_
#include <stdint.h>
#include "opencv2/core.hpp"
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[65536]; // FIXIT use preferined 8-bit lookup table for hamming
//!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;
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), MASK(65535), hammLut(hammingLUT){}
void operator()(const cv::Range &r) const CV_OVERRIDE {
for (int i = r.start; i < r.end ; i++)
{
int iw = i * width;
for (int j = kernelSize; j < width - kernelSize; j++)
{
int iwj = iw + j;
for (int d = 0; d <= v; d++)
{
int j2 = std::max(0, j - d);
int xorul = left[(iwj)] ^ right[(iw + j2)];
#if CV_POPCNT
if (checkHardwareSupport(CV_CPU_POPCNT))
{
c[(iwj)* (v + 1) + d] = (short)_mm_popcnt_u32(xorul);
}
else
#endif
{
c[(iwj)* (v + 1) + d] = (short)(hammLut[xorul & MASK] + hammLut[(xorul >> 16) & MASK]);
}
}
}
}
}
};
//!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 CV_OVERRIDE {
for (int i = r.start; i < r.end; i++)
{
int iwi = i * width;
for (int j = 0; j <= width; j++)
{
int w = (iwi + j) * (maxDisp + 1);
if (i < win + 1 || i >= height - win - 1 || j < win + 1 || j >= width - win - 1)
{
for (int d = 0; d <= maxDisp; d++)
{
c[w + d] = 0;
}
continue;
}
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);
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 CV_OVERRIDE {
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 CV_OVERRIDE {
for (int m = r.start; m < r.end; m++)
{
for (int n = 0; n < width; ++n)
{
if (m < 1 || m >= height - 1 || n < 4 || n >= width - 4)
{
filtered[m * width + n] = original[m * width + n]; // FIXIT replace with OpenCV function
continue;
}
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 CV_OVERRIDE {
for (int n = r.start; n < r.end; ++n)
{
for (int m = 4; m < height - 4; ++m)
{
if (m < 4 || m >= height - 4 || n < 1 || n >= width - 1)
{
filtered[m * width + n] = original[m * width + n]; // FIXIT replace with OpenCV function
continue;
}
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_<int> speckleY;
Mat_<int> speckleX;
Mat_<int> puss;
//int *specklePointX;
//int *specklePointY;
//long long *pus;
//!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.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);
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(0, height), 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.data != out.data && "inplace is not supported");
CV_Assert(currentMap.cols == out.cols);
CV_Assert(currentMap.rows == out.rows);
CV_Assert(t >= 0);
CV_Assert(!puss.empty());
int *specklePointX = (int *)speckleX.data;
int *specklePointY = (int *)speckleY.data;
puss.setTo(Scalar::all(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 = 0; i < height; i++)
{
int iw = i * width;
for (int j = 0; j < width; j++)
{
if (i < 1 || i >= height - 1 || j < 1 || j >= width - 1)
{
outputMap[iw + j] = 0;
continue;
}
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;
puss(i, 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 &&
puss(ii + di[d], 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++;
puss(ii + di[d], 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), 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(0, originalImage.rows), 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(0, originalImage.cols), 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
/*End of file*/
+55
View File
@@ -0,0 +1,55 @@
/*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*/
#ifndef __OPENCV_STEREO_PRECOMP_H__
#define __OPENCV_STEREO_PRECOMP_H__
#include "opencv2/core.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/features.hpp"
#include "opencv2/stereo.hpp"
#include "opencv2/xstereo.hpp"
#include "descriptor.hpp"
#include "matching.hpp"
#endif
+649
View File
@@ -0,0 +1,649 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#include "precomp.hpp"
#include <opencv2/video/tracking.hpp>
#include <opencv2/xstereo/quasi_dense_stereo.hpp>
#include <queue>
namespace cv {
namespace stereo {
#define NO_MATCH cv::Point(0,0)
typedef std::priority_queue<MatchQuasiDense, std::vector<MatchQuasiDense>, std::less<MatchQuasiDense> > t_matchPriorityQueue;
class QuasiDenseStereoImpl : public QuasiDenseStereo
{
public:
QuasiDenseStereoImpl(cv::Size monoImgSize, cv::String paramFilepath)
{
loadParameters(paramFilepath);
width = monoImgSize.width;
height = monoImgSize.height;
refMap = cv::Mat_<cv::Point2i>(monoImgSize);
mtcMap = cv::Mat_<cv::Point2i>(monoImgSize);
cv::Size integralSize = cv::Size(monoImgSize.width+1, monoImgSize.height+1);
sum0 = cv::Mat_<int32_t>(integralSize);
sum1 = cv::Mat_<int32_t>(integralSize);
ssum0 = cv::Mat_<double>(integralSize);
ssum1 = cv::Mat_<double>(integralSize);
// the disparity image.
disparity = cv::Mat_<float>(monoImgSize);
// texture images.
textureDescLeft = cv::Mat_<int> (monoImgSize);
textureDescRight = cv::Mat_<int> (monoImgSize);
}
~QuasiDenseStereoImpl()
{
rightFeatures.clear();
leftFeatures.clear();
refMap.release();
mtcMap.release();
sum0.release();
sum1.release();
ssum0.release();
ssum1.release();
// the disparity image.
disparity.release();
// texture images.
textureDescLeft.release();
textureDescRight.release();
}
/**
* @brief Computes sparse stereo. The output is stores in refMap and mthMap.
*
* This method used the "goodFeaturesToTrack" function of OpenCV to extracts salient points
* in the left image. Feature locations are used as inputs in the "calcOpticalFlowPyrLK"
* function of OpenCV along with the left and right images. The optical flow algorithm estimates
* tracks the locations of the features in the right image. The two set of locations constitute
* the sparse set of matches. These are then used as seeds in the intensification stage of the
* algorithm.
* @param[in] imgLeft The left Channel of a stereo image.
* @param[in] imgRight The right Channel of a stereo image.
* @param[out] featuresLeft (vector of points) The location of the features in the left image.
* @param[out] featuresRight (vector of points) The location of the features in the right image.
* @note featuresLeft and featuresRight must have the same length and corresponding features
* must be indexed the same way in both vectors.
*/
void sparseMatching(const cv::Mat &imgLeft ,const cv::Mat &imgRight,
std::vector< cv::Point2f > &featuresLeft,
std::vector< cv::Point2f > &featuresRight)
{
std::vector< uchar > featureStatus;
std::vector< float > error;
featuresLeft.clear();
featuresRight.clear();
cv::goodFeaturesToTrack(imgLeft, featuresLeft, Param.gftMaxNumFeatures,
Param.gftQualityThres, Param.gftMinSeperationDist);
cv::Size templateSize(Param.lkTemplateSize,Param.lkTemplateSize);
cv::TermCriteria termination(cv::TermCriteria::MAX_ITER | cv::TermCriteria::EPS,
Param.lkTermParam1, Param.lkTermParam2);
cv::calcOpticalFlowPyrLK(imgLeft, imgRight, featuresLeft, featuresRight,
featureStatus, error,
templateSize, Param.lkPyrLvl, termination);
//discard bad features.
for(size_t i=0; i<featuresLeft.size();)
{
if( featureStatus[i]==0 )
{
std::swap(featuresLeft[i], featuresLeft.back());
featuresLeft.pop_back();
std::swap(featureStatus[i], featureStatus.back());
featureStatus.pop_back();
std::swap(featuresRight[i], featuresRight.back());
featuresRight.pop_back();
}
else
++i;
}
}
/**
* @brief Based on the seeds computed in sparse stereo, this method calculates the semi dense
* set of correspondences.
*
* The method initially discards low quality matches based on their zero-normalized cross
* correlation (zncc) value. This is done by calling the "extractSparseSeeds" method. Remaining
* high quality Matches stored in a t_matchPriorityQueue sorted according to their zncc value.
* The priority queue allows for new matches to be added while keeping track of the best Match.
* The algorithm then process the queue iteratively. In every iteration a Match is popped from
* the queue. The algorithm then tries to find candidate matches by matching every point in a
* small patch around the left Match feature, with a point within a same sized patch around the
* corresponding right feature. For each candidate point match, the zncc is computed and if it
* surpasses a threshold, the candidate pair is stored in a temporary priority queue. After this
* process completed the candidate matches are popped from the Local priority queue and if a
* match is not registered in refMap, it means that is the best match for this point. The
* algorithm registers this point in refMap and also push it to the Seed queue. If a candidate
* match is already registered, it means that is not the best and the algorithm discards it.
*
* @note This method does not have input arguments, but uses the "leftFeatures" and
* "rightFeatures" vectors.
* Also there is no output since the method used refMap and mtcMap to store the results.
* @param[in] featuresLeft The location of the features in the left image.
* @param[in] featuresRight The location of the features in the right image.
*/
void quasiDenseMatching(const std::vector< cv::Point2f > &featuresLeft,
const std::vector< cv::Point2f > &featuresRight)
{
dMatchesLen = 0;
refMap = cv::Mat_<cv::Point2i>(cv::Size(width, height), cv::Point2i(0, 0));
mtcMap = cv::Point2i(0, 0);
// build texture homogeneity reference maps.
buildTextureDescriptor(grayLeft, textureDescLeft);
buildTextureDescriptor(grayRight, textureDescRight);
// generate the intergal images for fast variable window correlation calculations
cv::integral(grayLeft, sum0, ssum0);
cv::integral(grayRight, sum1, ssum1);
// Seed priority queue. The algorithm wants to pop the best seed available in order to densify
//the sparse set.
t_matchPriorityQueue seeds = extractSparseSeeds(featuresLeft, featuresRight,
refMap, mtcMap);
// Do the propagation part
while(!seeds.empty())
{
t_matchPriorityQueue Local;
// Get the best seed at the moment
MatchQuasiDense m = seeds.top();
seeds.pop();
// Ignore the border
if(!CheckBorder(m, Param.borderX, Param.borderY, width, height))
continue;
// For all neighbours of the seed in image 1
//the neighborghoud is defined with Param.N*2 dimentrion
for(int y=-Param.neighborhoodSize;y<=Param.neighborhoodSize;y++)
{
for(int x=-Param.neighborhoodSize;x<=Param.neighborhoodSize;x++)
{
cv::Point2i p0 = cv::Point2i(m.p0.x+x,m.p0.y+y);
// Check if its unique in ref
if(refMap.at<cv::Point2i>(p0.y,p0.x) != NO_MATCH)
continue;
// Check the texture descriptor for a boundary
if(textureDescLeft.at<int>(p0.y, p0.x) > Param.textrureThreshold)
continue;
// For all candidate matches.
for(int wy=-Param.disparityGradient; wy<=Param.disparityGradient; wy++)
{
for(int wx=-Param.disparityGradient; wx<=Param.disparityGradient; wx++)
{
cv::Point p1 = cv::Point(m.p1.x+x+wx,m.p1.y+y+wy);
// Check if its unique in ref
if(mtcMap.at<cv::Point2i>(p1.y, p1.x) != NO_MATCH)
continue;
// Check the texture descriptor for a boundary
if(textureDescRight.at<int>(p1.y, p1.x) > Param.textrureThreshold)
continue;
// Calculate ZNCC and store local match.
float corr = iZNCC_c1(p0,p1,Param.corrWinSizeX,Param.corrWinSizeY);
// push back if this is valid match
if( corr > Param.correlationThreshold )
{
MatchQuasiDense nm;
nm.p0 = p0;
nm.p1 = p1;
nm.corr = corr;
Local.push(nm);
}
}
}
}
}
// Get seeds from the local
while( !Local.empty() )
{
MatchQuasiDense lm = Local.top();
Local.pop();
// Check if its unique in both ref and dst.
if(refMap.at<cv::Point2i>(lm.p0.y, lm.p0.x) != NO_MATCH)
continue;
if(mtcMap.at<cv::Point2i>(lm.p1.y, lm.p1.x) != NO_MATCH)
continue;
// Unique match
refMap.at<cv::Point2i>(lm.p0.y, lm.p0.x) = lm.p1;
mtcMap.at<cv::Point2i>(lm.p1.y, lm.p1.x) = lm.p0;
dMatchesLen++;
// Add to the seed list
seeds.push(lm);
}
}
}
/**
* @brief Compute the disparity map based on the Euclidean distance of corresponding points.
* @param[in] matchMap A matrix of points, the same size as the left channel. Each cell of this
* matrix stores the location of the corresponding point in the right image.
* @param[out] dispMat The disparity map.
* @sa getDisparity
*/
void computeDisparity(const cv::Mat_<cv::Point2i> &matchMap,
cv::Mat_<float> &dispMat)
{
for(int row=0; row< height; row++)
{
for(int col=0; col<width; col++)
{
cv::Point2d tmpPoint(col, row);
if (matchMap.at<cv::Point2i>(tmpPoint) == NO_MATCH)
{
dispMat.at<float>(tmpPoint) = NAN;
continue;
}
//if a match is found, compute the difference in location of the match and current
//pixel.
int dx = col-matchMap.at<cv::Point2i>(tmpPoint).x;
int dy = row-matchMap.at<cv::Point2i>(tmpPoint).y;
//calculate disparity of current pixel.
dispMat.at<float>(tmpPoint) = sqrt(float(dx*dx+dy*dy));
}
}
}
/**
* @brief Compute the Zero-mean Normalized Cross-correlation.
*
* Compare a patch in the left image, centered in point p0 with a patch in the right image,
* centered in point p1. Patches are defined by wy, wx and the patch size is (2*wx+1) by
* (2*wy+1).
* @param [in] p0 The central point of the patch in the left image.
* @param [in] p1 The central point of the patch in the right image.
* @param [in] wx The distance from the center of the patch to the border in the x direction.
* @param [in] wy The distance from the center of the patch to the border in the y direction.
* @return The value of the the zero-mean normalized cross correlation.
* @note Default value for wx, wy is 1. in this case the patch is 3x3.
*/
float iZNCC_c1(const cv::Point2i p0, const cv::Point2i p1, const int wx=1, const int wy=1)
{
float m0=0.0 ,m1=0.0 ,s0=0.0 ,s1=0.0;
float wa = (float)(2*wy+1)*(2*wx+1);
float zncc=0.0;
patchSumSum2(p0, sum0, ssum0, m0, s0, wx, wy);
patchSumSum2(p1, sum1, ssum1, m1, s1, wx, wy);
m0 /= wa;
m1 /= wa;
// standard deviations
s0 = sqrt(s0-wa*m0*m0);
s1 = sqrt(s1-wa*m1*m1);
for (int col=-wy; col<=wy; col++)
{
for (int row=-wx; row<=wx; row++)
{
zncc += (float)grayLeft.at<uchar>(p0.y+row, p0.x+col) *
(float)grayRight.at<uchar>(p1.y+row, p1.x+col);
}
}
zncc = (zncc-wa*m0*m1)/(s0*s1);
return zncc;
}
/**
* @brief Compute the sum of values and the sum of squared values of a patch with dimensions
* 2*xWindow+1 by 2*yWindow+1 and centered in point p, using the integral image and integral
* image of squared pixel values.
* @param[in] p The center of the patch we want to calculate the sum and sum of squared values.
* @param[in] s The integral image
* @param[in] ss The integral image of squared values.
* @param[out] sum The sum of pixels inside the patch.
* @param[out] ssum The sum of squared values inside the patch.
* @param [in] xWindow The distance from the central pixel of the patch to the border in x
* direction.
* @param [in] yWindow The distance from the central pixel of the patch to the border in y
* direction.
* @note Default value for xWindow, yWindow is 1. in this case the patch is 3x3.
* @note integral images are very useful to sum values of patches in constant time independent
* of their size. For more information refer to the cv::Integral function OpenCV page.
*/
void patchSumSum2(const cv::Point2i p, const cv::Mat &sum, const cv::Mat &ssum,
float &s, float &ss, const int xWindow=1, const int yWindow=1)
{
cv::Point2i otl(p.x-xWindow, p.y-yWindow);
//outer top right
cv::Point2i otr(p.x+xWindow+1, p.y-yWindow);
//outer bottom left
cv::Point2i obl(p.x-xWindow, p.y+yWindow+1);
//outer bottom right
cv::Point2i obr(p.x+xWindow+1, p.y+yWindow+1);
// sum and squared sum for right window
s = (float)(sum.at<int>(otl) - sum.at<int>(otr)
- sum.at<int>(obl) + sum.at<int>(obr));
ss = (float)(ssum.at<double>(otl) - ssum.at<double>(otr)
- ssum.at<double>(obl) + ssum.at<double>(obr));
}
/**
* @brief Create a priority queue containing sparse Matches
*
* This method computes the zncc for each Match extracted in "sparseMatching". If the zncc is
* over the correlation threshold then the Match is inserted in the output priority queue.
* @param[in] featuresLeft The feature locations in the left image.
* @param[in] featuresRight The features locations in the right image.
* @param[out] leftMap A matrix of points, of the same size as the left image. Each cell of this
* matrix stores the location of the corresponding point in the right image.
* @param[out] rightMap A matrix of points, the same size as the right image. Each cell of this
* matrix stores the location of the corresponding point in the left image.
* @return Priority queue containing sparse matches.
*/
t_matchPriorityQueue extractSparseSeeds(const std::vector< cv::Point2f > &featuresLeft,
const std::vector< cv::Point2f > &featuresRight,
cv::Mat_<cv::Point2i> &leftMap,
cv::Mat_<cv::Point2i> &rightMap)
{
t_matchPriorityQueue seeds;
for(uint i=0; i < featuresLeft.size(); i++)
{
// Calculate correlation and store match in Seeds.
MatchQuasiDense m;
m.p0 = cv::Point2i(featuresLeft[i]);
m.p1 = cv::Point2i(featuresRight[i]);
m.corr = 0;
// Check if too close to boundary.
if(!CheckBorder(m,Param.borderX,Param.borderY, width, height))
continue;
m.corr = iZNCC_c1(m.p0, m.p1, Param.corrWinSizeX, Param.corrWinSizeY);
// Can we add it to the list
if( m.corr > Param.correlationThreshold )
{
seeds.push(m);
leftMap.at<cv::Point2i>(m.p0.y, m.p0.x) = m.p1;
rightMap.at<cv::Point2i>(m.p1.y, m.p1.x) = m.p0;
}
}
return seeds;
}
/**
* @brief Check if a match is close to the boarder of an image.
* @param[in] m The match containing points in both image.
* @param[in] bx The offset of the image edge that defines the border in x direction.
* @param[in] by The offset of the image edge that defines the border in y direction.
* @param[in] w The width of the image.
* @param[in] h The height of the image.
* @retval true If the feature is in the border of the image.
* @retval false If the feature is not in the border of image.
*/
bool CheckBorder(MatchQuasiDense m, int bx, int by, int w, int h)
{
if(m.p0.x<bx || m.p0.x>w-bx || m.p0.y<by || m.p0.y>h-by ||
m.p1.x<bx || m.p1.x>w-bx || m.p1.y<by || m.p1.y>h-by)
{
return false;
}
return true;
}
/**
* @brief Build a texture descriptor
* @param[in] img The image we need to compute the descriptor for.
* @param[out] descriptor The texture descriptor of the image.
*/
void buildTextureDescriptor(cv::Mat &img,cv::Mat &descriptor)
{
float a, b, c, d;
uint8_t center, top, bottom, right, left;
//reset descriptors
// traverse every pixel.
for(int row=1; row<height-1; row++)
{
for(int col=1; col<width-1; col++)
{
// the values of the current pixel.
center = img.at<uchar>(row,col);
top = img.at<uchar>(row-1,col);
bottom = img.at<uchar>(row+1,col);
left = img.at<uchar>(row,col-1);
right = img.at<uchar>(row,col+1);
a = (float)abs(center - top);
b = (float)abs(center - bottom);
c = (float)abs(center - left);
d = (float)abs(center - right);
//choose the biggest of them.
int val = (int) std::max(a, std::max(b, std::max(c, d)));
descriptor.at<int>(row, col) = val;
}
}
}
//-------------------------------------------------------------------------
void getSparseMatches(std::vector<stereo::MatchQuasiDense> &sMatches) override
{
MatchQuasiDense tmpMatch;
sMatches.clear();
sMatches.reserve(leftFeatures.size());
for (uint i=0; i<leftFeatures.size(); i++)
{
tmpMatch.p0 = leftFeatures[i];
tmpMatch.p1 = rightFeatures[i];
sMatches.push_back(tmpMatch);
}
}
int loadParameters(cv::String filepath) override
{
cv::FileStorage fs;
//if user specified a pathfile, try to use it.
if (!filepath.empty())
{
fs.open(filepath, cv::FileStorage::READ);
}
// If the file opened, read the parameters.
if (fs.isOpened())
{
fs["borderX"] >> Param.borderX;
fs["borderY"] >> Param.borderY;
fs["corrWinSizeX"] >> Param.corrWinSizeX;
fs["corrWinSizeY"] >> Param.corrWinSizeY;
fs["correlationThreshold"] >> Param.correlationThreshold;
fs["textrureThreshold"] >> Param.textrureThreshold;
fs["neighborhoodSize"] >> Param.neighborhoodSize;
fs["disparityGradient"] >> Param.disparityGradient;
fs["lkTemplateSize"] >> Param.lkTemplateSize;
fs["lkPyrLvl"] >> Param.lkPyrLvl;
fs["lkTermParam1"] >> Param.lkTermParam1;
fs["lkTermParam2"] >> Param.lkTermParam2;
fs["gftQualityThres"] >> Param.gftQualityThres;
fs["gftMinSeperationDist"] >> Param.gftMinSeperationDist;
fs["gftMaxNumFeatures"] >> Param.gftMaxNumFeatures;
fs.release();
return 1;
}
// If the filepath was incorrect or non existent, load default parameters.
Param.borderX = 15;
Param.borderY = 15;
// corr window size
Param.corrWinSizeX = 5;
Param.corrWinSizeY = 5;
Param.correlationThreshold = (float)0.5;
Param.textrureThreshold = 200;
Param.neighborhoodSize = 5;
Param.disparityGradient = 1;
Param.lkTemplateSize = 3;
Param.lkPyrLvl = 3;
Param.lkTermParam1 = 3;
Param.lkTermParam2 = (float)0.003;
Param.gftQualityThres = (float)0.01;
Param.gftMinSeperationDist = 10;
Param.gftMaxNumFeatures = 500;
// Return 0 if there was no filepath provides.
// Return -1 if there was a problem opening the filepath provided.
if(filepath.empty())
{
return 0;
}
return -1;
}
int saveParameters(cv::String filepath) override
{
cv::FileStorage fs(filepath, cv::FileStorage::WRITE);
if (fs.isOpened())
{
fs << "borderX" << Param.borderX;
fs << "borderY" << Param.borderY;
fs << "corrWinSizeX" << Param.corrWinSizeX;
fs << "corrWinSizeY" << Param.corrWinSizeY;
fs << "correlationThreshold" << Param.correlationThreshold;
fs << "textrureThreshold" << Param.textrureThreshold;
fs << "neighborhoodSize" << Param.neighborhoodSize;
fs << "disparityGradient" << Param.disparityGradient;
fs << "lkTemplateSize" << Param.lkTemplateSize;
fs << "lkPyrLvl" << Param.lkPyrLvl;
fs << "lkTermParam1" << Param.lkTermParam1;
fs << "lkTermParam2" << Param.lkTermParam2;
fs << "gftQualityThres" << Param.gftQualityThres;
fs << "gftMinSeperationDist" << Param.gftMinSeperationDist;
fs << "gftMaxNumFeatures" << Param.gftMaxNumFeatures;
fs.release();
}
return -1;
}
void getDenseMatches(std::vector<stereo::MatchQuasiDense> &denseMatches) override
{
MatchQuasiDense tmpMatch;
denseMatches.clear();
denseMatches.reserve(dMatchesLen);
for (int row=0; row<height; row++)
{
for(int col=0; col<width; col++)
{
tmpMatch.p0 = cv::Point(col, row);
tmpMatch.p1 = refMap.at<Point2i>(row, col);
if (tmpMatch.p1 == NO_MATCH)
{
continue;
}
denseMatches.push_back(tmpMatch);
}
}
}
void process(const cv::Mat &imgLeft , const cv::Mat &imgRight) override
{
if (imgLeft.channels()>1)
{
cv::cvtColor(imgLeft, grayLeft, cv::COLOR_BGR2GRAY);
cv::cvtColor(imgRight, grayRight, cv::COLOR_BGR2GRAY);
}
else
{
grayLeft = imgLeft.clone();
grayRight = imgRight.clone();
}
sparseMatching(grayLeft, grayRight, leftFeatures, rightFeatures);
quasiDenseMatching(leftFeatures, rightFeatures);
}
cv::Point2f getMatch(const int x, const int y) override
{
return refMap.at<cv::Point2i>(y, x);
}
cv::Mat getDisparity() override
{
computeDisparity(refMap, disparity);
return disparity;
}
// Variables used at sparse feature extraction.
// Container for left images' features, extracted with GFT algorithm.
std::vector< cv::Point2f > leftFeatures;
// Container for right images' features, matching is done with LK flow algorithm.
std::vector< cv::Point2f > rightFeatures;
// Width and height of a single image.
int width;
int height;
int dMatchesLen;
// Containers to store input images.
cv::Mat grayLeft;
cv::Mat grayRight;
// Containers to store the locations of each points pair.
cv::Mat_<cv::Point2i> refMap;
cv::Mat_<cv::Point2i> mtcMap;
cv::Mat_<int32_t> sum0;
cv::Mat_<int32_t> sum1;
cv::Mat_<double> ssum0;
cv::Mat_<double> ssum1;
// Container to store the disparity un-normalized
cv::Mat_<float> disparity;
// Containers to store textures descriptors.
cv::Mat_<int> textureDescLeft;
cv::Mat_<int> textureDescRight;
};
cv::Ptr<QuasiDenseStereo> QuasiDenseStereo::create(cv::Size monoImgSize, cv::String paramFilepath)
{
return cv::makePtr<QuasiDenseStereoImpl>(monoImgSize, paramFilepath);
}
QuasiDenseStereo::~QuasiDenseStereo(){
}
}
}
+507
View File
@@ -0,0 +1,507 @@
//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, Intel Corporation, all rights reserved.
// Copyright (C) 2013, OpenCV Foundation, 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*/
/****************************************************************************************\
* Very fast SAD-based (Sum-of-Absolute-Diffrences) stereo correspondence algorithm. *
* Contributed by Kurt Konolige *
\****************************************************************************************/
#include "precomp.hpp"
#include <stdio.h>
#include <limits>
namespace cv
{
namespace stereo
{
struct StereoBinaryBMParams
{
StereoBinaryBMParams(int _numDisparities = 64, int _kernelSize = 9)
{
preFilterType = StereoBinaryBM::PREFILTER_XSOBEL;
preFilterSize = 9;
preFilterCap = 31;
kernelSize = _kernelSize;
minDisparity = 0;
numDisparities = _numDisparities > 0 ? _numDisparities : 64;
textureThreshold = 10;
uniquenessRatio = 15;
speckleRange = speckleWindowSize = 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 kernelSize;
int minDisparity;
int numDisparities;
int textureThreshold;
int uniquenessRatio;
int speckleRange;
int speckleWindowSize;
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)
{
int x, y, wsz2 = winsize / 2;
int* vsum = (int*)alignPtr(buf + (wsz2 + 1)*sizeof(vsum[0]), 32);
int scale_g = winsize*winsize / 8, scale_s = (1024 + scale_g) / (scale_g * 2);
const int OFS = 256 * 5, TABSZ = OFS * 2 + 256;
uchar tab[TABSZ];
const uchar* sptr = src.ptr();
int srcstep = (int)src.step;
Size size = src.size();
scale_g *= scale_s;
for (x = 0; x < TABSZ; x++)
tab[x] = (uchar)(x - OFS < -ftzero ? 0 : x - OFS > ftzero ? ftzero * 2 : x - OFS + ftzero);
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);
const uchar* bottom = sptr + srcstep*MIN(y + wsz2, size.height - 1);
const uchar* prev = sptr + srcstep*MAX(y - 1, 0);
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]);
for (x = 0; x <= wsz2; x++)
{
vsum[-x - 1] = vsum[0];
vsum[size.width + x] = vsum[size.width - 1];
}
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];
val = ((curr[x] * 4 + curr[x - 1] + curr[x + 1] + prev[x] + next[x])*scale_g - sum*scale_s) >> 10;
dptr[x] = tab[val + OFS];
}
sum += vsum[x + wsz2] - vsum[x - wsz2 - 1];
val = ((curr[x] * 5 + curr[x - 1] + prev[x] + next[x])*scale_g - sum*scale_s) >> 10;
dptr[x] = tab[val + OFS];
}
}
static void
prefilterXSobel(const Mat& src, Mat& dst, int ftzero)
{
int x, y;
const int OFS = 256 * 4, TABSZ = OFS * 2 + 256;
uchar tab[TABSZ];
Size size = src.size();
for (x = 0; x < TABSZ; x++)
tab[x] = (uchar)(x - OFS < -ftzero ? 0 : x - OFS > ftzero ? ftzero * 2 : x - OFS + ftzero);
uchar val0 = tab[0 + OFS];
#if CV_SSE2
volatile bool useSIMD = checkHardwareSupport(CV_CPU_SSE2);
#endif
for (y = 0; y < size.height - 1; y += 2)
{
const uchar* srow1 = src.ptr<uchar>(y);
const uchar* srow0 = y > 0 ? srow1 - src.step : size.height > 1 ? srow1 + src.step : srow1;
const uchar* srow2 = y < size.height - 1 ? srow1 + src.step : size.height > 1 ? srow1 - src.step : srow1;
const uchar* srow3 = y < size.height - 2 ? srow1 + src.step * 2 : srow1;
uchar* dptr0 = dst.ptr<uchar>(y);
uchar* dptr1 = dptr0 + dst.step;
dptr0[0] = dptr0[size.width - 1] = dptr1[0] = dptr1[size.width - 1] = val0;
x = 1;
#if CV_SSE2
if (useSIMD)
{
__m128i z = _mm_setzero_si128(), ftz = _mm_set1_epi16((short)ftzero),
ftz2 = _mm_set1_epi8(cv::saturate_cast<uchar>(ftzero * 2));
for (; x <= size.width - 9; x += 8)
{
__m128i c0 = _mm_unpacklo_epi8(_mm_loadl_epi64((__m128i*)(srow0 + x - 1)), z);
__m128i c1 = _mm_unpacklo_epi8(_mm_loadl_epi64((__m128i*)(srow1 + x - 1)), z);
__m128i d0 = _mm_unpacklo_epi8(_mm_loadl_epi64((__m128i*)(srow0 + x + 1)), z);
__m128i d1 = _mm_unpacklo_epi8(_mm_loadl_epi64((__m128i*)(srow1 + x + 1)), z);
d0 = _mm_sub_epi16(d0, c0);
d1 = _mm_sub_epi16(d1, c1);
__m128i c2 = _mm_unpacklo_epi8(_mm_loadl_epi64((__m128i*)(srow2 + x - 1)), z);
__m128i c3 = _mm_unpacklo_epi8(_mm_loadl_epi64((__m128i*)(srow3 + x - 1)), z);
__m128i d2 = _mm_unpacklo_epi8(_mm_loadl_epi64((__m128i*)(srow2 + x + 1)), z);
__m128i d3 = _mm_unpacklo_epi8(_mm_loadl_epi64((__m128i*)(srow3 + x + 1)), z);
d2 = _mm_sub_epi16(d2, c2);
d3 = _mm_sub_epi16(d3, c3);
__m128i v0 = _mm_add_epi16(d0, _mm_add_epi16(d2, _mm_add_epi16(d1, d1)));
__m128i v1 = _mm_add_epi16(d1, _mm_add_epi16(d3, _mm_add_epi16(d2, d2)));
v0 = _mm_packus_epi16(_mm_add_epi16(v0, ftz), _mm_add_epi16(v1, ftz));
v0 = _mm_min_epu8(v0, ftz2);
_mm_storel_epi64((__m128i*)(dptr0 + x), v0);
_mm_storel_epi64((__m128i*)(dptr1 + x), _mm_unpackhi_epi64(v0, v0));
}
}
#endif
for (; x < size.width - 1; x++)
{
int d0 = srow0[x + 1] - srow0[x - 1], d1 = srow1[x + 1] - srow1[x - 1],
d2 = srow2[x + 1] - srow2[x - 1], d3 = srow3[x + 1] - srow3[x - 1];
int v0 = tab[d0 + d1 * 2 + d2 + OFS];
int v1 = tab[d1 + d2 * 2 + d3 + OFS];
dptr0[x] = (uchar)v0;
dptr1[x] = (uchar)v1;
}
}
for (; y < size.height; y++)
{
uchar* dptr = dst.ptr<uchar>(y);
for (x = 0; x < size.width; x++)
dptr[x] = val0;
}
}
static const int DISPARITY_SHIFT = 4;
struct PrefilterInvoker : public ParallelLoopBody
{
PrefilterInvoker(const Mat& left0, const Mat& right0, Mat& left, Mat& right,
uchar* buf0, uchar* buf1, StereoBinaryBMParams* _state)
{
imgs0[0] = &left0; imgs0[1] = &right0;
imgs[0] = &left; imgs[1] = &right;
buf[0] = buf0; buf[1] = buf1;
state = _state;
}
void operator()(const Range& range) const CV_OVERRIDE
{
for (int i = range.start; i < range.end; i++)
{
if (state->preFilterType == StereoBinaryBM::PREFILTER_NORMALIZED_RESPONSE)
prefilterNorm(*imgs0[i], *imgs[i], state->preFilterSize, state->preFilterCap, buf[i]);
else
prefilterXSobel(*imgs0[i], *imgs[i], state->preFilterCap);
}
}
const Mat* imgs0[2];
Mat* imgs[2];
uchar* buf[2];
StereoBinaryBMParams* state;
};
class StereoBinaryBMImpl CV_FINAL : public StereoBinaryBM, public Matching
{
public:
StereoBinaryBMImpl(): Matching(64)
{
params = StereoBinaryBMParams();
}
StereoBinaryBMImpl(int _numDisparities, int _kernelSize) : Matching(_numDisparities)
{
params = StereoBinaryBMParams(_numDisparities, _kernelSize);
}
void compute(InputArray leftarr, InputArray rightarr, OutputArray disparr) CV_OVERRIDE
{
int dtype = disparr.fixedType() ? disparr.type() : params.dispType;
Size leftsize = leftarr.size();
if (leftarr.size() != rightarr.size())
CV_Error(Error::StsUnmatchedSizes, "All the images must have the same size");
if (leftarr.type() != CV_8UC1 || rightarr.type() != CV_8UC1)
CV_Error(Error::StsUnsupportedFormat, "Both input images must have CV_8UC1");
if (dtype != CV_16SC1 && dtype != CV_32FC1)
CV_Error(Error::StsUnsupportedFormat, "Disparity image must have CV_16SC1 or CV_32FC1 format");
if (params.preFilterType != PREFILTER_NORMALIZED_RESPONSE &&
params.preFilterType != PREFILTER_XSOBEL)
CV_Error(Error::StsOutOfRange, "preFilterType must be = CV_STEREO_BM_NORMALIZED_RESPONSE");
if (params.preFilterSize < 5 || params.preFilterSize > 255 || params.preFilterSize % 2 == 0)
CV_Error(Error::StsOutOfRange, "preFilterSize must be odd and be within 5..255");
if (params.preFilterCap < 1 || params.preFilterCap > 63)
CV_Error(Error::StsOutOfRange, "preFilterCap must be within 1..63");
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");
if (params.textureThreshold < 0)
CV_Error(Error::StsOutOfRange, "texture threshold must be non-negative");
if (params.uniquenessRatio < 0)
CV_Error(Error::StsOutOfRange, "uniqueness ratio must be non-negative");
int FILTERED = (params.minDisparity - 1) << DISPARITY_SHIFT;
Mat left0 = leftarr.getMat(), right0 = rightarr.getMat();
Mat disp0 = disparr.getMat();
int width = left0.cols;
int height = left0.rows;
if (puss.total() != (size_t)width * height)
{
speckleX.create(height, width);
speckleY.create(height, width);
puss.create(height, width);
censusImage[0].create(left0.rows,left0.cols,CV_32SC4);
censusImage[1].create(left0.rows,left0.cols,CV_32SC4);
partialSumsLR.create(left0.rows + 1,(left0.cols + 1) * (params.numDisparities + 1),CV_16S);
agregatedHammingLRCost.create(left0.rows + 1,(left0.cols + 1) * (params.numDisparities + 1),CV_16S);
hammingDistance.create(left0.rows, left0.cols * (params.numDisparities + 1),CV_16S);
preFilteredImg0.create(left0.size(), CV_8U);
preFilteredImg1.create(left0.size(), CV_8U);
aux.create(height,width,CV_8UC1);
}
Mat left = preFilteredImg0, right = preFilteredImg1;
int bufSize1 = (int)((width + params.preFilterSize + 2) * sizeof(int) + 256);
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_DENSE_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)
{
Mat blurLeft; blur(left, blurLeft, Size(params.kernelSize, params.kernelSize));
Mat blurRight; blur(right, blurRight, Size(params.kernelSize, params.kernelSize));
modifiedCensusTransform(left, right, params.kernelSize, censusImage[0], censusImage[1], CV_MEAN_VARIATION, 0,
blurLeft, blurRight);
}
else if(params.kernelType == CV_STAR_KERNEL)
{
starCensusTransform(left,right,params.kernelSize,censusImage[0],censusImage[1]);
}
hammingDistanceBlockMatching(censusImage[0], censusImage[1], hammingDistance, params.kernelSize);
costGathering(hammingDistance, partialSumsLR);
blockAgregation(partialSumsLR, params.agregationWindowSize, agregatedHammingLRCost);
dispartyMapFormation(agregatedHammingLRCost, disp0, 3);
Median1x9Filter<uint8_t>(disp0, aux);
Median9x1Filter<uint8_t>(aux,disp0);
if(params.regionRemoval == CV_SPECKLE_REMOVAL_AVG_ALGORITHM)
{
smallRegionRemoval<uint8_t>(disp0.clone(),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 CV_OVERRIDE { return params.agregationWindowSize;}
void setAgregationWindowSize(int value = 9) CV_OVERRIDE { CV_Assert(value % 2 != 0); params.agregationWindowSize = value;}
int getBinaryKernelType() const CV_OVERRIDE { return params.kernelType;}
void setBinaryKernelType(int value = CV_MODIFIED_CENSUS_TRANSFORM) CV_OVERRIDE { CV_Assert(value < 7); params.kernelType = value; }
int getSpekleRemovalTechnique() const CV_OVERRIDE { return params.regionRemoval;}
void setSpekleRemovalTechnique(int factor = CV_SPECKLE_REMOVAL_AVG_ALGORITHM) CV_OVERRIDE { CV_Assert(factor < 2); params.regionRemoval = factor; }
bool getUsePrefilter() const CV_OVERRIDE { return params.usePrefilter;}
void setUsePrefilter(bool value = false) CV_OVERRIDE { params.usePrefilter = value;}
int getScalleFactor() const CV_OVERRIDE { return params.scalling;}
void setScalleFactor(int factor = 4) CV_OVERRIDE { CV_Assert(factor > 0); params.scalling = factor; setScallingFactor(factor); }
int getMinDisparity() const CV_OVERRIDE { return params.minDisparity; }
void setMinDisparity(int minDisparity) CV_OVERRIDE { CV_Assert(minDisparity >= 0); params.minDisparity = minDisparity; }
int getNumDisparities() const CV_OVERRIDE { return params.numDisparities; }
void setNumDisparities(int numDisparities) CV_OVERRIDE { CV_Assert(numDisparities > 0); params.numDisparities = numDisparities; }
int getBlockSize() const CV_OVERRIDE { return params.kernelSize; }
void setBlockSize(int blockSize) CV_OVERRIDE { CV_Assert(blockSize % 2 != 0); params.kernelSize = blockSize; }
int getSpeckleWindowSize() const CV_OVERRIDE { return params.speckleWindowSize; }
void setSpeckleWindowSize(int speckleWindowSize) CV_OVERRIDE { CV_Assert(speckleWindowSize >= 0); params.speckleWindowSize = speckleWindowSize; }
int getSpeckleRange() const CV_OVERRIDE { return params.speckleRange; }
void setSpeckleRange(int speckleRange) CV_OVERRIDE { CV_Assert(speckleRange >= 0); params.speckleRange = speckleRange; }
int getDisp12MaxDiff() const CV_OVERRIDE { return params.disp12MaxDiff; }
void setDisp12MaxDiff(int disp12MaxDiff) CV_OVERRIDE { CV_Assert(disp12MaxDiff >= 0); params.disp12MaxDiff = disp12MaxDiff; }
int getPreFilterType() const CV_OVERRIDE { return params.preFilterType; }
void setPreFilterType(int preFilterType) CV_OVERRIDE { CV_Assert(preFilterType >= 0); params.preFilterType = preFilterType; }
int getPreFilterSize() const CV_OVERRIDE { return params.preFilterSize; }
void setPreFilterSize(int preFilterSize) CV_OVERRIDE { CV_Assert(preFilterSize >= 0); params.preFilterSize = preFilterSize; }
int getPreFilterCap() const CV_OVERRIDE { return params.preFilterCap; }
void setPreFilterCap(int preFilterCap) CV_OVERRIDE { CV_Assert(preFilterCap >= 0); params.preFilterCap = preFilterCap; }
int getTextureThreshold() const CV_OVERRIDE { return params.textureThreshold; }
void setTextureThreshold(int textureThreshold) CV_OVERRIDE { CV_Assert(textureThreshold >= 0); params.textureThreshold = textureThreshold; }
int getUniquenessRatio() const CV_OVERRIDE { return params.uniquenessRatio; }
void setUniquenessRatio(int uniquenessRatio) CV_OVERRIDE { CV_Assert(uniquenessRatio >= 0); params.uniquenessRatio = uniquenessRatio; }
int getSmallerBlockSize() const CV_OVERRIDE { return 0; }
void setSmallerBlockSize(int) CV_OVERRIDE {}
void write(FileStorage& fs) const CV_OVERRIDE
{
fs << "name" << name_
<< "minDisparity" << params.minDisparity
<< "numDisparities" << params.numDisparities
<< "blockSize" << params.kernelSize
<< "speckleWindowSize" << params.speckleWindowSize
<< "speckleRange" << params.speckleRange
<< "disp12MaxDiff" << params.disp12MaxDiff
<< "preFilterType" << params.preFilterType
<< "preFilterSize" << params.preFilterSize
<< "preFilterCap" << params.preFilterCap
<< "textureThreshold" << params.textureThreshold
<< "uniquenessRatio" << params.uniquenessRatio;
}
void read(const FileNode& fn) CV_OVERRIDE
{
FileNode n = fn["name"];
CV_Assert(n.isString() && String(n) == name_);
params.minDisparity = (int)fn["minDisparity"];
params.numDisparities = (int)fn["numDisparities"];
params.kernelSize = (int)fn["blockSize"];
params.speckleWindowSize = (int)fn["speckleWindowSize"];
params.speckleRange = (int)fn["speckleRange"];
params.disp12MaxDiff = (int)fn["disp12MaxDiff"];
params.preFilterType = (int)fn["preFilterType"];
params.preFilterSize = (int)fn["preFilterSize"];
params.preFilterCap = (int)fn["preFilterCap"];
params.textureThreshold = (int)fn["textureThreshold"];
params.uniquenessRatio = (int)fn["uniquenessRatio"];
}
StereoBinaryBMParams params;
Mat preFilteredImg0, preFilteredImg1, cost, dispbuf;
Mat slidingSumBuf;
Mat censusImage[2];
Mat hammingDistance;
Mat partialSumsLR;
Mat agregatedHammingLRCost;
Mat aux;
static const char* name_;
};
const char* StereoBinaryBMImpl::name_ = "StereoBinaryMatcher.BM";
Ptr<StereoBinaryBM> StereoBinaryBM::create(int _numDisparities, int _kernelSize)
{
return makePtr<StereoBinaryBMImpl>(_numDisparities, _kernelSize);
}
}
}
/* End of file. */
+894
View File
@@ -0,0 +1,894 @@
/*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.
// Copyright (C) 2013, OpenCV Foundation, 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*/
/*
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>
namespace cv
{
namespace stereo
{
typedef uchar PixType;
typedef short CostType;
typedef short DispType;
enum { NR = 16, NR2 = NR/2 };
struct StereoBinarySGBMParams
{
StereoBinarySGBMParams()
{
minDisparity = numDisparities = 0;
kernelSize = 0;
P1 = P2 = 0;
disp12MaxDiff = 0;
preFilterCap = 0;
uniquenessRatio = 0;
speckleWindowSize = 0;
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,
int _mode )
{
minDisparity = _minDisparity;
numDisparities = _numDisparities;
kernelSize = _SADWindowSize;
P1 = _P1;
P2 = _P2;
disp12MaxDiff = _disp12MaxDiff;
preFilterCap = _preFilterCap;
uniquenessRatio = _uniquenessRatio;
speckleWindowSize = _speckleWindowSize;
speckleRange = _speckleRange;
mode = _mode;
regionRemoval = 1;
kernelType = CV_MODIFIED_CENSUS_TRANSFORM;
subpixelInterpolationMethod = CV_QUADRATIC_INTERPOLATION;
}
int minDisparity;
int numDisparities;
int kernelSize;
int preFilterCap;
int uniquenessRatio;
int P1;
int P2;
int speckleWindowSize;
int speckleRange;
int disp12MaxDiff;
int mode;
int regionRemoval;
int kernelType;
int subpixelInterpolationMethod;
};
/*
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,
Mat& disp1, const StereoBinarySGBMParams& params,
Mat& buffer,const Mat& hamDist)
{
#if CV_SSE2
static const uchar LSBTab[] =
{
0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
};
volatile bool useSIMD = checkHardwareSupport(CV_CPU_SSE2);
#endif
#if CV_NEON
volatile bool useSIMD = checkHardwareSupport(CV_CPU_NEON);
#endif
const int ALIGN = 16;
const int DISP_SHIFT = StereoMatcher::DISP_SHIFT;
const int DISP_SCALE = (1 << DISP_SHIFT);
const CostType MAX_COST = SHRT_MAX;
const int minD = params.minDisparity;
const int maxD = minD + params.numDisparities;
Size kernelSize;
kernelSize.width = kernelSize.height = params.kernelSize > 0 ? params.kernelSize : 5;
const int uniquenessRatio = params.uniquenessRatio >= 0 ? params.uniquenessRatio : 10;
const int disp12MaxDiff = params.disp12MaxDiff > 0 ? params.disp12MaxDiff : 1;
const int P1 = params.P1 > 0 ? params.P1 : 2;
const int P2 = std::max(params.P2 > 0 ? params.P2 : 5, P1+1);
const int width = disp1.cols, height = disp1.rows;
const int minX1 = std::max(-maxD, 0);
const int maxX1 = width + std::min(minD, 0);
const int D = maxD - minD;
const int width1 = maxX1 - minX1;
const int INVALID_DISP = minD - 1, INVALID_DISP_SCALED = INVALID_DISP*DISP_SCALE;
const int SW2 = kernelSize.width/2, SH2 = kernelSize.height/2;
const bool fullDP = params.mode == StereoBinarySGBM::MODE_HH;
const int npasses = fullDP ? 2 : 1;
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.
const int D2 = D+16;
const int 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;
short *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)
const size_t costBufSize = width1*D;
const size_t CSBufSize = costBufSize*(fullDP ? height : 1);
const size_t minLrSize = (width1 + LrBorder*2)*NR2;
const size_t LrSize = minLrSize*D2;
const int hsumBufNRows = SH2*2 + 2;
const size_t totalBufSize = (LrSize + minLrSize)*NLR*sizeof(CostType) + // minLr[] and Lr[]
costBufSize*(hsumBufNRows + 1)*sizeof(CostType) + // hsumBuf, pixdiff
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);
// add P2 to every C(x,y). it saves a few operations in the inner loops
for(int 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;
x1 = 0; x2 = width1; dx = 1;
}
else
{
y1 = height-1; y2 = -1; dy = -1;
x1 = width1-1; x2 = -1; dx = -1;
}
CostType *Lr[NLR]={0}, *minLr[NLR]={0};
for(int k = 0; k < NLR; k++ )
{
// shift Lr[k] and minLr[k] pointers, because we allocated them with the borders,
// and will occasionally use negative indices with the arrays
// we need to shift Lr[k] pointers by 1, to give the space for d=-1.
// however, then the alignment will be imperfect, i.e. bad for SSE,
// thus we shift the pointers by 8 (8*sizeof(short) == 16 - ideal alignment)
Lr[k] = pixDiff + costBufSize + LrSize*k + NRD2*LrBorder + 8;
memset( Lr[k] - LrBorder*NRD2 - 8, 0, LrSize*sizeof(CostType) );
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(int k = dy1; k <= dy2; k++ )
{
CostType* hsumAdd = hsumBuf + (std::min(k, height-1) % hsumBufNRows)*costBufSize;
if( k < height )
{
for(int ii = 0; ii < width; ii++)
{
// fill pixDiff with the hamming costs previously processed in earlier method
for(int dd = 0; dd <= params.numDisparities; dd++)
{
pixDiff[ii * (params.numDisparities)+ dd] = (CostType)(ham[(k * width + ii) * (params.numDisparities +1) + dd]);
}
}
memset(hsumAdd, 0, D*sizeof(CostType));
for( x = 0; x <= SW2*D; x += D )
{
const int scale = x == 0 ? SW2 + 1 : 1;
for( d = 0; d < D; d++ )
hsumAdd[d] = (CostType)(hsumAdd[d] + pixDiff[x + d]*scale);
}
if( y > 0 )
{
const CostType* hsumSub = hsumBuf + (std::max(y - SH2 - 1, 0) % hsumBufNRows)*costBufSize;
const CostType* Cprev = !fullDP || y == 0 ? C : C - costBufSize;
for( x = D; x < width1*D; x += D )
{
const CostType* pixAdd = pixDiff + std::min(x + SW2*D, (width1-1)*D);
const CostType* pixSub = pixDiff + std::max(x - (SW2+1)*D, 0);
#if CV_SSE2
if( useSIMD )
{
for( d = 0; d < D; d += 8 )
{
__m128i hv = _mm_load_si128((const __m128i*)(hsumAdd + x - D + d));
__m128i Cx = _mm_load_si128((__m128i*)(Cprev + x + d));
hv = _mm_adds_epi16(_mm_subs_epi16(hv,
_mm_load_si128((const __m128i*)(pixSub + d))),
_mm_load_si128((const __m128i*)(pixAdd + d)));
Cx = _mm_adds_epi16(_mm_subs_epi16(Cx,
_mm_load_si128((const __m128i*)(hsumSub + x + d))),
hv);
_mm_store_si128((__m128i*)(hsumAdd + x + d), hv);
_mm_store_si128((__m128i*)(C + x + d), Cx);
}
}
else
#endif
{
for( d = 0; d < D; d++ )
{
const int hv = hsumAdd[x + d] = (CostType)(hsumAdd[x - D + d] + pixAdd[d] - pixSub[d]);
C[x + d] = (CostType)(Cprev[x + d] + hv - hsumSub[x + d]);
}
}
}
}
else
{
for( x = D; x < width1*D; x += D )
{
const CostType* pixAdd = pixDiff + std::min(x + SW2*D, (width1-1)*D);
const CostType* pixSub = pixDiff + std::max(x - (SW2+1)*D, 0);
for( d = 0; d < D; d++ )
hsumAdd[x + d] = (CostType)(hsumAdd[x - D + d] + pixAdd[d] - pixSub[d]);
}
}
}
if( y == 0 )
{
const int scale = k == 0 ? SH2 + 1 : 1;
for( x = 0; x < width1*D; x++ )
C[x] = (CostType)(C[x] + hsumAdd[x]*scale);
}
}
// also, clear the S buffer
for(int 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) +
min(L_r(p-r, d),
L_r(p-r, d-1) + P1,
L_r(p-r, d+1) + P1,
min_k L_r(p-r, k) + P2) - min_k L_r(p-r, k)
where p = (x,y), r is one of the directions.
we process all the directions at once:
0: r=(-dx, 0)
1: r=(-1, -dy)
2: r=(0, -dy)
3: r=(1, -dy)
4: r=(-2, -dy)
5: r=(-1, -dy*2)
6: r=(1, -dy*2)
7: r=(2, -dy)
*/
for( x = x1; x != x2; x += dx )
{
const int xm = x*NR2;
const int xd = xm*D2;
const int delta0 = minLr[0][xm - dx*NR2] + P2;
const int delta1 = minLr[1][xm - NR2 + 1] + P2;
const int delta2 = minLr[1][xm + 2] + P2;
const int 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);
}
else
#elif CV_NEON
if ( useSIMD )
{
int16x8_t vP1 = vdupq_n_s16((short)P1);
int16x8_t vDelta0 = vdupq_n_s16((short)delta0);
int16x8_t vDelta1 = vdupq_n_s16((short)delta1);
int16x8_t vDelta2 = vdupq_n_s16((short)delta2);
int16x8_t vDelta3 = vdupq_n_s16((short)delta3);
int16x8_t vMinL0 = vdupq_n_s16((short)MAX_COST);
int16x8_t vCpd, vL0, vL1, vL2, vL3, vL0m1, vL0p1;
int16x8_t vL1m1, vL1p1, vL2m1, vL2p1, vL3m1, vL3p1;
for ( d = 0; d < D; d += 8 )
{
vCpd = vld1q_s16(Cp + d);
vL0 = vld1q_s16(Lr_p0 + d);
vL1 = vld1q_s16(Lr_p1 + d);
vL2 = vld1q_s16(Lr_p2 + d);
vL3 = vld1q_s16(Lr_p3 + d);
vL0m1 = vld1q_s16(Lr_p0 + d - 1);
vL0p1 = vld1q_s16(Lr_p0 + d + 1);
vL0 = vminq_s16(vL0, vqaddq_s16(vL0m1, vP1));
vL0 = vminq_s16(vL0, vqaddq_s16(vL0p1, vP1));
vL1m1 = vld1q_s16(Lr_p1 + d - 1);
vL1p1 = vld1q_s16(Lr_p1 + d + 1);
vL1 = vminq_s16(vL1, vqaddq_s16(vL1m1, vP1));
vL1 = vminq_s16(vL1, vqaddq_s16(vL1p1, vP1));
vL2m1 = vld1q_s16(Lr_p2 + d - 1);
vL2p1 = vld1q_s16(Lr_p2 + d + 1);
vL2 = vminq_s16(vL2, vqaddq_s16(vL2m1, vP1));
vL2 = vminq_s16(vL2, vqaddq_s16(vL2p1, vP1));
vL3m1 = vld1q_s16(Lr_p3 + d - 1);
vL3p1 = vld1q_s16(Lr_p3 + d + 1);
vL3 = vminq_s16(vL3, vqaddq_s16(vL3m1, vP1));
vL3 = vminq_s16(vL3, vqaddq_s16(vL3p1, vP1));
vL0 = vminq_s16(vL0, vDelta0);
vL0 = vqaddq_s16(vCpd, vqsubq_s16(vL0, vDelta0));
vL1 = vminq_s16(vL1, vDelta1);
vL1 = vqaddq_s16(vCpd, vqsubq_s16(vL1, vDelta1));
vL2 = vminq_s16(vL2, vDelta2);
vL2 = vqaddq_s16(vCpd, vqsubq_s16(vL2, vDelta2));
vL3 = vminq_s16(vL3, vDelta3);
vL3 = vqaddq_s16(vCpd, vqsubq_s16(vL3, vDelta3));
vst1q_s16(Lr_p + d, vL0);
vst1q_s16(Lr_p + d + D2, vL1);
vst1q_s16(Lr_p + d + D2 * 2, vL2);
vst1q_s16(Lr_p + d + D2 * 3, vL3);
int16x8_t t0 = vminq_s16(vcombine_s16(vget_low_s16(vL0), vget_low_s16(vL2)),
vcombine_s16(vget_high_s16(vL0), vget_high_s16(vL2)));
int16x8_t t1 = vminq_s16(vcombine_s16(vget_low_s16(vL1), vget_low_s16(vL3)),
vcombine_s16(vget_high_s16(vL1), vget_high_s16(vL3)));
int16x8_t t2 = vminq_s16(t0, t1);
vMinL0 = vminq_s16(vMinL0, t2);
int16x8_t Sval = vld1q_s16(Sp + d);
int16x8_t L01 = vqaddq_s16(vL0, vL1);
int16x8_t L23 = vqaddq_s16(vL2, vL3);
Sval = vqaddq_s16(Sval, L01);
Sval = vqaddq_s16(Sval, L23);
vst1q_s16(Sp + d, Sval);
}
int16x4_t minL = vpmin_s16(vget_low_s16(vMinL0), vget_high_s16(vMinL0));
minLr[0][xm] = vget_lane_s16(minL, 0);
}
else
#endif
{
int minL0 = MAX_COST, minL1 = MAX_COST, minL2 = MAX_COST, minL3 = MAX_COST;
for( d = 0; d < D; d++ )
{
const int Cpd = Cp[d];
const int L0 = Cpd + std::min((int)Lr_p0[d], std::min(Lr_p0[d-1] + P1, std::min(Lr_p0[d+1] + P1, delta0))) - delta0;
const int L1 = Cpd + std::min((int)Lr_p1[d], std::min(Lr_p1[d-1] + P1, std::min(Lr_p1[d+1] + P1, delta1))) - delta1;
const int L2 = Cpd + std::min((int)Lr_p2[d], std::min(Lr_p2[d-1] + P1, std::min(Lr_p2[d+1] + P1, delta2))) - delta2;
const int L3 = Cpd + std::min((int)Lr_p3[d], std::min(Lr_p3[d-1] + P1, std::min(Lr_p3[d+1] + P1, delta3))) - delta3;
Lr_p[d] = (CostType)L0;
minL0 = std::min(minL0, L0);
Lr_p[d + D2] = (CostType)L1;
minL1 = std::min(minL1, L1);
Lr_p[d + D2*2] = (CostType)L2;
minL2 = std::min(minL2, L2);
Lr_p[d + D2*3] = (CostType)L3;
minL3 = std::min(minL3, L3);
Sp[d] = saturate_cast<CostType>(Sp[d] + L0 + L1 + L2 + L3);
}
minLr[0][xm] = (CostType)minL0;
minLr[0][xm+1] = (CostType)minL1;
minLr[0][xm+2] = (CostType)minL2;
minLr[0][xm+3] = (CostType)minL3;
}
}
if( pass == npasses )
{
for( x = 0; x < width; x++ )
{
disp1ptr[x] = disp2ptr[x] = (DispType)INVALID_DISP_SCALED;
disp2cost[x] = MAX_COST;
}
for( x = width1 - 1; x >= 0; x-- )
{
CostType* Sp = S + x*D;
int minS = MAX_COST;
int bestDisp = -1;
if( npasses == 1 )
{
const int xm = x*NR2;
const int xd = xm*D2;
int minL0 = MAX_COST;
const int delta0 = minLr[0][xm + NR2] + P2;
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
#endif
{
for( d = 0; d < D; d++ )
{
const 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);
const int Sval = Sp[d] = saturate_cast<CostType>(Sp[d] + L0);
if( Sval < minS )
{
minS = Sval;
bestDisp = d;
}
}
minLr[0][xm] = (CostType)minL0;
}
}
else
{
for( d = 0; d < D; d++ )
{
const int Sval = Sp[d];
if( Sval < minS )
{
minS = Sval;
bestDisp = d;
}
}
}
for( d = 0; d < D; d++ )
{
if( Sp[d]*(100 - uniquenessRatio) < minS*100 && std::abs(bestDisp - d) > 1 )
break;
}
if( d < D )
continue;
d = bestDisp;
const int _x2 = x + minX1 - d - minD;
if( disp2cost[_x2] > minS )
{
disp2cost[_x2] = (CostType)minS;
disp2ptr[_x2] = (DispType)(d + minD);
}
if( 0 < d && d < D-1 )
{
if(params.subpixelInterpolationMethod == CV_SIMETRICV_INTERPOLATION)
{
const double m2 = Sp[d - 1];
const double m3 = Sp[d + 1];
const double m1 = Sp[d];
const double m2m1 = m2 - m1;
const double m3m1 = m3 - m1;
if (!(m2m1 == 0 || m3m1 == 0))
{
double p = 0;
if (m2 > m3)
{
p = (0.5 - 0.25 * ((m3m1 * m3m1) / (m2m1 * m2m1) + (m3m1 / m2m1)));
}
else
{
p = -1 * (0.5 - 0.25 * ((m2m1 * m2m1) / (m3m1 * m3m1) + (m2m1 / m3m1)));
}
if (p >= -0.5 && p <= 0.5)
d = (int)(d * DISP_SCALE + p * DISP_SCALE );
}
else
{
d *= DISP_SCALE;
}
}
else if(params.subpixelInterpolationMethod == CV_QUADRATIC_INTERPOLATION)
{
// do subpixel quadratic interpolation:
// fit parabola into (x1=d-1, y1=Sp[d-1]), (x2=d, y2=Sp[d]), (x3=d+1, y3=Sp[d+1])
// then find minimum of the parabola.
const 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
// if either of the corresponding disparities in disp2 is consistent.
// This is to give the computed disparity a chance to look valid if it is.
const int d1 = disp1ptr[x];
if( d1 == INVALID_DISP_SCALED )
continue;
const int _d = d1 >> DISP_SHIFT;
const int d_ = (d1 + DISP_SCALE-1) >> DISP_SHIFT;
const int _x = x - _d;
const int x_ = x - d_;
if( 0 <= _x && _x < width && disp2ptr[_x] >= minD && std::abs(disp2ptr[_x] - _d) > disp12MaxDiff &&
0 <= x_ && x_ < width && disp2ptr[x_] >= minD && std::abs(disp2ptr[x_] - d_) > disp12MaxDiff )
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 CV_FINAL : public StereoBinarySGBM, public Matching
{
public:
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 ):Matching(_numDisparities)
{
params = StereoBinarySGBMParams( _minDisparity, _numDisparities, _SADWindowSize,
_P1, _P2, _disp12MaxDiff, _preFilterCap,
_uniquenessRatio, _speckleWindowSize, _speckleRange,
_mode );
}
void compute( InputArray leftarr, InputArray rightarr, OutputArray disparr ) CV_OVERRIDE
{
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);
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_DENSE_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)
{
Mat blurLeft; blur(left, blurLeft, Size(params.kernelSize, params.kernelSize));
Mat blurRight; blur(right, blurRight, Size(params.kernelSize, params.kernelSize));
modifiedCensusTransform(left, right, params.kernelSize, censusImageLeft, censusImageRight, CV_MEAN_VARIATION, 0,
blurLeft, blurRight);
}
else if(params.kernelType == CV_STAR_KERNEL)
{
starCensusTransform(left,right,params.kernelSize,censusImageLeft,censusImageRight);
}
hammingDistanceBlockMatching(censusImageLeft, censusImageRight, hamDist, params.kernelSize);
computeDisparityBinarySGBM( left, disp, params, buffer,hamDist);
if(params.regionRemoval == CV_SPECKLE_REMOVAL_AVG_ALGORITHM)
{
int width = left.cols;
int height = left.rows;
if (puss.total() != (size_t)width * height)
{
speckleX.create(height, width);
speckleY.create(height, width);
puss.create(height, width);
}
Mat aux;
aux.create(height,width,CV_16S);
Median1x9Filter<short>(disp, aux);
Median9x1Filter<short>(aux,disp);
smallRegionRemoval<short>(disp.clone(), 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);
}
}
int getSubPixelInterpolationMethod() const CV_OVERRIDE { return params.subpixelInterpolationMethod;}
void setSubPixelInterpolationMethod(int value = CV_QUADRATIC_INTERPOLATION) CV_OVERRIDE { CV_Assert(value < 2); params.subpixelInterpolationMethod = value;}
int getBinaryKernelType() const CV_OVERRIDE { return params.kernelType;}
void setBinaryKernelType(int value = CV_MODIFIED_CENSUS_TRANSFORM) CV_OVERRIDE { CV_Assert(value < 7); params.kernelType = value; }
int getSpekleRemovalTechnique() const CV_OVERRIDE { return params.regionRemoval;}
void setSpekleRemovalTechnique(int factor = CV_SPECKLE_REMOVAL_AVG_ALGORITHM) CV_OVERRIDE { CV_Assert(factor < 2); params.regionRemoval = factor; }
int getMinDisparity() const CV_OVERRIDE { return params.minDisparity; }
void setMinDisparity(int minDisparity) CV_OVERRIDE {CV_Assert(minDisparity >= 0); params.minDisparity = minDisparity; }
int getNumDisparities() const CV_OVERRIDE { return params.numDisparities; }
void setNumDisparities(int numDisparities) CV_OVERRIDE { CV_Assert(numDisparities > 0); params.numDisparities = numDisparities;
Matching::setMaxDisparity(numDisparities /*- params.minDisparity*/);}
int getBlockSize() const CV_OVERRIDE { return params.kernelSize; }
void setBlockSize(int blockSize) CV_OVERRIDE {CV_Assert(blockSize % 2 != 0); params.kernelSize = blockSize; }
int getSpeckleWindowSize() const CV_OVERRIDE { return params.speckleWindowSize; }
void setSpeckleWindowSize(int speckleWindowSize) CV_OVERRIDE {CV_Assert(speckleWindowSize >= 0); params.speckleWindowSize = speckleWindowSize; }
int getSpeckleRange() const CV_OVERRIDE { return params.speckleRange; }
void setSpeckleRange(int speckleRange) CV_OVERRIDE { CV_Assert(speckleRange >= 0); params.speckleRange = speckleRange; }
int getDisp12MaxDiff() const CV_OVERRIDE { return params.disp12MaxDiff; }
void setDisp12MaxDiff(int disp12MaxDiff) CV_OVERRIDE {CV_Assert(disp12MaxDiff > 0); params.disp12MaxDiff = disp12MaxDiff; }
int getPreFilterCap() const CV_OVERRIDE { return params.preFilterCap; }
void setPreFilterCap(int preFilterCap) CV_OVERRIDE { CV_Assert(preFilterCap > 0); params.preFilterCap = preFilterCap; }
int getUniquenessRatio() const CV_OVERRIDE { return params.uniquenessRatio; }
void setUniquenessRatio(int uniquenessRatio) CV_OVERRIDE { CV_Assert(uniquenessRatio >= 0); params.uniquenessRatio = uniquenessRatio; }
int getP1() const CV_OVERRIDE { return params.P1; }
void setP1(int P1) CV_OVERRIDE { CV_Assert(P1 > 0); params.P1 = P1; }
int getP2() const CV_OVERRIDE { return params.P2; }
void setP2(int P2) CV_OVERRIDE {CV_Assert(P2 > 0); CV_Assert(P2 >= 2 * params.P1); params.P2 = P2; }
int getMode() const CV_OVERRIDE { return params.mode; }
void setMode(int mode) CV_OVERRIDE { params.mode = mode; }
void write(FileStorage& fs) const CV_OVERRIDE
{
fs << "name" << name_
<< "minDisparity" << params.minDisparity
<< "numDisparities" << params.numDisparities
<< "blockSize" << params.kernelSize
<< "speckleWindowSize" << params.speckleWindowSize
<< "speckleRange" << params.speckleRange
<< "disp12MaxDiff" << params.disp12MaxDiff
<< "preFilterCap" << params.preFilterCap
<< "uniquenessRatio" << params.uniquenessRatio
<< "P1" << params.P1
<< "P2" << params.P2
<< "mode" << params.mode;
}
void read(const FileNode& fn) CV_OVERRIDE
{
FileNode n = fn["name"];
CV_Assert( n.isString() && String(n) == name_ );
params.minDisparity = (int)fn["minDisparity"];
params.numDisparities = (int)fn["numDisparities"];
params.kernelSize = (int)fn["blockSize"];
params.speckleWindowSize = (int)fn["speckleWindowSize"];
params.speckleRange = (int)fn["speckleRange"];
params.disp12MaxDiff = (int)fn["disp12MaxDiff"];
params.preFilterCap = (int)fn["preFilterCap"];
params.uniquenessRatio = (int)fn["uniquenessRatio"];
params.P1 = (int)fn["P1"];
params.P2 = (int)fn["P2"];
params.mode = (int)fn["mode"];
}
StereoBinarySGBMParams params;
Mat buffer;
static const char* name_;
Mat censusImageLeft;
Mat censusImageRight;
Mat partialSumsLR;
Mat agregatedHammingLRCost;
Mat hamDist;
};
const char* StereoBinarySGBMImpl::name_ = "StereoBinaryMatcher.SGBM";
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, kernelSize,
P1, P2, disp12MaxDiff,
preFilterCap, uniquenessRatio,
speckleWindowSize, speckleRange,
mode));
}
typedef cv::Point_<short> Point2s;
}
}
@@ -0,0 +1,236 @@
/*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"
namespace opencv_test { namespace {
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;
image1 = imread(ts->get_data_path() + "stereomatching/datasets/tsukuba/im2.png", IMREAD_GRAYSCALE);
image2 = imread(ts->get_data_path() + "stereomatching/datasets/tsukuba/im6.png", IMREAD_GRAYSCALE);
gt = imread(ts->get_data_path() + "stereomatching/datasets/tsukuba/disp2.png", IMREAD_GRAYSCALE);
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 != image1.cols || gt.rows != image1.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;
image1 = imread(ts->get_data_path() + "stereomatching/datasets/tsukuba/im2.png", IMREAD_GRAYSCALE);
image2 = imread(ts->get_data_path() + "stereomatching/datasets/tsukuba/im6.png", IMREAD_GRAYSCALE);
gt = imread(ts->get_data_path() + "stereomatching/datasets/tsukuba/disp2.png", IMREAD_GRAYSCALE);
ts->printf(cvtest::TS::LOG,(ts->get_data_path() + "stereomatching/datasets/tsukuba/im2.png").c_str());
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 != image1.cols || gt.rows != image1.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(); }
}} // namespace
+465
View File
@@ -0,0 +1,465 @@
/*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"
namespace opencv_test { namespace {
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
left = imread(ts->get_data_path() + "stereomatching/datasets/tsukuba/im2.png", IMREAD_GRAYSCALE);
right = imread(ts->get_data_path() + "stereomatching/datasets/tsukuba/im6.png", IMREAD_GRAYSCALE);
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 = std::numeric_limits<int>::min();
int max = std::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(DISABLED_census_transform_testing, accuracy) { CV_CensusTransformTest test; test.safe_run(); }
TEST(DISABLED_symetric_census_testing, accuracy) { CV_SymetricCensusTest test; test.safe_run(); }
TEST(DISABLED_Dmodified_census_testing, accuracy) { CV_ModifiedCensusTransformTest test; test.safe_run(); }
TEST(DISABLED_Dstar_kernel_testing, accuracy) { CV_StarKernelCensusTest test; test.safe_run(); }
}} // namespace
+45
View File
@@ -0,0 +1,45 @@
/*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 "test_precomp.hpp"
CV_TEST_MAIN("cv")
+14
View File
@@ -0,0 +1,14 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#ifndef __OPENCV_TEST_PRECOMP_HPP__
#define __OPENCV_TEST_PRECOMP_HPP__
#include "opencv2/ts.hpp"
#include "opencv2/xstereo.hpp"
namespace opencv_test {
using namespace cv::stereo;
}
#endif
@@ -0,0 +1,65 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#include "test_precomp.hpp"
namespace opencv_test { namespace {
static float disparity_MAE(const Mat &reference, const Mat &estimation)
{
int elems=0;
float error=0;
for (int row=0; row< reference.rows; row++){
for (int col=0; col<reference.cols; col++){
float ref_val = reference.at<float>(row, col);
float estimated_val = estimation.at<float>(row, col);
// filter out pixels with unknown reference value and pixels whose disparity did not get estimated.
if (estimated_val == 0 || ref_val == 0 || std::isnan(estimated_val)){
continue;
}
else{
error+=abs(ref_val - estimated_val);
elems+=1;
}
}
}
return error/elems;
}
// void CV_QdsMatchingTest::run(int)
TEST(qds_getDisparity, accuracy)
{
//load data
Mat image1, image2, gt;
image1 = imread(cvtest::TS::ptr()->get_data_path() + "stereomatching/datasets/cones/im2.png", IMREAD_GRAYSCALE);
image2 = imread(cvtest::TS::ptr()->get_data_path() + "stereomatching/datasets/cones/im6.png", IMREAD_GRAYSCALE);
gt = imread(cvtest::TS::ptr()->get_data_path() + "stereomatching/datasets/cones/disp2.png", IMREAD_GRAYSCALE);
// reference scale factor is based on this https://github.com/opencv/opencv_extra/blob/master/testdata/cv/stereomatching/datasets/datasets.xml
gt.convertTo(gt, CV_32F);
gt =gt/4;
//test inputs
ASSERT_FALSE(image1.empty() || image2.empty() || gt.empty()) << "Issue with input data";
//configure disparity algorithm
cv::Size frameSize = image1.size();
Ptr<stereo::QuasiDenseStereo> qds_matcher = stereo::QuasiDenseStereo::create(frameSize);
//compute disparity
qds_matcher->process(image1, image2);
Mat outDisp = qds_matcher->getDisparity();
// test input output size consistency
ASSERT_EQ(gt.size(), outDisp.size()) << "Mismatch input/output dimensions";
ASSERT_LT(disparity_MAE(gt, outDisp),2) << "EPE should be 1.1053 for this sample/hyperparamters (Tested on version 4.5.1)";
}
}} // namespace
Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 457 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 457 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

@@ -0,0 +1,23 @@
Exporting a template parameter file {#tutorial_qds_export_parameters}
==================
Goal
----
In this tutorial you will learn how to
- create a simple parameter file template.
@include ./samples/export_param_file.cpp
## Explanation:
The class supports loading configuration parameters from a .yaml file using the method `loadParameters()`.
This is very useful for fine-tuning the class' parameters on the fly. To extract a template of this
parameter file you run the following code.
We create an instance of a `QuasiDenseStereo` object. Not specifying the second argument of the constructor,
makes the object to load default parameters.
@snippet ./samples/export_param_file.cpp create
By calling the method `saveParameters()`, we store the template file to the location specified by `parameterFileLocation`
@snippet ./samples/export_param_file.cpp write
@@ -0,0 +1,39 @@
Quasi dense Stereo {#tutorial_qds_quasi_dense_stereo}
==================
Goal
----
In this tutorial you will learn how to
- Configure a QuasiDenseStero object
- Compute dense Stereo correspondences.
@include ./samples/dense_disparity.cpp
## Explanation:
The program loads a stereo image pair.
After importing the images.
@snippet ./samples/dense_disparity.cpp load
We need to know the frame size of a single image, in order to create an instance of a `QuasiDesnseStereo` object.
@snippet ./samples/dense_disparity.cpp create
Because we didn't specify the second argument in the constructor, the `QuasiDesnseStereo` object will
load default parameters.
We can then pass the imported stereo images in the process method like this
@snippet ./samples/dense_disparity.cpp process
The process method contains most of the functionality of the class and does two main things.
- Computes a sparse stereo based in "Good Features to Track" and "pyramidal Lucas-Kanade" flow algorithm
- Based on those sparse stereo points, densifies the stereo correspondences using Quasi Dense Stereo method.
After the execution of `process()` we can display the disparity Image of the stereo.
@snippet ./samples/dense_disparity.cpp disp
At this point we can also extract all the corresponding points using `getDenseMatches()` method and export them in a file.
@snippet ./samples/dense_disparity.cpp export
@@ -0,0 +1,14 @@
Quasi Dense Stereo (stereo module) {#tutorial_table_of_content_quasi_dense_stereo}
==========================================================
Quasi Dense Stereo is method for performing dense stereo matching. `QuasiDenseStereo` implements this process.
The code uses pyramidal Lucas-Kanade with Shi-Tomasi features to get the initial seed correspondences.
Then these seeds are propagated by using mentioned growing scheme.
- @subpage tutorial_qds_quasi_dense_stereo
Example showing how to get dense correspondences from a stereo image pair.
- @subpage tutorial_qds_export_parameters
Example showing how to genereate a parameter file template.