mirror of
https://github.com/introlab/rtabmap.git
synced 2026-10-04 00:57:46 +08:00
Added StereoDense, StereoBM and StereoSGBM doc and tests
This commit is contained in:
@@ -35,24 +35,97 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
|
|
||||||
namespace rtabmap {
|
namespace rtabmap {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class StereoDense
|
||||||
|
* @brief Abstract base class for dense stereo matching algorithms
|
||||||
|
*
|
||||||
|
* This class provides the interface for dense stereo matching algorithms that
|
||||||
|
* compute disparity maps from stereo image pairs. It uses a factory pattern
|
||||||
|
* to create specific implementations (Block Matching or Semi-Global Block Matching).
|
||||||
|
*
|
||||||
|
* The disparity map is a 16-bit signed integer image (CV_16SC1) where each pixel
|
||||||
|
* value represents the horizontal displacement between corresponding pixels in
|
||||||
|
* the left and right stereo images. Higher disparity values indicate closer objects.
|
||||||
|
*
|
||||||
|
* @note Both input images must have the same size and be either grayscale (CV_8UC1)
|
||||||
|
* or color (CV_8UC3). Color images are automatically converted to grayscale.
|
||||||
|
*/
|
||||||
class RTABMAP_CORE_EXPORT StereoDense {
|
class RTABMAP_CORE_EXPORT StereoDense {
|
||||||
public:
|
public:
|
||||||
|
/**
|
||||||
|
* @enum Type
|
||||||
|
* @brief Enumeration of available stereo matching algorithm types
|
||||||
|
*/
|
||||||
enum Type {
|
enum Type {
|
||||||
kTypeBM = 0,
|
kTypeBM = 0, ///< Block Matching algorithm (faster, less accurate)
|
||||||
kTypeSGBM = 1
|
kTypeSGBM = 1 ///< Semi-Global Block Matching algorithm (slower, more accurate)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Factory method to create a StereoDense instance from parameters
|
||||||
|
*
|
||||||
|
* Creates a StereoDense instance based on the stereo dense strategy parameter
|
||||||
|
* in the provided parameters map. The strategy is determined by the
|
||||||
|
* Parameters::kStereoDenseStrategy() parameter.
|
||||||
|
*
|
||||||
|
* @param parameters Parameters map containing configuration values
|
||||||
|
* @return Pointer to the created StereoDense instance (caller owns the memory)
|
||||||
|
* @see create(Type, const ParametersMap&)
|
||||||
|
*/
|
||||||
static StereoDense * create(const ParametersMap & parameters);
|
static StereoDense * create(const ParametersMap & parameters);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Factory method to create a StereoDense instance of a specific type
|
||||||
|
*
|
||||||
|
* Creates a StereoDense instance of the specified type with the given parameters.
|
||||||
|
*
|
||||||
|
* @param type The type of stereo matching algorithm to create
|
||||||
|
* @param parameters Optional parameters map containing configuration values
|
||||||
|
* @return Pointer to the created StereoDense instance (caller owns the memory)
|
||||||
|
* @see create(const ParametersMap&)
|
||||||
|
*/
|
||||||
static StereoDense * create(StereoDense::Type type, const ParametersMap & parameters = ParametersMap());
|
static StereoDense * create(StereoDense::Type type, const ParametersMap & parameters = ParametersMap());
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Virtual destructor
|
||||||
|
*/
|
||||||
virtual ~StereoDense() {}
|
virtual ~StereoDense() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parse parameters from a parameters map
|
||||||
|
*
|
||||||
|
* Updates the algorithm's configuration based on the provided parameters map.
|
||||||
|
* Derived classes should override this method to parse their specific parameters.
|
||||||
|
*
|
||||||
|
* @param parameters Parameters map containing configuration values
|
||||||
|
*/
|
||||||
virtual void parseParameters(const ParametersMap & parameters) {}
|
virtual void parseParameters(const ParametersMap & parameters) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compute disparity map from stereo image pair
|
||||||
|
*
|
||||||
|
* Computes a disparity map from the given left and right stereo images.
|
||||||
|
* The images must have the same size and be either grayscale (CV_8UC1) or
|
||||||
|
* color (CV_8UC3). Color images are automatically converted to grayscale.
|
||||||
|
*
|
||||||
|
* @param leftImage Left stereo image (CV_8UC1 or CV_8UC3)
|
||||||
|
* @param rightImage Right stereo image (CV_8UC1 or CV_8UC3), must have same size as leftImage
|
||||||
|
* @return Disparity map as a 16-bit signed integer image (CV_16SC1)
|
||||||
|
* @note The disparity values are stored as fixed-point numbers with 4 fractional bits.
|
||||||
|
* To get the actual disparity, divide by 16.
|
||||||
|
* @note Invalid disparities are typically represented by negative values or zero.
|
||||||
|
*/
|
||||||
virtual cv::Mat computeDisparity(
|
virtual cv::Mat computeDisparity(
|
||||||
const cv::Mat & leftImage,
|
const cv::Mat & leftImage,
|
||||||
const cv::Mat & rightImage) const = 0;
|
const cv::Mat & rightImage) const = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
/**
|
||||||
|
* @brief Protected constructor
|
||||||
|
*
|
||||||
|
* @param parameters Optional parameters map for initialization
|
||||||
|
*/
|
||||||
StereoDense(const ParametersMap & parameters = ParametersMap()) {}
|
StereoDense(const ParametersMap & parameters = ParametersMap()) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -34,28 +34,88 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
|
|
||||||
namespace rtabmap {
|
namespace rtabmap {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class StereoBM
|
||||||
|
* @brief Block Matching algorithm for dense stereo matching
|
||||||
|
*
|
||||||
|
* This class implements the Block Matching (BM) algorithm for computing disparity
|
||||||
|
* maps from stereo image pairs.
|
||||||
|
*
|
||||||
|
* @note This class wraps OpenCV's cv::StereoBM implementation.
|
||||||
|
* @see StereoSGBM for a more accurate but slower alternative
|
||||||
|
*/
|
||||||
class RTABMAP_CORE_EXPORT StereoBM : public StereoDense {
|
class RTABMAP_CORE_EXPORT StereoBM : public StereoDense {
|
||||||
public:
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Constructor with explicit block size and number of disparities
|
||||||
|
*
|
||||||
|
* @param blockSize Size of the block window for matching (must be odd, typically 5-21)
|
||||||
|
* @param numDisparities Number of disparity levels to search (must be divisible by 16)
|
||||||
|
*/
|
||||||
StereoBM(int blockSize, int numDisparities);
|
StereoBM(int blockSize, int numDisparities);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constructor with parameters map
|
||||||
|
*
|
||||||
|
* Creates a StereoBM instance and initializes it from the provided parameters map.
|
||||||
|
*
|
||||||
|
* @param parameters Parameters map containing configuration values
|
||||||
|
*/
|
||||||
StereoBM(const ParametersMap & parameters = ParametersMap());
|
StereoBM(const ParametersMap & parameters = ParametersMap());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Virtual destructor
|
||||||
|
*/
|
||||||
virtual ~StereoBM() {}
|
virtual ~StereoBM() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parse parameters from a parameters map
|
||||||
|
*
|
||||||
|
* Updates the algorithm's configuration based on the provided parameters map.
|
||||||
|
* Supported parameters:
|
||||||
|
* - Parameters::kStereoBMBlockSize()
|
||||||
|
* - Parameters::kStereoBMMinDisparity()
|
||||||
|
* - Parameters::kStereoBMNumDisparities()
|
||||||
|
* - Parameters::kStereoBMPreFilterSize()
|
||||||
|
* - Parameters::kStereoBMPreFilterCap()
|
||||||
|
* - Parameters::kStereoBMUniquenessRatio()
|
||||||
|
* - Parameters::kStereoBMTextureThreshold()
|
||||||
|
* - Parameters::kStereoBMSpeckleWindowSize()
|
||||||
|
* - Parameters::kStereoBMSpeckleRange()
|
||||||
|
* - Parameters::kStereoBMDisp12MaxDiff()
|
||||||
|
*
|
||||||
|
* @param parameters Parameters map containing configuration values
|
||||||
|
*/
|
||||||
virtual void parseParameters(const ParametersMap & parameters);
|
virtual void parseParameters(const ParametersMap & parameters);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compute disparity map from stereo image pair
|
||||||
|
*
|
||||||
|
* Computes a disparity map using the Block Matching algorithm. The images
|
||||||
|
* must have the same size and be either grayscale (CV_8UC1) or color (CV_8UC3).
|
||||||
|
* Color images are automatically converted to grayscale.
|
||||||
|
*
|
||||||
|
* @param leftImage Left stereo image (CV_8UC1 or CV_8UC3)
|
||||||
|
* @param rightImage Right stereo image (CV_8UC1 or CV_8UC3), must have same size as leftImage
|
||||||
|
* @return Disparity map as a 16-bit signed integer image (CV_16SC1)
|
||||||
|
* @note The disparity values are stored as fixed-point numbers with 4 fractional bits.
|
||||||
|
* To get the actual disparity, divide by 16.
|
||||||
|
*/
|
||||||
virtual cv::Mat computeDisparity(
|
virtual cv::Mat computeDisparity(
|
||||||
const cv::Mat & leftImage,
|
const cv::Mat & leftImage,
|
||||||
const cv::Mat & rightImage) const;
|
const cv::Mat & rightImage) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int blockSize_; //15
|
int blockSize_; ///< Size of the block window for matching (default: 15, must be odd)
|
||||||
int minDisparity_; //0
|
int minDisparity_; ///< Minimum disparity value to search (default: 0)
|
||||||
int numDisparities_; //64
|
int numDisparities_; ///< Number of disparity levels to search (default: 64, must be divisible by 16)
|
||||||
int preFilterSize_; //9
|
int preFilterSize_; ///< Size of the prefiltering window (default: 9, must be odd)
|
||||||
int preFilterCap_; //31
|
int preFilterCap_; ///< Prefiltering cap value (default: 31)
|
||||||
int uniquenessRatio_; //15
|
int uniquenessRatio_; ///< Uniqueness ratio for matching (default: 15)
|
||||||
int textureThreshold_; //10
|
int textureThreshold_; ///< Texture threshold for filtering (default: 10)
|
||||||
int speckleWindowSize_; //100
|
int speckleWindowSize_; ///< Maximum size of smooth disparity regions to consider as speckles (default: 100)
|
||||||
int speckleRange_; //4
|
int speckleRange_; ///< Maximum disparity variation within each connected component (default: 4)
|
||||||
int disp12MaxDiff_; //-1
|
int disp12MaxDiff_; ///< Maximum allowed difference in left-right disparity check (default: -1, disabled)
|
||||||
};
|
};
|
||||||
|
|
||||||
} /* namespace rtabmap */
|
} /* namespace rtabmap */
|
||||||
|
|||||||
@@ -34,28 +34,86 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
|
|
||||||
namespace rtabmap {
|
namespace rtabmap {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class StereoSGBM
|
||||||
|
* @brief Semi-Global Block Matching algorithm for dense stereo matching
|
||||||
|
*
|
||||||
|
* This class implements the Semi-Global Block Matching (SGBM) algorithm for
|
||||||
|
* computing disparity maps from stereo image pairs.
|
||||||
|
*
|
||||||
|
* SGBM generally produces more accurate results than Block Matching, especially
|
||||||
|
* in textureless regions and areas with repetitive patterns, but is computationally
|
||||||
|
* more expensive.
|
||||||
|
*
|
||||||
|
* @note This class wraps OpenCV's cv::StereoSGBM implementation.
|
||||||
|
* @see StereoBM for a faster but less accurate alternative
|
||||||
|
*/
|
||||||
class RTABMAP_CORE_EXPORT StereoSGBM : public StereoDense {
|
class RTABMAP_CORE_EXPORT StereoSGBM : public StereoDense {
|
||||||
public:
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Constructor with parameters map
|
||||||
|
*
|
||||||
|
* Creates a StereoSGBM instance and initializes it from the provided parameters map.
|
||||||
|
*
|
||||||
|
* @param parameters Parameters map containing configuration values
|
||||||
|
*/
|
||||||
StereoSGBM(const ParametersMap & parameters = ParametersMap());
|
StereoSGBM(const ParametersMap & parameters = ParametersMap());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Virtual destructor
|
||||||
|
*/
|
||||||
virtual ~StereoSGBM() {}
|
virtual ~StereoSGBM() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parse parameters from a parameters map
|
||||||
|
*
|
||||||
|
* Updates the algorithm's configuration based on the provided parameters map.
|
||||||
|
* Supported parameters:
|
||||||
|
* - Parameters::kStereoSGBMBlockSize()
|
||||||
|
* - Parameters::kStereoSGBMMinDisparity()
|
||||||
|
* - Parameters::kStereoSGBMNumDisparities()
|
||||||
|
* - Parameters::kStereoSGBMPreFilterCap()
|
||||||
|
* - Parameters::kStereoSGBMUniquenessRatio()
|
||||||
|
* - Parameters::kStereoSGBMSpeckleWindowSize()
|
||||||
|
* - Parameters::kStereoSGBMSpeckleRange()
|
||||||
|
* - Parameters::kStereoSGBMP1()
|
||||||
|
* - Parameters::kStereoSGBMP2()
|
||||||
|
* - Parameters::kStereoSGBMDisp12MaxDiff()
|
||||||
|
* - Parameters::kStereoSGBMMode()
|
||||||
|
*
|
||||||
|
* @param parameters Parameters map containing configuration values
|
||||||
|
*/
|
||||||
virtual void parseParameters(const ParametersMap & parameters);
|
virtual void parseParameters(const ParametersMap & parameters);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compute disparity map from stereo image pair
|
||||||
|
*
|
||||||
|
* Computes a disparity map using the Semi-Global Block Matching algorithm.
|
||||||
|
* The images must have the same size and be either grayscale (CV_8UC1) or
|
||||||
|
* color (CV_8UC3). Color images are automatically converted to grayscale.
|
||||||
|
*
|
||||||
|
* @param leftImage Left stereo image (CV_8UC1 or CV_8UC3)
|
||||||
|
* @param rightImage Right stereo image (CV_8UC1 or CV_8UC3), must have same size as leftImage
|
||||||
|
* @return Disparity map as a 16-bit signed integer image (CV_16SC1)
|
||||||
|
* @note The disparity values are stored as fixed-point numbers with 4 fractional bits.
|
||||||
|
* To get the actual disparity, divide by 16.
|
||||||
|
*/
|
||||||
virtual cv::Mat computeDisparity(
|
virtual cv::Mat computeDisparity(
|
||||||
const cv::Mat & leftImage,
|
const cv::Mat & leftImage,
|
||||||
const cv::Mat & rightImage) const;
|
const cv::Mat & rightImage) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int blockSize_; //15
|
int blockSize_; ///< Size of the block window for matching (default: 15, must be odd)
|
||||||
int minDisparity_; //0
|
int minDisparity_; ///< Minimum disparity value to search (default: 0)
|
||||||
int numDisparities_; //64
|
int numDisparities_; ///< Number of disparity levels to search (default: 64, must be divisible by 16)
|
||||||
int preFilterCap_; //31
|
int preFilterCap_; ///< Prefiltering cap value (default: 31)
|
||||||
int uniquenessRatio_; //15
|
int uniquenessRatio_; ///< Uniqueness ratio for matching (default: 15)
|
||||||
int speckleWindowSize_; //100
|
int speckleWindowSize_; ///< Maximum size of smooth disparity regions to consider as speckles (default: 100)
|
||||||
int speckleRange_; //4
|
int speckleRange_; ///< Maximum disparity variation within each connected component (default: 4)
|
||||||
int P1_; //0
|
int P1_; ///< Penalty parameter for disparity changes of 1 pixel (default: 0, typically 8*blockSize*blockSize)
|
||||||
int P2_; //0
|
int P2_; ///< Penalty parameter for disparity changes of more than 1 pixel (default: 0, typically 32*blockSize*blockSize)
|
||||||
int disp12MaxDiff_; //0
|
int disp12MaxDiff_; ///< Maximum allowed difference in left-right disparity check (default: 0)
|
||||||
int mode_; //0=cv::StereoSGBM::MODE_SGBM;
|
int mode_; ///< Algorithm mode: 0=cv::StereoSGBM::MODE_SGBM, 1=cv::StereoSGBM::MODE_HH (default: 0)
|
||||||
};
|
};
|
||||||
|
|
||||||
} /* namespace rtabmap */
|
} /* namespace rtabmap */
|
||||||
|
|||||||
@@ -64,4 +64,9 @@ gtest_discover_tests(test_vwdictionary)
|
|||||||
#Transform.h
|
#Transform.h
|
||||||
add_executable(test_transform test_transform.cpp)
|
add_executable(test_transform test_transform.cpp)
|
||||||
target_link_libraries(test_transform gtest_main rtabmap_core)
|
target_link_libraries(test_transform gtest_main rtabmap_core)
|
||||||
gtest_discover_tests(test_transform)
|
gtest_discover_tests(test_transform)
|
||||||
|
|
||||||
|
#StereoDense.h (tests both BM and SGBM strategies)
|
||||||
|
add_executable(test_stereo_dense stereo/test_stereo_dense.cpp)
|
||||||
|
target_link_libraries(test_stereo_dense gtest_main rtabmap_core)
|
||||||
|
gtest_discover_tests(test_stereo_dense)
|
||||||
@@ -0,0 +1,276 @@
|
|||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <opencv2/core.hpp>
|
||||||
|
#include <opencv2/imgproc.hpp>
|
||||||
|
#include "rtabmap/core/StereoDense.h"
|
||||||
|
#include "rtabmap/core/stereo/StereoBM.h"
|
||||||
|
#include "rtabmap/core/stereo/StereoSGBM.h"
|
||||||
|
#include "rtabmap/core/Parameters.h"
|
||||||
|
#include "rtabmap/utilite/ULogger.h"
|
||||||
|
#include "rtabmap/utilite/UException.h"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
using namespace rtabmap;
|
||||||
|
|
||||||
|
class StereoDenseTest : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
void SetUp() override {
|
||||||
|
// Create synthetic stereo images for testing
|
||||||
|
// Generate random pattern for left image
|
||||||
|
int imageWidth = 160;
|
||||||
|
int imageHeight = 120;
|
||||||
|
leftImage_ = cv::Mat(imageHeight, imageWidth, CV_8UC1);
|
||||||
|
cv::randu(leftImage_, cv::Scalar(0), cv::Scalar(256));
|
||||||
|
|
||||||
|
// Shift the entire left image left by shiftPixels_ to create the right image
|
||||||
|
// Right image will have zeros on the right edge where there's no correspondence
|
||||||
|
shiftPixels_ = 5;
|
||||||
|
rightImage_ = cv::Mat::zeros(leftImage_.size(), CV_8UC1);
|
||||||
|
cv::Rect leftROI(shiftPixels_, 0, leftImage_.cols - shiftPixels_, leftImage_.rows);
|
||||||
|
cv::Rect rightROI(0, 0, leftImage_.cols - shiftPixels_, leftImage_.rows);
|
||||||
|
leftImage_(leftROI).copyTo(rightImage_(rightROI));
|
||||||
|
|
||||||
|
// Create color versions
|
||||||
|
#if CV_MAJOR_VERSION >= 3
|
||||||
|
cv::cvtColor(leftImage_, leftImageColor_, cv::COLOR_GRAY2BGR);
|
||||||
|
cv::cvtColor(rightImage_, rightImageColor_, cv::COLOR_GRAY2BGR);
|
||||||
|
#else
|
||||||
|
cv::cvtColor(leftImage_, leftImageColor_, CV_GRAY2BGR);
|
||||||
|
cv::cvtColor(rightImage_, rightImageColor_, CV_GRAY2BGR);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void TearDown() override {
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::Mat leftImage_;
|
||||||
|
cv::Mat rightImage_;
|
||||||
|
cv::Mat leftImageColor_;
|
||||||
|
cv::Mat rightImageColor_;
|
||||||
|
int shiftPixels_; ///< Number of pixels the right image is shifted left (expected disparity)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper function to get strategy name for error messages
|
||||||
|
const char* getStrategyName(StereoDense::Type type) {
|
||||||
|
switch(type) {
|
||||||
|
case StereoDense::kTypeBM: return "BM";
|
||||||
|
case StereoDense::kTypeSGBM: return "SGBM";
|
||||||
|
default: return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StereoDense Tests
|
||||||
|
|
||||||
|
TEST_F(StereoDenseTest, Constructor)
|
||||||
|
{
|
||||||
|
// Test with all stereo strategies
|
||||||
|
StereoDense::Type strategies[] = {
|
||||||
|
StereoDense::kTypeBM,
|
||||||
|
StereoDense::kTypeSGBM
|
||||||
|
};
|
||||||
|
|
||||||
|
for(StereoDense::Type strategy : strategies)
|
||||||
|
{
|
||||||
|
ParametersMap params;
|
||||||
|
|
||||||
|
// Set strategy-specific parameters
|
||||||
|
if(strategy == StereoDense::kTypeBM)
|
||||||
|
{
|
||||||
|
params.insert(ParametersPair(Parameters::kStereoBMBlockSize(), "15"));
|
||||||
|
params.insert(ParametersPair(Parameters::kStereoBMNumDisparities(), "64"));
|
||||||
|
}
|
||||||
|
else // kTypeSGBM
|
||||||
|
{
|
||||||
|
params.insert(ParametersPair(Parameters::kStereoSGBMBlockSize(), "5"));
|
||||||
|
params.insert(ParametersPair(Parameters::kStereoSGBMNumDisparities(), "128"));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<StereoDense> stereo(StereoDense::create(strategy, params));
|
||||||
|
EXPECT_NE(stereo.get(), nullptr) << "Strategy: " << getStrategyName(strategy);
|
||||||
|
|
||||||
|
// Test default constructor
|
||||||
|
ParametersMap params2;
|
||||||
|
std::unique_ptr<StereoDense> stereo2(StereoDense::create(strategy, params2));
|
||||||
|
EXPECT_NE(stereo2.get(), nullptr) << "Strategy: " << getStrategyName(strategy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(StereoDenseTest, ParseParameters)
|
||||||
|
{
|
||||||
|
// Test with all stereo strategies
|
||||||
|
StereoDense::Type strategies[] = {
|
||||||
|
StereoDense::kTypeBM,
|
||||||
|
StereoDense::kTypeSGBM
|
||||||
|
};
|
||||||
|
|
||||||
|
for(StereoDense::Type strategy : strategies)
|
||||||
|
{
|
||||||
|
std::unique_ptr<StereoDense> stereo(StereoDense::create(strategy));
|
||||||
|
EXPECT_NE(stereo.get(), nullptr) << "Strategy: " << getStrategyName(strategy);
|
||||||
|
|
||||||
|
ParametersMap params;
|
||||||
|
|
||||||
|
if(strategy == StereoDense::kTypeBM)
|
||||||
|
{
|
||||||
|
params.insert(ParametersPair(Parameters::kStereoBMBlockSize(), "21"));
|
||||||
|
params.insert(ParametersPair(Parameters::kStereoBMMinDisparity(), "5"));
|
||||||
|
params.insert(ParametersPair(Parameters::kStereoBMNumDisparities(), "128"));
|
||||||
|
params.insert(ParametersPair(Parameters::kStereoBMPreFilterSize(), "11"));
|
||||||
|
params.insert(ParametersPair(Parameters::kStereoBMPreFilterCap(), "63"));
|
||||||
|
params.insert(ParametersPair(Parameters::kStereoBMUniquenessRatio(), "20"));
|
||||||
|
params.insert(ParametersPair(Parameters::kStereoBMTextureThreshold(), "15"));
|
||||||
|
params.insert(ParametersPair(Parameters::kStereoBMSpeckleWindowSize(), "200"));
|
||||||
|
params.insert(ParametersPair(Parameters::kStereoBMSpeckleRange(), "8"));
|
||||||
|
params.insert(ParametersPair(Parameters::kStereoBMDisp12MaxDiff(), "1"));
|
||||||
|
}
|
||||||
|
else // kTypeSGBM
|
||||||
|
{
|
||||||
|
params.insert(ParametersPair(Parameters::kStereoSGBMBlockSize(), "5"));
|
||||||
|
params.insert(ParametersPair(Parameters::kStereoSGBMMinDisparity(), "5"));
|
||||||
|
params.insert(ParametersPair(Parameters::kStereoSGBMNumDisparities(), "128"));
|
||||||
|
params.insert(ParametersPair(Parameters::kStereoSGBMPreFilterCap(), "63"));
|
||||||
|
params.insert(ParametersPair(Parameters::kStereoSGBMUniquenessRatio(), "20"));
|
||||||
|
params.insert(ParametersPair(Parameters::kStereoSGBMSpeckleWindowSize(), "200"));
|
||||||
|
params.insert(ParametersPair(Parameters::kStereoSGBMSpeckleRange(), "8"));
|
||||||
|
params.insert(ParametersPair(Parameters::kStereoSGBMP1(), "100"));
|
||||||
|
params.insert(ParametersPair(Parameters::kStereoSGBMP2(), "1000"));
|
||||||
|
params.insert(ParametersPair(Parameters::kStereoSGBMDisp12MaxDiff(), "1"));
|
||||||
|
params.insert(ParametersPair(Parameters::kStereoSGBMMode(), "0"));
|
||||||
|
}
|
||||||
|
|
||||||
|
stereo->parseParameters(params);
|
||||||
|
// Should not throw
|
||||||
|
EXPECT_TRUE(true) << "Strategy: " << getStrategyName(strategy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(StereoDenseTest, ComputeDisparityGrayscale)
|
||||||
|
{
|
||||||
|
// Test with all stereo strategies
|
||||||
|
StereoDense::Type strategies[] = {
|
||||||
|
StereoDense::kTypeBM,
|
||||||
|
StereoDense::kTypeSGBM
|
||||||
|
};
|
||||||
|
|
||||||
|
StereoBM stereoBM;
|
||||||
|
cv::Mat disparity = stereoBM.computeDisparity(leftImage_, rightImage_);
|
||||||
|
|
||||||
|
for(StereoDense::Type strategy : strategies)
|
||||||
|
{
|
||||||
|
std::unique_ptr<StereoDense> stereo(StereoDense::create(strategy));
|
||||||
|
EXPECT_NE(stereo.get(), nullptr) << "Strategy: " << getStrategyName(strategy);
|
||||||
|
|
||||||
|
cv::Mat disparity = stereo->computeDisparity(leftImage_, rightImage_);
|
||||||
|
|
||||||
|
EXPECT_FALSE(disparity.empty()) << "Strategy: " << getStrategyName(strategy);
|
||||||
|
EXPECT_EQ(disparity.rows, leftImage_.rows) << "Strategy: " << getStrategyName(strategy);
|
||||||
|
EXPECT_EQ(disparity.cols, leftImage_.cols) << "Strategy: " << getStrategyName(strategy);
|
||||||
|
EXPECT_EQ(disparity.type(), CV_16SC1) << "Strategy: " << getStrategyName(strategy);
|
||||||
|
|
||||||
|
// Check that disparity is around shiftPixels_ (5 pixels) across the image
|
||||||
|
// The entire left image was shifted left by shiftPixels_ to create the right image
|
||||||
|
// In fixed-point format (4 fractional bits): shiftPixels_ * 16
|
||||||
|
// Exclude the right edge where there's no correspondence (disparity = 0 or negative)
|
||||||
|
cv::Rect validRegion(0, 0, disparity.cols - shiftPixels_, disparity.rows);
|
||||||
|
cv::Mat validDisparity = disparity(validRegion);
|
||||||
|
|
||||||
|
// Count valid disparities (positive values) in the valid region
|
||||||
|
cv::Mat validMask = validDisparity > 0;
|
||||||
|
int validCount = cv::countNonZero(validMask);
|
||||||
|
|
||||||
|
// Should have many valid disparities in the valid region
|
||||||
|
EXPECT_GT(validCount, 0) << "Strategy: " << getStrategyName(strategy);
|
||||||
|
|
||||||
|
// Check that the mean disparity is approximately shiftPixels_ (5 pixels)
|
||||||
|
// In fixed-point format: shiftPixels_ * 16 = 80
|
||||||
|
// Allow some tolerance for stereo matching imperfections
|
||||||
|
cv::Scalar meanDisparity = cv::mean(validDisparity, validMask);
|
||||||
|
double expectedDisparity = shiftPixels_ * 16.0; // shiftPixels_ in fixed-point format
|
||||||
|
EXPECT_NEAR(meanDisparity[0], expectedDisparity, 20.0) << "Strategy: " << getStrategyName(strategy); // Allow ±20 units tolerance (±1.25 pixels)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(StereoDenseTest, ComputeDisparityColor)
|
||||||
|
{
|
||||||
|
// Test with all stereo strategies
|
||||||
|
StereoDense::Type strategies[] = {
|
||||||
|
StereoDense::kTypeBM,
|
||||||
|
StereoDense::kTypeSGBM
|
||||||
|
};
|
||||||
|
|
||||||
|
for(StereoDense::Type strategy : strategies)
|
||||||
|
{
|
||||||
|
std::unique_ptr<StereoDense> stereo(StereoDense::create(strategy));
|
||||||
|
EXPECT_NE(stereo.get(), nullptr) << "Strategy: " << getStrategyName(strategy);
|
||||||
|
|
||||||
|
cv::Mat disparity = stereo->computeDisparity(leftImageColor_, rightImageColor_);
|
||||||
|
|
||||||
|
EXPECT_FALSE(disparity.empty()) << "Strategy: " << getStrategyName(strategy);
|
||||||
|
EXPECT_EQ(disparity.rows, leftImageColor_.rows) << "Strategy: " << getStrategyName(strategy);
|
||||||
|
EXPECT_EQ(disparity.cols, leftImageColor_.cols) << "Strategy: " << getStrategyName(strategy);
|
||||||
|
EXPECT_EQ(disparity.type(), CV_16SC1) << "Strategy: " << getStrategyName(strategy);
|
||||||
|
|
||||||
|
// Check that disparity is around shiftPixels_ (5 pixels) across the image
|
||||||
|
// The entire left image was shifted left by shiftPixels_ to create the right image
|
||||||
|
// In fixed-point format (4 fractional bits): shiftPixels_ * 16
|
||||||
|
// Exclude the right edge where there's no correspondence (disparity = 0 or negative)
|
||||||
|
cv::Rect validRegion(0, 0, disparity.cols - shiftPixels_, disparity.rows);
|
||||||
|
cv::Mat validDisparity = disparity(validRegion);
|
||||||
|
|
||||||
|
// Count valid disparities (positive values) in the valid region
|
||||||
|
cv::Mat validMask = validDisparity > 0;
|
||||||
|
int validCount = cv::countNonZero(validMask);
|
||||||
|
|
||||||
|
// Should have many valid disparities in the valid region
|
||||||
|
EXPECT_GT(validCount, 0) << "Strategy: " << getStrategyName(strategy);
|
||||||
|
|
||||||
|
// Check that the mean disparity is approximately shiftPixels_ (5 pixels)
|
||||||
|
// In fixed-point format: shiftPixels_ * 16 = 80
|
||||||
|
// Allow some tolerance for stereo matching imperfections
|
||||||
|
cv::Scalar meanDisparity = cv::mean(validDisparity, validMask);
|
||||||
|
double expectedDisparity = shiftPixels_ * 16.0; // shiftPixels_ in fixed-point format
|
||||||
|
EXPECT_NEAR(meanDisparity[0], expectedDisparity, 20.0) << "Strategy: " << getStrategyName(strategy); // Allow ±20 units tolerance (±1.25 pixels)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(StereoDenseTest, ComputeDisparityDifferentSizes)
|
||||||
|
{
|
||||||
|
// Test with all stereo strategies
|
||||||
|
StereoDense::Type strategies[] = {
|
||||||
|
StereoDense::kTypeBM,
|
||||||
|
StereoDense::kTypeSGBM
|
||||||
|
};
|
||||||
|
|
||||||
|
for(StereoDense::Type strategy : strategies)
|
||||||
|
{
|
||||||
|
std::unique_ptr<StereoDense> stereo(StereoDense::create(strategy));
|
||||||
|
EXPECT_NE(stereo.get(), nullptr) << "Strategy: " << getStrategyName(strategy);
|
||||||
|
|
||||||
|
cv::Mat smallLeft = cv::Mat::zeros(75, 75, CV_8UC1);
|
||||||
|
cv::Mat smallRight = cv::Mat::zeros(50, 50, CV_8UC1);
|
||||||
|
|
||||||
|
// Should throw
|
||||||
|
EXPECT_THROW(stereo->computeDisparity(smallLeft, smallRight), UException) << "Strategy: " << getStrategyName(strategy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(StereoDenseTest, ComputeDisparityEmptyImages)
|
||||||
|
{
|
||||||
|
// Test with all stereo strategies
|
||||||
|
StereoDense::Type strategies[] = {
|
||||||
|
StereoDense::kTypeBM,
|
||||||
|
StereoDense::kTypeSGBM
|
||||||
|
};
|
||||||
|
|
||||||
|
for(StereoDense::Type strategy : strategies)
|
||||||
|
{
|
||||||
|
std::unique_ptr<StereoDense> stereo(StereoDense::create(strategy));
|
||||||
|
EXPECT_NE(stereo.get(), nullptr) << "Strategy: " << getStrategyName(strategy);
|
||||||
|
|
||||||
|
cv::Mat emptyLeft, emptyRight;
|
||||||
|
|
||||||
|
// Should throw
|
||||||
|
EXPECT_THROW(stereo->computeDisparity(emptyLeft, emptyRight), UException) << "Strategy: " << getStrategyName(strategy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user