vendor: OpenCV 5.0.0 snapshot at 40738fb16ceddb5fb3fea747585f7ce6abb0605b
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
set(the_description "Depth from Stereo")
|
||||
|
||||
set(debug_modules "")
|
||||
if(DEBUG_opencv_stereo)
|
||||
list(APPEND debug_modules opencv_highgui)
|
||||
endif()
|
||||
ocv_define_module(stereo opencv_imgproc opencv_geometry ${debug_modules}
|
||||
WRAP java objc python js
|
||||
)
|
||||
@@ -0,0 +1,489 @@
|
||||
// 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_HPP
|
||||
#define OPENCV_STEREO_HPP
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
|
||||
/**
|
||||
@defgroup stereo Stereo Correspondence
|
||||
|
||||
*/
|
||||
|
||||
namespace cv {
|
||||
|
||||
enum
|
||||
{
|
||||
STEREO_ZERO_DISPARITY=0x00400
|
||||
};
|
||||
|
||||
//! @addtogroup stereo
|
||||
//! @{
|
||||
|
||||
/** @brief Computes rectification transforms for each head of a calibrated stereo camera.
|
||||
|
||||
@param cameraMatrix1 First camera intrinsic matrix.
|
||||
@param distCoeffs1 First camera distortion parameters.
|
||||
@param cameraMatrix2 Second camera intrinsic matrix.
|
||||
@param distCoeffs2 Second camera distortion parameters.
|
||||
@param imageSize Size of the image used for stereo calibration.
|
||||
@param R Rotation matrix from the coordinate system of the first camera to the second camera,
|
||||
see @ref stereoCalibrate.
|
||||
@param T Translation vector from the coordinate system of the first camera to the second camera,
|
||||
see @ref stereoCalibrate.
|
||||
@param R1 Output 3x3 rectification transform (rotation matrix) for the first camera. This matrix
|
||||
brings points given in the unrectified first camera's coordinate system to points in the rectified
|
||||
first camera's coordinate system. In more technical terms, it performs a change of basis from the
|
||||
unrectified first camera's coordinate system to the rectified first camera's coordinate system.
|
||||
@param R2 Output 3x3 rectification transform (rotation matrix) for the second camera. This matrix
|
||||
brings points given in the unrectified second camera's coordinate system to points in the rectified
|
||||
second camera's coordinate system. In more technical terms, it performs a change of basis from the
|
||||
unrectified second camera's coordinate system to the rectified second camera's coordinate system.
|
||||
@param P1 Output 3x4 projection matrix in the new (rectified) coordinate systems for the first
|
||||
camera, i.e. it projects points given in the rectified first camera coordinate system into the
|
||||
rectified first camera's image.
|
||||
@param P2 Output 3x4 projection matrix in the new (rectified) coordinate systems for the second
|
||||
camera, i.e. it projects points given in the rectified first camera coordinate system into the
|
||||
rectified second camera's image.
|
||||
@param Q Output \f$4 \times 4\f$ disparity-to-depth mapping matrix (see @ref reprojectImageTo3D).
|
||||
@param flags Operation flags that may be zero or @ref STEREO_ZERO_DISPARITY . If the flag is set,
|
||||
the function makes the principal points of each camera have the same pixel coordinates in the
|
||||
rectified views. And if the flag is not set, the function may still shift the images in the
|
||||
horizontal or vertical direction (depending on the orientation of epipolar lines) to maximize the
|
||||
useful image area.
|
||||
@param alpha Free scaling parameter. If it is -1 or absent, the function performs the default
|
||||
scaling. Otherwise, the parameter should be between 0 and 1. alpha=0 means that the rectified
|
||||
images are zoomed and shifted so that only valid pixels are visible (no black areas after
|
||||
rectification). alpha=1 means that the rectified image is decimated and shifted so that all the
|
||||
pixels from the original images from the cameras are retained in the rectified images (no source
|
||||
image pixels are lost). Any intermediate value yields an intermediate result between
|
||||
those two extreme cases.
|
||||
@param newImageSize New image resolution after rectification. The same size should be passed to
|
||||
#initUndistortRectifyMap (see the stereo_calib.cpp sample in OpenCV samples directory). When (0,0)
|
||||
is passed (default), it is set to the original imageSize . Setting it to a larger value can help you
|
||||
preserve details in the original image, especially when there is a big radial distortion.
|
||||
@param validPixROI1 Optional output rectangles inside the rectified images where all the pixels
|
||||
are valid. If alpha=0 , the ROIs cover the whole images. Otherwise, they are likely to be smaller
|
||||
(see the picture below).
|
||||
@param validPixROI2 Optional output rectangles inside the rectified images where all the pixels
|
||||
are valid. If alpha=0 , the ROIs cover the whole images. Otherwise, they are likely to be smaller
|
||||
(see the picture below).
|
||||
|
||||
The function computes the rotation matrices for each camera that (virtually) make both camera image
|
||||
planes the same plane. Consequently, this makes all the epipolar lines parallel and thus simplifies
|
||||
the dense stereo correspondence problem. The function takes the matrices computed by #stereoCalibrate
|
||||
as input. As output, it provides two rotation matrices and also two projection matrices in the new
|
||||
coordinates. The function distinguishes the following two cases:
|
||||
|
||||
- **Horizontal stereo**: the first and the second camera views are shifted relative to each other
|
||||
mainly along the x-axis (with possible small vertical shift). In the rectified images, the
|
||||
corresponding epipolar lines in the left and right cameras are horizontal and have the same
|
||||
y-coordinate. P1 and P2 look like:
|
||||
|
||||
\f[\texttt{P1} = \begin{bmatrix}
|
||||
f & 0 & cx_1 & 0 \\
|
||||
0 & f & cy & 0 \\
|
||||
0 & 0 & 1 & 0
|
||||
\end{bmatrix}\f]
|
||||
|
||||
\f[\texttt{P2} = \begin{bmatrix}
|
||||
f & 0 & cx_2 & T_x \cdot f \\
|
||||
0 & f & cy & 0 \\
|
||||
0 & 0 & 1 & 0
|
||||
\end{bmatrix} ,\f]
|
||||
|
||||
\f[\texttt{Q} = \begin{bmatrix}
|
||||
1 & 0 & 0 & -cx_1 \\
|
||||
0 & 1 & 0 & -cy \\
|
||||
0 & 0 & 0 & f \\
|
||||
0 & 0 & -\frac{1}{T_x} & \frac{cx_1 - cx_2}{T_x}
|
||||
\end{bmatrix} \f]
|
||||
|
||||
where \f$T_x\f$ is a horizontal shift between the cameras and \f$cx_1=cx_2\f$ if
|
||||
@ref STEREO_ZERO_DISPARITY is set.
|
||||
|
||||
- **Vertical stereo**: the first and the second camera views are shifted relative to each other
|
||||
mainly in the vertical direction (and probably a bit in the horizontal direction too). The epipolar
|
||||
lines in the rectified images are vertical and have the same x-coordinate. P1 and P2 look like:
|
||||
|
||||
\f[\texttt{P1} = \begin{bmatrix}
|
||||
f & 0 & cx & 0 \\
|
||||
0 & f & cy_1 & 0 \\
|
||||
0 & 0 & 1 & 0
|
||||
\end{bmatrix}\f]
|
||||
|
||||
\f[\texttt{P2} = \begin{bmatrix}
|
||||
f & 0 & cx & 0 \\
|
||||
0 & f & cy_2 & T_y \cdot f \\
|
||||
0 & 0 & 1 & 0
|
||||
\end{bmatrix},\f]
|
||||
|
||||
\f[\texttt{Q} = \begin{bmatrix}
|
||||
1 & 0 & 0 & -cx \\
|
||||
0 & 1 & 0 & -cy_1 \\
|
||||
0 & 0 & 0 & f \\
|
||||
0 & 0 & -\frac{1}{T_y} & \frac{cy_1 - cy_2}{T_y}
|
||||
\end{bmatrix} \f]
|
||||
|
||||
where \f$T_y\f$ is a vertical shift between the cameras and \f$cy_1=cy_2\f$ if
|
||||
@ref STEREO_ZERO_DISPARITY is set.
|
||||
|
||||
As you can see, the first three columns of P1 and P2 will effectively be the new "rectified" camera
|
||||
matrices. The matrices, together with R1 and R2 , can then be passed to #initUndistortRectifyMap to
|
||||
initialize the rectification map for each camera.
|
||||
|
||||
See below the screenshot from the stereo_calib.cpp sample. Some red horizontal lines pass through
|
||||
the corresponding image regions. This means that the images are well rectified, which is what most
|
||||
stereo correspondence algorithms rely on. The green rectangles are roi1 and roi2 . You see that
|
||||
their interiors are all valid pixels.
|
||||
|
||||

|
||||
*/
|
||||
CV_EXPORTS_W void stereoRectify( InputArray cameraMatrix1, InputArray distCoeffs1,
|
||||
InputArray cameraMatrix2, InputArray distCoeffs2,
|
||||
Size imageSize, InputArray R, InputArray T,
|
||||
OutputArray R1, OutputArray R2,
|
||||
OutputArray P1, OutputArray P2,
|
||||
OutputArray Q, int flags = STEREO_ZERO_DISPARITY,
|
||||
double alpha = -1, Size newImageSize = Size(),
|
||||
CV_OUT Rect* validPixROI1 = 0, CV_OUT Rect* validPixROI2 = 0 );
|
||||
|
||||
/** @brief Computes a rectification transform for an uncalibrated stereo camera.
|
||||
|
||||
@param points1 Array of feature points in the first image.
|
||||
@param points2 The corresponding points in the second image. The same formats as in
|
||||
#findFundamentalMat are supported.
|
||||
@param F Input fundamental matrix. It can be computed from the same set of point pairs using
|
||||
#findFundamentalMat .
|
||||
@param imgSize Size of the image.
|
||||
@param H1 Output rectification homography matrix for the first image.
|
||||
@param H2 Output rectification homography matrix for the second image.
|
||||
@param threshold Optional threshold used to filter out the outliers. If the parameter is greater
|
||||
than zero, all the point pairs that do not comply with the epipolar geometry (that is, the points
|
||||
for which \f$|\texttt{points2[i]}^T \cdot \texttt{F} \cdot \texttt{points1[i]}|>\texttt{threshold}\f$ )
|
||||
are rejected prior to computing the homographies. Otherwise, all the points are considered inliers.
|
||||
|
||||
The function computes the rectification transformations without knowing intrinsic parameters of the
|
||||
cameras and their relative position in the space, which explains the suffix "uncalibrated". Another
|
||||
related difference from #stereoRectify is that the function outputs not the rectification
|
||||
transformations in the object (3D) space, but the planar perspective transformations encoded by the
|
||||
homography matrices H1 and H2 . The function implements the algorithm @cite Hartley99 .
|
||||
|
||||
@note
|
||||
While the algorithm does not need to know the intrinsic parameters of the cameras, it heavily
|
||||
depends on the epipolar geometry. Therefore, if the camera lenses have a significant distortion,
|
||||
it would be better to correct it before computing the fundamental matrix and calling this
|
||||
function. For example, distortion coefficients can be estimated for each head of stereo camera
|
||||
separately by using #calibrateCamera . Then, the images can be corrected using #undistort , or
|
||||
just the point coordinates can be corrected with #undistortPoints .
|
||||
*/
|
||||
CV_EXPORTS_W bool stereoRectifyUncalibrated( InputArray points1, InputArray points2,
|
||||
InputArray F, Size imgSize,
|
||||
OutputArray H1, OutputArray H2,
|
||||
double threshold = 5 );
|
||||
|
||||
|
||||
CV_EXPORTS float rectify3Collinear( InputArray _cameraMatrix1, InputArray _distCoeffs1,
|
||||
InputArray _cameraMatrix2, InputArray _distCoeffs2,
|
||||
InputArray _cameraMatrix3, InputArray _distCoeffs3,
|
||||
InputArrayOfArrays _imgpt1,
|
||||
InputArrayOfArrays _imgpt3,
|
||||
Size imageSize, InputArray _Rmat12, InputArray _Tmat12,
|
||||
InputArray _Rmat13, InputArray _Tmat13,
|
||||
OutputArray _Rmat1, OutputArray _Rmat2, OutputArray _Rmat3,
|
||||
OutputArray _Pmat1, OutputArray _Pmat2, OutputArray _Pmat3,
|
||||
OutputArray _Qmat,
|
||||
double alpha, Size newImgSize,
|
||||
Rect* roi1, Rect* roi2, int flags );
|
||||
|
||||
namespace fisheye {
|
||||
|
||||
/** @brief Stereo rectification for fisheye camera model
|
||||
|
||||
@param K1 First camera intrinsic matrix.
|
||||
@param D1 First camera distortion parameters.
|
||||
@param K2 Second camera intrinsic matrix.
|
||||
@param D2 Second camera distortion parameters.
|
||||
@param imageSize Size of the image used for stereo calibration.
|
||||
@param R Rotation matrix between the coordinate systems of the first and the second
|
||||
cameras.
|
||||
@param tvec Translation vector between coordinate systems of the cameras.
|
||||
@param R1 Output 3x3 rectification transform (rotation matrix) for the first camera.
|
||||
@param R2 Output 3x3 rectification transform (rotation matrix) for the second camera.
|
||||
@param P1 Output 3x4 projection matrix in the new (rectified) coordinate systems for the first
|
||||
camera.
|
||||
@param P2 Output 3x4 projection matrix in the new (rectified) coordinate systems for the second
|
||||
camera.
|
||||
@param Q Output \f$4 \times 4\f$ disparity-to-depth mapping matrix (see reprojectImageTo3D ).
|
||||
@param flags Operation flags that may be zero or @ref cv::CALIB_ZERO_DISPARITY . If the flag is set,
|
||||
the function makes the principal points of each camera have the same pixel coordinates in the
|
||||
rectified views. And if the flag is not set, the function may still shift the images in the
|
||||
horizontal or vertical direction (depending on the orientation of epipolar lines) to maximize the
|
||||
useful image area.
|
||||
@param newImageSize New image resolution after rectification. The same size should be passed to
|
||||
#initUndistortRectifyMap (see the stereo_calib.cpp sample in OpenCV samples directory). When (0,0)
|
||||
is passed (default), it is set to the original imageSize . Setting it to larger value can help you
|
||||
preserve details in the original image, especially when there is a big radial distortion.
|
||||
@param balance Sets the new focal length in range between the min focal length and the max focal
|
||||
length. Balance is in range of [0, 1].
|
||||
@param fov_scale Divisor for new focal length.
|
||||
*/
|
||||
CV_EXPORTS_W void stereoRectify(InputArray K1, InputArray D1, InputArray K2, InputArray D2, const Size &imageSize, InputArray R, InputArray tvec,
|
||||
OutputArray R1, OutputArray R2, OutputArray P1, OutputArray P2, OutputArray Q, int flags, const Size &newImageSize = Size(),
|
||||
double balance = 0.0, double fov_scale = 1.0);
|
||||
|
||||
} // namespace fisheye
|
||||
|
||||
/** @brief The base class for stereo correspondence algorithms.
|
||||
*/
|
||||
class CV_EXPORTS_W StereoMatcher : public Algorithm
|
||||
{
|
||||
public:
|
||||
enum { DISP_SHIFT = 4,
|
||||
DISP_SCALE = (1 << DISP_SHIFT)
|
||||
};
|
||||
|
||||
/** @brief Computes disparity map for the specified stereo pair
|
||||
|
||||
@param left Left 8-bit single-channel image.
|
||||
@param right Right image of the same size and the same type as the left one.
|
||||
@param disparity Output disparity map. It has the same size as the input images. Some algorithms,
|
||||
like StereoBM or StereoSGBM compute 16-bit fixed-point disparity map (where each disparity value
|
||||
has 4 fractional bits), whereas other algorithms output 32-bit floating-point disparity map.
|
||||
*/
|
||||
CV_WRAP virtual void compute( InputArray left, InputArray right,
|
||||
OutputArray disparity ) = 0;
|
||||
|
||||
CV_WRAP virtual int getMinDisparity() const = 0;
|
||||
CV_WRAP virtual void setMinDisparity(int minDisparity) = 0;
|
||||
|
||||
CV_WRAP virtual int getNumDisparities() const = 0;
|
||||
CV_WRAP virtual void setNumDisparities(int numDisparities) = 0;
|
||||
|
||||
CV_WRAP virtual int getBlockSize() const = 0;
|
||||
CV_WRAP virtual void setBlockSize(int blockSize) = 0;
|
||||
|
||||
CV_WRAP virtual int getSpeckleWindowSize() const = 0;
|
||||
CV_WRAP virtual void setSpeckleWindowSize(int speckleWindowSize) = 0;
|
||||
|
||||
CV_WRAP virtual int getSpeckleRange() const = 0;
|
||||
CV_WRAP virtual void setSpeckleRange(int speckleRange) = 0;
|
||||
|
||||
CV_WRAP virtual int getDisp12MaxDiff() const = 0;
|
||||
CV_WRAP virtual void setDisp12MaxDiff(int disp12MaxDiff) = 0;
|
||||
};
|
||||
|
||||
|
||||
/** @brief Class for computing stereo correspondence using the block matching algorithm, introduced and
|
||||
contributed to OpenCV by K. Konolige.
|
||||
*/
|
||||
class CV_EXPORTS_W StereoBM : public StereoMatcher
|
||||
{
|
||||
public:
|
||||
enum { PREFILTER_NORMALIZED_RESPONSE = 0,
|
||||
PREFILTER_XSOBEL = 1
|
||||
};
|
||||
|
||||
CV_WRAP virtual int getPreFilterType() const = 0;
|
||||
CV_WRAP virtual void setPreFilterType(int preFilterType) = 0;
|
||||
|
||||
CV_WRAP virtual int getPreFilterSize() const = 0;
|
||||
CV_WRAP virtual void setPreFilterSize(int preFilterSize) = 0;
|
||||
|
||||
CV_WRAP virtual int getPreFilterCap() const = 0;
|
||||
CV_WRAP virtual void setPreFilterCap(int preFilterCap) = 0;
|
||||
|
||||
CV_WRAP virtual int getTextureThreshold() const = 0;
|
||||
CV_WRAP virtual void setTextureThreshold(int textureThreshold) = 0;
|
||||
|
||||
CV_WRAP virtual int getUniquenessRatio() const = 0;
|
||||
CV_WRAP virtual void setUniquenessRatio(int uniquenessRatio) = 0;
|
||||
|
||||
CV_WRAP virtual int getSmallerBlockSize() const = 0;
|
||||
CV_WRAP virtual void setSmallerBlockSize(int blockSize) = 0;
|
||||
|
||||
CV_WRAP virtual Rect getROI1() const = 0;
|
||||
CV_WRAP virtual void setROI1(Rect roi1) = 0;
|
||||
|
||||
CV_WRAP virtual Rect getROI2() const = 0;
|
||||
CV_WRAP virtual void setROI2(Rect roi2) = 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_WRAP static Ptr<StereoBM> create(int numDisparities = 0, int blockSize = 21);
|
||||
};
|
||||
|
||||
/** @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/python/stereo_match.py
|
||||
*/
|
||||
class CV_EXPORTS_W StereoSGBM : public StereoMatcher
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
MODE_SGBM = 0,
|
||||
MODE_HH = 1,
|
||||
MODE_SGBM_3WAY = 2,
|
||||
MODE_HH4 = 3
|
||||
};
|
||||
|
||||
CV_WRAP virtual int getPreFilterCap() const = 0;
|
||||
CV_WRAP virtual void setPreFilterCap(int preFilterCap) = 0;
|
||||
|
||||
CV_WRAP virtual int getUniquenessRatio() const = 0;
|
||||
CV_WRAP virtual void setUniquenessRatio(int uniquenessRatio) = 0;
|
||||
|
||||
CV_WRAP virtual int getP1() const = 0;
|
||||
CV_WRAP virtual void setP1(int P1) = 0;
|
||||
|
||||
CV_WRAP virtual int getP2() const = 0;
|
||||
CV_WRAP virtual void setP2(int P2) = 0;
|
||||
|
||||
CV_WRAP virtual int getMode() const = 0;
|
||||
CV_WRAP virtual void setMode(int mode) = 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. See below.
|
||||
@param P2 The second parameter controlling the disparity smoothness. The larger the values are,
|
||||
the smoother the disparity is. P1 is the penalty on the disparity change by plus or minus 1
|
||||
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\*blockSize\*blockSize and
|
||||
32\*number_of_image_channels\*blockSize\*blockSize , 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_WRAP static Ptr<StereoSGBM> create(int minDisparity = 0, int numDisparities = 16, int blockSize = 3,
|
||||
int P1 = 0, int P2 = 0, int disp12MaxDiff = 0,
|
||||
int preFilterCap = 0, int uniquenessRatio = 0,
|
||||
int speckleWindowSize = 0, int speckleRange = 0,
|
||||
int mode = StereoSGBM::MODE_SGBM);
|
||||
};
|
||||
|
||||
/** @brief Filters off small noise blobs (speckles) in the disparity map
|
||||
|
||||
@param img The input 16-bit signed disparity image
|
||||
@param newVal The disparity value used to paint-off the speckles
|
||||
@param maxSpeckleSize The maximum speckle size to consider it a speckle. Larger blobs are not
|
||||
affected by the algorithm
|
||||
@param maxDiff Maximum difference between neighbor disparity pixels to put them into the same
|
||||
blob. Note that since StereoBM, StereoSGBM and may be other algorithms return a fixed-point
|
||||
disparity map, where disparity values are multiplied by 16, this scale factor should be taken into
|
||||
account when specifying this parameter value.
|
||||
@param buf The optional temporary buffer to avoid memory allocation within the function.
|
||||
*/
|
||||
CV_EXPORTS_W void filterSpeckles( InputOutputArray img, double newVal,
|
||||
int maxSpeckleSize, double maxDiff,
|
||||
InputOutputArray buf = noArray() );
|
||||
|
||||
//! computes valid disparity ROI from the valid ROIs of the rectified images (that are returned by #stereoRectify)
|
||||
CV_EXPORTS_W Rect getValidDisparityROI( Rect roi1, Rect roi2,
|
||||
int minDisparity, int numberOfDisparities,
|
||||
int blockSize );
|
||||
|
||||
//! validates disparity using the left-right check. The matrix "cost" should be computed by the stereo correspondence algorithm
|
||||
CV_EXPORTS_W void validateDisparity( InputOutputArray disparity, InputArray cost,
|
||||
int minDisparity, int numberOfDisparities,
|
||||
int disp12MaxDisp = 1 );
|
||||
|
||||
/** @brief Reprojects a disparity image to 3D space.
|
||||
|
||||
@param disparity Input single-channel 8-bit unsigned, 16-bit signed, 32-bit signed or 32-bit
|
||||
floating-point disparity image. The values of 8-bit / 16-bit signed formats are assumed to have no
|
||||
fractional bits. If the disparity is 16-bit signed format, as computed by @ref StereoBM or
|
||||
@ref StereoSGBM and maybe other algorithms, it should be divided by 16 (and scaled to float) before
|
||||
being used here.
|
||||
@param _3dImage Output 3-channel floating-point image of the same size as disparity. Each element of
|
||||
_3dImage(x,y) contains 3D coordinates of the point (x,y) computed from the disparity map. If one
|
||||
uses Q obtained by @ref stereoRectify, then the returned points are represented in the first
|
||||
camera's rectified coordinate system.
|
||||
@param Q \f$4 \times 4\f$ perspective transformation matrix that can be obtained with
|
||||
@ref stereoRectify.
|
||||
@param handleMissingValues Indicates, whether the function should handle missing values (i.e.
|
||||
points where the disparity was not computed). If handleMissingValues=true, then pixels with the
|
||||
minimal disparity that corresponds to the outliers (see StereoMatcher::compute ) are transformed
|
||||
to 3D points with a very large Z value (currently set to 10000).
|
||||
@param ddepth The optional output array depth. If it is -1, the output image will have CV_32F
|
||||
depth. ddepth can also be set to CV_16S, CV_32S or CV_32F.
|
||||
|
||||
The function transforms a single-channel disparity map to a 3-channel image representing a 3D
|
||||
surface. That is, for each pixel (x,y) and the corresponding disparity d=disparity(x,y) , it
|
||||
computes:
|
||||
|
||||
\f[\begin{bmatrix}
|
||||
X \\
|
||||
Y \\
|
||||
Z \\
|
||||
W
|
||||
\end{bmatrix} = Q \begin{bmatrix}
|
||||
x \\
|
||||
y \\
|
||||
\texttt{disparity} (x,y) \\
|
||||
1
|
||||
\end{bmatrix}.\f]
|
||||
|
||||
@sa
|
||||
To reproject a sparse set of points {(x,y,d),...} to 3D space, use perspectiveTransform.
|
||||
*/
|
||||
CV_EXPORTS_W void reprojectImageTo3D( InputArray disparity,
|
||||
OutputArray _3dImage, InputArray Q,
|
||||
bool handleMissingValues = false,
|
||||
int ddepth = -1 );
|
||||
|
||||
} // namespace cv
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"namespaces_dict": {
|
||||
"cv.fisheye": "fisheye"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.opencv.test.stereo;
|
||||
|
||||
import org.opencv.test.OpenCVTestCase;
|
||||
|
||||
public class StereoBMTest extends OpenCVTestCase {
|
||||
|
||||
public void testComputeMatMatMat() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testComputeMatMatMatInt() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testStereoBM() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testStereoBMInt() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testStereoBMIntInt() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testStereoBMIntIntInt() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
package org.opencv.test.stereoBase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.opencv.stereo.Stereo;
|
||||
import org.opencv.core.Core;
|
||||
import org.opencv.core.CvType;
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.core.MatOfDouble;
|
||||
import org.opencv.core.MatOfPoint2f;
|
||||
import org.opencv.core.MatOfPoint3f;
|
||||
import org.opencv.core.Point;
|
||||
import org.opencv.core.Scalar;
|
||||
import org.opencv.core.Size;
|
||||
import org.opencv.test.OpenCVTestCase;
|
||||
import org.opencv.imgproc.Imgproc;
|
||||
|
||||
public class StereoBaseTest extends OpenCVTestCase {
|
||||
|
||||
Size size;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
size = new Size(3, 3);
|
||||
}
|
||||
|
||||
public void testFilterSpecklesMatDoubleIntDouble() {
|
||||
gray_16s_1024.copyTo(dst);
|
||||
Point center = new Point(gray_16s_1024.rows() / 2., gray_16s_1024.cols() / 2.);
|
||||
Imgproc.circle(dst, center, 1, Scalar.all(4096));
|
||||
|
||||
assertMatNotEqual(gray_16s_1024, dst);
|
||||
Stereo.filterSpeckles(dst, 1024.0, 100, 0.);
|
||||
assertMatEqual(gray_16s_1024, dst);
|
||||
}
|
||||
|
||||
public void testFilterSpecklesMatDoubleIntDoubleMat() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testGetValidDisparityROI() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testRectify3Collinear() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testReprojectImageTo3DMatMatMat() {
|
||||
Mat transformMatrix = new Mat(4, 4, CvType.CV_64F);
|
||||
transformMatrix.put(0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
|
||||
Mat disparity = new Mat(matSize, matSize, CvType.CV_32F);
|
||||
|
||||
float[] disp = new float[matSize * matSize];
|
||||
for (int i = 0; i < matSize; i++)
|
||||
for (int j = 0; j < matSize; j++)
|
||||
disp[i * matSize + j] = i - j;
|
||||
disparity.put(0, 0, disp);
|
||||
|
||||
Mat _3dPoints = new Mat();
|
||||
|
||||
Stereo.reprojectImageTo3D(disparity, _3dPoints, transformMatrix);
|
||||
|
||||
assertEquals(CvType.CV_32FC3, _3dPoints.type());
|
||||
assertEquals(matSize, _3dPoints.rows());
|
||||
assertEquals(matSize, _3dPoints.cols());
|
||||
|
||||
truth = new Mat(matSize, matSize, CvType.CV_32FC3);
|
||||
|
||||
float[] _truth = new float[matSize * matSize * 3];
|
||||
for (int i = 0; i < matSize; i++)
|
||||
for (int j = 0; j < matSize; j++) {
|
||||
_truth[(i * matSize + j) * 3 + 0] = i;
|
||||
_truth[(i * matSize + j) * 3 + 1] = j;
|
||||
_truth[(i * matSize + j) * 3 + 2] = i - j;
|
||||
}
|
||||
truth.put(0, 0, _truth);
|
||||
|
||||
assertMatEqual(truth, _3dPoints, EPS);
|
||||
}
|
||||
|
||||
public void testReprojectImageTo3DMatMatMatBoolean() {
|
||||
Mat transformMatrix = new Mat(4, 4, CvType.CV_64F);
|
||||
transformMatrix.put(0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
|
||||
Mat disparity = new Mat(matSize, matSize, CvType.CV_32F);
|
||||
|
||||
float[] disp = new float[matSize * matSize];
|
||||
for (int i = 0; i < matSize; i++)
|
||||
for (int j = 0; j < matSize; j++)
|
||||
disp[i * matSize + j] = i - j;
|
||||
disp[0] = -Float.MAX_VALUE;
|
||||
disparity.put(0, 0, disp);
|
||||
|
||||
Mat _3dPoints = new Mat();
|
||||
|
||||
Stereo.reprojectImageTo3D(disparity, _3dPoints, transformMatrix, true);
|
||||
|
||||
assertEquals(CvType.CV_32FC3, _3dPoints.type());
|
||||
assertEquals(matSize, _3dPoints.rows());
|
||||
assertEquals(matSize, _3dPoints.cols());
|
||||
|
||||
truth = new Mat(matSize, matSize, CvType.CV_32FC3);
|
||||
|
||||
float[] _truth = new float[matSize * matSize * 3];
|
||||
for (int i = 0; i < matSize; i++)
|
||||
for (int j = 0; j < matSize; j++) {
|
||||
_truth[(i * matSize + j) * 3 + 0] = i;
|
||||
_truth[(i * matSize + j) * 3 + 1] = j;
|
||||
_truth[(i * matSize + j) * 3 + 2] = i - j;
|
||||
}
|
||||
_truth[2] = 10000;
|
||||
truth.put(0, 0, _truth);
|
||||
|
||||
assertMatEqual(truth, _3dPoints, EPS);
|
||||
}
|
||||
|
||||
public void testReprojectImageTo3DMatMatMatBooleanInt() {
|
||||
Mat transformMatrix = new Mat(4, 4, CvType.CV_64F);
|
||||
transformMatrix.put(0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||
|
||||
Mat disparity = new Mat(matSize, matSize, CvType.CV_32F);
|
||||
|
||||
float[] disp = new float[matSize * matSize];
|
||||
for (int i = 0; i < matSize; i++)
|
||||
for (int j = 0; j < matSize; j++)
|
||||
disp[i * matSize + j] = i - j;
|
||||
disparity.put(0, 0, disp);
|
||||
|
||||
Mat _3dPoints = new Mat();
|
||||
|
||||
Stereo.reprojectImageTo3D(disparity, _3dPoints, transformMatrix, false, CvType.CV_16S);
|
||||
|
||||
assertEquals(CvType.CV_16SC3, _3dPoints.type());
|
||||
assertEquals(matSize, _3dPoints.rows());
|
||||
assertEquals(matSize, _3dPoints.cols());
|
||||
|
||||
truth = new Mat(matSize, matSize, CvType.CV_16SC3);
|
||||
|
||||
short[] _truth = new short[matSize * matSize * 3];
|
||||
for (short i = 0; i < matSize; i++)
|
||||
for (short j = 0; j < matSize; j++) {
|
||||
_truth[(i * matSize + j) * 3 + 0] = i;
|
||||
_truth[(i * matSize + j) * 3 + 1] = j;
|
||||
_truth[(i * matSize + j) * 3 + 2] = (short) (i - j);
|
||||
}
|
||||
truth.put(0, 0, _truth);
|
||||
|
||||
assertMatEqual(truth, _3dPoints, EPS);
|
||||
}
|
||||
|
||||
public void testStereoCalibrateListOfMatListOfMatListOfMatMatMatMatMatSizeMatMatMatMat() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testStereoCalibrateListOfMatListOfMatListOfMatMatMatMatMatSizeMatMatMatMatTermCriteria() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testStereoCalibrateListOfMatListOfMatListOfMatMatMatMatMatSizeMatMatMatMatTermCriteriaInt() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testStereoRectifyUncalibratedMatMatMatSizeMatMat() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testStereoRectifyUncalibratedMatMatMatSizeMatMatDouble() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testValidateDisparityMatMatIntInt() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testValidateDisparityMatMatIntIntInt() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package org.opencv.test.stereo;
|
||||
|
||||
import org.opencv.test.OpenCVTestCase;
|
||||
|
||||
public class StereoSGBMTest extends OpenCVTestCase {
|
||||
|
||||
public void testCompute() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testGet_disp12MaxDiff() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testGet_fullDP() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testGet_minDisparity() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testGet_numberOfDisparities() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testGet_P1() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testGet_P2() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testGet_preFilterCap() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testGet_SADWindowSize() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testGet_speckleRange() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testGet_speckleWindowSize() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testGet_uniquenessRatio() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testSet_disp12MaxDiff() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testSet_fullDP() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testSet_minDisparity() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testSet_numberOfDisparities() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testSet_P1() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testSet_P2() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testSet_preFilterCap() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testSet_SADWindowSize() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testSet_speckleRange() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testSet_speckleWindowSize() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testSet_uniquenessRatio() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testStereoSGBM() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testStereoSGBMIntIntInt() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testStereoSGBMIntIntIntInt() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testStereoSGBMIntIntIntIntInt() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testStereoSGBMIntIntIntIntIntInt() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testStereoSGBMIntIntIntIntIntIntInt() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testStereoSGBMIntIntIntIntIntIntIntInt() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testStereoSGBMIntIntIntIntIntIntIntIntInt() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testStereoSGBMIntIntIntIntIntIntIntIntIntInt() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testStereoSGBMIntIntIntIntIntIntIntIntIntIntBoolean() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*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) 2010-2012, Multicoreware, Inc., all rights reserved.
|
||||
// Copyright (C) 2010-2012, Advanced Micro Devices, 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"
|
||||
#include "opencv2/ts/ocl_perf.hpp"
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
|
||||
namespace opencv_test {
|
||||
namespace ocl {
|
||||
|
||||
typedef tuple<int, int> StereoBMFixture_t;
|
||||
typedef TestBaseWithParam<StereoBMFixture_t> StereoBMFixture;
|
||||
|
||||
OCL_PERF_TEST_P(StereoBMFixture, StereoBM, ::testing::Combine(OCL_PERF_ENUM(32, 64, 128), OCL_PERF_ENUM(11,21) ) )
|
||||
{
|
||||
const int n_disp = get<0>(GetParam()), winSize = get<1>(GetParam());
|
||||
UMat left, right, disp;
|
||||
|
||||
imread(getDataPath("gpu/stereobm/aloe-L.png"), IMREAD_GRAYSCALE).copyTo(left);
|
||||
imread(getDataPath("gpu/stereobm/aloe-R.png"), IMREAD_GRAYSCALE).copyTo(right);
|
||||
ASSERT_FALSE(left.empty());
|
||||
ASSERT_FALSE(right.empty());
|
||||
|
||||
declare.in(left, right);
|
||||
|
||||
Ptr<StereoBM> bm = StereoBM::create( n_disp, winSize );
|
||||
bm->setPreFilterType(bm->PREFILTER_XSOBEL);
|
||||
bm->setTextureThreshold(0);
|
||||
|
||||
OCL_TEST_CYCLE() bm->compute(left, right, disp);
|
||||
|
||||
SANITY_CHECK(disp, 1e-3, ERROR_RELATIVE);
|
||||
}
|
||||
|
||||
}//ocl
|
||||
}//cvtest
|
||||
#endif
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "perf_precomp.hpp"
|
||||
|
||||
#if defined(HAVE_HPX)
|
||||
#include <hpx/hpx_main.hpp>
|
||||
#endif
|
||||
|
||||
CV_PERF_TEST_MAIN(calib3d)
|
||||
@@ -0,0 +1,10 @@
|
||||
// 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/stereo.hpp"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* 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)
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "perf_precomp.hpp"
|
||||
|
||||
namespace opencv_test
|
||||
{
|
||||
using namespace perf;
|
||||
using namespace testing;
|
||||
|
||||
static void MakeArtificialExample(Mat& dst_left_view, Mat& dst_view);
|
||||
|
||||
CV_ENUM(SGBMModes, StereoSGBM::MODE_SGBM, StereoSGBM::MODE_SGBM_3WAY, StereoSGBM::MODE_HH4)
|
||||
typedef tuple<Size, int, SGBMModes> SGBMParams;
|
||||
typedef TestBaseWithParam<SGBMParams> TestStereoCorrespSGBM;
|
||||
|
||||
#ifndef _DEBUG
|
||||
PERF_TEST_P( TestStereoCorrespSGBM, SGBM, Combine(Values(Size(1280,720),Size(640,480)), Values(256,128), SGBMModes::all()) )
|
||||
#else
|
||||
PERF_TEST_P( TestStereoCorrespSGBM, DISABLED_TooLongInDebug_SGBM, Combine(Values(Size(1280,720),Size(640,480)), Values(256,128), SGBMModes::all()) )
|
||||
#endif
|
||||
{
|
||||
SGBMParams params = GetParam();
|
||||
|
||||
Size sz = get<0>(params);
|
||||
int num_disparities = get<1>(params);
|
||||
int mode = get<2>(params);
|
||||
|
||||
Mat src_left(sz, CV_8UC3);
|
||||
Mat src_right(sz, CV_8UC3);
|
||||
Mat dst(sz, CV_16S);
|
||||
|
||||
MakeArtificialExample(src_left,src_right);
|
||||
|
||||
int wsize = 3;
|
||||
int P1 = 8*src_left.channels()*wsize*wsize;
|
||||
TEST_CYCLE()
|
||||
{
|
||||
Ptr<StereoSGBM> sgbm = StereoSGBM::create(0,num_disparities,wsize,P1,4*P1,1,63,25,0,0,mode);
|
||||
sgbm->compute(src_left,src_right,dst);
|
||||
}
|
||||
|
||||
SANITY_CHECK(dst, .01, ERROR_RELATIVE);
|
||||
}
|
||||
|
||||
typedef tuple<Size, int> BMParams;
|
||||
typedef TestBaseWithParam<BMParams> TestStereoCorrespBM;
|
||||
|
||||
PERF_TEST_P(TestStereoCorrespBM, BM, Combine(Values(Size(1280, 720), Size(640, 480)), Values(256, 128)))
|
||||
{
|
||||
BMParams params = GetParam();
|
||||
Size sz = get<0>(params);
|
||||
int num_disparities = get<1>(params);
|
||||
|
||||
Mat src_left(sz, CV_8UC1);
|
||||
Mat src_right(sz, CV_8UC1);
|
||||
Mat dst(sz, CV_16S);
|
||||
|
||||
MakeArtificialExample(src_left, src_right);
|
||||
|
||||
int wsize = 21;
|
||||
TEST_CYCLE()
|
||||
{
|
||||
Ptr<StereoBM> bm = StereoBM::create(num_disparities, wsize);
|
||||
bm->compute(src_left, src_right, dst);
|
||||
}
|
||||
|
||||
SANITY_CHECK(dst, .01, ERROR_RELATIVE);
|
||||
}
|
||||
|
||||
void MakeArtificialExample(Mat& dst_left_view, Mat& dst_right_view)
|
||||
{
|
||||
RNG rng(0);
|
||||
int w = dst_left_view.cols;
|
||||
int h = dst_left_view.rows;
|
||||
|
||||
//params:
|
||||
unsigned char bg_level = (unsigned char)rng.uniform(0.0,255.0);
|
||||
unsigned char fg_level = (unsigned char)rng.uniform(0.0,255.0);
|
||||
int rect_width = (int)rng.uniform(w/16,w/2);
|
||||
int rect_height = (int)rng.uniform(h/16,h/2);
|
||||
int rect_disparity = (int)(0.15*w);
|
||||
double sigma = 3.0;
|
||||
|
||||
int rect_x_offset = (w-rect_width) /2;
|
||||
int rect_y_offset = (h-rect_height)/2;
|
||||
|
||||
if(dst_left_view.channels()==3)
|
||||
{
|
||||
dst_left_view = Scalar(Vec3b(bg_level,bg_level,bg_level));
|
||||
dst_right_view = Scalar(Vec3b(bg_level,bg_level,bg_level));
|
||||
}
|
||||
else
|
||||
{
|
||||
dst_left_view = Scalar(bg_level);
|
||||
dst_right_view = Scalar(bg_level);
|
||||
}
|
||||
|
||||
Mat dst_left_view_rect = Mat(dst_left_view, Rect(rect_x_offset,rect_y_offset,rect_width,rect_height));
|
||||
if(dst_left_view.channels()==3)
|
||||
dst_left_view_rect = Scalar(Vec3b(fg_level,fg_level,fg_level));
|
||||
else
|
||||
dst_left_view_rect = Scalar(fg_level);
|
||||
|
||||
rect_x_offset-=rect_disparity;
|
||||
|
||||
Mat dst_right_view_rect = Mat(dst_right_view, Rect(rect_x_offset,rect_y_offset,rect_width,rect_height));
|
||||
if(dst_right_view.channels()==3)
|
||||
dst_right_view_rect = Scalar(Vec3b(fg_level,fg_level,fg_level));
|
||||
else
|
||||
dst_right_view_rect = Scalar(fg_level);
|
||||
|
||||
//add some gaussian noise:
|
||||
unsigned char *l, *r;
|
||||
for(int i=0;i<h;i++)
|
||||
{
|
||||
l = dst_left_view.ptr(i);
|
||||
r = dst_right_view.ptr(i);
|
||||
|
||||
if(dst_left_view.channels()==3)
|
||||
{
|
||||
for(int j=0;j<w;j++)
|
||||
{
|
||||
l[0] = saturate_cast<unsigned char>(l[0] + rng.gaussian(sigma));
|
||||
l[1] = saturate_cast<unsigned char>(l[1] + rng.gaussian(sigma));
|
||||
l[2] = saturate_cast<unsigned char>(l[2] + rng.gaussian(sigma));
|
||||
l+=3;
|
||||
|
||||
r[0] = saturate_cast<unsigned char>(r[0] + rng.gaussian(sigma));
|
||||
r[1] = saturate_cast<unsigned char>(r[1] + rng.gaussian(sigma));
|
||||
r[2] = saturate_cast<unsigned char>(r[2] + rng.gaussian(sigma));
|
||||
r+=3;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int j=0;j<w;j++)
|
||||
{
|
||||
l[0] = saturate_cast<unsigned char>(l[0] + rng.gaussian(sigma));
|
||||
l++;
|
||||
|
||||
r[0] = saturate_cast<unsigned char>(r[0] + rng.gaussian(sigma));
|
||||
r++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*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) 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:
|
||||
//
|
||||
// * 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*/
|
||||
|
||||
//
|
||||
// Library initialization file
|
||||
//
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
IPP_INITIALIZER_AUTO
|
||||
|
||||
/* End of file. */
|
||||
@@ -0,0 +1,334 @@
|
||||
/*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) 2010-2012, Institute Of Software Chinese Academy Of Science, all rights reserved.
|
||||
// Copyright (C) 2010-2012, Advanced Micro Devices, 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*/
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////// stereoBM //////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define MAX_VAL 32767
|
||||
|
||||
#ifndef WSZ
|
||||
#define WSZ 2
|
||||
#endif
|
||||
|
||||
#define WSZ2 (WSZ / 2)
|
||||
|
||||
#ifdef DEFINE_KERNEL_STEREOBM
|
||||
|
||||
#define DISPARITY_SHIFT 4
|
||||
#define FILTERED ((MIN_DISP - 1) << DISPARITY_SHIFT)
|
||||
|
||||
void calcDisp(__local short * cost, __global short * disp, int uniquenessRatio,
|
||||
__local int * bestDisp, __local int * bestCost, int d, int x, int y, int cols, int rows)
|
||||
{
|
||||
int best_disp = *bestDisp, best_cost = *bestCost;
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
short c = cost[0];
|
||||
int thresh = best_cost + (best_cost * uniquenessRatio / 100);
|
||||
bool notUniq = ( (c <= thresh) && (d < (best_disp - 1) || d > (best_disp + 1) ) );
|
||||
|
||||
if (notUniq)
|
||||
*bestCost = FILTERED;
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
if( *bestCost != FILTERED && x < cols - WSZ2 - MIN_DISP && y < rows - WSZ2 && d == best_disp)
|
||||
{
|
||||
int d_aprox = 0;
|
||||
int yp =0, yn = 0;
|
||||
if ((0 < best_disp) && (best_disp < NUM_DISP - 1))
|
||||
{
|
||||
yp = cost[-2 * BLOCK_SIZE_Y];
|
||||
yn = cost[2 * BLOCK_SIZE_Y];
|
||||
d_aprox = yp + yn - 2 * c + abs(yp - yn);
|
||||
}
|
||||
disp[0] = (short)(((best_disp + MIN_DISP)*256 + (d_aprox != 0 ? (yp - yn) * 256 / d_aprox : 0) + 15) >> 4);
|
||||
}
|
||||
}
|
||||
|
||||
short calcCostBorder(__global const uchar * leftptr, __global const uchar * rightptr, int x, int y, int nthread,
|
||||
short * costbuf, int *h, int cols, int d, short cost)
|
||||
{
|
||||
int head = (*h) % WSZ;
|
||||
__global const uchar * left, * right;
|
||||
int idx = mad24(y + WSZ2 * (2 * nthread - 1), cols, x + WSZ2 * (1 - 2 * nthread));
|
||||
left = leftptr + idx;
|
||||
right = rightptr + (idx - d);
|
||||
|
||||
short costdiff = 0;
|
||||
if (0 == nthread)
|
||||
{
|
||||
#pragma unroll
|
||||
for (int i = 0; i < WSZ; i++)
|
||||
{
|
||||
costdiff += abs( left[0] - right[0] );
|
||||
left += cols;
|
||||
right += cols;
|
||||
}
|
||||
}
|
||||
else // (1 == nthread)
|
||||
{
|
||||
#pragma unroll
|
||||
for (int i = 0; i < WSZ; i++)
|
||||
{
|
||||
costdiff += abs(left[i] - right[i]);
|
||||
}
|
||||
}
|
||||
cost += costdiff - costbuf[head];
|
||||
costbuf[head] = costdiff;
|
||||
*h = head + 1;
|
||||
return cost;
|
||||
}
|
||||
|
||||
short calcCostInside(__global const uchar * leftptr, __global const uchar * rightptr, int x, int y,
|
||||
int cols, int d, short cost_up_left, short cost_up, short cost_left)
|
||||
{
|
||||
__global const uchar * left, * right;
|
||||
int idx = mad24(y - WSZ2 - 1, cols, x - WSZ2 - 1);
|
||||
left = leftptr + idx;
|
||||
right = rightptr + (idx - d);
|
||||
int idx2 = WSZ*cols;
|
||||
|
||||
uchar corrner1 = abs(left[0] - right[0]),
|
||||
corrner2 = abs(left[WSZ] - right[WSZ]),
|
||||
corrner3 = abs(left[idx2] - right[idx2]),
|
||||
corrner4 = abs(left[idx2 + WSZ] - right[idx2 + WSZ]);
|
||||
|
||||
return cost_up + cost_left - cost_up_left + corrner1 -
|
||||
corrner2 - corrner3 + corrner4;
|
||||
}
|
||||
|
||||
__kernel void stereoBM(__global const uchar * leftptr,
|
||||
__global const uchar * rightptr,
|
||||
__global uchar * dispptr, int disp_step, int disp_offset,
|
||||
int rows, int cols, // rows, cols of left and right images, not disp
|
||||
int textureThreshold, int uniquenessRatio)
|
||||
{
|
||||
int lz = get_local_id(0);
|
||||
int gx = get_global_id(1) * BLOCK_SIZE_X;
|
||||
int gy = get_global_id(2) * BLOCK_SIZE_Y;
|
||||
|
||||
int nthread = lz / NUM_DISP;
|
||||
int disp_idx = lz % NUM_DISP;
|
||||
|
||||
__global short * disp;
|
||||
__global const uchar * left, * right;
|
||||
|
||||
__local short costFunc[2 * BLOCK_SIZE_Y * NUM_DISP];
|
||||
|
||||
__local short * cost;
|
||||
__local int best_disp[2];
|
||||
__local int best_cost[2];
|
||||
best_cost[nthread] = MAX_VAL;
|
||||
best_disp[nthread] = -1;
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
short costbuf[WSZ];
|
||||
int head = 0;
|
||||
|
||||
int shiftX = WSZ2 + NUM_DISP + MIN_DISP - 1;
|
||||
int shiftY = WSZ2;
|
||||
|
||||
int x = gx + shiftX, y = gy + shiftY, lx = 0, ly = 0;
|
||||
|
||||
int costIdx = disp_idx * 2 * BLOCK_SIZE_Y + (BLOCK_SIZE_Y - 1);
|
||||
cost = costFunc + costIdx;
|
||||
|
||||
int tempcost = 0;
|
||||
if (x < cols - WSZ2 - MIN_DISP && y < rows - WSZ2)
|
||||
{
|
||||
if (0 == nthread)
|
||||
{
|
||||
#pragma unroll
|
||||
for (int i = 0; i < WSZ; i++)
|
||||
{
|
||||
int idx = mad24(y - WSZ2, cols, x - WSZ2 + i);
|
||||
left = leftptr + idx;
|
||||
right = rightptr + (idx - disp_idx);
|
||||
short costdiff = 0;
|
||||
for(int j = 0; j < WSZ; j++)
|
||||
{
|
||||
costdiff += abs( left[0] - right[0] );
|
||||
left += cols;
|
||||
right += cols;
|
||||
}
|
||||
costbuf[i] = costdiff;
|
||||
}
|
||||
}
|
||||
else // (1 == nthread)
|
||||
{
|
||||
#pragma unroll
|
||||
for (int i = 0; i < WSZ; i++)
|
||||
{
|
||||
int idx = mad24(y - WSZ2 + i, cols, x - WSZ2);
|
||||
left = leftptr + idx;
|
||||
right = rightptr + (idx - disp_idx);
|
||||
short costdiff = 0;
|
||||
for (int j = 0; j < WSZ; j++)
|
||||
{
|
||||
costdiff += abs( left[j] - right[j]);
|
||||
}
|
||||
tempcost += costdiff;
|
||||
costbuf[i] = costdiff;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nthread == 1)
|
||||
{
|
||||
cost[0] = tempcost;
|
||||
atomic_min(best_cost + 1, tempcost);
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
if (best_cost[1] == tempcost)
|
||||
atomic_max(best_disp + 1, disp_idx);
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
int dispIdx = mad24(gy, disp_step, mad24((int)sizeof(short), gx, disp_offset));
|
||||
disp = (__global short *)(dispptr + dispIdx);
|
||||
calcDisp(cost, disp, uniquenessRatio, best_disp + 1, best_cost + 1, disp_idx, x, y, cols, rows);
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
lx = 1 - nthread;
|
||||
ly = nthread;
|
||||
|
||||
for (int i = 0; i < BLOCK_SIZE_Y * BLOCK_SIZE_X / 2; i++)
|
||||
{
|
||||
x = (lx < BLOCK_SIZE_X) ? gx + shiftX + lx : cols;
|
||||
y = (ly < BLOCK_SIZE_Y) ? gy + shiftY + ly : rows;
|
||||
|
||||
best_cost[nthread] = MAX_VAL;
|
||||
best_disp[nthread] = -1;
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
costIdx = mad24(2 * BLOCK_SIZE_Y, disp_idx, (BLOCK_SIZE_Y - 1 - ly + lx));
|
||||
if (0 > costIdx)
|
||||
costIdx = BLOCK_SIZE_Y - 1;
|
||||
cost = costFunc + costIdx;
|
||||
if (x < cols - WSZ2 - MIN_DISP && y < rows - WSZ2)
|
||||
{
|
||||
tempcost = (ly * (1 - nthread) + lx * nthread == 0) ?
|
||||
calcCostBorder(leftptr, rightptr, x, y, nthread, costbuf, &head, cols, disp_idx, cost[2*nthread-1]) :
|
||||
calcCostInside(leftptr, rightptr, x, y, cols, disp_idx, cost[0], cost[1], cost[-1]);
|
||||
}
|
||||
cost[0] = tempcost;
|
||||
atomic_min(best_cost + nthread, tempcost);
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
if (best_cost[nthread] == tempcost)
|
||||
atomic_max(best_disp + nthread, disp_idx);
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
dispIdx = mad24(gy + ly, disp_step, mad24((int)sizeof(short), (gx + lx), disp_offset));
|
||||
disp = (__global short *)(dispptr + dispIdx);
|
||||
calcDisp(cost, disp, uniquenessRatio, best_disp + nthread, best_cost + nthread, disp_idx, x, y, cols, rows);
|
||||
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
if (lx + nthread - 1 == ly)
|
||||
{
|
||||
lx = (lx + nthread + 1) * (1 - nthread);
|
||||
ly = (ly + 1) * nthread;
|
||||
}
|
||||
else
|
||||
{
|
||||
lx += nthread;
|
||||
ly = ly - nthread + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif //DEFINE_KERNEL_STEREOBM
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////// Norm Prefiler ////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
__kernel void prefilter_norm(__global unsigned char *input, __global unsigned char *output,
|
||||
int rows, int cols, int prefilterCap, int scale_g, int scale_s)
|
||||
{
|
||||
// prefilterCap in range 1..63, checked in StereoBMImpl::compute
|
||||
|
||||
int x = get_global_id(0);
|
||||
int y = get_global_id(1);
|
||||
|
||||
if(x < cols && y < rows)
|
||||
{
|
||||
int cov1 = input[ max(y-1, 0) * cols + x] * 1 +
|
||||
input[y * cols + max(x-1,0)] * 1 + input[ y * cols + x] * 4 + input[y * cols + min(x+1, cols-1)] * 1 +
|
||||
input[min(y+1, rows-1) * cols + x] * 1;
|
||||
int cov2 = 0;
|
||||
for(int i = -WSZ2; i < WSZ2+1; i++)
|
||||
for(int j = -WSZ2; j < WSZ2+1; j++)
|
||||
cov2 += input[clamp(y+i, 0, rows-1) * cols + clamp(x+j, 0, cols-1)];
|
||||
|
||||
int res = (cov1*scale_g - cov2*scale_s)>>10;
|
||||
res = clamp(res, -prefilterCap, prefilterCap) + prefilterCap;
|
||||
output[y * cols + x] = res;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////// Sobel Prefiler ////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
__kernel void prefilter_xsobel(__global unsigned char *input, __global unsigned char *output,
|
||||
int rows, int cols, int prefilterCap)
|
||||
{
|
||||
// prefilterCap in range 1..63, checked in StereoBMImpl::compute
|
||||
int x = get_global_id(0);
|
||||
int y = get_global_id(1);
|
||||
if(x < cols && y < rows)
|
||||
{
|
||||
if (0 < x && !((y == rows-1) & (rows%2==1) ) )
|
||||
{
|
||||
int cov = input[ ((y > 0) ? y-1 : y+1) * cols + (x-1)] * (-1) + input[ ((y > 0) ? y-1 : y+1) * cols + ((x<cols-1) ? x+1 : x-1)] * (1) +
|
||||
input[ (y) * cols + (x-1)] * (-2) + input[ (y) * cols + ((x<cols-1) ? x+1 : x-1)] * (2) +
|
||||
input[((y<rows-1)?(y+1):(y-1))* cols + (x-1)] * (-1) + input[((y<rows-1)?(y+1):(y-1))* cols + ((x<cols-1) ? x+1 : x-1)] * (1);
|
||||
|
||||
cov = clamp(cov, -prefilterCap, prefilterCap) + prefilterCap;
|
||||
output[y * cols + x] = cov;
|
||||
}
|
||||
else
|
||||
output[y * cols + x] = prefilterCap;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/*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_PRECOMP_H__
|
||||
#define __OPENCV_PRECOMP_H__
|
||||
|
||||
#include "opencv2/core/utility.hpp"
|
||||
#include "opencv2/core/private.hpp"
|
||||
|
||||
#include "opencv2/stereo.hpp"
|
||||
#include "opencv2/geometry.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
|
||||
#include "opencv2/core/ocl.hpp"
|
||||
|
||||
#define GET_OPTIMIZED(func) (func)
|
||||
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
/**
|
||||
* Compute the number of iterations given the confidence, outlier ratio, number
|
||||
* of model points and the maximum iteration number.
|
||||
*
|
||||
* @param p confidence value
|
||||
* @param ep outlier ratio
|
||||
* @param modelPoints number of model points required for estimation
|
||||
* @param maxIters maximum number of iterations
|
||||
* @return The number of iterations according to the formula
|
||||
* \f[
|
||||
* \frac{\ln(1-p)}{\ln\left(1-(1-ep)^\mathrm{modelPoints}\right)}
|
||||
* \f]
|
||||
*
|
||||
* If the computed number of iterations is larger than maxIters, then maxIters is returned.
|
||||
*/
|
||||
int RANSACUpdateNumIters( double p, double ep, int modelPoints, int maxIters );
|
||||
|
||||
class CV_EXPORTS PointSetRegistrator : public Algorithm
|
||||
{
|
||||
public:
|
||||
class CV_EXPORTS Callback
|
||||
{
|
||||
public:
|
||||
virtual ~Callback() {}
|
||||
virtual int runKernel(InputArray m1, InputArray m2, OutputArray model) const = 0;
|
||||
virtual void computeError(InputArray m1, InputArray m2, InputArray model, OutputArray err) const = 0;
|
||||
virtual bool checkSubset(InputArray, InputArray, int) const { return true; }
|
||||
};
|
||||
|
||||
virtual void setCallback(const Ptr<PointSetRegistrator::Callback>& cb) = 0;
|
||||
virtual bool run(InputArray m1, InputArray m2, OutputArray model, OutputArray mask) const = 0;
|
||||
};
|
||||
|
||||
CV_EXPORTS Ptr<PointSetRegistrator> createRANSACPointSetRegistrator(const Ptr<PointSetRegistrator::Callback>& cb,
|
||||
int modelPoints, double threshold,
|
||||
double confidence=0.99, int maxIters=1000 );
|
||||
|
||||
CV_EXPORTS Ptr<PointSetRegistrator> createLMeDSPointSetRegistrator(const Ptr<PointSetRegistrator::Callback>& cb,
|
||||
int modelPoints, double confidence=0.99, int maxIters=1000 );
|
||||
|
||||
template<typename T> inline int compressElems( T* ptr, const uchar* mask, int mstep, int count )
|
||||
{
|
||||
int i, j;
|
||||
for( i = j = 0; i < count; i++ )
|
||||
if( mask[i*mstep] )
|
||||
{
|
||||
if( i > j )
|
||||
ptr[j] = ptr[i];
|
||||
j++;
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
static inline bool haveCollinearPoints( const Mat& m, int count )
|
||||
{
|
||||
int j, k, i = count-1;
|
||||
const Point2f* ptr = m.ptr<Point2f>();
|
||||
|
||||
// check that the i-th selected point does not belong
|
||||
// to a line connecting some previously selected points
|
||||
// also checks that points are not too close to each other
|
||||
for( j = 0; j < i; j++ )
|
||||
{
|
||||
double dx1 = ptr[j].x - ptr[i].x;
|
||||
double dy1 = ptr[j].y - ptr[i].y;
|
||||
for( k = 0; k < j; k++ )
|
||||
{
|
||||
double dx2 = ptr[k].x - ptr[i].x;
|
||||
double dy2 = ptr[k].y - ptr[i].y;
|
||||
if( fabs(dx2*dy1 - dy2*dx1) <= FLT_EPSILON*(fabs(dx1) + fabs(dy1) + fabs(dx2) + fabs(dy2)))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace cv
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,712 @@
|
||||
// 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"
|
||||
|
||||
namespace cv {
|
||||
|
||||
void reprojectImageTo3D( InputArray _disparity, OutputArray __3dImage,
|
||||
InputArray _Qmat, bool handleMissingValues, int dtype )
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Mat disparity = _disparity.getMat(), Q = _Qmat.getMat();
|
||||
int stype = disparity.type();
|
||||
|
||||
CV_Assert( stype == CV_8UC1 || stype == CV_16SC1 ||
|
||||
stype == CV_32SC1 || stype == CV_32FC1 );
|
||||
CV_Assert( Q.size() == Size(4,4) );
|
||||
|
||||
if( dtype >= 0 )
|
||||
dtype = CV_MAKETYPE(CV_MAT_DEPTH(dtype), 3);
|
||||
|
||||
if( __3dImage.fixedType() )
|
||||
{
|
||||
int dtype_ = __3dImage.type();
|
||||
CV_Assert( dtype == -1 || dtype == dtype_ );
|
||||
dtype = dtype_;
|
||||
}
|
||||
|
||||
if( dtype < 0 )
|
||||
dtype = CV_32FC3;
|
||||
else
|
||||
CV_Assert( dtype == CV_16SC3 || dtype == CV_32SC3 || dtype == CV_32FC3 );
|
||||
|
||||
__3dImage.create(disparity.size(), dtype);
|
||||
Mat _3dImage = __3dImage.getMat();
|
||||
|
||||
const float bigZ = 10000.f;
|
||||
Matx44d _Q;
|
||||
Q.convertTo(_Q, CV_64F);
|
||||
|
||||
int x, cols = disparity.cols;
|
||||
CV_Assert( cols >= 0 );
|
||||
|
||||
std::vector<float> _sbuf(cols);
|
||||
std::vector<Vec3f> _dbuf(cols);
|
||||
float* sbuf = &_sbuf[0];
|
||||
Vec3f* dbuf = &_dbuf[0];
|
||||
double minDisparity = FLT_MAX;
|
||||
|
||||
// NOTE: here we quietly assume that at least one pixel in the disparity map is not defined.
|
||||
// and we set the corresponding Z's to some fixed big value.
|
||||
if( handleMissingValues )
|
||||
cv::minMaxIdx( disparity, &minDisparity, 0, 0, 0 );
|
||||
|
||||
for( int y = 0; y < disparity.rows; y++ )
|
||||
{
|
||||
float* sptr = sbuf;
|
||||
Vec3f* dptr = dbuf;
|
||||
|
||||
if( stype == CV_8UC1 )
|
||||
{
|
||||
const uchar* sptr0 = disparity.ptr<uchar>(y);
|
||||
for( x = 0; x < cols; x++ )
|
||||
sptr[x] = (float)sptr0[x];
|
||||
}
|
||||
else if( stype == CV_16SC1 )
|
||||
{
|
||||
const short* sptr0 = disparity.ptr<short>(y);
|
||||
for( x = 0; x < cols; x++ )
|
||||
sptr[x] = (float)sptr0[x];
|
||||
}
|
||||
else if( stype == CV_32SC1 )
|
||||
{
|
||||
const int* sptr0 = disparity.ptr<int>(y);
|
||||
for( x = 0; x < cols; x++ )
|
||||
sptr[x] = (float)sptr0[x];
|
||||
}
|
||||
else
|
||||
sptr = disparity.ptr<float>(y);
|
||||
|
||||
if( dtype == CV_32FC3 )
|
||||
dptr = _3dImage.ptr<Vec3f>(y);
|
||||
|
||||
for( x = 0; x < cols; x++)
|
||||
{
|
||||
double d = sptr[x];
|
||||
Vec4d homg_pt = _Q*Vec4d(x, y, d, 1.0);
|
||||
dptr[x] = Vec3d(homg_pt.val);
|
||||
dptr[x] /= homg_pt[3];
|
||||
|
||||
if( fabs(d-minDisparity) <= FLT_EPSILON )
|
||||
dptr[x][2] = bigZ;
|
||||
}
|
||||
|
||||
if( dtype == CV_16SC3 )
|
||||
{
|
||||
Vec3s* dptr0 = _3dImage.ptr<Vec3s>(y);
|
||||
for( x = 0; x < cols; x++ )
|
||||
{
|
||||
dptr0[x] = dptr[x];
|
||||
}
|
||||
}
|
||||
else if( dtype == CV_32SC3 )
|
||||
{
|
||||
Vec3i* dptr0 = _3dImage.ptr<Vec3i>(y);
|
||||
for( x = 0; x < cols; x++ )
|
||||
{
|
||||
dptr0[x] = dptr[x];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void stereoRectify( InputArray _cameraMatrix1, InputArray _distCoeffs1,
|
||||
InputArray _cameraMatrix2, InputArray _distCoeffs2,
|
||||
Size imageSize, InputArray R, InputArray T,
|
||||
OutputArray _R1, OutputArray _R2,
|
||||
OutputArray _P1, OutputArray _P2,
|
||||
OutputArray _Qmat, int flags,
|
||||
double alpha, Size newImgSize,
|
||||
Rect* roi1, Rect* roi2 )
|
||||
{
|
||||
Mat matR = Mat_<double>(R.getMat()), matT = Mat_<double>(T.getMat());
|
||||
|
||||
Mat om, r_r;
|
||||
Mat Z = Mat::zeros(3, 1, CV_64F);
|
||||
double nx = imageSize.width, ny = imageSize.height;
|
||||
|
||||
if( matR.rows == 3 && matR.cols == 3 )
|
||||
Rodrigues(matR, om); // get vector rotation
|
||||
else
|
||||
matR.copyTo(om);
|
||||
om *= -0.5; // get average rotation
|
||||
Rodrigues(om, r_r);
|
||||
Mat t = r_r * matT; // rotate cameras to same orientation by averaging
|
||||
|
||||
int idx = fabs(t.at<double>(0)) > fabs(t.at<double>(1)) ? 0 : 1;
|
||||
double c = t.at<double>(idx), nt = norm(t, NORM_L2);
|
||||
double _uu[3]={0, 0, 0};
|
||||
_uu[idx] = c > 0 ? 1 : -1;
|
||||
|
||||
CV_Assert(nt > 0.0);
|
||||
|
||||
// calculate global Z rotation
|
||||
Mat ww = t.cross(Mat(3, 1, CV_64F, _uu)), wR;
|
||||
double nw = norm(ww, NORM_L2);
|
||||
if (nw > 0.0)
|
||||
ww *= std::acos(fabs(c)/nt)/nw;
|
||||
Rodrigues(ww, wR);
|
||||
|
||||
Mat Ri;
|
||||
// apply to both views
|
||||
gemm(wR, r_r, 1, Mat(), 0, Ri, GEMM_2_T);
|
||||
Ri.copyTo(_R1);
|
||||
gemm(wR, r_r, 1, Mat(), 0, Ri, 0);
|
||||
Ri.copyTo(_R2);
|
||||
t = Ri * matT;
|
||||
|
||||
// calculate projection/camera matrices
|
||||
// these contain the relevant rectified image internal params (fx, fy=fx, cx, cy)
|
||||
Point2d cc_new[2]={};
|
||||
|
||||
newImgSize = newImgSize.width * newImgSize.height != 0 ? newImgSize : imageSize;
|
||||
const double ratio_x = (double)newImgSize.width / imageSize.width / 2;
|
||||
const double ratio_y = (double)newImgSize.height / imageSize.height / 2;
|
||||
const double ratio = idx == 1 ? ratio_x : ratio_y;
|
||||
|
||||
Mat cameraMatrix1 = Mat_<double>(_cameraMatrix1.getMat());
|
||||
Mat cameraMatrix2 = Mat_<double>(_cameraMatrix2.getMat());
|
||||
Mat distCoeffs1, distCoeffs2;
|
||||
if (!_distCoeffs1.empty())
|
||||
distCoeffs1 = Mat_<double>(_distCoeffs1.getMat());
|
||||
if (!_distCoeffs2.empty())
|
||||
distCoeffs2 = Mat_<double>(_distCoeffs2.getMat());
|
||||
|
||||
double fc_new = (cameraMatrix1.at<double>(idx ^ 1, idx ^ 1) + cameraMatrix2.at<double>(idx ^ 1, idx ^ 1)) * ratio;
|
||||
|
||||
for( int k = 0; k < 2; k++ )
|
||||
{
|
||||
const Mat& A = k == 0 ? cameraMatrix1 : cameraMatrix2;
|
||||
const Mat& Dk = k == 0 ? distCoeffs1 : distCoeffs2;
|
||||
Point2f _pts[4] = {};
|
||||
Point3f _pts_3[4] = {};
|
||||
Mat pts(1, 4, CV_32FC2, _pts);
|
||||
Mat pts_3(1, 4, CV_32FC3, _pts_3);
|
||||
|
||||
for( int i = 0; i < 4; i++ )
|
||||
{
|
||||
int j = (i<2) ? 0 : 1;
|
||||
_pts[i].x = (float)((i % 2)*(nx-1));
|
||||
_pts[i].y = (float)(j*(ny-1));
|
||||
}
|
||||
undistortPoints(pts, pts, A, Dk, Mat(), Mat());
|
||||
convertPointsToHomogeneous(pts, pts_3);
|
||||
|
||||
// Change the camera matrix to have cc=[0,0] and fc = fc_new
|
||||
double _a_tmp[3][3] = {{fc_new, 0, 0}, {0, fc_new, 0}, {0, 0, 1}};
|
||||
Mat A_tmp(3, 3, CV_64F, _a_tmp);
|
||||
projectPoints(pts_3, (k == 0 ? _R1 : _R2), Z, A_tmp, Mat(), pts);
|
||||
Scalar avg = mean(pts);
|
||||
cc_new[k].x = (nx-1)/2 - avg.val[0];
|
||||
cc_new[k].y = (ny-1)/2 - avg.val[1];
|
||||
}
|
||||
|
||||
// vertical focal length must be the same for both images to keep the epipolar constraint
|
||||
// (for horizontal epipolar lines -- TBD: check for vertical epipolar lines)
|
||||
// use fy for fx also, for simplicity
|
||||
|
||||
// For simplicity, set the principal points for both cameras to be the average
|
||||
// of the two principal points (either one of or both x- and y- coordinates)
|
||||
if( flags & STEREO_ZERO_DISPARITY )
|
||||
{
|
||||
cc_new[0].x = cc_new[1].x = (cc_new[0].x + cc_new[1].x)*0.5;
|
||||
cc_new[0].y = cc_new[1].y = (cc_new[0].y + cc_new[1].y)*0.5;
|
||||
}
|
||||
else if( idx == 0 ) // horizontal stereo
|
||||
cc_new[0].y = cc_new[1].y = (cc_new[0].y + cc_new[1].y)*0.5;
|
||||
else // vertical stereo
|
||||
cc_new[0].x = cc_new[1].x = (cc_new[0].x + cc_new[1].x)*0.5;
|
||||
|
||||
double t_idx = t.at<double>(idx);
|
||||
|
||||
Mat pp = Mat::zeros(3, 4, CV_64F);
|
||||
pp.at<double>(0, 0) = pp.at<double>(1, 1) = fc_new;
|
||||
pp.at<double>(0, 2) = cc_new[0].x;
|
||||
pp.at<double>(1, 2) = cc_new[0].y;
|
||||
pp.at<double>(2, 2) = 1.;
|
||||
pp.copyTo(_P1);
|
||||
|
||||
pp.at<double>(0, 2) = cc_new[1].x;
|
||||
pp.at<double>(1, 2) = cc_new[1].y;
|
||||
pp.at<double>(idx, 3) = t_idx*fc_new; // baseline * focal length
|
||||
pp.copyTo(_P2);
|
||||
|
||||
alpha = MIN(alpha, 1.);
|
||||
|
||||
cv::Rect_<double> inner1, inner2, outer1, outer2;
|
||||
getUndistortRectangles(cameraMatrix1, distCoeffs1, _R1, _P1, imageSize, inner1, outer1);
|
||||
getUndistortRectangles(cameraMatrix2, distCoeffs2, _R2, _P2, imageSize, inner2, outer2);
|
||||
|
||||
{
|
||||
newImgSize = newImgSize.width*newImgSize.height != 0 ? newImgSize : imageSize;
|
||||
double cx1_0 = cc_new[0].x;
|
||||
double cy1_0 = cc_new[0].y;
|
||||
double cx2_0 = cc_new[1].x;
|
||||
double cy2_0 = cc_new[1].y;
|
||||
double cx1 = newImgSize.width*cx1_0/imageSize.width;
|
||||
double cy1 = newImgSize.height*cy1_0/imageSize.height;
|
||||
double cx2 = newImgSize.width*cx2_0/imageSize.width;
|
||||
double cy2 = newImgSize.height*cy2_0/imageSize.height;
|
||||
double s = 1.;
|
||||
|
||||
if( alpha >= 0 )
|
||||
{
|
||||
double s0 = std::max(std::max(std::max((double)cx1/(cx1_0 - inner1.x), (double)cy1/(cy1_0 - inner1.y)),
|
||||
(double)(newImgSize.width - 1 - cx1)/(inner1.x + inner1.width - cx1_0)),
|
||||
(double)(newImgSize.height - 1 - cy1)/(inner1.y + inner1.height - cy1_0));
|
||||
s0 = std::max(std::max(std::max(std::max((double)cx2/(cx2_0 - inner2.x), (double)cy2/(cy2_0 - inner2.y)),
|
||||
(double)(newImgSize.width - 1 - cx2)/(inner2.x + inner2.width - cx2_0)),
|
||||
(double)(newImgSize.height - 1 - cy2)/(inner2.y + inner2.height - cy2_0)),
|
||||
s0);
|
||||
|
||||
double s1 = std::min(std::min(std::min((double)cx1/(cx1_0 - outer1.x), (double)cy1/(cy1_0 - outer1.y)),
|
||||
(double)(newImgSize.width - 1 - cx1)/(outer1.x + outer1.width - cx1_0)),
|
||||
(double)(newImgSize.height - 1 - cy1)/(outer1.y + outer1.height - cy1_0));
|
||||
s1 = std::min(std::min(std::min(std::min((double)cx2/(cx2_0 - outer2.x), (double)cy2/(cy2_0 - outer2.y)),
|
||||
(double)(newImgSize.width - 1 - cx2)/(outer2.x + outer2.width - cx2_0)),
|
||||
(double)(newImgSize.height - 1 - cy2)/(outer2.y + outer2.height - cy2_0)),
|
||||
s1);
|
||||
|
||||
s = s0*(1 - alpha) + s1*alpha;
|
||||
}
|
||||
|
||||
fc_new *= s;
|
||||
cc_new[0] = Point2d(cx1, cy1);
|
||||
cc_new[1] = Point2d(cx2, cy2);
|
||||
|
||||
pp.at<double>(0, 0) = pp.at<double>(1, 1) = fc_new;
|
||||
pp.at<double>(0, 2) = cx2;
|
||||
pp.at<double>(1, 2) = cy2;
|
||||
pp.at<double>(idx, 3) *= s;
|
||||
pp.copyTo(_P2);
|
||||
|
||||
pp.at<double>(0, 2) = cx1;
|
||||
pp.at<double>(1, 2) = cy1;
|
||||
pp.at<double>(idx, 3) = 0.;
|
||||
pp.copyTo(_P1);
|
||||
|
||||
if(roi1)
|
||||
{
|
||||
*roi1 =
|
||||
cv::Rect(cvCeil((inner1.x - cx1_0)*s + cx1),
|
||||
cvCeil((inner1.y - cy1_0)*s + cy1),
|
||||
cvFloor(inner1.width*s), cvFloor(inner1.height*s))
|
||||
& cv::Rect(0, 0, newImgSize.width, newImgSize.height)
|
||||
;
|
||||
}
|
||||
|
||||
if(roi2)
|
||||
{
|
||||
*roi2 =
|
||||
cv::Rect(cvCeil((inner2.x - cx2_0)*s + cx2),
|
||||
cvCeil((inner2.y - cy2_0)*s + cy2),
|
||||
cvFloor(inner2.width*s), cvFloor(inner2.height*s))
|
||||
& cv::Rect(0, 0, newImgSize.width, newImgSize.height)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
if( _Qmat.needed() )
|
||||
{
|
||||
double q[] =
|
||||
{
|
||||
1, 0, 0, -cc_new[0].x,
|
||||
0, 1, 0, -cc_new[0].y,
|
||||
0, 0, 0, fc_new,
|
||||
0, 0, -1./t_idx,
|
||||
(idx == 0 ? cc_new[0].x - cc_new[1].x : cc_new[0].y - cc_new[1].y)/t_idx
|
||||
};
|
||||
Mat Q(4, 4, CV_64F, q);
|
||||
Q.copyTo(_Qmat);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
CV_IMPL int cvStereoRectifyUncalibrated(
|
||||
const CvMat* _points1, const CvMat* _points2,
|
||||
const CvMat* F0, CvSize imgSize,
|
||||
CvMat* _H1, CvMat* _H2, double threshold )
|
||||
*/
|
||||
bool stereoRectifyUncalibrated( InputArray _points1, InputArray _points2,
|
||||
InputArray _Fmat, Size imgSize,
|
||||
OutputArray _Hmat1, OutputArray _Hmat2, double threshold )
|
||||
{
|
||||
Mat points1 = _points1.getMat(), points2 = _points2.getMat();
|
||||
CV_Assert( points1.size() == points2.size() );
|
||||
|
||||
int npoints = points1.checkVector(2);
|
||||
CV_Assert(npoints > 0);
|
||||
|
||||
Mat _m1, _m2;
|
||||
|
||||
points1.convertTo(_m1, CV_64F);
|
||||
points2.convertTo(_m2, CV_64F);
|
||||
_m1 = _m1.reshape(2, 1);
|
||||
_m2 = _m2.reshape(2, 1);
|
||||
|
||||
Mat F0 = _Fmat.getMat(), F, Wdiag, U, Vt;
|
||||
F0.convertTo(F, CV_64F);
|
||||
|
||||
SVDecomp(F, Wdiag, U, Vt, 0);
|
||||
Wdiag.at<double>(2) = 0.;
|
||||
Mat W = Mat::diag(Wdiag), UW;
|
||||
gemm(U, W, 1, Mat(), 0, UW);
|
||||
gemm(UW, Vt, 1, Mat(), 0, F);
|
||||
|
||||
double cx = cvRound( (imgSize.width-1)*0.5 );
|
||||
double cy = cvRound( (imgSize.height-1)*0.5 );
|
||||
|
||||
if( threshold > 0 )
|
||||
{
|
||||
Mat _lines1, _lines2;
|
||||
computeCorrespondEpilines(_m1, 1, F, _lines1);
|
||||
computeCorrespondEpilines(_m2, 2, F, _lines2);
|
||||
CV_Assert(_m1.isContinuous() && _m2.isContinuous() &&
|
||||
_lines1.isContinuous() && _lines2.isContinuous());
|
||||
Point2d* m1 = (Point2d*)_m1.data;
|
||||
Point2d* m2 = (Point2d*)_m2.data;
|
||||
Point3d* lines1 = (Point3d*)_lines1.data;
|
||||
Point3d* lines2 = (Point3d*)_lines2.data;
|
||||
|
||||
// measure distance from points to the corresponding epilines, mark outliers
|
||||
int i, j;
|
||||
for( i = j = 0; i < npoints; i++ )
|
||||
{
|
||||
if( fabs(m1[i].x*lines2[i].x +
|
||||
m1[i].y*lines2[i].y +
|
||||
lines2[i].z) <= threshold &&
|
||||
fabs(m2[i].x*lines1[i].x +
|
||||
m2[i].y*lines1[i].y +
|
||||
lines1[i].z) <= threshold )
|
||||
{
|
||||
if( j < i )
|
||||
{
|
||||
m1[j] = m1[i];
|
||||
m2[j] = m2[i];
|
||||
}
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
npoints = j;
|
||||
if( npoints == 0 )
|
||||
return false;
|
||||
_m1.cols = _m2.cols = npoints;
|
||||
}
|
||||
|
||||
Mat E2 = U.col(2).clone();
|
||||
if (E2.at<double>(2) < 0)
|
||||
E2 *= -1.0;
|
||||
|
||||
double t[] =
|
||||
{
|
||||
1, 0, -cx,
|
||||
0, 1, -cy,
|
||||
0, 0, 1
|
||||
};
|
||||
Mat T(3, 3, CV_64F, t);
|
||||
E2 = T*E2;
|
||||
|
||||
double* e2 = (double*)E2.data;
|
||||
int mirror = e2[0] < 0;
|
||||
double d = std::sqrt(e2[0]*e2[0] + e2[1]*e2[1]);
|
||||
d = MAX(d, DBL_EPSILON);
|
||||
double alpha = e2[0]/d;
|
||||
double beta = e2[1]/d;
|
||||
double r[] =
|
||||
{
|
||||
alpha, beta, 0,
|
||||
-beta, alpha, 0,
|
||||
0, 0, 1
|
||||
};
|
||||
Mat R(3, 3, CV_64F, r);
|
||||
T = R*T;
|
||||
E2 = R*E2;
|
||||
double invf = fabs(e2[2]) < 1e-6*fabs(e2[0]) ? 0 : -e2[2]/e2[0];
|
||||
double k[] =
|
||||
{
|
||||
1, 0, 0,
|
||||
0, 1, 0,
|
||||
invf, 0, 1
|
||||
};
|
||||
Mat K(3, 3, CV_64F, k);
|
||||
Mat H2 = K*T;
|
||||
E2 = K*E2;
|
||||
|
||||
double it[] =
|
||||
{
|
||||
1, 0, cx,
|
||||
0, 1, cy,
|
||||
0, 0, 1
|
||||
};
|
||||
Mat iT( 3, 3, CV_64F, it );
|
||||
H2 = iT*H2;
|
||||
|
||||
U.col(2).copyTo(E2);
|
||||
if (E2.at<double>(2) < 0)
|
||||
E2 *= -1.0;
|
||||
|
||||
double e2_x[] =
|
||||
{
|
||||
0, -e2[2], e2[1],
|
||||
e2[2], 0, -e2[0],
|
||||
-e2[1], e2[0], 0
|
||||
};
|
||||
double e2_111[] =
|
||||
{
|
||||
e2[0], e2[0], e2[0],
|
||||
e2[1], e2[1], e2[1],
|
||||
e2[2], e2[2], e2[2],
|
||||
};
|
||||
Mat E2_x(3, 3, CV_64F, e2_x);
|
||||
Mat E2_111(3, 3, CV_64F, e2_111);
|
||||
Mat H0 = E2_x*F + E2_111;
|
||||
H0 = H2*H0;
|
||||
Mat E1(3, 1, CV_64F, (double*)Vt.data+6);
|
||||
E1 = H0*E1;
|
||||
|
||||
perspectiveTransform( _m1, _m1, H0 );
|
||||
perspectiveTransform( _m2, _m2, H2 );
|
||||
Mat A, X;
|
||||
convertPointsToHomogeneous(_m1, A, CV_64F);
|
||||
A = A.reshape(1, npoints);
|
||||
Mat BxBy = _m2.reshape(1, npoints);
|
||||
Mat B = BxBy.col(0);
|
||||
solve(A, B, X, DECOMP_SVD);
|
||||
CV_Assert(X.isContinuous());
|
||||
double* x = X.ptr<double>();
|
||||
|
||||
double ha[] =
|
||||
{
|
||||
x[0], x[1], x[2],
|
||||
0, 1, 0,
|
||||
0, 0, 1
|
||||
};
|
||||
Mat Ha(3, 3, CV_64F, ha);
|
||||
Mat H1 = Ha*H0;
|
||||
perspectiveTransform( _m1, _m1, Ha );
|
||||
|
||||
if( mirror )
|
||||
{
|
||||
double mm[] = { -1, 0, cx*2, 0, -1, cy*2, 0, 0, 1 };
|
||||
Mat MM(3, 3, CV_64F, mm);
|
||||
H1 = MM*H1;
|
||||
H2 = MM*H2;
|
||||
}
|
||||
|
||||
H1.copyTo(_Hmat1);
|
||||
H2.copyTo(_Hmat2);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static void adjust3rdMatrix(InputArrayOfArrays _imgpt1_0,
|
||||
InputArrayOfArrays _imgpt3_0,
|
||||
const Mat& cameraMatrix1, const Mat& distCoeffs1,
|
||||
const Mat& cameraMatrix3, const Mat& distCoeffs3,
|
||||
const Mat& R1, const Mat& R3, const Mat& P1, Mat& P3 )
|
||||
{
|
||||
size_t n1 = _imgpt1_0.total(), n3 = _imgpt3_0.total();
|
||||
std::vector<Point2f> imgpt1, imgpt3;
|
||||
|
||||
for( int i = 0; i < (int)std::min(n1, n3); i++ )
|
||||
{
|
||||
Mat pt1 = _imgpt1_0.getMat(i), pt3 = _imgpt3_0.getMat(i);
|
||||
int ni1 = pt1.checkVector(2, CV_32F), ni3 = pt3.checkVector(2, CV_32F);
|
||||
CV_Assert( ni1 > 0 && ni1 == ni3 );
|
||||
const Point2f* pt1data = pt1.ptr<Point2f>();
|
||||
const Point2f* pt3data = pt3.ptr<Point2f>();
|
||||
std::copy(pt1data, pt1data + ni1, std::back_inserter(imgpt1));
|
||||
std::copy(pt3data, pt3data + ni3, std::back_inserter(imgpt3));
|
||||
}
|
||||
|
||||
undistortPoints(imgpt1, imgpt1, cameraMatrix1, distCoeffs1, R1, P1);
|
||||
undistortPoints(imgpt3, imgpt3, cameraMatrix3, distCoeffs3, R3, P3);
|
||||
|
||||
double y1_ = 0, y2_ = 0, y1y1_ = 0, y1y2_ = 0;
|
||||
size_t n = imgpt1.size();
|
||||
CV_DbgAssert(n > 0);
|
||||
|
||||
for( size_t i = 0; i < n; i++ )
|
||||
{
|
||||
double y1 = imgpt3[i].y, y2 = imgpt1[i].y;
|
||||
|
||||
y1_ += y1; y2_ += y2;
|
||||
y1y1_ += y1*y1; y1y2_ += y1*y2;
|
||||
}
|
||||
|
||||
y1_ /= n;
|
||||
y2_ /= n;
|
||||
y1y1_ /= n;
|
||||
y1y2_ /= n;
|
||||
|
||||
double a = (y1y2_ - y1_*y2_)/(y1y1_ - y1_*y1_);
|
||||
double b = y2_ - a*y1_;
|
||||
|
||||
P3.at<double>(0,0) *= a;
|
||||
P3.at<double>(1,1) *= a;
|
||||
P3.at<double>(0,2) = P3.at<double>(0,2)*a;
|
||||
P3.at<double>(1,2) = P3.at<double>(1,2)*a + b;
|
||||
P3.at<double>(0,3) *= a;
|
||||
P3.at<double>(1,3) *= a;
|
||||
}
|
||||
|
||||
float rectify3Collinear( InputArray _cameraMatrix1, InputArray _distCoeffs1,
|
||||
InputArray _cameraMatrix2, InputArray _distCoeffs2,
|
||||
InputArray _cameraMatrix3, InputArray _distCoeffs3,
|
||||
InputArrayOfArrays _imgpt1,
|
||||
InputArrayOfArrays _imgpt3,
|
||||
Size imageSize, InputArray _Rmat12, InputArray _Tmat12,
|
||||
InputArray _Rmat13, InputArray _Tmat13,
|
||||
OutputArray _Rmat1, OutputArray _Rmat2, OutputArray _Rmat3,
|
||||
OutputArray _Pmat1, OutputArray _Pmat2, OutputArray _Pmat3,
|
||||
OutputArray _Qmat,
|
||||
double alpha, Size newImgSize,
|
||||
Rect* roi1, Rect* roi2, int flags )
|
||||
{
|
||||
// first, rectify the 1-2 stereo pair
|
||||
stereoRectify( _cameraMatrix1, _distCoeffs1, _cameraMatrix2, _distCoeffs2,
|
||||
imageSize, _Rmat12, _Tmat12, _Rmat1, _Rmat2, _Pmat1, _Pmat2, _Qmat,
|
||||
flags, alpha, newImgSize, roi1, roi2 );
|
||||
|
||||
Mat R12 = _Rmat12.getMat(), R13 = _Rmat13.getMat(), T12 = _Tmat12.getMat(), T13 = _Tmat13.getMat();
|
||||
|
||||
_Rmat3.create(3, 3, CV_64F);
|
||||
_Pmat3.create(3, 4, CV_64F);
|
||||
|
||||
Mat P1 = _Pmat1.getMat(), P2 = _Pmat2.getMat();
|
||||
Mat R3 = _Rmat3.getMat(), P3 = _Pmat3.getMat();
|
||||
|
||||
// recompute rectification transforms for cameras 1 & 2.
|
||||
Mat om, r_r, r_r13;
|
||||
|
||||
if( R13.size() != Size(3,3) )
|
||||
Rodrigues(R13, r_r13);
|
||||
else
|
||||
R13.copyTo(r_r13);
|
||||
|
||||
if( R12.size() == Size(3,3) )
|
||||
Rodrigues(R12, om);
|
||||
else
|
||||
R12.copyTo(om);
|
||||
|
||||
om *= -0.5;
|
||||
Rodrigues(om, r_r); // rotate cameras to same orientation by averaging
|
||||
Mat_<double> t12 = r_r * T12;
|
||||
|
||||
int idx = fabs(t12(0,0)) > fabs(t12(1,0)) ? 0 : 1;
|
||||
double c = t12(idx,0), nt = norm(t12, NORM_L2);
|
||||
CV_Assert(fabs(nt) > 0);
|
||||
Mat_<double> uu = Mat_<double>::zeros(3,1);
|
||||
uu(idx, 0) = c > 0 ? 1 : -1;
|
||||
|
||||
// calculate global Z rotation
|
||||
Mat_<double> ww = t12.cross(uu), wR;
|
||||
double nw = norm(ww, NORM_L2);
|
||||
CV_Assert(fabs(nw) > 0);
|
||||
ww *= std::acos(fabs(c)/nt)/nw;
|
||||
Rodrigues(ww, wR);
|
||||
|
||||
// now rotate camera 3 to make its optical axis parallel to cameras 1 and 2.
|
||||
R3 = wR*r_r.t()*r_r13.t();
|
||||
Mat_<double> t13 = R3 * T13;
|
||||
|
||||
P2.copyTo(P3);
|
||||
Mat t = P3.col(3);
|
||||
t13.copyTo(t);
|
||||
P3.at<double>(0,3) *= P3.at<double>(0,0);
|
||||
P3.at<double>(1,3) *= P3.at<double>(1,1);
|
||||
|
||||
if( !_imgpt1.empty() && !_imgpt3.empty() )
|
||||
adjust3rdMatrix(_imgpt1, _imgpt3, _cameraMatrix1.getMat(), _distCoeffs1.getMat(),
|
||||
_cameraMatrix3.getMat(), _distCoeffs3.getMat(), _Rmat1.getMat(), R3, P1, P3);
|
||||
|
||||
return (float)((P3.at<double>(idx,3)/P3.at<double>(idx,idx))/
|
||||
(P2.at<double>(idx,3)/P2.at<double>(idx,idx)));
|
||||
}
|
||||
|
||||
void cv::fisheye::stereoRectify( InputArray K1, InputArray D1, InputArray K2, InputArray D2, const Size& imageSize,
|
||||
InputArray _R, InputArray _tvec, OutputArray R1, OutputArray R2, OutputArray P1, OutputArray P2,
|
||||
OutputArray Q, int flags, const Size& newImageSize, double balance, double fov_scale)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
CV_Assert((_R.size() == Size(3, 3) || _R.total() * _R.channels() == 3) && (_R.depth() == CV_32F || _R.depth() == CV_64F));
|
||||
CV_Assert(_tvec.total() * _tvec.channels() == 3 && (_tvec.depth() == CV_32F || _tvec.depth() == CV_64F));
|
||||
|
||||
|
||||
Mat aaa = _tvec.getMat().reshape(3, 1);
|
||||
|
||||
Vec3d rvec; // Rodrigues vector
|
||||
if (_R.size() == Size(3, 3))
|
||||
{
|
||||
Matx33d rmat;
|
||||
_R.getMat().convertTo(rmat, CV_64F);
|
||||
rvec = Affine3d(rmat).rvec();
|
||||
}
|
||||
else if (_R.total() * _R.channels() == 3)
|
||||
_R.getMat().convertTo(rvec, CV_64F);
|
||||
|
||||
Vec3d tvec;
|
||||
_tvec.getMat().convertTo(tvec, CV_64F);
|
||||
|
||||
// rectification algorithm
|
||||
rvec *= -0.5; // get average rotation
|
||||
|
||||
Matx33d r_r;
|
||||
Rodrigues(rvec, r_r); // rotate cameras to same orientation by averaging
|
||||
|
||||
Vec3d t = r_r * tvec;
|
||||
Vec3d uu(t[0] > 0 ? 1 : -1, 0, 0);
|
||||
|
||||
// calculate global Z rotation
|
||||
Vec3d ww = t.cross(uu);
|
||||
double nw = norm(ww);
|
||||
if (nw > 0.0)
|
||||
ww *= std::acos(fabs(t[0])/cv::norm(t))/nw;
|
||||
|
||||
Matx33d wr;
|
||||
Rodrigues(ww, wr);
|
||||
|
||||
// apply to both views
|
||||
Matx33d ri1 = wr * r_r.t();
|
||||
Mat(ri1, false).convertTo(R1, R1.empty() ? CV_64F : R1.type());
|
||||
Matx33d ri2 = wr * r_r;
|
||||
Mat(ri2, false).convertTo(R2, R2.empty() ? CV_64F : R2.type());
|
||||
Vec3d tnew = ri2 * tvec;
|
||||
|
||||
// calculate projection/camera matrices. these contain the relevant rectified image internal params (fx, fy=fx, cx, cy)
|
||||
Matx33d newK1, newK2;
|
||||
fisheye::estimateNewCameraMatrixForUndistortRectify(K1, D1, imageSize, R1, newK1, balance, newImageSize, fov_scale);
|
||||
fisheye::estimateNewCameraMatrixForUndistortRectify(K2, D2, imageSize, R2, newK2, balance, newImageSize, fov_scale);
|
||||
|
||||
double fc_new = std::min(newK1(1,1), newK2(1,1));
|
||||
Point2d cc_new[2] = { Vec2d(newK1(0, 2), newK1(1, 2)), Vec2d(newK2(0, 2), newK2(1, 2)) };
|
||||
|
||||
// Vertical focal length must be the same for both images to keep the epipolar constraint use fy for fx also.
|
||||
// For simplicity, set the principal points for both cameras to be the average
|
||||
// of the two principal points (either one of or both x- and y- coordinates)
|
||||
if( flags & STEREO_ZERO_DISPARITY )
|
||||
cc_new[0] = cc_new[1] = (cc_new[0] + cc_new[1]) * 0.5;
|
||||
else
|
||||
cc_new[0].y = cc_new[1].y = (cc_new[0].y + cc_new[1].y)*0.5;
|
||||
|
||||
Mat(Matx34d(fc_new, 0, cc_new[0].x, 0,
|
||||
0, fc_new, cc_new[0].y, 0,
|
||||
0, 0, 1, 0), false).convertTo(P1, P1.empty() ? CV_64F : P1.type());
|
||||
|
||||
Mat(Matx34d(fc_new, 0, cc_new[1].x, tnew[0]*fc_new, // baseline * focal length;,
|
||||
0, fc_new, cc_new[1].y, 0,
|
||||
0, 0, 1, 0), false).convertTo(P2, P2.empty() ? CV_64F : P2.type());
|
||||
|
||||
if (Q.needed())
|
||||
Mat(Matx44d(1, 0, 0, -cc_new[0].x,
|
||||
0, 1, 0, -cc_new[0].y,
|
||||
0, 0, 0, fc_new,
|
||||
0, 0, -1./tnew[0], (cc_new[0].x - cc_new[1].x)/tnew[0]), false).convertTo(Q, Q.empty() ? CV_64F : Q.depth());
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,97 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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) 2010-2012, Institute Of Software Chinese Academy Of Science, all rights reserved.
|
||||
// Copyright (C) 2010-2012, Advanced Micro Devices, 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"
|
||||
#include "cvconfig.h"
|
||||
#include "opencv2/ts/ocl_test.hpp"
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
|
||||
namespace opencv_test {
|
||||
namespace ocl {
|
||||
|
||||
PARAM_TEST_CASE(StereoBMFixture, int, int)
|
||||
{
|
||||
int n_disp;
|
||||
int winSize;
|
||||
Mat left, right, disp;
|
||||
UMat uleft, uright, udisp;
|
||||
|
||||
virtual void SetUp()
|
||||
{
|
||||
n_disp = GET_PARAM(0);
|
||||
winSize = GET_PARAM(1);
|
||||
|
||||
left = readImage("gpu/stereobm/aloe-L.png", IMREAD_GRAYSCALE);
|
||||
right = readImage("gpu/stereobm/aloe-R.png", IMREAD_GRAYSCALE);
|
||||
|
||||
ASSERT_FALSE(left.empty());
|
||||
ASSERT_FALSE(right.empty());
|
||||
|
||||
left.copyTo(uleft);
|
||||
right.copyTo(uright);
|
||||
}
|
||||
|
||||
void Near(double eps = 0.0)
|
||||
{
|
||||
EXPECT_MAT_NEAR_RELATIVE(disp, udisp, eps);
|
||||
}
|
||||
};
|
||||
|
||||
OCL_TEST_P(StereoBMFixture, StereoBM)
|
||||
{
|
||||
Ptr<StereoBM> bm = StereoBM::create( n_disp, winSize);
|
||||
bm->setPreFilterType(bm->PREFILTER_XSOBEL);
|
||||
bm->setTextureThreshold(0);
|
||||
|
||||
OCL_OFF(bm->compute(left, right, disp));
|
||||
OCL_ON(bm->compute(uleft, uright, udisp));
|
||||
|
||||
Near(1e-3);
|
||||
}
|
||||
|
||||
OCL_INSTANTIATE_TEST_CASE_P(StereoMatcher, StereoBMFixture, testing::Combine(testing::Values(32, 64, 128),
|
||||
testing::Values(11, 21)));
|
||||
}//ocl
|
||||
}//cvtest
|
||||
|
||||
#endif //HAVE_OPENCL
|
||||
@@ -0,0 +1,395 @@
|
||||
// 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"
|
||||
#include <opencv2/ts/cuda_test.hpp> // EXPECT_MAT_NEAR
|
||||
#include "opencv2/geometry.hpp"
|
||||
#include <opencv2/core/utils/logger.hpp>
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
static bool checkPandROI(const Matx33d& M, const Matx<double, 5, 1>& D,
|
||||
const Mat& R, const Mat& P, Size imgsize, Rect roi)
|
||||
{
|
||||
const double eps = 0.05;
|
||||
const int N = 21;
|
||||
int x, y, k;
|
||||
vector<Point2f> pts, upts;
|
||||
|
||||
// step 1. check that all the original points belong to the destination image
|
||||
for( y = 0; y < N; y++ )
|
||||
for( x = 0; x < N; x++ )
|
||||
pts.push_back(Point2f((float)x*imgsize.width/(N-1), (float)y*imgsize.height/(N-1)));
|
||||
|
||||
undistortPoints(pts, upts, M, D, R, P );
|
||||
for( k = 0; k < N*N; k++ )
|
||||
if( upts[k].x < -imgsize.width*eps || upts[k].x > imgsize.width*(1+eps) ||
|
||||
upts[k].y < -imgsize.height*eps || upts[k].y > imgsize.height*(1+eps) )
|
||||
{
|
||||
CV_LOG_ERROR(NULL, cv::format("The point (%g, %g) was mapped to (%g, %g) which is out of image\n",
|
||||
pts[k].x, pts[k].y, upts[k].x, upts[k].y));
|
||||
return false;
|
||||
}
|
||||
|
||||
// step 2. check that all the points inside ROI belong to the original source image
|
||||
Mat temp(imgsize, CV_8U), utemp, map1, map2;
|
||||
temp = Scalar::all(1);
|
||||
initUndistortRectifyMap(M, D, R, P, imgsize, CV_16SC2, map1, map2);
|
||||
remap(temp, utemp, map1, map2, INTER_LINEAR);
|
||||
|
||||
if(roi.x < 0 || roi.y < 0 || roi.x + roi.width > imgsize.width || roi.y + roi.height > imgsize.height)
|
||||
{
|
||||
CV_LOG_ERROR(NULL, cv::format("The ROI=(%d, %d, %d, %d) is outside of the imge rectangle\n",
|
||||
roi.x, roi.y, roi.width, roi.height));
|
||||
return false;
|
||||
}
|
||||
double s = sum(utemp(roi))[0];
|
||||
if( s > roi.area() || roi.area() - s > roi.area()*(1-eps) )
|
||||
{
|
||||
CV_LOG_ERROR(NULL, cv::format("The ratio of black pixels inside the valid ROI (~%g%%) is too large\n",
|
||||
s*100./roi.area()));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST(StereoGeometry, stereoRectify)
|
||||
{
|
||||
// camera parameters are extracted from the original calib3d test CV_StereoCalibrationTest::run
|
||||
const Matx33d M1(
|
||||
530.4643719672913, 0, 319.5,
|
||||
0, 529.7477570329314, 239.5,
|
||||
0, 0, 1);
|
||||
const Matx<double, 5, 1> D1(-0.2982901576925627, 0.1134645765152131, 0, 0, 0);
|
||||
|
||||
const Matx33d M2(
|
||||
530.4643719672913, 0, 319.5,
|
||||
0, 529.7477570329314, 239.5,
|
||||
0, 0, 1);
|
||||
const Matx<double, 5, 1> D2(-0.2833068597502156, 0.0944810713984697, 0, 0, 0);
|
||||
|
||||
const Matx33d R(0.9996903750450727, 0.005330951201286465, -0.02430504066096785,
|
||||
-0.004837810799471072, 0.9997821583334892, 0.02030348405319902,
|
||||
0.02440798289310936, -0.02017961439967296, 0.9994983909610711);
|
||||
const Matx31d T(-3.328706469151101, 0.05621025406095936, -0.02956576727262086);
|
||||
|
||||
const Size imageSize(640, 480);
|
||||
|
||||
Mat R1, R2, P1, P2, Q;
|
||||
Rect roi1, roi2;
|
||||
|
||||
stereoRectify( M1, D1, M2, D2, imageSize, R, T, R1, R2, P1, P2, Q, 0, 1, imageSize, &roi1, &roi2 );
|
||||
|
||||
Mat eye33 = Mat::eye(3,3,CV_64F);
|
||||
Mat R1t = R1.t(), R2t = R2.t();
|
||||
|
||||
EXPECT_LE(cvtest::norm(R1t*R1 - eye33, NORM_L2), 0.01) << "R1 is not orthogonal!";
|
||||
EXPECT_LE(cvtest::norm(R2t*R2 - eye33, NORM_L2), 0.01) << "R2 is not orthogonal!";
|
||||
|
||||
//check that Tx after rectification is equal to distance between cameras
|
||||
double tx = fabs(P2.at<double>(0, 3) / P2.at<double>(0, 0));
|
||||
EXPECT_LE(fabs(tx - cvtest::norm(T, NORM_L2)), 1e-5);
|
||||
EXPECT_TRUE(checkPandROI(M1, D1, R1, P1, imageSize, roi1));
|
||||
EXPECT_TRUE(checkPandROI(M2, D2, R2, P2, imageSize, roi2));
|
||||
|
||||
//check that Q reprojects points before the camera
|
||||
double testPoint[4] = {0.0, 0.0, 100.0, 1.0};
|
||||
Mat reprojectedTestPoint = Q * Mat_<double>(4, 1, testPoint);
|
||||
CV_Assert(reprojectedTestPoint.type() == CV_64FC1);
|
||||
EXPECT_GT( reprojectedTestPoint.at<double>(2) / reprojectedTestPoint.at<double>(3), 0 ) << \
|
||||
"A point after rectification is reprojected behind the camera";
|
||||
}
|
||||
|
||||
TEST(StereoGeometry, regression_10791)
|
||||
{
|
||||
const Matx33d M1(
|
||||
853.1387981631528, 0, 704.154907802121,
|
||||
0, 853.6445089162528, 520.3600712930319,
|
||||
0, 0, 1
|
||||
);
|
||||
const Matx33d M2(
|
||||
848.6090216909176, 0, 701.6162856852185,
|
||||
0, 849.7040162357157, 509.1864036137,
|
||||
0, 0, 1
|
||||
);
|
||||
const Matx<double, 14, 1> D1(-6.463598629567206, 79.00104930508179, -0.0001006144444464403, -0.0005437499822299972,
|
||||
12.56900616588467, -6.056719942752855, 76.3842481414836, 45.57460250612659,
|
||||
0, 0, 0, 0, 0, 0);
|
||||
const Matx<double, 14, 1> D2(0.6123436439798265, -0.4671756923224087, -0.0001261947899033442, -0.000597334584036978,
|
||||
-0.05660119809538371, 1.037075740629769, -0.3076042835831711, -0.2502169324283623,
|
||||
0, 0, 0, 0, 0, 0);
|
||||
|
||||
const Matx33d R(
|
||||
0.9999926627018476, -0.0001095586963765905, 0.003829169539302921,
|
||||
0.0001021735876758584, 0.9999981346680941, 0.0019287874145156,
|
||||
-0.003829373712065528, -0.001928382022437616, 0.9999908085776333
|
||||
);
|
||||
const Matx31d T(-58.9161771697128, -0.01581306249996402, -0.8492960216760961);
|
||||
|
||||
const Size imageSize(1280, 960);
|
||||
|
||||
Mat R1, R2, P1, P2, Q;
|
||||
Rect roi1, roi2;
|
||||
stereoRectify(M1, D1, M2, D2, imageSize, R, T,
|
||||
R1, R2, P1, P2, Q,
|
||||
STEREO_ZERO_DISPARITY, 1, imageSize, &roi1, &roi2);
|
||||
|
||||
EXPECT_GE(roi1.area(), 400*300) << roi1;
|
||||
EXPECT_GE(roi2.area(), 400*300) << roi2;
|
||||
}
|
||||
|
||||
TEST(StereoGeometry, regression_11131)
|
||||
{
|
||||
const Matx33d M1(
|
||||
1457.572438721727, 0, 1212.945694211622,
|
||||
0, 1457.522226502963, 1007.32058848921,
|
||||
0, 0, 1
|
||||
);
|
||||
const Matx33d M2(
|
||||
1460.868570835972, 0, 1215.024068023046,
|
||||
0, 1460.791367088, 1011.107202932225,
|
||||
0, 0, 1
|
||||
);
|
||||
const Matx<double, 5, 1> D1(0, 0, 0, 0, 0);
|
||||
const Matx<double, 5, 1> D2(0, 0, 0, 0, 0);
|
||||
|
||||
const Matx33d R(
|
||||
0.9985404059825475, 0.02963547172078553, -0.04515303352041626,
|
||||
-0.03103795276460111, 0.9990471552537432, -0.03068268351343364,
|
||||
0.04420071389006859, 0.03203935697372317, 0.9985087763742083
|
||||
);
|
||||
const Matx31d T(0.9995500167379527, 0.0116311595111068, 0.02764923448462666);
|
||||
|
||||
const Size imageSize(2456, 2058);
|
||||
|
||||
Mat R1, R2, P1, P2, Q;
|
||||
Rect roi1, roi2;
|
||||
stereoRectify(M1, D1, M2, D2, imageSize, R, T,
|
||||
R1, R2, P1, P2, Q,
|
||||
STEREO_ZERO_DISPARITY, 1, imageSize, &roi1, &roi2);
|
||||
|
||||
EXPECT_GT(P1.at<double>(0, 0), 0);
|
||||
EXPECT_GT(P2.at<double>(0, 0), 0);
|
||||
EXPECT_GT(R1.at<double>(0, 0), 0);
|
||||
EXPECT_GT(R2.at<double>(0, 0), 0);
|
||||
EXPECT_GE(roi1.area(), 400*300) << roi1;
|
||||
EXPECT_GE(roi2.area(), 400*300) << roi2;
|
||||
}
|
||||
|
||||
TEST(StereoGeometry, regression_23305)
|
||||
{
|
||||
const Matx33d M1(
|
||||
850, 0, 640,
|
||||
0, 850, 640,
|
||||
0, 0, 1
|
||||
);
|
||||
|
||||
const Matx34d P1_gold(
|
||||
850, 0, 640, 0,
|
||||
0, 850, 640, 0,
|
||||
0, 0, 1, 0
|
||||
);
|
||||
|
||||
const Matx33d M2(
|
||||
850, 0, 640,
|
||||
0, 850, 640,
|
||||
0, 0, 1
|
||||
);
|
||||
|
||||
const Matx34d P2_gold(
|
||||
850, 0, 640, -2*850, // correcponds to T(-2., 0., 0.)
|
||||
0, 850, 640, 0,
|
||||
0, 0, 1, 0
|
||||
);
|
||||
|
||||
const Matx<double, 5, 1> D1(0, 0, 0, 0, 0);
|
||||
const Matx<double, 5, 1> D2(0, 0, 0, 0, 0);
|
||||
|
||||
const Matx33d R(
|
||||
1., 0., 0.,
|
||||
0., 1., 0.,
|
||||
0., 0., 1.
|
||||
);
|
||||
const Matx31d T(-2., 0., 0.);
|
||||
|
||||
const Size imageSize(1280, 1280);
|
||||
|
||||
Mat R1, R2, P1, P2, Q;
|
||||
Rect roi1, roi2;
|
||||
stereoRectify(M1, D1, M2, D2, imageSize, R, T,
|
||||
R1, R2, P1, P2, Q,
|
||||
STEREO_ZERO_DISPARITY, 0, imageSize, &roi1, &roi2);
|
||||
|
||||
EXPECT_EQ(cv::norm(P1, P1_gold), 0.);
|
||||
EXPECT_EQ(cv::norm(P2, P2_gold), 0.);
|
||||
}
|
||||
|
||||
class fisheyeTest : public ::testing::Test {
|
||||
|
||||
protected:
|
||||
const static cv::Size imageSize;
|
||||
const static cv::Matx33d K;
|
||||
const static cv::Vec4d D;
|
||||
const static cv::Matx33d R;
|
||||
const static cv::Vec3d T;
|
||||
std::string datasets_repository_path;
|
||||
|
||||
virtual void SetUp() {
|
||||
datasets_repository_path = combine(cvtest::TS::ptr()->get_data_path(), "cv/cameracalibration/fisheye");
|
||||
}
|
||||
|
||||
protected:
|
||||
std::string combine(const std::string& _item1, const std::string& _item2);
|
||||
static void merge4(const cv::Mat& tl, const cv::Mat& tr, const cv::Mat& bl, const cv::Mat& br, cv::Mat& merged);
|
||||
};
|
||||
|
||||
const cv::Size fisheyeTest::imageSize(1280, 800);
|
||||
|
||||
const cv::Matx33d fisheyeTest::K(558.478087865323, 0, 620.458515360843,
|
||||
0, 560.506767351568, 381.939424848348,
|
||||
0, 0, 1);
|
||||
|
||||
const cv::Vec4d fisheyeTest::D(-0.0014613319981768, -0.00329861110580401, 0.00605760088590183, -0.00374209380722371);
|
||||
|
||||
|
||||
const cv::Matx33d fisheyeTest::R ( 9.9756700084424932e-01, 6.9698277640183867e-02, 1.4929569991321144e-03,
|
||||
-6.9711825162322980e-02, 9.9748249845531767e-01, 1.2997180766418455e-02,
|
||||
-5.8331736398316541e-04,-1.3069635393884985e-02, 9.9991441852366736e-01);
|
||||
|
||||
const cv::Vec3d fisheyeTest::T(-9.9217369356044638e-02, 3.1741831972356663e-03, 1.8551007952921010e-04);
|
||||
|
||||
std::string fisheyeTest::combine(const std::string& _item1, const std::string& _item2)
|
||||
{
|
||||
std::string item1 = _item1, item2 = _item2;
|
||||
std::replace(item1.begin(), item1.end(), '\\', '/');
|
||||
std::replace(item2.begin(), item2.end(), '\\', '/');
|
||||
|
||||
if (item1.empty())
|
||||
return item2;
|
||||
|
||||
if (item2.empty())
|
||||
return item1;
|
||||
|
||||
char last = item1[item1.size()-1];
|
||||
return item1 + (last != '/' ? "/" : "") + item2;
|
||||
}
|
||||
|
||||
void fisheyeTest::merge4(const cv::Mat& tl, const cv::Mat& tr, const cv::Mat& bl, const cv::Mat& br, cv::Mat& merged)
|
||||
{
|
||||
int type = tl.type();
|
||||
cv::Size sz = tl.size();
|
||||
ASSERT_EQ(type, tr.type()); ASSERT_EQ(type, bl.type()); ASSERT_EQ(type, br.type());
|
||||
ASSERT_EQ(sz.width, tr.cols); ASSERT_EQ(sz.width, bl.cols); ASSERT_EQ(sz.width, br.cols);
|
||||
ASSERT_EQ(sz.height, tr.rows); ASSERT_EQ(sz.height, bl.rows); ASSERT_EQ(sz.height, br.rows);
|
||||
|
||||
merged.create(cv::Size(sz.width * 2, sz.height * 2), type);
|
||||
tl.copyTo(merged(cv::Rect(0, 0, sz.width, sz.height)));
|
||||
tr.copyTo(merged(cv::Rect(sz.width, 0, sz.width, sz.height)));
|
||||
bl.copyTo(merged(cv::Rect(0, sz.height, sz.width, sz.height)));
|
||||
br.copyTo(merged(cv::Rect(sz.width, sz.height, sz.width, sz.height)));
|
||||
}
|
||||
|
||||
TEST_F(fisheyeTest, stereoRectify)
|
||||
{
|
||||
const std::string folder = combine(datasets_repository_path, "calib-3_stereo_from_JY");
|
||||
|
||||
cv::Size calibration_size = this->imageSize, requested_size = calibration_size;
|
||||
cv::Matx33d K1 = this->K, K2 = K1;
|
||||
cv::Mat D1 = cv::Mat(this->D), D2 = D1;
|
||||
|
||||
cv::Vec3d theT = this->T;
|
||||
cv::Matx33d theR = this->R;
|
||||
|
||||
double balance = 0.0, fov_scale = 1.1;
|
||||
cv::Mat R1, R2, P1, P2, Q;
|
||||
cv::fisheye::stereoRectify(K1, D1, K2, D2, calibration_size, theR, theT, R1, R2, P1, P2, Q,
|
||||
cv::STEREO_ZERO_DISPARITY, requested_size, balance, fov_scale);
|
||||
|
||||
// Collected with these CMake flags: -DWITH_IPP=OFF -DCV_ENABLE_INTRINSICS=OFF -DCV_DISABLE_OPTIMIZATION=ON -DCMAKE_BUILD_TYPE=Debug
|
||||
cv::Matx33d R1_ref(
|
||||
0.9992853269091279, 0.03779164101000276, -0.0007920188690205426,
|
||||
-0.03778569762983931, 0.9992646472015868, 0.006511981857667881,
|
||||
0.001037534936357442, -0.006477400933964018, 0.9999784831677112
|
||||
);
|
||||
cv::Matx33d R2_ref(
|
||||
0.9994868963898833, -0.03197579751378937, -0.001868774538573449,
|
||||
0.03196298186616116, 0.9994677442608699, -0.0065265589947392,
|
||||
0.002076471801477729, 0.006463478587068991, 0.9999769555891836
|
||||
);
|
||||
cv::Matx34d P1_ref(
|
||||
420.9684016542647, 0, 586.3059567784627, 0,
|
||||
0, 420.9684016542647, 374.8571836462291, 0,
|
||||
0, 0, 1, 0
|
||||
);
|
||||
cv::Matx34d P2_ref(
|
||||
420.9684016542647, 0, 586.3059567784627, -41.78881938824554,
|
||||
0, 420.9684016542647, 374.8571836462291, 0,
|
||||
0, 0, 1, 0
|
||||
);
|
||||
cv::Matx44d Q_ref(
|
||||
1, 0, 0, -586.3059567784627,
|
||||
0, 1, 0, -374.8571836462291,
|
||||
0, 0, 0, 420.9684016542647,
|
||||
0, 0, 10.07370889670733, -0
|
||||
);
|
||||
|
||||
const double eps = 1e-10;
|
||||
EXPECT_MAT_NEAR(R1_ref, R1, eps);
|
||||
EXPECT_MAT_NEAR(R2_ref, R2, eps);
|
||||
EXPECT_MAT_NEAR(P1_ref, P1, eps);
|
||||
EXPECT_MAT_NEAR(P2_ref, P2, eps);
|
||||
EXPECT_MAT_NEAR(Q_ref, Q, eps);
|
||||
|
||||
if (::testing::Test::HasFailure())
|
||||
{
|
||||
std::cout << "Actual values are:" << std::endl
|
||||
<< "R1 =" << std::endl << R1 << std::endl
|
||||
<< "R2 =" << std::endl << R2 << std::endl
|
||||
<< "P1 =" << std::endl << P1 << std::endl
|
||||
<< "P2 =" << std::endl << P2 << std::endl
|
||||
<< "Q =" << std::endl << Q << std::endl;
|
||||
}
|
||||
|
||||
if (cvtest::debugLevel == 0)
|
||||
return;
|
||||
// DEBUG code is below
|
||||
|
||||
cv::Mat lmapx, lmapy, rmapx, rmapy;
|
||||
//rewrite for fisheye
|
||||
cv::fisheye::initUndistortRectifyMap(K1, D1, R1, P1, requested_size, CV_32F, lmapx, lmapy);
|
||||
cv::fisheye::initUndistortRectifyMap(K2, D2, R2, P2, requested_size, CV_32F, rmapx, rmapy);
|
||||
|
||||
cv::Mat l, r, lundist, rundist;
|
||||
for (int i = 0; i < 34; ++i)
|
||||
{
|
||||
SCOPED_TRACE(cv::format("image %d", i));
|
||||
l = imread(combine(folder, cv::format("left/stereo_pair_%03d.jpg", i)), cv::IMREAD_COLOR);
|
||||
r = imread(combine(folder, cv::format("right/stereo_pair_%03d.jpg", i)), cv::IMREAD_COLOR);
|
||||
ASSERT_FALSE(l.empty());
|
||||
ASSERT_FALSE(r.empty());
|
||||
|
||||
int ndisp = 128;
|
||||
cv::rectangle(l, cv::Rect(255, 0, 829, l.rows-1), cv::Scalar(0, 0, 255));
|
||||
cv::rectangle(r, cv::Rect(255, 0, 829, l.rows-1), cv::Scalar(0, 0, 255));
|
||||
cv::rectangle(r, cv::Rect(255-ndisp, 0, 829+ndisp ,l.rows-1), cv::Scalar(0, 0, 255));
|
||||
cv::remap(l, lundist, lmapx, lmapy, cv::INTER_LINEAR);
|
||||
cv::remap(r, rundist, rmapx, rmapy, cv::INTER_LINEAR);
|
||||
|
||||
for (int ii = 0; ii < lundist.rows; ii += 20)
|
||||
{
|
||||
cv::line(lundist, cv::Point(0, ii), cv::Point(lundist.cols, ii), cv::Scalar(0, 255, 0));
|
||||
cv::line(rundist, cv::Point(0, ii), cv::Point(lundist.cols, ii), cv::Scalar(0, 255, 0));
|
||||
}
|
||||
|
||||
cv::Mat rectification;
|
||||
merge4(l, r, lundist, rundist, rectification);
|
||||
|
||||
// Add the "--test_debug" to arguments for file output
|
||||
if (cvtest::debugLevel > 0)
|
||||
cv::imwrite(cv::format("fisheye_rectification_AB_%03d.png", i), rectification);
|
||||
}
|
||||
}
|
||||
|
||||
}}
|
||||
@@ -0,0 +1,10 @@
|
||||
// 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"
|
||||
|
||||
#if defined(HAVE_HPX)
|
||||
#include <hpx/hpx_main.hpp>
|
||||
#endif
|
||||
|
||||
CV_TEST_MAIN("")
|
||||
@@ -0,0 +1,13 @@
|
||||
// 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 <functional>
|
||||
#include <numeric>
|
||||
|
||||
#include "opencv2/ts.hpp"
|
||||
#include "opencv2/stereo.hpp"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,173 @@
|
||||
/*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"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
template<class T> double thres() { return 1.0; }
|
||||
template<> double thres<float>() { return 1e-5; }
|
||||
|
||||
class CV_ReprojectImageTo3DTest : public cvtest::BaseTest
|
||||
{
|
||||
public:
|
||||
CV_ReprojectImageTo3DTest() {}
|
||||
~CV_ReprojectImageTo3DTest() {}
|
||||
protected:
|
||||
|
||||
|
||||
void run(int)
|
||||
{
|
||||
ts->set_failed_test_info(cvtest::TS::OK);
|
||||
int progress = 0;
|
||||
int caseId = 0;
|
||||
|
||||
progress = update_progress( progress, 1, 14, 0 );
|
||||
runCase<float, float>(++caseId, -100.f, 100.f);
|
||||
progress = update_progress( progress, 2, 14, 0 );
|
||||
runCase<int, float>(++caseId, -100, 100);
|
||||
progress = update_progress( progress, 3, 14, 0 );
|
||||
runCase<short, float>(++caseId, -100, 100);
|
||||
progress = update_progress( progress, 4, 14, 0 );
|
||||
runCase<unsigned char, float>(++caseId, 10, 100);
|
||||
progress = update_progress( progress, 5, 14, 0 );
|
||||
|
||||
runCase<float, int>(++caseId, -100.f, 100.f);
|
||||
progress = update_progress( progress, 6, 14, 0 );
|
||||
runCase<int, int>(++caseId, -100, 100);
|
||||
progress = update_progress( progress, 7, 14, 0 );
|
||||
runCase<short, int>(++caseId, -100, 100);
|
||||
progress = update_progress( progress, 8, 14, 0 );
|
||||
runCase<unsigned char, int>(++caseId, 10, 100);
|
||||
progress = update_progress( progress, 10, 14, 0 );
|
||||
|
||||
runCase<float, short>(++caseId, -100.f, 100.f);
|
||||
progress = update_progress( progress, 11, 14, 0 );
|
||||
runCase<int, short>(++caseId, -100, 100);
|
||||
progress = update_progress( progress, 12, 14, 0 );
|
||||
runCase<short, short>(++caseId, -100, 100);
|
||||
progress = update_progress( progress, 13, 14, 0 );
|
||||
runCase<unsigned char, short>(++caseId, 10, 100);
|
||||
progress = update_progress( progress, 14, 14, 0 );
|
||||
}
|
||||
|
||||
template<class U, class V> double error(const Vec<U, 3>& v1, const Vec<V, 3>& v2) const
|
||||
{
|
||||
double tmp, sum = 0;
|
||||
double nsum = 0;
|
||||
for(int i = 0; i < 3; ++i)
|
||||
{
|
||||
tmp = v1[i];
|
||||
nsum += tmp * tmp;
|
||||
|
||||
tmp = tmp - v2[i];
|
||||
sum += tmp * tmp;
|
||||
|
||||
}
|
||||
return sqrt(sum)/(sqrt(nsum)+1.);
|
||||
}
|
||||
|
||||
template<class InT, class OutT> void runCase(int caseId, InT min, InT max)
|
||||
{
|
||||
typedef Vec<OutT, 3> out3d_t;
|
||||
|
||||
bool handleMissingValues = (unsigned)theRNG() % 2 == 0;
|
||||
|
||||
Mat_<InT> disp(Size(320, 240));
|
||||
randu(disp, Scalar(min), Scalar(max));
|
||||
|
||||
if (handleMissingValues)
|
||||
disp(disp.rows/2, disp.cols/2) = min - 1;
|
||||
|
||||
Mat_<double> Q(4, 4);
|
||||
randu(Q, Scalar(-5), Scalar(5));
|
||||
Mat_<out3d_t> _3dImg(disp.size());
|
||||
|
||||
reprojectImageTo3D(disp, _3dImg, Q, handleMissingValues);
|
||||
|
||||
for(int y = 0; y < disp.rows; ++y)
|
||||
for(int x = 0; x < disp.cols; ++x)
|
||||
{
|
||||
InT d = disp(y, x);
|
||||
|
||||
double from[4] = {
|
||||
static_cast<double>(x),
|
||||
static_cast<double>(y),
|
||||
static_cast<double>(d),
|
||||
1.0,
|
||||
};
|
||||
Mat_<double> res = Q * Mat_<double>(4, 1, from);
|
||||
res /= res(3, 0);
|
||||
|
||||
out3d_t pixel_exp = *res.ptr<Vec3d>();
|
||||
out3d_t pixel_out = _3dImg(y, x);
|
||||
|
||||
const int largeZValue = 10000; /* see documentation */
|
||||
|
||||
if (handleMissingValues && y == disp.rows/2 && x == disp.cols/2)
|
||||
{
|
||||
if (pixel_out[2] == largeZValue)
|
||||
continue;
|
||||
|
||||
ts->printf(cvtest::TS::LOG, "Missing values are handled improperly\n");
|
||||
ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY );
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
double err = error(pixel_out, pixel_exp), t = thres<OutT>();
|
||||
if ( err > t )
|
||||
{
|
||||
ts->printf(cvtest::TS::LOG, "case %d. too big error at (%d, %d): %g vs expected %g: res = (%g, %g, %g, w=%g) vs pixel_out = (%g, %g, %g)\n",
|
||||
caseId, x, y, err, t, res(0,0), res(1,0), res(2,0), res(3,0),
|
||||
(double)pixel_out[0], (double)pixel_out[1], (double)pixel_out[2]);
|
||||
ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST(Calib3d_ReprojectImageTo3D, accuracy) { CV_ReprojectImageTo3DTest test; test.safe_run(); }
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,977 @@
|
||||
/*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*/
|
||||
|
||||
/*
|
||||
This is a regression test for stereo matching algorithms. This test gets some quality metrics
|
||||
described in "A Taxonomy and Evaluation of Dense Two-Frame Stereo Correspondence Algorithms".
|
||||
Daniel Scharstein, Richard Szeliski
|
||||
*/
|
||||
|
||||
#include "test_precomp.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
const float EVAL_BAD_THRESH = 1.f;
|
||||
const int EVAL_TEXTURELESS_WIDTH = 3;
|
||||
const float EVAL_TEXTURELESS_THRESH = 4.f;
|
||||
const float EVAL_DISP_THRESH = 1.f;
|
||||
const float EVAL_DISP_GAP = 2.f;
|
||||
const int EVAL_DISCONT_WIDTH = 9;
|
||||
const int EVAL_IGNORE_BORDER = 10;
|
||||
|
||||
const int ERROR_KINDS_COUNT = 6;
|
||||
|
||||
//============================== quality measuring functions =================================================
|
||||
|
||||
/*
|
||||
Calculate textureless regions of image (regions where the squared horizontal intensity gradient averaged over
|
||||
a square window of size=evalTexturelessWidth is below a threshold=evalTexturelessThresh) and textured regions.
|
||||
*/
|
||||
void computeTextureBasedMasks( const Mat& _img, Mat* texturelessMask, Mat* texturedMask,
|
||||
int texturelessWidth = EVAL_TEXTURELESS_WIDTH, float texturelessThresh = EVAL_TEXTURELESS_THRESH )
|
||||
{
|
||||
if( !texturelessMask && !texturedMask )
|
||||
return;
|
||||
if( _img.empty() )
|
||||
CV_Error( Error::StsBadArg, "img is empty" );
|
||||
|
||||
Mat img = _img;
|
||||
if( _img.channels() > 1)
|
||||
{
|
||||
Mat tmp; cvtColor( _img, tmp, COLOR_BGR2GRAY ); img = tmp;
|
||||
}
|
||||
Mat dxI; Sobel( img, dxI, CV_32FC1, 1, 0, 3 );
|
||||
Mat dxI2; pow( dxI / 8.f/*normalize*/, 2, dxI2 );
|
||||
Mat avgDxI2; boxFilter( dxI2, avgDxI2, CV_32FC1, Size(texturelessWidth,texturelessWidth) );
|
||||
|
||||
if( texturelessMask )
|
||||
*texturelessMask = avgDxI2 < texturelessThresh;
|
||||
if( texturedMask )
|
||||
*texturedMask = avgDxI2 >= texturelessThresh;
|
||||
}
|
||||
|
||||
void checkTypeAndSizeOfDisp( const Mat& dispMap, const Size* sz )
|
||||
{
|
||||
if( dispMap.empty() )
|
||||
CV_Error( Error::StsBadArg, "dispMap is empty" );
|
||||
if( dispMap.type() != CV_32FC1 )
|
||||
CV_Error( Error::StsBadArg, "dispMap must have CV_32FC1 type" );
|
||||
if( sz && (dispMap.rows != sz->height || dispMap.cols != sz->width) )
|
||||
CV_Error( Error::StsBadArg, "dispMap has incorrect size" );
|
||||
}
|
||||
|
||||
void checkTypeAndSizeOfMask( const Mat& mask, Size sz )
|
||||
{
|
||||
if( mask.empty() )
|
||||
CV_Error( Error::StsBadArg, "mask is empty" );
|
||||
if( mask.type() != CV_8UC1 )
|
||||
CV_Error( Error::StsBadArg, "mask must have CV_8UC1 type" );
|
||||
if( mask.rows != sz.height || mask.cols != sz.width )
|
||||
CV_Error( Error::StsBadArg, "mask has incorrect size" );
|
||||
}
|
||||
|
||||
void checkDispMapsAndUnknDispMasks( const Mat& leftDispMap, const Mat& rightDispMap,
|
||||
const Mat& leftUnknDispMask, const Mat& rightUnknDispMask )
|
||||
{
|
||||
// check type and size of disparity maps
|
||||
checkTypeAndSizeOfDisp( leftDispMap, 0 );
|
||||
if( !rightDispMap.empty() )
|
||||
{
|
||||
Size sz = leftDispMap.size();
|
||||
checkTypeAndSizeOfDisp( rightDispMap, &sz );
|
||||
}
|
||||
|
||||
// check size and type of unknown disparity maps
|
||||
if( !leftUnknDispMask.empty() )
|
||||
checkTypeAndSizeOfMask( leftUnknDispMask, leftDispMap.size() );
|
||||
if( !rightUnknDispMask.empty() )
|
||||
checkTypeAndSizeOfMask( rightUnknDispMask, rightDispMap.size() );
|
||||
|
||||
// check values of disparity maps (known disparity values musy be positive)
|
||||
double leftMinVal = 0, rightMinVal = 0;
|
||||
if( leftUnknDispMask.empty() )
|
||||
minMaxLoc( leftDispMap, &leftMinVal );
|
||||
else
|
||||
minMaxLoc( leftDispMap, &leftMinVal, 0, 0, 0, ~leftUnknDispMask );
|
||||
if( !rightDispMap.empty() )
|
||||
{
|
||||
if( rightUnknDispMask.empty() )
|
||||
minMaxLoc( rightDispMap, &rightMinVal );
|
||||
else
|
||||
minMaxLoc( rightDispMap, &rightMinVal, 0, 0, 0, ~rightUnknDispMask );
|
||||
}
|
||||
if( leftMinVal < 0 || rightMinVal < 0)
|
||||
CV_Error( Error::StsBadArg, "known disparity values must be positive" );
|
||||
}
|
||||
|
||||
/*
|
||||
Calculate occluded regions of reference image (left image) (regions that are occluded in the matching image (right image),
|
||||
i.e., where the forward-mapped disparity lands at a location with a larger (nearer) disparity) and non occluded regions.
|
||||
*/
|
||||
void computeOcclusionBasedMasks( const Mat& leftDisp, const Mat& _rightDisp,
|
||||
Mat* occludedMask, Mat* nonOccludedMask,
|
||||
const Mat& leftUnknDispMask = Mat(), const Mat& rightUnknDispMask = Mat(),
|
||||
float dispThresh = EVAL_DISP_THRESH )
|
||||
{
|
||||
if( !occludedMask && !nonOccludedMask )
|
||||
return;
|
||||
checkDispMapsAndUnknDispMasks( leftDisp, _rightDisp, leftUnknDispMask, rightUnknDispMask );
|
||||
|
||||
Mat rightDisp;
|
||||
if( _rightDisp.empty() )
|
||||
{
|
||||
if( !rightUnknDispMask.empty() )
|
||||
CV_Error( Error::StsBadArg, "rightUnknDispMask must be empty if _rightDisp is empty" );
|
||||
rightDisp.create(leftDisp.size(), CV_32FC1);
|
||||
rightDisp.setTo(Scalar::all(0) );
|
||||
for( int leftY = 0; leftY < leftDisp.rows; leftY++ )
|
||||
{
|
||||
for( int leftX = 0; leftX < leftDisp.cols; leftX++ )
|
||||
{
|
||||
if( !leftUnknDispMask.empty() && leftUnknDispMask.at<uchar>(leftY,leftX) )
|
||||
continue;
|
||||
float leftDispVal = leftDisp.at<float>(leftY, leftX);
|
||||
int rightX = leftX - cvRound(leftDispVal), rightY = leftY;
|
||||
if( rightX >= 0)
|
||||
rightDisp.at<float>(rightY,rightX) = max(rightDisp.at<float>(rightY,rightX), leftDispVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
_rightDisp.copyTo(rightDisp);
|
||||
|
||||
if( occludedMask )
|
||||
{
|
||||
occludedMask->create(leftDisp.size(), CV_8UC1);
|
||||
occludedMask->setTo(Scalar::all(0) );
|
||||
}
|
||||
if( nonOccludedMask )
|
||||
{
|
||||
nonOccludedMask->create(leftDisp.size(), CV_8UC1);
|
||||
nonOccludedMask->setTo(Scalar::all(0) );
|
||||
}
|
||||
for( int leftY = 0; leftY < leftDisp.rows; leftY++ )
|
||||
{
|
||||
for( int leftX = 0; leftX < leftDisp.cols; leftX++ )
|
||||
{
|
||||
if( !leftUnknDispMask.empty() && leftUnknDispMask.at<uchar>(leftY,leftX) )
|
||||
continue;
|
||||
float leftDispVal = leftDisp.at<float>(leftY, leftX);
|
||||
int rightX = leftX - cvRound(leftDispVal), rightY = leftY;
|
||||
if( rightX < 0 && occludedMask )
|
||||
occludedMask->at<uchar>(leftY, leftX) = 255;
|
||||
else
|
||||
{
|
||||
if( !rightUnknDispMask.empty() && rightUnknDispMask.at<uchar>(rightY,rightX) )
|
||||
continue;
|
||||
float rightDispVal = rightDisp.at<float>(rightY, rightX);
|
||||
if( rightDispVal > leftDispVal + dispThresh )
|
||||
{
|
||||
if( occludedMask )
|
||||
occludedMask->at<uchar>(leftY, leftX) = 255;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( nonOccludedMask )
|
||||
nonOccludedMask->at<uchar>(leftY, leftX) = 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Calculate depth discontinuity regions: pixels whose neighboring disparities differ by more than
|
||||
dispGap, dilated by window of width discontWidth.
|
||||
*/
|
||||
void computeDepthDiscontMask( const Mat& disp, Mat& depthDiscontMask, const Mat& unknDispMask = Mat(),
|
||||
float dispGap = EVAL_DISP_GAP, int discontWidth = EVAL_DISCONT_WIDTH )
|
||||
{
|
||||
if( disp.empty() )
|
||||
CV_Error( Error::StsBadArg, "disp is empty" );
|
||||
if( disp.type() != CV_32FC1 )
|
||||
CV_Error( Error::StsBadArg, "disp must have CV_32FC1 type" );
|
||||
if( !unknDispMask.empty() )
|
||||
checkTypeAndSizeOfMask( unknDispMask, disp.size() );
|
||||
|
||||
Mat curDisp; disp.copyTo( curDisp );
|
||||
if( !unknDispMask.empty() )
|
||||
curDisp.setTo( Scalar(std::numeric_limits<float>::min()), unknDispMask );
|
||||
Mat maxNeighbDisp; dilate( curDisp, maxNeighbDisp, Mat(3, 3, CV_8UC1, Scalar(1)) );
|
||||
if( !unknDispMask.empty() )
|
||||
curDisp.setTo( Scalar(std::numeric_limits<float>::max()), unknDispMask );
|
||||
Mat minNeighbDisp; erode( curDisp, minNeighbDisp, Mat(3, 3, CV_8UC1, Scalar(1)) );
|
||||
depthDiscontMask = max( (Mat)(maxNeighbDisp-disp), (Mat)(disp-minNeighbDisp) ) > dispGap;
|
||||
if( !unknDispMask.empty() )
|
||||
depthDiscontMask &= ~unknDispMask;
|
||||
dilate( depthDiscontMask, depthDiscontMask, Mat(discontWidth, discontWidth, CV_8UC1, Scalar(1)) );
|
||||
}
|
||||
|
||||
/*
|
||||
Get evaluation masks excluding a border.
|
||||
*/
|
||||
Mat getBorderedMask( Size maskSize, int border = EVAL_IGNORE_BORDER )
|
||||
{
|
||||
CV_Assert( border >= 0 );
|
||||
Mat mask(maskSize, CV_8UC1, Scalar(0));
|
||||
int w = maskSize.width - 2*border, h = maskSize.height - 2*border;
|
||||
if( w < 0 || h < 0 )
|
||||
mask.setTo(Scalar(0));
|
||||
else
|
||||
mask( Rect(Point(border,border),Size(w,h)) ).setTo(Scalar(255));
|
||||
return mask;
|
||||
}
|
||||
|
||||
/*
|
||||
Calculate root-mean-squared error between the computed disparity map (computedDisp) and ground truth map (groundTruthDisp).
|
||||
*/
|
||||
float dispRMS( const Mat& computedDisp, const Mat& groundTruthDisp, const Mat& mask )
|
||||
{
|
||||
checkTypeAndSizeOfDisp( groundTruthDisp, 0 );
|
||||
Size sz = groundTruthDisp.size();
|
||||
checkTypeAndSizeOfDisp( computedDisp, &sz );
|
||||
|
||||
int pointsCount = sz.height*sz.width;
|
||||
if( !mask.empty() )
|
||||
{
|
||||
checkTypeAndSizeOfMask( mask, sz );
|
||||
pointsCount = countNonZero(mask);
|
||||
}
|
||||
return 1.f/sqrt((float)pointsCount) * (float)cvtest::norm(computedDisp, groundTruthDisp, NORM_L2, mask);
|
||||
}
|
||||
|
||||
/*
|
||||
Calculate fraction of bad matching pixels.
|
||||
*/
|
||||
float badMatchPxlsFraction( const Mat& computedDisp, const Mat& groundTruthDisp, const Mat& mask,
|
||||
float _badThresh = EVAL_BAD_THRESH )
|
||||
{
|
||||
int badThresh = cvRound(_badThresh);
|
||||
checkTypeAndSizeOfDisp( groundTruthDisp, 0 );
|
||||
Size sz = groundTruthDisp.size();
|
||||
checkTypeAndSizeOfDisp( computedDisp, &sz );
|
||||
|
||||
Mat badPxlsMap;
|
||||
absdiff( computedDisp, groundTruthDisp, badPxlsMap );
|
||||
badPxlsMap = badPxlsMap > badThresh;
|
||||
int pointsCount = sz.height*sz.width;
|
||||
if( !mask.empty() )
|
||||
{
|
||||
checkTypeAndSizeOfMask( mask, sz );
|
||||
badPxlsMap = badPxlsMap & mask;
|
||||
pointsCount = countNonZero(mask);
|
||||
}
|
||||
return 1.f/pointsCount * countNonZero(badPxlsMap);
|
||||
}
|
||||
|
||||
//===================== regression test for stereo matching algorithms ==============================
|
||||
|
||||
const string ALGORITHMS_DIR = "stereomatching/algorithms/";
|
||||
const string DATASETS_DIR = "stereomatching/datasets/";
|
||||
const string DATASETS_FILE = "datasets.xml";
|
||||
|
||||
const string RUN_PARAMS_FILE = "_params.xml";
|
||||
const string RESULT_FILE = "_res.xml";
|
||||
|
||||
const string LEFT_IMG_NAME = "im2.png";
|
||||
const string RIGHT_IMG_NAME = "im6.png";
|
||||
const string TRUE_LEFT_DISP_NAME = "disp2.png";
|
||||
const string TRUE_RIGHT_DISP_NAME = "disp6.png";
|
||||
|
||||
string ERROR_PREFIXES[] = { "borderedAll",
|
||||
"borderedNoOccl",
|
||||
"borderedOccl",
|
||||
"borderedTextured",
|
||||
"borderedTextureless",
|
||||
"borderedDepthDiscont" }; // size of ERROR_KINDS_COUNT
|
||||
|
||||
string ROI_PREFIXES[] = { "roiX",
|
||||
"roiY",
|
||||
"roiWidth",
|
||||
"roiHeight" };
|
||||
|
||||
|
||||
const string RMS_STR = "RMS";
|
||||
const string BAD_PXLS_FRACTION_STR = "BadPxlsFraction";
|
||||
const string ROI_STR = "ValidDisparityROI";
|
||||
|
||||
class QualityEvalParams
|
||||
{
|
||||
public:
|
||||
QualityEvalParams() { setDefaults(); }
|
||||
QualityEvalParams( int _ignoreBorder )
|
||||
{
|
||||
setDefaults();
|
||||
ignoreBorder = _ignoreBorder;
|
||||
}
|
||||
void setDefaults()
|
||||
{
|
||||
badThresh = EVAL_BAD_THRESH;
|
||||
texturelessWidth = EVAL_TEXTURELESS_WIDTH;
|
||||
texturelessThresh = EVAL_TEXTURELESS_THRESH;
|
||||
dispThresh = EVAL_DISP_THRESH;
|
||||
dispGap = EVAL_DISP_GAP;
|
||||
discontWidth = EVAL_DISCONT_WIDTH;
|
||||
ignoreBorder = EVAL_IGNORE_BORDER;
|
||||
}
|
||||
float badThresh;
|
||||
int texturelessWidth;
|
||||
float texturelessThresh;
|
||||
float dispThresh;
|
||||
float dispGap;
|
||||
int discontWidth;
|
||||
int ignoreBorder;
|
||||
};
|
||||
|
||||
class CV_StereoMatchingTest : public cvtest::BaseTest
|
||||
{
|
||||
public:
|
||||
CV_StereoMatchingTest()
|
||||
{ rmsEps.resize( ERROR_KINDS_COUNT, 0.01f ); fracEps.resize( ERROR_KINDS_COUNT, 1.e-6f ); }
|
||||
protected:
|
||||
// assumed that left image is a reference image
|
||||
virtual int runStereoMatchingAlgorithm( const Mat& leftImg, const Mat& rightImg,
|
||||
Rect& calcROI, Mat& leftDisp, Mat& rightDisp, int caseIdx ) = 0; // return ignored border width
|
||||
|
||||
int readDatasetsParams( FileStorage& fs );
|
||||
virtual int readRunParams( FileStorage& fs );
|
||||
void writeErrors( const string& errName, const vector<float>& errors, FileStorage* fs = 0 );
|
||||
void writeROI( const Rect& calcROI, FileStorage* fs = 0 );
|
||||
void readErrors( FileNode& fn, const string& errName, vector<float>& errors );
|
||||
void readROI( FileNode& fn, Rect& trueROI );
|
||||
int compareErrors( const vector<float>& calcErrors, const vector<float>& validErrors,
|
||||
const vector<float>& eps, const string& errName );
|
||||
int compareROI( const Rect& calcROI, const Rect& validROI );
|
||||
int processStereoMatchingResults( FileStorage& fs, int caseIdx, bool isWrite,
|
||||
const Mat& leftImg, const Mat& rightImg,
|
||||
const Rect& calcROI,
|
||||
const Mat& trueLeftDisp, const Mat& trueRightDisp,
|
||||
const Mat& leftDisp, const Mat& rightDisp,
|
||||
const QualityEvalParams& qualityEvalParams );
|
||||
void run( int );
|
||||
|
||||
vector<float> rmsEps;
|
||||
vector<float> fracEps;
|
||||
|
||||
struct DatasetParams
|
||||
{
|
||||
int dispScaleFactor;
|
||||
int dispUnknVal;
|
||||
};
|
||||
map<string, DatasetParams> datasetsParams;
|
||||
|
||||
vector<string> caseNames;
|
||||
vector<string> caseDatasets;
|
||||
};
|
||||
|
||||
void CV_StereoMatchingTest::run(int)
|
||||
{
|
||||
string dataPath = ts->get_data_path() + "cv/";
|
||||
string algorithmName = name;
|
||||
CV_Assert( !algorithmName.empty() );
|
||||
if( dataPath.empty() )
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "dataPath is empty" );
|
||||
ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ARG_CHECK );
|
||||
return;
|
||||
}
|
||||
|
||||
FileStorage datasetsFS( dataPath + DATASETS_DIR + DATASETS_FILE, FileStorage::READ );
|
||||
int code = readDatasetsParams( datasetsFS );
|
||||
if( code != cvtest::TS::OK )
|
||||
{
|
||||
ts->set_failed_test_info( code );
|
||||
return;
|
||||
}
|
||||
FileStorage runParamsFS( dataPath + ALGORITHMS_DIR + algorithmName + RUN_PARAMS_FILE, FileStorage::READ );
|
||||
code = readRunParams( runParamsFS );
|
||||
if( code != cvtest::TS::OK )
|
||||
{
|
||||
ts->set_failed_test_info( code );
|
||||
return;
|
||||
}
|
||||
|
||||
string fullResultFilename = dataPath + ALGORITHMS_DIR + algorithmName + RESULT_FILE;
|
||||
FileStorage resFS( fullResultFilename, FileStorage::READ );
|
||||
bool isWrite = true; // write or compare results
|
||||
if( resFS.isOpened() )
|
||||
isWrite = false;
|
||||
else
|
||||
{
|
||||
resFS.open( fullResultFilename, FileStorage::WRITE );
|
||||
if( !resFS.isOpened() )
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "file %s can not be read or written\n", fullResultFilename.c_str() );
|
||||
ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ARG_CHECK );
|
||||
return;
|
||||
}
|
||||
resFS << "stereo_matching" << "{";
|
||||
}
|
||||
|
||||
int progress = 0, caseCount = (int)caseNames.size();
|
||||
for( int ci = 0; ci < caseCount; ci++)
|
||||
{
|
||||
progress = update_progress( progress, ci, caseCount, 0 );
|
||||
printf("progress: %d%%\n", progress);
|
||||
fflush(stdout);
|
||||
string datasetName = caseDatasets[ci];
|
||||
string datasetFullDirName = dataPath + DATASETS_DIR + datasetName + "/";
|
||||
Mat leftImg = imread(datasetFullDirName + LEFT_IMG_NAME);
|
||||
Mat rightImg = imread(datasetFullDirName + RIGHT_IMG_NAME);
|
||||
Mat trueLeftDisp = imread(datasetFullDirName + TRUE_LEFT_DISP_NAME, IMREAD_GRAYSCALE);
|
||||
Mat trueRightDisp = imread(datasetFullDirName + TRUE_RIGHT_DISP_NAME, IMREAD_GRAYSCALE);
|
||||
Rect calcROI;
|
||||
|
||||
if( leftImg.empty() || rightImg.empty() || trueLeftDisp.empty() )
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "images or left ground-truth disparities of dataset %s can not be read", datasetName.c_str() );
|
||||
code = cvtest::TS::FAIL_INVALID_TEST_DATA;
|
||||
continue;
|
||||
}
|
||||
int dispScaleFactor = datasetsParams[datasetName].dispScaleFactor;
|
||||
Mat tmp;
|
||||
|
||||
trueLeftDisp.convertTo( tmp, CV_32FC1, 1.f/dispScaleFactor );
|
||||
trueLeftDisp = tmp;
|
||||
tmp.release();
|
||||
|
||||
if( !trueRightDisp.empty() )
|
||||
{
|
||||
trueRightDisp.convertTo( tmp, CV_32FC1, 1.f/dispScaleFactor );
|
||||
trueRightDisp = tmp;
|
||||
tmp.release();
|
||||
}
|
||||
|
||||
Mat leftDisp, rightDisp;
|
||||
int ignBorder = max(runStereoMatchingAlgorithm(leftImg, rightImg, calcROI, leftDisp, rightDisp, ci), EVAL_IGNORE_BORDER);
|
||||
|
||||
leftDisp.convertTo( tmp, CV_32FC1 );
|
||||
leftDisp = tmp;
|
||||
tmp.release();
|
||||
|
||||
rightDisp.convertTo( tmp, CV_32FC1 );
|
||||
rightDisp = tmp;
|
||||
tmp.release();
|
||||
|
||||
int tempCode = processStereoMatchingResults( resFS, ci, isWrite,
|
||||
leftImg, rightImg, calcROI, trueLeftDisp, trueRightDisp, leftDisp, rightDisp, QualityEvalParams(ignBorder));
|
||||
code = tempCode==cvtest::TS::OK ? code : tempCode;
|
||||
}
|
||||
|
||||
if( isWrite )
|
||||
resFS << "}"; // "stereo_matching"
|
||||
|
||||
ts->set_failed_test_info( code );
|
||||
}
|
||||
|
||||
void calcErrors( const Mat& leftImg, const Mat& /*rightImg*/,
|
||||
const Mat& trueLeftDisp, const Mat& trueRightDisp,
|
||||
const Mat& trueLeftUnknDispMask, const Mat& trueRightUnknDispMask,
|
||||
const Mat& calcLeftDisp, const Mat& /*calcRightDisp*/,
|
||||
vector<float>& rms, vector<float>& badPxlsFractions,
|
||||
const QualityEvalParams& qualityEvalParams )
|
||||
{
|
||||
Mat texturelessMask, texturedMask;
|
||||
computeTextureBasedMasks( leftImg, &texturelessMask, &texturedMask,
|
||||
qualityEvalParams.texturelessWidth, qualityEvalParams.texturelessThresh );
|
||||
Mat occludedMask, nonOccludedMask;
|
||||
computeOcclusionBasedMasks( trueLeftDisp, trueRightDisp, &occludedMask, &nonOccludedMask,
|
||||
trueLeftUnknDispMask, trueRightUnknDispMask, qualityEvalParams.dispThresh);
|
||||
Mat depthDiscontMask;
|
||||
computeDepthDiscontMask( trueLeftDisp, depthDiscontMask, trueLeftUnknDispMask,
|
||||
qualityEvalParams.dispGap, qualityEvalParams.discontWidth);
|
||||
|
||||
Mat borderedKnownMask = getBorderedMask( leftImg.size(), qualityEvalParams.ignoreBorder ) & ~trueLeftUnknDispMask;
|
||||
|
||||
nonOccludedMask &= borderedKnownMask;
|
||||
occludedMask &= borderedKnownMask;
|
||||
texturedMask &= nonOccludedMask; // & borderedKnownMask
|
||||
texturelessMask &= nonOccludedMask; // & borderedKnownMask
|
||||
depthDiscontMask &= nonOccludedMask; // & borderedKnownMask
|
||||
|
||||
rms.resize(ERROR_KINDS_COUNT);
|
||||
rms[0] = dispRMS( calcLeftDisp, trueLeftDisp, borderedKnownMask );
|
||||
rms[1] = dispRMS( calcLeftDisp, trueLeftDisp, nonOccludedMask );
|
||||
rms[2] = dispRMS( calcLeftDisp, trueLeftDisp, occludedMask );
|
||||
rms[3] = dispRMS( calcLeftDisp, trueLeftDisp, texturedMask );
|
||||
rms[4] = dispRMS( calcLeftDisp, trueLeftDisp, texturelessMask );
|
||||
rms[5] = dispRMS( calcLeftDisp, trueLeftDisp, depthDiscontMask );
|
||||
|
||||
badPxlsFractions.resize(ERROR_KINDS_COUNT);
|
||||
badPxlsFractions[0] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, borderedKnownMask, qualityEvalParams.badThresh );
|
||||
badPxlsFractions[1] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, nonOccludedMask, qualityEvalParams.badThresh );
|
||||
badPxlsFractions[2] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, occludedMask, qualityEvalParams.badThresh );
|
||||
badPxlsFractions[3] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, texturedMask, qualityEvalParams.badThresh );
|
||||
badPxlsFractions[4] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, texturelessMask, qualityEvalParams.badThresh );
|
||||
badPxlsFractions[5] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, depthDiscontMask, qualityEvalParams.badThresh );
|
||||
}
|
||||
|
||||
int CV_StereoMatchingTest::processStereoMatchingResults( FileStorage& fs, int caseIdx, bool isWrite,
|
||||
const Mat& leftImg, const Mat& rightImg,
|
||||
const Rect& calcROI,
|
||||
const Mat& trueLeftDisp, const Mat& trueRightDisp,
|
||||
const Mat& leftDisp, const Mat& rightDisp,
|
||||
const QualityEvalParams& qualityEvalParams )
|
||||
{
|
||||
// rightDisp is not used in current test virsion
|
||||
int code = cvtest::TS::OK;
|
||||
CV_Assert( fs.isOpened() );
|
||||
CV_Assert( trueLeftDisp.type() == CV_32FC1 );
|
||||
CV_Assert( trueRightDisp.empty() || trueRightDisp.type() == CV_32FC1 );
|
||||
CV_Assert( leftDisp.type() == CV_32FC1 && (rightDisp.empty() || rightDisp.type() == CV_32FC1) );
|
||||
|
||||
// get masks for unknown ground truth disparity values
|
||||
Mat leftUnknMask, rightUnknMask;
|
||||
DatasetParams params = datasetsParams[caseDatasets[caseIdx]];
|
||||
absdiff( trueLeftDisp, Scalar(params.dispUnknVal), leftUnknMask );
|
||||
leftUnknMask = leftUnknMask < std::numeric_limits<float>::epsilon();
|
||||
CV_Assert(leftUnknMask.type() == CV_8UC1);
|
||||
if( !trueRightDisp.empty() )
|
||||
{
|
||||
absdiff( trueRightDisp, Scalar(params.dispUnknVal), rightUnknMask );
|
||||
rightUnknMask = rightUnknMask < std::numeric_limits<float>::epsilon();
|
||||
CV_Assert(rightUnknMask.type() == CV_8UC1);
|
||||
}
|
||||
|
||||
// calculate errors
|
||||
vector<float> rmss, badPxlsFractions;
|
||||
calcErrors( leftImg, rightImg, trueLeftDisp, trueRightDisp, leftUnknMask, rightUnknMask,
|
||||
leftDisp, rightDisp, rmss, badPxlsFractions, qualityEvalParams );
|
||||
|
||||
if( isWrite )
|
||||
{
|
||||
fs << caseNames[caseIdx] << "{";
|
||||
fs.writeComment( RMS_STR, 0 );
|
||||
writeErrors( RMS_STR, rmss, &fs );
|
||||
fs.writeComment( BAD_PXLS_FRACTION_STR, 0 );
|
||||
writeErrors( BAD_PXLS_FRACTION_STR, badPxlsFractions, &fs );
|
||||
fs.writeComment( ROI_STR, 0 );
|
||||
writeROI( calcROI, &fs );
|
||||
fs << "}"; // datasetName
|
||||
}
|
||||
else // compare
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "\nquality of case named %s\n", caseNames[caseIdx].c_str() );
|
||||
ts->printf( cvtest::TS::LOG, "%s\n", RMS_STR.c_str() );
|
||||
writeErrors( RMS_STR, rmss );
|
||||
ts->printf( cvtest::TS::LOG, "%s\n", BAD_PXLS_FRACTION_STR.c_str() );
|
||||
writeErrors( BAD_PXLS_FRACTION_STR, badPxlsFractions );
|
||||
ts->printf( cvtest::TS::LOG, "%s\n", ROI_STR.c_str() );
|
||||
writeROI( calcROI );
|
||||
|
||||
FileNode fn = fs.getFirstTopLevelNode()[caseNames[caseIdx]];
|
||||
vector<float> validRmss, validBadPxlsFractions;
|
||||
Rect validROI;
|
||||
|
||||
readErrors( fn, RMS_STR, validRmss );
|
||||
readErrors( fn, BAD_PXLS_FRACTION_STR, validBadPxlsFractions );
|
||||
readROI( fn, validROI );
|
||||
int tempCode = compareErrors( rmss, validRmss, rmsEps, RMS_STR );
|
||||
code = tempCode==cvtest::TS::OK ? code : tempCode;
|
||||
tempCode = compareErrors( badPxlsFractions, validBadPxlsFractions, fracEps, BAD_PXLS_FRACTION_STR );
|
||||
code = tempCode==cvtest::TS::OK ? code : tempCode;
|
||||
tempCode = compareROI( calcROI, validROI );
|
||||
code = tempCode==cvtest::TS::OK ? code : tempCode;
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
int CV_StereoMatchingTest::readDatasetsParams( FileStorage& fs )
|
||||
{
|
||||
if( !fs.isOpened() )
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "datasetsParams can not be read " );
|
||||
return cvtest::TS::FAIL_INVALID_TEST_DATA;
|
||||
}
|
||||
datasetsParams.clear();
|
||||
FileNode fn = fs.getFirstTopLevelNode();
|
||||
CV_Assert(fn.isSeq());
|
||||
for( int i = 0; i < (int)fn.size(); i+=3 )
|
||||
{
|
||||
String _name = fn[i];
|
||||
DatasetParams params;
|
||||
String sf = fn[i+1]; params.dispScaleFactor = atoi(sf.c_str());
|
||||
String uv = fn[i+2]; params.dispUnknVal = atoi(uv.c_str());
|
||||
datasetsParams[_name] = params;
|
||||
}
|
||||
return cvtest::TS::OK;
|
||||
}
|
||||
|
||||
int CV_StereoMatchingTest::readRunParams( FileStorage& fs )
|
||||
{
|
||||
if( !fs.isOpened() )
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "runParams can not be read " );
|
||||
return cvtest::TS::FAIL_INVALID_TEST_DATA;
|
||||
}
|
||||
caseNames.clear();;
|
||||
caseDatasets.clear();
|
||||
return cvtest::TS::OK;
|
||||
}
|
||||
|
||||
void CV_StereoMatchingTest::writeErrors( const string& errName, const vector<float>& errors, FileStorage* fs )
|
||||
{
|
||||
CV_Assert( (int)errors.size() == ERROR_KINDS_COUNT );
|
||||
vector<float>::const_iterator it = errors.begin();
|
||||
if( fs )
|
||||
for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++it )
|
||||
*fs << ERROR_PREFIXES[i] + errName << *it;
|
||||
else
|
||||
for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++it )
|
||||
ts->printf( cvtest::TS::LOG, "%s = %f\n", string(ERROR_PREFIXES[i]+errName).c_str(), *it );
|
||||
}
|
||||
|
||||
void CV_StereoMatchingTest::writeROI( const Rect& calcROI, FileStorage* fs )
|
||||
{
|
||||
if( fs )
|
||||
{
|
||||
*fs << ROI_PREFIXES[0] << calcROI.x;
|
||||
*fs << ROI_PREFIXES[1] << calcROI.y;
|
||||
*fs << ROI_PREFIXES[2] << calcROI.width;
|
||||
*fs << ROI_PREFIXES[3] << calcROI.height;
|
||||
}
|
||||
else
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "%s = %d\n", ROI_PREFIXES[0].c_str(), calcROI.x );
|
||||
ts->printf( cvtest::TS::LOG, "%s = %d\n", ROI_PREFIXES[1].c_str(), calcROI.y );
|
||||
ts->printf( cvtest::TS::LOG, "%s = %d\n", ROI_PREFIXES[2].c_str(), calcROI.width );
|
||||
ts->printf( cvtest::TS::LOG, "%s = %d\n", ROI_PREFIXES[3].c_str(), calcROI.height );
|
||||
}
|
||||
}
|
||||
|
||||
void CV_StereoMatchingTest::readErrors( FileNode& fn, const string& errName, vector<float>& errors )
|
||||
{
|
||||
errors.resize( ERROR_KINDS_COUNT );
|
||||
vector<float>::iterator it = errors.begin();
|
||||
for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++it )
|
||||
fn[ERROR_PREFIXES[i]+errName] >> *it;
|
||||
}
|
||||
|
||||
void CV_StereoMatchingTest::readROI( FileNode& fn, Rect& validROI )
|
||||
{
|
||||
fn[ROI_PREFIXES[0]] >> validROI.x;
|
||||
fn[ROI_PREFIXES[1]] >> validROI.y;
|
||||
fn[ROI_PREFIXES[2]] >> validROI.width;
|
||||
fn[ROI_PREFIXES[3]] >> validROI.height;
|
||||
}
|
||||
|
||||
int CV_StereoMatchingTest::compareErrors( const vector<float>& calcErrors, const vector<float>& validErrors,
|
||||
const vector<float>& eps, const string& errName )
|
||||
{
|
||||
CV_Assert( (int)calcErrors.size() == ERROR_KINDS_COUNT );
|
||||
CV_Assert( (int)validErrors.size() == ERROR_KINDS_COUNT );
|
||||
CV_Assert( (int)eps.size() == ERROR_KINDS_COUNT );
|
||||
vector<float>::const_iterator calcIt = calcErrors.begin(),
|
||||
validIt = validErrors.begin(),
|
||||
epsIt = eps.begin();
|
||||
bool ok = true;
|
||||
for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++calcIt, ++validIt, ++epsIt )
|
||||
if( *calcIt - *validIt > *epsIt )
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "bad accuracy of %s (valid=%f; calc=%f)\n", string(ERROR_PREFIXES[i]+errName).c_str(), *validIt, *calcIt );
|
||||
ok = false;
|
||||
}
|
||||
return ok ? cvtest::TS::OK : cvtest::TS::FAIL_BAD_ACCURACY;
|
||||
}
|
||||
|
||||
int CV_StereoMatchingTest::compareROI( const Rect& calcROI, const Rect& validROI )
|
||||
{
|
||||
int compare[4][2] = {
|
||||
{ calcROI.x, validROI.x },
|
||||
{ calcROI.y, validROI.y },
|
||||
{ calcROI.width, validROI.width },
|
||||
{ calcROI.height, validROI.height },
|
||||
};
|
||||
bool ok = true;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (compare[i][0] != compare[i][1])
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "bad accuracy of %s (valid=%d; calc=%d)\n", ROI_PREFIXES[i].c_str(), compare[i][1], compare[i][0] );
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
return ok ? cvtest::TS::OK : cvtest::TS::FAIL_BAD_ACCURACY;
|
||||
}
|
||||
|
||||
//----------------------------------- StereoBM test -----------------------------------------------------
|
||||
|
||||
class CV_StereoBMTest : public CV_StereoMatchingTest
|
||||
{
|
||||
public:
|
||||
CV_StereoBMTest()
|
||||
{
|
||||
name = "stereobm";
|
||||
std::fill(rmsEps.begin(), rmsEps.end(), 0.4f);
|
||||
std::fill(fracEps.begin(), fracEps.end(), 0.022f);
|
||||
}
|
||||
|
||||
protected:
|
||||
struct RunParams
|
||||
{
|
||||
int ndisp;
|
||||
int mindisp;
|
||||
int winSize;
|
||||
};
|
||||
vector<RunParams> caseRunParams;
|
||||
|
||||
virtual int readRunParams( FileStorage& fs )
|
||||
{
|
||||
int code = CV_StereoMatchingTest::readRunParams( fs );
|
||||
FileNode fn = fs.getFirstTopLevelNode();
|
||||
CV_Assert(fn.isSeq());
|
||||
for( int i = 0; i < (int)fn.size(); i+=5 )
|
||||
{
|
||||
String caseName = fn[i], datasetName = fn[i+1];
|
||||
RunParams params;
|
||||
String ndisp = fn[i+2]; params.ndisp = atoi(ndisp.c_str());
|
||||
String mindisp = fn[i+3]; params.mindisp = atoi(mindisp.c_str());
|
||||
String winSize = fn[i+4]; params.winSize = atoi(winSize.c_str());
|
||||
caseNames.push_back( caseName );
|
||||
caseDatasets.push_back( datasetName );
|
||||
caseRunParams.push_back( params );
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
virtual int runStereoMatchingAlgorithm( const Mat& _leftImg, const Mat& _rightImg,
|
||||
Rect& calcROI, Mat& leftDisp, Mat& /*rightDisp*/, int caseIdx )
|
||||
{
|
||||
RunParams params = caseRunParams[caseIdx];
|
||||
CV_Assert( params.ndisp%16 == 0 );
|
||||
CV_Assert( _leftImg.type() == CV_8UC3 && _rightImg.type() == CV_8UC3 );
|
||||
Mat leftImg; cvtColor( _leftImg, leftImg, COLOR_BGR2GRAY );
|
||||
Mat rightImg; cvtColor( _rightImg, rightImg, COLOR_BGR2GRAY );
|
||||
|
||||
Ptr<StereoBM> bm = StereoBM::create( params.ndisp, params.winSize );
|
||||
Mat tempDisp;
|
||||
bm->setMinDisparity(params.mindisp);
|
||||
|
||||
Rect cROI(0, 0, _leftImg.cols, _leftImg.rows);
|
||||
calcROI = getValidDisparityROI(cROI, cROI, params.mindisp, params.ndisp, params.winSize);
|
||||
|
||||
bm->compute( leftImg, rightImg, tempDisp );
|
||||
tempDisp.convertTo(leftDisp, CV_32F, 1./static_cast<double>(StereoMatcher::DISP_SCALE));
|
||||
|
||||
//check for fixed-type disparity data type
|
||||
Mat_<float> fixedFloatDisp;
|
||||
bm->compute( leftImg, rightImg, fixedFloatDisp );
|
||||
EXPECT_LT(cvtest::norm(fixedFloatDisp, leftDisp, cv::NORM_L2 | cv::NORM_RELATIVE),
|
||||
0.005 + DBL_EPSILON);
|
||||
|
||||
if (params.mindisp != 0)
|
||||
for (int y = 0; y < leftDisp.rows; y++)
|
||||
for (int x = 0; x < leftDisp.cols; x++)
|
||||
{
|
||||
if (leftDisp.at<float>(y, x) < params.mindisp)
|
||||
leftDisp.at<float>(y, x) = -1./static_cast<double>(StereoMatcher::DISP_SCALE); // treat disparity < mindisp as no disparity
|
||||
}
|
||||
|
||||
return params.winSize/2;
|
||||
}
|
||||
};
|
||||
|
||||
TEST(Calib3d_StereoBM, regression) { CV_StereoBMTest test; test.safe_run(); }
|
||||
|
||||
/* < preFilter, < preFilterCap, SADWindowSize > >*/
|
||||
typedef tuple < int, tuple < int, int > > BufferBM_Params_t;
|
||||
|
||||
typedef testing::TestWithParam< BufferBM_Params_t > Calib3d_StereoBM_BufferBM;
|
||||
|
||||
const int preFilters[] =
|
||||
{
|
||||
StereoBM::PREFILTER_NORMALIZED_RESPONSE,
|
||||
StereoBM::PREFILTER_XSOBEL
|
||||
};
|
||||
|
||||
const tuple < int, int > useShortsConditions[] =
|
||||
{
|
||||
make_tuple(30, 19),
|
||||
make_tuple(32, 23)
|
||||
};
|
||||
|
||||
TEST_P(Calib3d_StereoBM_BufferBM, memAllocsTest)
|
||||
{
|
||||
const int preFilter = get<0>(GetParam());
|
||||
const int preFilterCap = get<0>(get<1>(GetParam()));
|
||||
const int SADWindowSize = get<1>(get<1>(GetParam()));
|
||||
|
||||
String path = cvtest::TS::ptr()->get_data_path() + "cv/stereomatching/datasets/teddy/";
|
||||
Mat leftImg = imread(path + "im2.png", IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(leftImg.empty());
|
||||
Mat rightImg = imread(path + "im6.png", IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(rightImg.empty());
|
||||
Mat leftDisp;
|
||||
{
|
||||
Ptr<StereoBM> bm = StereoBM::create(16,9);
|
||||
bm->setPreFilterType(preFilter);
|
||||
bm->setPreFilterCap(preFilterCap);
|
||||
bm->setBlockSize(SADWindowSize);
|
||||
bm->compute( leftImg, rightImg, leftDisp);
|
||||
|
||||
ASSERT_FALSE(leftDisp.empty());
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(/*nothing*/, Calib3d_StereoBM_BufferBM,
|
||||
testing::Combine(
|
||||
testing::ValuesIn(preFilters),
|
||||
testing::ValuesIn(useShortsConditions)
|
||||
)
|
||||
);
|
||||
|
||||
//----------------------------------- StereoSGBM test -----------------------------------------------------
|
||||
|
||||
class CV_StereoSGBMTest : public CV_StereoMatchingTest
|
||||
{
|
||||
public:
|
||||
CV_StereoSGBMTest()
|
||||
{
|
||||
name = "stereosgbm";
|
||||
std::fill(rmsEps.begin(), rmsEps.end(), 0.25f);
|
||||
std::fill(fracEps.begin(), fracEps.end(), 0.01f);
|
||||
}
|
||||
|
||||
protected:
|
||||
struct RunParams
|
||||
{
|
||||
int ndisp;
|
||||
int winSize;
|
||||
int mode;
|
||||
};
|
||||
vector<RunParams> caseRunParams;
|
||||
|
||||
virtual int readRunParams( FileStorage& fs )
|
||||
{
|
||||
int code = CV_StereoMatchingTest::readRunParams(fs);
|
||||
FileNode fn = fs.getFirstTopLevelNode();
|
||||
CV_Assert(fn.isSeq());
|
||||
for( int i = 0; i < (int)fn.size(); i+=5 )
|
||||
{
|
||||
String caseName = fn[i], datasetName = fn[i+1];
|
||||
RunParams params;
|
||||
String ndisp = fn[i+2]; params.ndisp = atoi(ndisp.c_str());
|
||||
String winSize = fn[i+3]; params.winSize = atoi(winSize.c_str());
|
||||
String mode = fn[i+4]; params.mode = atoi(mode.c_str());
|
||||
caseNames.push_back( caseName );
|
||||
caseDatasets.push_back( datasetName );
|
||||
caseRunParams.push_back( params );
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
virtual int runStereoMatchingAlgorithm( const Mat& leftImg, const Mat& rightImg,
|
||||
Rect& calcROI, Mat& leftDisp, Mat& /*rightDisp*/, int caseIdx )
|
||||
{
|
||||
RunParams params = caseRunParams[caseIdx];
|
||||
CV_Assert( params.ndisp%16 == 0 );
|
||||
Ptr<StereoSGBM> sgbm = StereoSGBM::create( 0, params.ndisp, params.winSize,
|
||||
10*params.winSize*params.winSize,
|
||||
40*params.winSize*params.winSize,
|
||||
1, 63, 10, 100, 32, params.mode );
|
||||
|
||||
Rect cROI(0, 0, leftImg.cols, leftImg.rows);
|
||||
calcROI = getValidDisparityROI(cROI, cROI, 0, params.ndisp, params.winSize);
|
||||
|
||||
sgbm->compute( leftImg, rightImg, leftDisp );
|
||||
CV_Assert( leftDisp.type() == CV_16SC1 );
|
||||
leftDisp/=16;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
TEST(Calib3d_StereoSGBM, regression) { CV_StereoSGBMTest test; test.safe_run(); }
|
||||
|
||||
TEST(Calib3d_StereoSGBM, deterministic) {
|
||||
cv::Ptr<cv::StereoSGBM> matcher = cv::StereoSGBM::create(16, 11);
|
||||
|
||||
// Expect throw error (non-determinism case)
|
||||
int widthNarrow = 28;
|
||||
int height = 15;
|
||||
|
||||
cv::Mat leftNarrow(height, widthNarrow, CV_8UC1);
|
||||
cv::Mat rightNarrow(height, widthNarrow, CV_8UC1);
|
||||
randu(leftNarrow, cv::Scalar(0), cv::Scalar(255));
|
||||
randu(rightNarrow, cv::Scalar(0), cv::Scalar(255));
|
||||
cv::Mat disp;
|
||||
|
||||
EXPECT_THROW(matcher->compute(leftNarrow, rightNarrow, disp), cv::Exception);
|
||||
|
||||
// Deterministic case, image is sufficiently large for StereSGBM parameters
|
||||
int widthWide = 40;
|
||||
cv::Mat leftWide(height, widthWide, CV_8UC1);
|
||||
cv::Mat rightWide(height, widthWide, CV_8UC1);
|
||||
randu(leftWide, cv::Scalar(0), cv::Scalar(255));
|
||||
randu(rightWide, cv::Scalar(0), cv::Scalar(255));
|
||||
cv::Mat disp1, disp2;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
matcher->compute(leftWide, rightWide, disp1);
|
||||
matcher->compute(leftWide, rightWide, disp2);
|
||||
cv::Mat dst;
|
||||
cv::bitwise_xor(disp1, disp2, dst);
|
||||
EXPECT_EQ(cv::countNonZero(dst), 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TEST(Calib3d_StereoSGBM_HH4, regression)
|
||||
{
|
||||
String path = cvtest::TS::ptr()->get_data_path() + "cv/stereomatching/datasets/teddy/";
|
||||
Mat leftImg = imread(path + "im2.png", IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(leftImg.empty());
|
||||
Mat rightImg = imread(path + "im6.png", IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(rightImg.empty());
|
||||
Mat testData = imread(path + "disp2_hh4.png",-1);
|
||||
ASSERT_FALSE(testData.empty());
|
||||
Mat leftDisp;
|
||||
Mat toCheck;
|
||||
{
|
||||
Ptr<StereoSGBM> sgbm = StereoSGBM::create( 0, 48, 3, 90, 360, 1, 63, 10, 100, 32, StereoSGBM::MODE_HH4);
|
||||
sgbm->compute( leftImg, rightImg, leftDisp);
|
||||
CV_Assert( leftDisp.type() == CV_16SC1 );
|
||||
leftDisp.convertTo(toCheck, CV_16UC1,1,16);
|
||||
}
|
||||
Mat diff;
|
||||
absdiff(toCheck, testData,diff);
|
||||
CV_Assert( countNonZero(diff)==0);
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
Reference in New Issue
Block a user