mirror of
https://github.com/opencv/opencv.git
synced 2025-06-07 09:25:45 +08:00
Merge remote-tracking branch 'upstream/3.4' into merge-3.4
This commit is contained in:
commit
332c37f332
@ -143,6 +143,8 @@ elseif(MSVC)
|
||||
set(OpenCV_RUNTIME vc14)
|
||||
elseif(MSVC_VERSION MATCHES "^191[0-9]$")
|
||||
set(OpenCV_RUNTIME vc15)
|
||||
elseif(MSVC_VERSION MATCHES "^192[0-9]$")
|
||||
set(OpenCV_RUNTIME vc16)
|
||||
else()
|
||||
message(WARNING "OpenCV does not recognize MSVC_VERSION \"${MSVC_VERSION}\". Cannot set OpenCV_RUNTIME")
|
||||
endif()
|
||||
|
@ -113,6 +113,16 @@ elseif(MSVC)
|
||||
if(NOT has_VS2017)
|
||||
set(OpenCV_RUNTIME vc14) # selecting previous compatible runtime version
|
||||
endif()
|
||||
elseif(MSVC_VERSION MATCHES "^192[0-9]$")
|
||||
set(OpenCV_RUNTIME vc16)
|
||||
check_one_config(has_VS2019)
|
||||
if(NOT has_VS2019)
|
||||
set(OpenCV_RUNTIME vc15) # selecting previous compatible runtime version
|
||||
check_one_config(has_VS2017)
|
||||
if(NOT has_VS2017)
|
||||
set(OpenCV_RUNTIME vc14) # selecting previous compatible runtime version
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
elseif(MINGW)
|
||||
set(OpenCV_RUNTIME mingw)
|
||||
|
@ -17,6 +17,21 @@
|
||||
number = {7},
|
||||
url = {http://www.bmva.org/bmvc/2013/Papers/paper0013/paper0013.pdf}
|
||||
}
|
||||
@inproceedings{Andreff99,
|
||||
author = {Andreff, Nicolas and Horaud, Radu and Espiau, Bernard},
|
||||
title = {On-line Hand-eye Calibration},
|
||||
booktitle = {Proceedings of the 2Nd International Conference on 3-D Digital Imaging and Modeling},
|
||||
series = {3DIM'99},
|
||||
year = {1999},
|
||||
isbn = {0-7695-0062-5},
|
||||
location = {Ottawa, Canada},
|
||||
pages = {430--436},
|
||||
numpages = {7},
|
||||
url = {http://dl.acm.org/citation.cfm?id=1889712.1889775},
|
||||
acmid = {1889775},
|
||||
publisher = {IEEE Computer Society},
|
||||
address = {Washington, DC, USA},
|
||||
}
|
||||
@inproceedings{Arandjelovic:2012:TTE:2354409.2355123,
|
||||
author = {Arandjelovic, Relja},
|
||||
title = {Three Things Everyone Should Know to Improve Object Retrieval},
|
||||
@ -180,6 +195,14 @@
|
||||
volume = {9},
|
||||
publisher = {Walter de Gruyter}
|
||||
}
|
||||
@article{Daniilidis98,
|
||||
author = {Konstantinos Daniilidis},
|
||||
title = {Hand-Eye Calibration Using Dual Quaternions},
|
||||
journal = {International Journal of Robotics Research},
|
||||
year = {1998},
|
||||
volume = {18},
|
||||
pages = {286--298}
|
||||
}
|
||||
@inproceedings{DM03,
|
||||
author = {Drago, Fr{\'e}d{\'e}ric and Myszkowski, Karol and Annen, Thomas and Chiba, Norishige},
|
||||
title = {Adaptive logarithmic mapping for displaying high contrast scenes},
|
||||
@ -431,6 +454,24 @@
|
||||
publisher = {Cambridge university press},
|
||||
url = {http://cds.cern.ch/record/1598612/files/0521540518_TOC.pdf}
|
||||
}
|
||||
@article{Horaud95,
|
||||
author = {Horaud, Radu and Dornaika, Fadi},
|
||||
title = {Hand-eye Calibration},
|
||||
journal = {Int. J. Rob. Res.},
|
||||
issue_date = {June 1995},
|
||||
volume = {14},
|
||||
number = {3},
|
||||
month = jun,
|
||||
year = {1995},
|
||||
issn = {0278-3649},
|
||||
pages = {195--210},
|
||||
numpages = {16},
|
||||
url = {http://dx.doi.org/10.1177/027836499501400301},
|
||||
doi = {10.1177/027836499501400301},
|
||||
acmid = {208622},
|
||||
publisher = {Sage Publications, Inc.},
|
||||
address = {Thousand Oaks, CA, USA}
|
||||
}
|
||||
@article{Horn81,
|
||||
author = {Horn, Berthold KP and Schunck, Brian G},
|
||||
title = {Determining Optical Flow},
|
||||
@ -667,6 +708,18 @@
|
||||
number = {2},
|
||||
publisher = {Elsevier}
|
||||
}
|
||||
@article{Park94,
|
||||
author = {F. C. Park and B. J. Martin},
|
||||
journal = {IEEE Transactions on Robotics and Automation},
|
||||
title = {Robot sensor calibration: solving AX=XB on the Euclidean group},
|
||||
year = {1994},
|
||||
volume = {10},
|
||||
number = {5},
|
||||
pages = {717-721},
|
||||
doi = {10.1109/70.326576},
|
||||
ISSN = {1042-296X},
|
||||
month = {Oct}
|
||||
}
|
||||
@inproceedings{PM03,
|
||||
author = {P{\'e}rez, Patrick and Gangnet, Michel and Blake, Andrew},
|
||||
title = {Poisson image editing},
|
||||
@ -841,6 +894,18 @@
|
||||
number = {1},
|
||||
publisher = {Taylor \& Francis}
|
||||
}
|
||||
@article{Tsai89,
|
||||
author = {R. Y. Tsai and R. K. Lenz},
|
||||
journal = {IEEE Transactions on Robotics and Automation},
|
||||
title = {A new technique for fully autonomous and efficient 3D robotics hand/eye calibration},
|
||||
year = {1989},
|
||||
volume = {5},
|
||||
number = {3},
|
||||
pages = {345-358},
|
||||
doi = {10.1109/70.34770},
|
||||
ISSN = {1042-296X},
|
||||
month = {June}
|
||||
}
|
||||
@inproceedings{UES01,
|
||||
author = {Uyttendaele, Matthew and Eden, Ashley and Skeliski, R},
|
||||
title = {Eliminating ghosting and exposure artifacts in image mosaics},
|
||||
|
@ -32,11 +32,11 @@ Generate documentation {#tutorial_documentation_generate}
|
||||
- Create build directory near the sources folder(s) and go into it
|
||||
- Run cmake (assuming you put sources to _opencv_ folder):
|
||||
@code{.sh}
|
||||
cmake ../opencv
|
||||
cmake -DBUILD_DOCS=ON ../opencv
|
||||
@endcode
|
||||
Or if you get contrib sources too:
|
||||
@code{.sh}
|
||||
cmake -DOPENCV_EXTRA_MODULES_PATH=../opencv_contrib/modules ../opencv
|
||||
cmake -DBUILD_DOCS=ON -DOPENCV_EXTRA_MODULES_PATH=../opencv_contrib/modules ../opencv
|
||||
@endcode
|
||||
- Run make:
|
||||
@code{.sh}
|
||||
|
BIN
modules/calib3d/doc/pics/hand-eye_figure.png
Normal file
BIN
modules/calib3d/doc/pics/hand-eye_figure.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
@ -280,7 +280,7 @@ enum { CALIB_NINTRINSIC = 18,
|
||||
// for stereo rectification
|
||||
CALIB_ZERO_DISPARITY = 0x00400,
|
||||
CALIB_USE_LU = (1 << 17), //!< use LU instead of SVD decomposition for solving. much faster but potentially less precise
|
||||
CALIB_USE_EXTRINSIC_GUESS = (1 << 22), //!< for stereoCalibrate
|
||||
CALIB_USE_EXTRINSIC_GUESS = (1 << 22) //!< for stereoCalibrate
|
||||
};
|
||||
|
||||
//! the algorithm for finding fundamental matrix
|
||||
@ -290,6 +290,14 @@ enum { FM_7POINT = 1, //!< 7-point algorithm
|
||||
FM_RANSAC = 8 //!< RANSAC algorithm. It needs at least 15 points. 7-point algorithm is used.
|
||||
};
|
||||
|
||||
enum HandEyeCalibrationMethod
|
||||
{
|
||||
CALIB_HAND_EYE_TSAI = 0, //!< A New Technique for Fully Autonomous and Efficient 3D Robotics Hand/Eye Calibration @cite Tsai89
|
||||
CALIB_HAND_EYE_PARK = 1, //!< Robot Sensor Calibration: Solving AX = XB on the Euclidean Group @cite Park94
|
||||
CALIB_HAND_EYE_HORAUD = 2, //!< Hand-eye Calibration @cite Horaud95
|
||||
CALIB_HAND_EYE_ANDREFF = 3, //!< On-line Hand-Eye Calibration @cite Andreff99
|
||||
CALIB_HAND_EYE_DANIILIDIS = 4 //!< Hand-Eye Calibration Using Dual Quaternions @cite Daniilidis98
|
||||
};
|
||||
|
||||
|
||||
/** @brief Converts a rotation matrix to a rotation vector or vice versa.
|
||||
@ -1569,6 +1577,139 @@ CV_EXPORTS_W Mat getOptimalNewCameraMatrix( InputArray cameraMatrix, InputArray
|
||||
CV_OUT Rect* validPixROI = 0,
|
||||
bool centerPrincipalPoint = false);
|
||||
|
||||
/** @brief Computes Hand-Eye calibration: \f$_{}^{g}\textrm{T}_c\f$
|
||||
|
||||
@param[in] R_gripper2base Rotation part extracted from the homogeneous matrix that transforms a point
|
||||
expressed in the gripper frame to the robot base frame (\f$_{}^{b}\textrm{T}_g\f$).
|
||||
This is a vector (`vector<Mat>`) that contains the rotation matrices for all the transformations
|
||||
from gripper frame to robot base frame.
|
||||
@param[in] t_gripper2base Translation part extracted from the homogeneous matrix that transforms a point
|
||||
expressed in the gripper frame to the robot base frame (\f$_{}^{b}\textrm{T}_g\f$).
|
||||
This is a vector (`vector<Mat>`) that contains the translation vectors for all the transformations
|
||||
from gripper frame to robot base frame.
|
||||
@param[in] R_target2cam Rotation part extracted from the homogeneous matrix that transforms a point
|
||||
expressed in the target frame to the camera frame (\f$_{}^{c}\textrm{T}_t\f$).
|
||||
This is a vector (`vector<Mat>`) that contains the rotation matrices for all the transformations
|
||||
from calibration target frame to camera frame.
|
||||
@param[in] t_target2cam Rotation part extracted from the homogeneous matrix that transforms a point
|
||||
expressed in the target frame to the camera frame (\f$_{}^{c}\textrm{T}_t\f$).
|
||||
This is a vector (`vector<Mat>`) that contains the translation vectors for all the transformations
|
||||
from calibration target frame to camera frame.
|
||||
@param[out] R_cam2gripper Estimated rotation part extracted from the homogeneous matrix that transforms a point
|
||||
expressed in the camera frame to the gripper frame (\f$_{}^{g}\textrm{T}_c\f$).
|
||||
@param[out] t_cam2gripper Estimated translation part extracted from the homogeneous matrix that transforms a point
|
||||
expressed in the camera frame to the gripper frame (\f$_{}^{g}\textrm{T}_c\f$).
|
||||
@param[in] method One of the implemented Hand-Eye calibration method, see cv::HandEyeCalibrationMethod
|
||||
|
||||
The function performs the Hand-Eye calibration using various methods. One approach consists in estimating the
|
||||
rotation then the translation (separable solutions) and the following methods are implemented:
|
||||
- R. Tsai, R. Lenz A New Technique for Fully Autonomous and Efficient 3D Robotics Hand/EyeCalibration \cite Tsai89
|
||||
- F. Park, B. Martin Robot Sensor Calibration: Solving AX = XB on the Euclidean Group \cite Park94
|
||||
- R. Horaud, F. Dornaika Hand-Eye Calibration \cite Horaud95
|
||||
|
||||
Another approach consists in estimating simultaneously the rotation and the translation (simultaneous solutions),
|
||||
with the following implemented method:
|
||||
- N. Andreff, R. Horaud, B. Espiau On-line Hand-Eye Calibration \cite Andreff99
|
||||
- K. Daniilidis Hand-Eye Calibration Using Dual Quaternions \cite Daniilidis98
|
||||
|
||||
The following picture describes the Hand-Eye calibration problem where the transformation between a camera ("eye")
|
||||
mounted on a robot gripper ("hand") has to be estimated.
|
||||
|
||||

|
||||
|
||||
The calibration procedure is the following:
|
||||
- a static calibration pattern is used to estimate the transformation between the target frame
|
||||
and the camera frame
|
||||
- the robot gripper is moved in order to acquire several poses
|
||||
- for each pose, the homogeneous transformation between the gripper frame and the robot base frame is recorded using for
|
||||
instance the robot kinematics
|
||||
\f[
|
||||
\begin{bmatrix}
|
||||
X_b\\
|
||||
Y_b\\
|
||||
Z_b\\
|
||||
1
|
||||
\end{bmatrix}
|
||||
=
|
||||
\begin{bmatrix}
|
||||
_{}^{b}\textrm{R}_g & _{}^{b}\textrm{t}_g \\
|
||||
0_{1 \times 3} & 1
|
||||
\end{bmatrix}
|
||||
\begin{bmatrix}
|
||||
X_g\\
|
||||
Y_g\\
|
||||
Z_g\\
|
||||
1
|
||||
\end{bmatrix}
|
||||
\f]
|
||||
- for each pose, the homogeneous transformation between the calibration target frame and the camera frame is recorded using
|
||||
for instance a pose estimation method (PnP) from 2D-3D point correspondences
|
||||
\f[
|
||||
\begin{bmatrix}
|
||||
X_c\\
|
||||
Y_c\\
|
||||
Z_c\\
|
||||
1
|
||||
\end{bmatrix}
|
||||
=
|
||||
\begin{bmatrix}
|
||||
_{}^{c}\textrm{R}_t & _{}^{c}\textrm{t}_t \\
|
||||
0_{1 \times 3} & 1
|
||||
\end{bmatrix}
|
||||
\begin{bmatrix}
|
||||
X_t\\
|
||||
Y_t\\
|
||||
Z_t\\
|
||||
1
|
||||
\end{bmatrix}
|
||||
\f]
|
||||
|
||||
The Hand-Eye calibration procedure returns the following homogeneous transformation
|
||||
\f[
|
||||
\begin{bmatrix}
|
||||
X_g\\
|
||||
Y_g\\
|
||||
Z_g\\
|
||||
1
|
||||
\end{bmatrix}
|
||||
=
|
||||
\begin{bmatrix}
|
||||
_{}^{g}\textrm{R}_c & _{}^{g}\textrm{t}_c \\
|
||||
0_{1 \times 3} & 1
|
||||
\end{bmatrix}
|
||||
\begin{bmatrix}
|
||||
X_c\\
|
||||
Y_c\\
|
||||
Z_c\\
|
||||
1
|
||||
\end{bmatrix}
|
||||
\f]
|
||||
|
||||
This problem is also known as solving the \f$\mathbf{A}\mathbf{X}=\mathbf{X}\mathbf{B}\f$ equation:
|
||||
\f[
|
||||
\begin{align*}
|
||||
^{b}{\textrm{T}_g}^{(1)} \hspace{0.2em} ^{g}\textrm{T}_c \hspace{0.2em} ^{c}{\textrm{T}_t}^{(1)} &=
|
||||
\hspace{0.1em} ^{b}{\textrm{T}_g}^{(2)} \hspace{0.2em} ^{g}\textrm{T}_c \hspace{0.2em} ^{c}{\textrm{T}_t}^{(2)} \\
|
||||
|
||||
(^{b}{\textrm{T}_g}^{(2)})^{-1} \hspace{0.2em} ^{b}{\textrm{T}_g}^{(1)} \hspace{0.2em} ^{g}\textrm{T}_c &=
|
||||
\hspace{0.1em} ^{g}\textrm{T}_c \hspace{0.2em} ^{c}{\textrm{T}_t}^{(2)} (^{c}{\textrm{T}_t}^{(1)})^{-1} \\
|
||||
|
||||
\textrm{A}_i \textrm{X} &= \textrm{X} \textrm{B}_i \\
|
||||
\end{align*}
|
||||
\f]
|
||||
|
||||
\note
|
||||
Additional information can be found on this [website](http://campar.in.tum.de/Chair/HandEyeCalibration).
|
||||
\note
|
||||
A minimum of 2 motions with non parallel rotation axes are necessary to determine the hand-eye transformation.
|
||||
So at least 3 different poses are required, but it is strongly recommended to use many more poses.
|
||||
|
||||
*/
|
||||
CV_EXPORTS_W void calibrateHandEye( InputArrayOfArrays R_gripper2base, InputArrayOfArrays t_gripper2base,
|
||||
InputArrayOfArrays R_target2cam, InputArrayOfArrays t_target2cam,
|
||||
OutputArray R_cam2gripper, OutputArray t_cam2gripper,
|
||||
HandEyeCalibrationMethod method=CALIB_HAND_EYE_TSAI );
|
||||
|
||||
/** @brief Converts points from Euclidean to homogeneous space.
|
||||
|
||||
@param src Input vector of N-dimensional points.
|
||||
|
770
modules/calib3d/src/calibration_handeye.cpp
Normal file
770
modules/calib3d/src/calibration_handeye.cpp
Normal file
@ -0,0 +1,770 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "opencv2/calib3d.hpp"
|
||||
|
||||
namespace cv {
|
||||
|
||||
static Mat homogeneousInverse(const Mat& T)
|
||||
{
|
||||
CV_Assert(T.rows == 4 && T.cols == 4);
|
||||
|
||||
Mat R = T(Rect(0, 0, 3, 3));
|
||||
Mat t = T(Rect(3, 0, 1, 3));
|
||||
Mat Rt = R.t();
|
||||
Mat tinv = -Rt * t;
|
||||
Mat Tinv = Mat::eye(4, 4, T.type());
|
||||
Rt.copyTo(Tinv(Rect(0, 0, 3, 3)));
|
||||
tinv.copyTo(Tinv(Rect(3, 0, 1, 3)));
|
||||
|
||||
return Tinv;
|
||||
}
|
||||
|
||||
// q = rot2quatMinimal(R)
|
||||
//
|
||||
// R - 3x3 rotation matrix, or 4x4 homogeneous matrix
|
||||
// q - 3x1 unit quaternion <qx, qy, qz>
|
||||
// q = sin(theta/2) * v
|
||||
// theta - rotation angle
|
||||
// v - unit rotation axis, |v| = 1
|
||||
static Mat rot2quatMinimal(const Mat& R)
|
||||
{
|
||||
CV_Assert(R.type() == CV_64FC1 && R.rows >= 3 && R.cols >= 3);
|
||||
|
||||
double m00 = R.at<double>(0,0), m01 = R.at<double>(0,1), m02 = R.at<double>(0,2);
|
||||
double m10 = R.at<double>(1,0), m11 = R.at<double>(1,1), m12 = R.at<double>(1,2);
|
||||
double m20 = R.at<double>(2,0), m21 = R.at<double>(2,1), m22 = R.at<double>(2,2);
|
||||
double trace = m00 + m11 + m22;
|
||||
|
||||
double qx, qy, qz;
|
||||
if (trace > 0) {
|
||||
double S = sqrt(trace + 1.0) * 2; // S=4*qw
|
||||
qx = (m21 - m12) / S;
|
||||
qy = (m02 - m20) / S;
|
||||
qz = (m10 - m01) / S;
|
||||
} else if ((m00 > m11)&(m00 > m22)) {
|
||||
double S = sqrt(1.0 + m00 - m11 - m22) * 2; // S=4*qx
|
||||
qx = 0.25 * S;
|
||||
qy = (m01 + m10) / S;
|
||||
qz = (m02 + m20) / S;
|
||||
} else if (m11 > m22) {
|
||||
double S = sqrt(1.0 + m11 - m00 - m22) * 2; // S=4*qy
|
||||
qx = (m01 + m10) / S;
|
||||
qy = 0.25 * S;
|
||||
qz = (m12 + m21) / S;
|
||||
} else {
|
||||
double S = sqrt(1.0 + m22 - m00 - m11) * 2; // S=4*qz
|
||||
qx = (m02 + m20) / S;
|
||||
qy = (m12 + m21) / S;
|
||||
qz = 0.25 * S;
|
||||
}
|
||||
|
||||
return (Mat_<double>(3,1) << qx, qy, qz);
|
||||
}
|
||||
|
||||
static Mat skew(const Mat& v)
|
||||
{
|
||||
CV_Assert(v.type() == CV_64FC1 && v.rows == 3 && v.cols == 1);
|
||||
|
||||
double vx = v.at<double>(0,0);
|
||||
double vy = v.at<double>(1,0);
|
||||
double vz = v.at<double>(2,0);
|
||||
return (Mat_<double>(3,3) << 0, -vz, vy,
|
||||
vz, 0, -vx,
|
||||
-vy, vx, 0);
|
||||
}
|
||||
|
||||
// R = quatMinimal2rot(q)
|
||||
//
|
||||
// q - 3x1 unit quaternion <qx, qy, qz>
|
||||
// R - 3x3 rotation matrix
|
||||
// q = sin(theta/2) * v
|
||||
// theta - rotation angle
|
||||
// v - unit rotation axis, |v| = 1
|
||||
static Mat quatMinimal2rot(const Mat& q)
|
||||
{
|
||||
CV_Assert(q.type() == CV_64FC1 && q.rows == 3 && q.cols == 1);
|
||||
|
||||
Mat p = q.t()*q;
|
||||
double w = sqrt(1 - p.at<double>(0,0));
|
||||
|
||||
Mat diag_p = Mat::eye(3,3,CV_64FC1)*p.at<double>(0,0);
|
||||
return 2*q*q.t() + 2*w*skew(q) + Mat::eye(3,3,CV_64FC1) - 2*diag_p;
|
||||
}
|
||||
|
||||
// q = rot2quat(R)
|
||||
//
|
||||
// q - 4x1 unit quaternion <qw, qx, qy, qz>
|
||||
// R - 3x3 rotation matrix
|
||||
static Mat rot2quat(const Mat& R)
|
||||
{
|
||||
CV_Assert(R.type() == CV_64FC1 && R.rows >= 3 && R.cols >= 3);
|
||||
|
||||
double m00 = R.at<double>(0,0), m01 = R.at<double>(0,1), m02 = R.at<double>(0,2);
|
||||
double m10 = R.at<double>(1,0), m11 = R.at<double>(1,1), m12 = R.at<double>(1,2);
|
||||
double m20 = R.at<double>(2,0), m21 = R.at<double>(2,1), m22 = R.at<double>(2,2);
|
||||
double trace = m00 + m11 + m22;
|
||||
|
||||
double qw, qx, qy, qz;
|
||||
if (trace > 0) {
|
||||
double S = sqrt(trace + 1.0) * 2; // S=4*qw
|
||||
qw = 0.25 * S;
|
||||
qx = (m21 - m12) / S;
|
||||
qy = (m02 - m20) / S;
|
||||
qz = (m10 - m01) / S;
|
||||
} else if ((m00 > m11)&(m00 > m22)) {
|
||||
double S = sqrt(1.0 + m00 - m11 - m22) * 2; // S=4*qx
|
||||
qw = (m21 - m12) / S;
|
||||
qx = 0.25 * S;
|
||||
qy = (m01 + m10) / S;
|
||||
qz = (m02 + m20) / S;
|
||||
} else if (m11 > m22) {
|
||||
double S = sqrt(1.0 + m11 - m00 - m22) * 2; // S=4*qy
|
||||
qw = (m02 - m20) / S;
|
||||
qx = (m01 + m10) / S;
|
||||
qy = 0.25 * S;
|
||||
qz = (m12 + m21) / S;
|
||||
} else {
|
||||
double S = sqrt(1.0 + m22 - m00 - m11) * 2; // S=4*qz
|
||||
qw = (m10 - m01) / S;
|
||||
qx = (m02 + m20) / S;
|
||||
qy = (m12 + m21) / S;
|
||||
qz = 0.25 * S;
|
||||
}
|
||||
|
||||
return (Mat_<double>(4,1) << qw, qx, qy, qz);
|
||||
}
|
||||
|
||||
// R = quat2rot(q)
|
||||
//
|
||||
// q - 4x1 unit quaternion <qw, qx, qy, qz>
|
||||
// R - 3x3 rotation matrix
|
||||
static Mat quat2rot(const Mat& q)
|
||||
{
|
||||
CV_Assert(q.type() == CV_64FC1 && q.rows == 4 && q.cols == 1);
|
||||
|
||||
double qw = q.at<double>(0,0);
|
||||
double qx = q.at<double>(1,0);
|
||||
double qy = q.at<double>(2,0);
|
||||
double qz = q.at<double>(3,0);
|
||||
|
||||
Mat R(3, 3, CV_64FC1);
|
||||
R.at<double>(0, 0) = 1 - 2*qy*qy - 2*qz*qz;
|
||||
R.at<double>(0, 1) = 2*qx*qy - 2*qz*qw;
|
||||
R.at<double>(0, 2) = 2*qx*qz + 2*qy*qw;
|
||||
|
||||
R.at<double>(1, 0) = 2*qx*qy + 2*qz*qw;
|
||||
R.at<double>(1, 1) = 1 - 2*qx*qx - 2*qz*qz;
|
||||
R.at<double>(1, 2) = 2*qy*qz - 2*qx*qw;
|
||||
|
||||
R.at<double>(2, 0) = 2*qx*qz - 2*qy*qw;
|
||||
R.at<double>(2, 1) = 2*qy*qz + 2*qx*qw;
|
||||
R.at<double>(2, 2) = 1 - 2*qx*qx - 2*qy*qy;
|
||||
|
||||
return R;
|
||||
}
|
||||
|
||||
// Kronecker product or tensor product
|
||||
// https://stackoverflow.com/a/36552682
|
||||
static Mat kron(const Mat& A, const Mat& B)
|
||||
{
|
||||
CV_Assert(A.channels() == 1 && B.channels() == 1);
|
||||
|
||||
Mat1d Ad, Bd;
|
||||
A.convertTo(Ad, CV_64F);
|
||||
B.convertTo(Bd, CV_64F);
|
||||
|
||||
Mat1d Kd(Ad.rows * Bd.rows, Ad.cols * Bd.cols, 0.0);
|
||||
for (int ra = 0; ra < Ad.rows; ra++)
|
||||
{
|
||||
for (int ca = 0; ca < Ad.cols; ca++)
|
||||
{
|
||||
Kd(Range(ra*Bd.rows, (ra + 1)*Bd.rows), Range(ca*Bd.cols, (ca + 1)*Bd.cols)) = Bd.mul(Ad(ra, ca));
|
||||
}
|
||||
}
|
||||
|
||||
Mat K;
|
||||
Kd.convertTo(K, A.type());
|
||||
return K;
|
||||
}
|
||||
|
||||
// quaternion multiplication
|
||||
static Mat qmult(const Mat& s, const Mat& t)
|
||||
{
|
||||
CV_Assert(s.type() == CV_64FC1 && t.type() == CV_64FC1);
|
||||
CV_Assert(s.rows == 4 && s.cols == 1);
|
||||
CV_Assert(t.rows == 4 && t.cols == 1);
|
||||
|
||||
double s0 = s.at<double>(0,0);
|
||||
double s1 = s.at<double>(1,0);
|
||||
double s2 = s.at<double>(2,0);
|
||||
double s3 = s.at<double>(3,0);
|
||||
|
||||
double t0 = t.at<double>(0,0);
|
||||
double t1 = t.at<double>(1,0);
|
||||
double t2 = t.at<double>(2,0);
|
||||
double t3 = t.at<double>(3,0);
|
||||
|
||||
Mat q(4, 1, CV_64FC1);
|
||||
q.at<double>(0,0) = s0*t0 - s1*t1 - s2*t2 - s3*t3;
|
||||
q.at<double>(1,0) = s0*t1 + s1*t0 + s2*t3 - s3*t2;
|
||||
q.at<double>(2,0) = s0*t2 - s1*t3 + s2*t0 + s3*t1;
|
||||
q.at<double>(3,0) = s0*t3 + s1*t2 - s2*t1 + s3*t0;
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
// dq = homogeneous2dualQuaternion(H)
|
||||
//
|
||||
// H - 4x4 homogeneous transformation: [R | t; 0 0 0 | 1]
|
||||
// dq - 8x1 dual quaternion: <q (rotation part), qprime (translation part)>
|
||||
static Mat homogeneous2dualQuaternion(const Mat& H)
|
||||
{
|
||||
CV_Assert(H.type() == CV_64FC1 && H.rows == 4 && H.cols == 4);
|
||||
|
||||
Mat dualq(8, 1, CV_64FC1);
|
||||
Mat R = H(Rect(0, 0, 3, 3));
|
||||
Mat t = H(Rect(3, 0, 1, 3));
|
||||
|
||||
Mat q = rot2quat(R);
|
||||
Mat qt = Mat::zeros(4, 1, CV_64FC1);
|
||||
t.copyTo(qt(Rect(0, 1, 1, 3)));
|
||||
Mat qprime = 0.5 * qmult(qt, q);
|
||||
|
||||
q.copyTo(dualq(Rect(0, 0, 1, 4)));
|
||||
qprime.copyTo(dualq(Rect(0, 4, 1, 4)));
|
||||
|
||||
return dualq;
|
||||
}
|
||||
|
||||
// H = dualQuaternion2homogeneous(dq)
|
||||
//
|
||||
// H - 4x4 homogeneous transformation: [R | t; 0 0 0 | 1]
|
||||
// dq - 8x1 dual quaternion: <q (rotation part), qprime (translation part)>
|
||||
static Mat dualQuaternion2homogeneous(const Mat& dualq)
|
||||
{
|
||||
CV_Assert(dualq.type() == CV_64FC1 && dualq.rows == 8 && dualq.cols == 1);
|
||||
|
||||
Mat q = dualq(Rect(0, 0, 1, 4));
|
||||
Mat qprime = dualq(Rect(0, 4, 1, 4));
|
||||
|
||||
Mat R = quat2rot(q);
|
||||
q.at<double>(1,0) = -q.at<double>(1,0);
|
||||
q.at<double>(2,0) = -q.at<double>(2,0);
|
||||
q.at<double>(3,0) = -q.at<double>(3,0);
|
||||
|
||||
Mat qt = 2*qmult(qprime, q);
|
||||
Mat t = qt(Rect(0, 1, 1, 3));
|
||||
|
||||
Mat H = Mat::eye(4, 4, CV_64FC1);
|
||||
R.copyTo(H(Rect(0, 0, 3, 3)));
|
||||
t.copyTo(H(Rect(3, 0, 1, 3)));
|
||||
|
||||
return H;
|
||||
}
|
||||
|
||||
//Reference:
|
||||
//R. Y. Tsai and R. K. Lenz, "A new technique for fully autonomous and efficient 3D robotics hand/eye calibration."
|
||||
//In IEEE Transactions on Robotics and Automation, vol. 5, no. 3, pp. 345-358, June 1989.
|
||||
//C++ code converted from Zoran Lazarevic's Matlab code:
|
||||
//http://lazax.com/www.cs.columbia.edu/~laza/html/Stewart/matlab/handEye.m
|
||||
static void calibrateHandEyeTsai(const std::vector<Mat>& Hg, const std::vector<Mat>& Hc,
|
||||
Mat& R_cam2gripper, Mat& t_cam2gripper)
|
||||
{
|
||||
//Number of unique camera position pairs
|
||||
int K = static_cast<int>((Hg.size()*Hg.size() - Hg.size()) / 2.0);
|
||||
//Will store: skew(Pgij+Pcij)
|
||||
Mat A(3*K, 3, CV_64FC1);
|
||||
//Will store: Pcij - Pgij
|
||||
Mat B(3*K, 1, CV_64FC1);
|
||||
|
||||
std::vector<Mat> vec_Hgij, vec_Hcij;
|
||||
vec_Hgij.reserve(static_cast<size_t>(K));
|
||||
vec_Hcij.reserve(static_cast<size_t>(K));
|
||||
|
||||
int idx = 0;
|
||||
for (size_t i = 0; i < Hg.size(); i++)
|
||||
{
|
||||
for (size_t j = i+1; j < Hg.size(); j++, idx++)
|
||||
{
|
||||
//Defines coordinate transformation from Gi to Gj
|
||||
//Hgi is from Gi (gripper) to RW (robot base)
|
||||
//Hgj is from Gj (gripper) to RW (robot base)
|
||||
Mat Hgij = homogeneousInverse(Hg[j]) * Hg[i]; //eq 6
|
||||
vec_Hgij.push_back(Hgij);
|
||||
//Rotation axis for Rgij which is the 3D rotation from gripper coordinate frame Gi to Gj
|
||||
Mat Pgij = 2*rot2quatMinimal(Hgij);
|
||||
|
||||
//Defines coordinate transformation from Ci to Cj
|
||||
//Hci is from CW (calibration target) to Ci (camera)
|
||||
//Hcj is from CW (calibration target) to Cj (camera)
|
||||
Mat Hcij = Hc[j] * homogeneousInverse(Hc[i]); //eq 7
|
||||
vec_Hcij.push_back(Hcij);
|
||||
//Rotation axis for Rcij
|
||||
Mat Pcij = 2*rot2quatMinimal(Hcij);
|
||||
|
||||
//Left-hand side: skew(Pgij+Pcij)
|
||||
skew(Pgij+Pcij).copyTo(A(Rect(0, idx*3, 3, 3)));
|
||||
//Right-hand side: Pcij - Pgij
|
||||
Mat diff = Pcij - Pgij;
|
||||
diff.copyTo(B(Rect(0, idx*3, 1, 3)));
|
||||
}
|
||||
}
|
||||
|
||||
Mat Pcg_;
|
||||
//Rotation from camera to gripper is obtained from the set of equations:
|
||||
// skew(Pgij+Pcij) * Pcg_ = Pcij - Pgij (eq 12)
|
||||
solve(A, B, Pcg_, DECOMP_SVD);
|
||||
|
||||
Mat Pcg_norm = Pcg_.t() * Pcg_;
|
||||
//Obtained non-unit quaternion is scaled back to unit value that
|
||||
//designates camera-gripper rotation
|
||||
Mat Pcg = 2 * Pcg_ / sqrt(1 + Pcg_norm.at<double>(0,0)); //eq 14
|
||||
|
||||
Mat Rcg = quatMinimal2rot(Pcg/2.0);
|
||||
|
||||
idx = 0;
|
||||
for (size_t i = 0; i < Hg.size(); i++)
|
||||
{
|
||||
for (size_t j = i+1; j < Hg.size(); j++, idx++)
|
||||
{
|
||||
//Defines coordinate transformation from Gi to Gj
|
||||
//Hgi is from Gi (gripper) to RW (robot base)
|
||||
//Hgj is from Gj (gripper) to RW (robot base)
|
||||
Mat Hgij = vec_Hgij[static_cast<size_t>(idx)];
|
||||
//Defines coordinate transformation from Ci to Cj
|
||||
//Hci is from CW (calibration target) to Ci (camera)
|
||||
//Hcj is from CW (calibration target) to Cj (camera)
|
||||
Mat Hcij = vec_Hcij[static_cast<size_t>(idx)];
|
||||
|
||||
//Left-hand side: (Rgij - I)
|
||||
Mat diff = Hgij(Rect(0,0,3,3)) - Mat::eye(3,3,CV_64FC1);
|
||||
diff.copyTo(A(Rect(0, idx*3, 3, 3)));
|
||||
|
||||
//Right-hand side: Rcg*Tcij - Tgij
|
||||
diff = Rcg*Hcij(Rect(3, 0, 1, 3)) - Hgij(Rect(3, 0, 1, 3));
|
||||
diff.copyTo(B(Rect(0, idx*3, 1, 3)));
|
||||
}
|
||||
}
|
||||
|
||||
Mat Tcg;
|
||||
//Translation from camera to gripper is obtained from the set of equations:
|
||||
// (Rgij - I) * Tcg = Rcg*Tcij - Tgij (eq 15)
|
||||
solve(A, B, Tcg, DECOMP_SVD);
|
||||
|
||||
R_cam2gripper = Rcg;
|
||||
t_cam2gripper = Tcg;
|
||||
}
|
||||
|
||||
//Reference:
|
||||
//F. Park, B. Martin, "Robot Sensor Calibration: Solving AX = XB on the Euclidean Group."
|
||||
//In IEEE Transactions on Robotics and Automation, 10(5): 717-721, 1994.
|
||||
//Matlab code: http://math.loyola.edu/~mili/Calibration/
|
||||
static void calibrateHandEyePark(const std::vector<Mat>& Hg, const std::vector<Mat>& Hc,
|
||||
Mat& R_cam2gripper, Mat& t_cam2gripper)
|
||||
{
|
||||
Mat M = Mat::zeros(3, 3, CV_64FC1);
|
||||
|
||||
for (size_t i = 0; i < Hg.size(); i++)
|
||||
{
|
||||
for (size_t j = i+1; j < Hg.size(); j++)
|
||||
{
|
||||
Mat Hgij = homogeneousInverse(Hg[j]) * Hg[i];
|
||||
Mat Hcij = Hc[j] * homogeneousInverse(Hc[i]);
|
||||
|
||||
Mat Rgij = Hgij(Rect(0, 0, 3, 3));
|
||||
Mat Rcij = Hcij(Rect(0, 0, 3, 3));
|
||||
|
||||
Mat a, b;
|
||||
Rodrigues(Rgij, a);
|
||||
Rodrigues(Rcij, b);
|
||||
|
||||
M += b * a.t();
|
||||
}
|
||||
}
|
||||
|
||||
Mat eigenvalues, eigenvectors;
|
||||
eigen(M.t()*M, eigenvalues, eigenvectors);
|
||||
|
||||
Mat v = Mat::zeros(3, 3, CV_64FC1);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
v.at<double>(i,i) = 1.0 / sqrt(eigenvalues.at<double>(i,0));
|
||||
}
|
||||
|
||||
Mat R = eigenvectors.t() * v * eigenvectors * M.t();
|
||||
R_cam2gripper = R;
|
||||
|
||||
int K = static_cast<int>((Hg.size()*Hg.size() - Hg.size()) / 2.0);
|
||||
Mat C(3*K, 3, CV_64FC1);
|
||||
Mat d(3*K, 1, CV_64FC1);
|
||||
Mat I3 = Mat::eye(3, 3, CV_64FC1);
|
||||
|
||||
int idx = 0;
|
||||
for (size_t i = 0; i < Hg.size(); i++)
|
||||
{
|
||||
for (size_t j = i+1; j < Hg.size(); j++, idx++)
|
||||
{
|
||||
Mat Hgij = homogeneousInverse(Hg[j]) * Hg[i];
|
||||
Mat Hcij = Hc[j] * homogeneousInverse(Hc[i]);
|
||||
|
||||
Mat Rgij = Hgij(Rect(0, 0, 3, 3));
|
||||
|
||||
Mat tgij = Hgij(Rect(3, 0, 1, 3));
|
||||
Mat tcij = Hcij(Rect(3, 0, 1, 3));
|
||||
|
||||
Mat I_tgij = I3 - Rgij;
|
||||
I_tgij.copyTo(C(Rect(0, 3*idx, 3, 3)));
|
||||
|
||||
Mat A_RB = tgij - R*tcij;
|
||||
A_RB.copyTo(d(Rect(0, 3*idx, 1, 3)));
|
||||
}
|
||||
}
|
||||
|
||||
Mat t;
|
||||
solve(C, d, t, DECOMP_SVD);
|
||||
t_cam2gripper = t;
|
||||
}
|
||||
|
||||
//Reference:
|
||||
//R. Horaud, F. Dornaika, "Hand-Eye Calibration"
|
||||
//In International Journal of Robotics Research, 14(3): 195-210, 1995.
|
||||
//Matlab code: http://math.loyola.edu/~mili/Calibration/
|
||||
static void calibrateHandEyeHoraud(const std::vector<Mat>& Hg, const std::vector<Mat>& Hc,
|
||||
Mat& R_cam2gripper, Mat& t_cam2gripper)
|
||||
{
|
||||
Mat A = Mat::zeros(4, 4, CV_64FC1);
|
||||
|
||||
for (size_t i = 0; i < Hg.size(); i++)
|
||||
{
|
||||
for (size_t j = i+1; j < Hg.size(); j++)
|
||||
{
|
||||
Mat Hgij = homogeneousInverse(Hg[j]) * Hg[i];
|
||||
Mat Hcij = Hc[j] * homogeneousInverse(Hc[i]);
|
||||
|
||||
Mat Rgij = Hgij(Rect(0, 0, 3, 3));
|
||||
Mat Rcij = Hcij(Rect(0, 0, 3, 3));
|
||||
|
||||
Mat qgij = rot2quat(Rgij);
|
||||
double r0 = qgij.at<double>(0,0);
|
||||
double rx = qgij.at<double>(1,0);
|
||||
double ry = qgij.at<double>(2,0);
|
||||
double rz = qgij.at<double>(3,0);
|
||||
|
||||
// Q(r) Appendix A
|
||||
Matx44d Qvi(r0, -rx, -ry, -rz,
|
||||
rx, r0, -rz, ry,
|
||||
ry, rz, r0, -rx,
|
||||
rz, -ry, rx, r0);
|
||||
|
||||
Mat qcij = rot2quat(Rcij);
|
||||
r0 = qcij.at<double>(0,0);
|
||||
rx = qcij.at<double>(1,0);
|
||||
ry = qcij.at<double>(2,0);
|
||||
rz = qcij.at<double>(3,0);
|
||||
|
||||
// W(r) Appendix A
|
||||
Matx44d Wvi(r0, -rx, -ry, -rz,
|
||||
rx, r0, rz, -ry,
|
||||
ry, -rz, r0, rx,
|
||||
rz, ry, -rx, r0);
|
||||
|
||||
// Ai = (Q(vi') - W(vi))^T (Q(vi') - W(vi))
|
||||
A += (Qvi - Wvi).t() * (Qvi - Wvi);
|
||||
}
|
||||
}
|
||||
|
||||
Mat eigenvalues, eigenvectors;
|
||||
eigen(A, eigenvalues, eigenvectors);
|
||||
|
||||
Mat R = quat2rot(eigenvectors.row(3).t());
|
||||
R_cam2gripper = R;
|
||||
|
||||
int K = static_cast<int>((Hg.size()*Hg.size() - Hg.size()) / 2.0);
|
||||
Mat C(3*K, 3, CV_64FC1);
|
||||
Mat d(3*K, 1, CV_64FC1);
|
||||
Mat I3 = Mat::eye(3, 3, CV_64FC1);
|
||||
|
||||
int idx = 0;
|
||||
for (size_t i = 0; i < Hg.size(); i++)
|
||||
{
|
||||
for (size_t j = i+1; j < Hg.size(); j++, idx++)
|
||||
{
|
||||
Mat Hgij = homogeneousInverse(Hg[j]) * Hg[i];
|
||||
Mat Hcij = Hc[j] * homogeneousInverse(Hc[i]);
|
||||
|
||||
Mat Rgij = Hgij(Rect(0, 0, 3, 3));
|
||||
|
||||
Mat tgij = Hgij(Rect(3, 0, 1, 3));
|
||||
Mat tcij = Hcij(Rect(3, 0, 1, 3));
|
||||
|
||||
Mat I_tgij = I3 - Rgij;
|
||||
I_tgij.copyTo(C(Rect(0, 3*idx, 3, 3)));
|
||||
|
||||
Mat A_RB = tgij - R*tcij;
|
||||
A_RB.copyTo(d(Rect(0, 3*idx, 1, 3)));
|
||||
}
|
||||
}
|
||||
|
||||
Mat t;
|
||||
solve(C, d, t, DECOMP_SVD);
|
||||
t_cam2gripper = t;
|
||||
}
|
||||
|
||||
// sign function, return -1 if negative values, +1 otherwise
|
||||
static int sign_double(double val)
|
||||
{
|
||||
return (0 < val) - (val < 0);
|
||||
}
|
||||
|
||||
//Reference:
|
||||
//N. Andreff, R. Horaud, B. Espiau, "On-line Hand-Eye Calibration."
|
||||
//In Second International Conference on 3-D Digital Imaging and Modeling (3DIM'99), pages 430-436, 1999.
|
||||
//Matlab code: http://math.loyola.edu/~mili/Calibration/
|
||||
static void calibrateHandEyeAndreff(const std::vector<Mat>& Hg, const std::vector<Mat>& Hc,
|
||||
Mat& R_cam2gripper, Mat& t_cam2gripper)
|
||||
{
|
||||
int K = static_cast<int>((Hg.size()*Hg.size() - Hg.size()) / 2.0);
|
||||
Mat A(12*K, 12, CV_64FC1);
|
||||
Mat B(12*K, 1, CV_64FC1);
|
||||
|
||||
Mat I9 = Mat::eye(9, 9, CV_64FC1);
|
||||
Mat I3 = Mat::eye(3, 3, CV_64FC1);
|
||||
Mat O9x3 = Mat::zeros(9, 3, CV_64FC1);
|
||||
Mat O9x1 = Mat::zeros(9, 1, CV_64FC1);
|
||||
|
||||
int idx = 0;
|
||||
for (size_t i = 0; i < Hg.size(); i++)
|
||||
{
|
||||
for (size_t j = i+1; j < Hg.size(); j++, idx++)
|
||||
{
|
||||
Mat Hgij = homogeneousInverse(Hg[j]) * Hg[i];
|
||||
Mat Hcij = Hc[j] * homogeneousInverse(Hc[i]);
|
||||
|
||||
Mat Rgij = Hgij(Rect(0, 0, 3, 3));
|
||||
Mat Rcij = Hcij(Rect(0, 0, 3, 3));
|
||||
|
||||
Mat tgij = Hgij(Rect(3, 0, 1, 3));
|
||||
Mat tcij = Hcij(Rect(3, 0, 1, 3));
|
||||
|
||||
//Eq 10
|
||||
Mat a00 = I9 - kron(Rgij, Rcij);
|
||||
Mat a01 = O9x3;
|
||||
Mat a10 = kron(I3, tcij.t());
|
||||
Mat a11 = I3 - Rgij;
|
||||
|
||||
a00.copyTo(A(Rect(0, idx*12, 9, 9)));
|
||||
a01.copyTo(A(Rect(9, idx*12, 3, 9)));
|
||||
a10.copyTo(A(Rect(0, idx*12 + 9, 9, 3)));
|
||||
a11.copyTo(A(Rect(9, idx*12 + 9, 3, 3)));
|
||||
|
||||
O9x1.copyTo(B(Rect(0, idx*12, 1, 9)));
|
||||
tgij.copyTo(B(Rect(0, idx*12 + 9, 1, 3)));
|
||||
}
|
||||
}
|
||||
|
||||
Mat X;
|
||||
solve(A, B, X, DECOMP_SVD);
|
||||
|
||||
Mat R = X(Rect(0, 0, 1, 9));
|
||||
int newSize[] = {3, 3};
|
||||
R = R.reshape(1, 2, newSize);
|
||||
//Eq 15
|
||||
double det = determinant(R);
|
||||
R = pow(sign_double(det) / abs(det), 1.0/3.0) * R;
|
||||
|
||||
Mat w, u, vt;
|
||||
SVDecomp(R, w, u, vt);
|
||||
R = u*vt;
|
||||
|
||||
if (determinant(R) < 0)
|
||||
{
|
||||
Mat diag = (Mat_<double>(3,3) << 1.0, 0.0, 0.0,
|
||||
0.0, 1.0, 0.0,
|
||||
0.0, 0.0, -1.0);
|
||||
R = u*diag*vt;
|
||||
}
|
||||
|
||||
R_cam2gripper = R;
|
||||
|
||||
Mat t = X(Rect(0, 9, 1, 3));
|
||||
t_cam2gripper = t;
|
||||
}
|
||||
|
||||
//Reference:
|
||||
//K. Daniilidis, "Hand-Eye Calibration Using Dual Quaternions."
|
||||
//In The International Journal of Robotics Research,18(3): 286-298, 1998.
|
||||
//Matlab code: http://math.loyola.edu/~mili/Calibration/
|
||||
static void calibrateHandEyeDaniilidis(const std::vector<Mat>& Hg, const std::vector<Mat>& Hc,
|
||||
Mat& R_cam2gripper, Mat& t_cam2gripper)
|
||||
{
|
||||
int K = static_cast<int>((Hg.size()*Hg.size() - Hg.size()) / 2.0);
|
||||
Mat T = Mat::zeros(6*K, 8, CV_64FC1);
|
||||
|
||||
int idx = 0;
|
||||
for (size_t i = 0; i < Hg.size(); i++)
|
||||
{
|
||||
for (size_t j = i+1; j < Hg.size(); j++, idx++)
|
||||
{
|
||||
Mat Hgij = homogeneousInverse(Hg[j]) * Hg[i];
|
||||
Mat Hcij = Hc[j] * homogeneousInverse(Hc[i]);
|
||||
|
||||
Mat dualqa = homogeneous2dualQuaternion(Hgij);
|
||||
Mat dualqb = homogeneous2dualQuaternion(Hcij);
|
||||
|
||||
Mat a = dualqa(Rect(0, 1, 1, 3));
|
||||
Mat b = dualqb(Rect(0, 1, 1, 3));
|
||||
|
||||
Mat aprime = dualqa(Rect(0, 5, 1, 3));
|
||||
Mat bprime = dualqb(Rect(0, 5, 1, 3));
|
||||
|
||||
//Eq 31
|
||||
Mat s00 = a - b;
|
||||
Mat s01 = skew(a + b);
|
||||
Mat s10 = aprime - bprime;
|
||||
Mat s11 = skew(aprime + bprime);
|
||||
Mat s12 = a - b;
|
||||
Mat s13 = skew(a + b);
|
||||
|
||||
s00.copyTo(T(Rect(0, idx*6, 1, 3)));
|
||||
s01.copyTo(T(Rect(1, idx*6, 3, 3)));
|
||||
s10.copyTo(T(Rect(0, idx*6 + 3, 1, 3)));
|
||||
s11.copyTo(T(Rect(1, idx*6 + 3, 3, 3)));
|
||||
s12.copyTo(T(Rect(4, idx*6 + 3, 1, 3)));
|
||||
s13.copyTo(T(Rect(5, idx*6 + 3, 3, 3)));
|
||||
}
|
||||
}
|
||||
|
||||
Mat w, u, vt;
|
||||
SVDecomp(T, w, u, vt);
|
||||
Mat v = vt.t();
|
||||
|
||||
Mat u1 = v(Rect(6, 0, 1, 4));
|
||||
Mat v1 = v(Rect(6, 4, 1, 4));
|
||||
Mat u2 = v(Rect(7, 0, 1, 4));
|
||||
Mat v2 = v(Rect(7, 4, 1, 4));
|
||||
|
||||
//Solves Eq 34, Eq 35
|
||||
Mat ma = u1.t()*v1;
|
||||
Mat mb = u1.t()*v2 + u2.t()*v1;
|
||||
Mat mc = u2.t()*v2;
|
||||
|
||||
double a = ma.at<double>(0,0);
|
||||
double b = mb.at<double>(0,0);
|
||||
double c = mc.at<double>(0,0);
|
||||
|
||||
double s1 = (-b + sqrt(b*b - 4*a*c)) / (2*a);
|
||||
double s2 = (-b - sqrt(b*b - 4*a*c)) / (2*a);
|
||||
|
||||
Mat sol1 = s1*s1*u1.t()*u1 + 2*s1*u1.t()*u2 + u2.t()*u2;
|
||||
Mat sol2 = s2*s2*u1.t()*u1 + 2*s2*u1.t()*u2 + u2.t()*u2;
|
||||
double s, val;
|
||||
if (sol1.at<double>(0,0) > sol2.at<double>(0,0))
|
||||
{
|
||||
s = s1;
|
||||
val = sol1.at<double>(0,0);
|
||||
}
|
||||
else
|
||||
{
|
||||
s = s2;
|
||||
val = sol2.at<double>(0,0);
|
||||
}
|
||||
|
||||
double lambda2 = sqrt(1.0 / val);
|
||||
double lambda1 = s * lambda2;
|
||||
|
||||
Mat dualq = lambda1 * v(Rect(6, 0, 1, 8)) + lambda2*v(Rect(7, 0, 1, 8));
|
||||
Mat X = dualQuaternion2homogeneous(dualq);
|
||||
|
||||
Mat R = X(Rect(0, 0, 3, 3));
|
||||
Mat t = X(Rect(3, 0, 1, 3));
|
||||
R_cam2gripper = R;
|
||||
t_cam2gripper = t;
|
||||
}
|
||||
|
||||
void calibrateHandEye(InputArrayOfArrays R_gripper2base, InputArrayOfArrays t_gripper2base,
|
||||
InputArrayOfArrays R_target2cam, InputArrayOfArrays t_target2cam,
|
||||
OutputArray R_cam2gripper, OutputArray t_cam2gripper,
|
||||
HandEyeCalibrationMethod method)
|
||||
{
|
||||
CV_Assert(R_gripper2base.isMatVector() && t_gripper2base.isMatVector() &&
|
||||
R_target2cam.isMatVector() && t_target2cam.isMatVector());
|
||||
|
||||
std::vector<Mat> R_gripper2base_, t_gripper2base_;
|
||||
R_gripper2base.getMatVector(R_gripper2base_);
|
||||
t_gripper2base.getMatVector(t_gripper2base_);
|
||||
|
||||
std::vector<Mat> R_target2cam_, t_target2cam_;
|
||||
R_target2cam.getMatVector(R_target2cam_);
|
||||
t_target2cam.getMatVector(t_target2cam_);
|
||||
|
||||
CV_Assert(R_gripper2base_.size() == t_gripper2base_.size() &&
|
||||
R_target2cam_.size() == t_target2cam_.size() &&
|
||||
R_gripper2base_.size() == R_target2cam_.size());
|
||||
CV_Assert(R_gripper2base_.size() >= 3);
|
||||
|
||||
//Notation used in Tsai paper
|
||||
//Defines coordinate transformation from G (gripper) to RW (robot base)
|
||||
std::vector<Mat> Hg;
|
||||
Hg.reserve(R_gripper2base_.size());
|
||||
for (size_t i = 0; i < R_gripper2base_.size(); i++)
|
||||
{
|
||||
Mat m = Mat::eye(4, 4, CV_64FC1);
|
||||
Mat R = m(Rect(0, 0, 3, 3));
|
||||
R_gripper2base_[i].convertTo(R, CV_64F);
|
||||
|
||||
Mat t = m(Rect(3, 0, 1, 3));
|
||||
t_gripper2base_[i].convertTo(t, CV_64F);
|
||||
|
||||
Hg.push_back(m);
|
||||
}
|
||||
|
||||
//Defines coordinate transformation from CW (calibration target) to C (camera)
|
||||
std::vector<Mat> Hc;
|
||||
Hc.reserve(R_target2cam_.size());
|
||||
for (size_t i = 0; i < R_target2cam_.size(); i++)
|
||||
{
|
||||
Mat m = Mat::eye(4, 4, CV_64FC1);
|
||||
Mat R = m(Rect(0, 0, 3, 3));
|
||||
R_target2cam_[i].convertTo(R, CV_64F);
|
||||
|
||||
Mat t = m(Rect(3, 0, 1, 3));
|
||||
t_target2cam_[i].convertTo(t, CV_64F);
|
||||
|
||||
Hc.push_back(m);
|
||||
}
|
||||
|
||||
Mat Rcg = Mat::eye(3, 3, CV_64FC1);
|
||||
Mat Tcg = Mat::zeros(3, 1, CV_64FC1);
|
||||
|
||||
switch (method)
|
||||
{
|
||||
case CALIB_HAND_EYE_TSAI:
|
||||
calibrateHandEyeTsai(Hg, Hc, Rcg, Tcg);
|
||||
break;
|
||||
|
||||
case CALIB_HAND_EYE_PARK:
|
||||
calibrateHandEyePark(Hg, Hc, Rcg, Tcg);
|
||||
break;
|
||||
|
||||
case CALIB_HAND_EYE_HORAUD:
|
||||
calibrateHandEyeHoraud(Hg, Hc, Rcg, Tcg);
|
||||
break;
|
||||
|
||||
case CALIB_HAND_EYE_ANDREFF:
|
||||
calibrateHandEyeAndreff(Hg, Hc, Rcg, Tcg);
|
||||
break;
|
||||
|
||||
case CALIB_HAND_EYE_DANIILIDIS:
|
||||
calibrateHandEyeDaniilidis(Hg, Hc, Rcg, Tcg);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Rcg.copyTo(R_cam2gripper);
|
||||
Tcg.copyTo(t_cam2gripper);
|
||||
}
|
||||
}
|
381
modules/calib3d/test/test_calibration_hand_eye.cpp
Normal file
381
modules/calib3d/test/test_calibration_hand_eye.cpp
Normal file
@ -0,0 +1,381 @@
|
||||
// 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/calib3d.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
class CV_CalibrateHandEyeTest : public cvtest::BaseTest
|
||||
{
|
||||
public:
|
||||
CV_CalibrateHandEyeTest() {
|
||||
eps_rvec[CALIB_HAND_EYE_TSAI] = 1.0e-8;
|
||||
eps_rvec[CALIB_HAND_EYE_PARK] = 1.0e-8;
|
||||
eps_rvec[CALIB_HAND_EYE_HORAUD] = 1.0e-8;
|
||||
eps_rvec[CALIB_HAND_EYE_ANDREFF] = 1.0e-8;
|
||||
eps_rvec[CALIB_HAND_EYE_DANIILIDIS] = 1.0e-8;
|
||||
|
||||
eps_tvec[CALIB_HAND_EYE_TSAI] = 1.0e-8;
|
||||
eps_tvec[CALIB_HAND_EYE_PARK] = 1.0e-8;
|
||||
eps_tvec[CALIB_HAND_EYE_HORAUD] = 1.0e-8;
|
||||
eps_tvec[CALIB_HAND_EYE_ANDREFF] = 1.0e-8;
|
||||
eps_tvec[CALIB_HAND_EYE_DANIILIDIS] = 1.0e-8;
|
||||
|
||||
eps_rvec_noise[CALIB_HAND_EYE_TSAI] = 2.0e-2;
|
||||
eps_rvec_noise[CALIB_HAND_EYE_PARK] = 2.0e-2;
|
||||
eps_rvec_noise[CALIB_HAND_EYE_HORAUD] = 2.0e-2;
|
||||
eps_rvec_noise[CALIB_HAND_EYE_ANDREFF] = 1.0e-2;
|
||||
eps_rvec_noise[CALIB_HAND_EYE_DANIILIDIS] = 1.0e-2;
|
||||
|
||||
eps_tvec_noise[CALIB_HAND_EYE_TSAI] = 5.0e-2;
|
||||
eps_tvec_noise[CALIB_HAND_EYE_PARK] = 5.0e-2;
|
||||
eps_tvec_noise[CALIB_HAND_EYE_HORAUD] = 5.0e-2;
|
||||
eps_tvec_noise[CALIB_HAND_EYE_ANDREFF] = 5.0e-2;
|
||||
eps_tvec_noise[CALIB_HAND_EYE_DANIILIDIS] = 5.0e-2;
|
||||
}
|
||||
protected:
|
||||
virtual void run(int);
|
||||
void generatePose(RNG& rng, double min_theta, double max_theta,
|
||||
double min_tx, double max_tx,
|
||||
double min_ty, double max_ty,
|
||||
double min_tz, double max_tz,
|
||||
Mat& R, Mat& tvec,
|
||||
bool randSign=false);
|
||||
void simulateData(RNG& rng, int nPoses,
|
||||
std::vector<Mat> &R_gripper2base, std::vector<Mat> &t_gripper2base,
|
||||
std::vector<Mat> &R_target2cam, std::vector<Mat> &t_target2cam,
|
||||
bool noise, Mat& R_cam2gripper, Mat& t_cam2gripper);
|
||||
Mat homogeneousInverse(const Mat& T);
|
||||
std::string getMethodName(HandEyeCalibrationMethod method);
|
||||
double sign_double(double val);
|
||||
|
||||
double eps_rvec[5];
|
||||
double eps_tvec[5];
|
||||
double eps_rvec_noise[5];
|
||||
double eps_tvec_noise[5];
|
||||
};
|
||||
|
||||
void CV_CalibrateHandEyeTest::run(int)
|
||||
{
|
||||
ts->set_failed_test_info(cvtest::TS::OK);
|
||||
|
||||
RNG& rng = ts->get_rng();
|
||||
|
||||
std::vector<std::vector<double> > vec_rvec_diff(5);
|
||||
std::vector<std::vector<double> > vec_tvec_diff(5);
|
||||
std::vector<std::vector<double> > vec_rvec_diff_noise(5);
|
||||
std::vector<std::vector<double> > vec_tvec_diff_noise(5);
|
||||
|
||||
std::vector<HandEyeCalibrationMethod> methods;
|
||||
methods.push_back(CALIB_HAND_EYE_TSAI);
|
||||
methods.push_back(CALIB_HAND_EYE_PARK);
|
||||
methods.push_back(CALIB_HAND_EYE_HORAUD);
|
||||
methods.push_back(CALIB_HAND_EYE_ANDREFF);
|
||||
methods.push_back(CALIB_HAND_EYE_DANIILIDIS);
|
||||
|
||||
const int nTests = 100;
|
||||
for (int i = 0; i < nTests; i++)
|
||||
{
|
||||
const int nPoses = 10;
|
||||
{
|
||||
//No noise
|
||||
std::vector<Mat> R_gripper2base, t_gripper2base;
|
||||
std::vector<Mat> R_target2cam, t_target2cam;
|
||||
Mat R_cam2gripper_true, t_cam2gripper_true;
|
||||
|
||||
const bool noise = false;
|
||||
simulateData(rng, nPoses, R_gripper2base, t_gripper2base, R_target2cam, t_target2cam, noise, R_cam2gripper_true, t_cam2gripper_true);
|
||||
|
||||
for (size_t idx = 0; idx < methods.size(); idx++)
|
||||
{
|
||||
Mat rvec_cam2gripper_true;
|
||||
cv::Rodrigues(R_cam2gripper_true, rvec_cam2gripper_true);
|
||||
|
||||
Mat R_cam2gripper_est, t_cam2gripper_est;
|
||||
calibrateHandEye(R_gripper2base, t_gripper2base, R_target2cam, t_target2cam, R_cam2gripper_est, t_cam2gripper_est, methods[idx]);
|
||||
|
||||
Mat rvec_cam2gripper_est;
|
||||
cv::Rodrigues(R_cam2gripper_est, rvec_cam2gripper_est);
|
||||
|
||||
double rvecDiff = cvtest::norm(rvec_cam2gripper_true, rvec_cam2gripper_est, NORM_L2);
|
||||
double tvecDiff = cvtest::norm(t_cam2gripper_true, t_cam2gripper_est, NORM_L2);
|
||||
|
||||
vec_rvec_diff[idx].push_back(rvecDiff);
|
||||
vec_tvec_diff[idx].push_back(tvecDiff);
|
||||
|
||||
const double epsilon_rvec = eps_rvec[idx];
|
||||
const double epsilon_tvec = eps_tvec[idx];
|
||||
|
||||
//Maybe a better accuracy test would be to compare the mean and std errors with some thresholds?
|
||||
if (rvecDiff > epsilon_rvec || tvecDiff > epsilon_tvec)
|
||||
{
|
||||
ts->printf(cvtest::TS::LOG, "Invalid accuracy (no noise) for method: %s, rvecDiff: %f, epsilon_rvec: %f, tvecDiff: %f, epsilon_tvec: %f\n",
|
||||
getMethodName(methods[idx]).c_str(), rvecDiff, epsilon_rvec, tvecDiff, epsilon_tvec);
|
||||
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
//Gaussian noise on transformations between calibration target frame and camera frame and between gripper and robot base frames
|
||||
std::vector<Mat> R_gripper2base, t_gripper2base;
|
||||
std::vector<Mat> R_target2cam, t_target2cam;
|
||||
Mat R_cam2gripper_true, t_cam2gripper_true;
|
||||
|
||||
const bool noise = true;
|
||||
simulateData(rng, nPoses, R_gripper2base, t_gripper2base, R_target2cam, t_target2cam, noise, R_cam2gripper_true, t_cam2gripper_true);
|
||||
|
||||
for (size_t idx = 0; idx < methods.size(); idx++)
|
||||
{
|
||||
Mat rvec_cam2gripper_true;
|
||||
cv::Rodrigues(R_cam2gripper_true, rvec_cam2gripper_true);
|
||||
|
||||
Mat R_cam2gripper_est, t_cam2gripper_est;
|
||||
calibrateHandEye(R_gripper2base, t_gripper2base, R_target2cam, t_target2cam, R_cam2gripper_est, t_cam2gripper_est, methods[idx]);
|
||||
|
||||
Mat rvec_cam2gripper_est;
|
||||
cv::Rodrigues(R_cam2gripper_est, rvec_cam2gripper_est);
|
||||
|
||||
double rvecDiff = cvtest::norm(rvec_cam2gripper_true, rvec_cam2gripper_est, NORM_L2);
|
||||
double tvecDiff = cvtest::norm(t_cam2gripper_true, t_cam2gripper_est, NORM_L2);
|
||||
|
||||
vec_rvec_diff_noise[idx].push_back(rvecDiff);
|
||||
vec_tvec_diff_noise[idx].push_back(tvecDiff);
|
||||
|
||||
const double epsilon_rvec = eps_rvec_noise[idx];
|
||||
const double epsilon_tvec = eps_tvec_noise[idx];
|
||||
|
||||
//Maybe a better accuracy test would be to compare the mean and std errors with some thresholds?
|
||||
if (rvecDiff > epsilon_rvec || tvecDiff > epsilon_tvec)
|
||||
{
|
||||
ts->printf(cvtest::TS::LOG, "Invalid accuracy (noise) for method: %s, rvecDiff: %f, epsilon_rvec: %f, tvecDiff: %f, epsilon_tvec: %f\n",
|
||||
getMethodName(methods[idx]).c_str(), rvecDiff, epsilon_rvec, tvecDiff, epsilon_tvec);
|
||||
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t idx = 0; idx < methods.size(); idx++)
|
||||
{
|
||||
{
|
||||
double max_rvec_diff = *std::max_element(vec_rvec_diff[idx].begin(), vec_rvec_diff[idx].end());
|
||||
double mean_rvec_diff = std::accumulate(vec_rvec_diff[idx].begin(),
|
||||
vec_rvec_diff[idx].end(), 0.0) / vec_rvec_diff[idx].size();
|
||||
double sq_sum_rvec_diff = std::inner_product(vec_rvec_diff[idx].begin(), vec_rvec_diff[idx].end(),
|
||||
vec_rvec_diff[idx].begin(), 0.0);
|
||||
double std_rvec_diff = std::sqrt(sq_sum_rvec_diff / vec_rvec_diff[idx].size() - mean_rvec_diff * mean_rvec_diff);
|
||||
|
||||
double max_tvec_diff = *std::max_element(vec_tvec_diff[idx].begin(), vec_tvec_diff[idx].end());
|
||||
double mean_tvec_diff = std::accumulate(vec_tvec_diff[idx].begin(),
|
||||
vec_tvec_diff[idx].end(), 0.0) / vec_tvec_diff[idx].size();
|
||||
double sq_sum_tvec_diff = std::inner_product(vec_tvec_diff[idx].begin(), vec_tvec_diff[idx].end(),
|
||||
vec_tvec_diff[idx].begin(), 0.0);
|
||||
double std_tvec_diff = std::sqrt(sq_sum_tvec_diff / vec_tvec_diff[idx].size() - mean_tvec_diff * mean_tvec_diff);
|
||||
|
||||
std::cout << "\nMethod " << getMethodName(methods[idx]) << ":\n"
|
||||
<< "Max rvec error: " << max_rvec_diff << ", Mean rvec error: " << mean_rvec_diff
|
||||
<< ", Std rvec error: " << std_rvec_diff << "\n"
|
||||
<< "Max tvec error: " << max_tvec_diff << ", Mean tvec error: " << mean_tvec_diff
|
||||
<< ", Std tvec error: " << std_tvec_diff << std::endl;
|
||||
}
|
||||
{
|
||||
double max_rvec_diff = *std::max_element(vec_rvec_diff_noise[idx].begin(), vec_rvec_diff_noise[idx].end());
|
||||
double mean_rvec_diff = std::accumulate(vec_rvec_diff_noise[idx].begin(),
|
||||
vec_rvec_diff_noise[idx].end(), 0.0) / vec_rvec_diff_noise[idx].size();
|
||||
double sq_sum_rvec_diff = std::inner_product(vec_rvec_diff_noise[idx].begin(), vec_rvec_diff_noise[idx].end(),
|
||||
vec_rvec_diff_noise[idx].begin(), 0.0);
|
||||
double std_rvec_diff = std::sqrt(sq_sum_rvec_diff / vec_rvec_diff_noise[idx].size() - mean_rvec_diff * mean_rvec_diff);
|
||||
|
||||
double max_tvec_diff = *std::max_element(vec_tvec_diff_noise[idx].begin(), vec_tvec_diff_noise[idx].end());
|
||||
double mean_tvec_diff = std::accumulate(vec_tvec_diff_noise[idx].begin(),
|
||||
vec_tvec_diff_noise[idx].end(), 0.0) / vec_tvec_diff_noise[idx].size();
|
||||
double sq_sum_tvec_diff = std::inner_product(vec_tvec_diff_noise[idx].begin(), vec_tvec_diff_noise[idx].end(),
|
||||
vec_tvec_diff_noise[idx].begin(), 0.0);
|
||||
double std_tvec_diff = std::sqrt(sq_sum_tvec_diff / vec_tvec_diff_noise[idx].size() - mean_tvec_diff * mean_tvec_diff);
|
||||
|
||||
std::cout << "Method (noise) " << getMethodName(methods[idx]) << ":\n"
|
||||
<< "Max rvec error: " << max_rvec_diff << ", Mean rvec error: " << mean_rvec_diff
|
||||
<< ", Std rvec error: " << std_rvec_diff << "\n"
|
||||
<< "Max tvec error: " << max_tvec_diff << ", Mean tvec error: " << mean_tvec_diff
|
||||
<< ", Std tvec error: " << std_tvec_diff << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CV_CalibrateHandEyeTest::generatePose(RNG& rng, double min_theta, double max_theta,
|
||||
double min_tx, double max_tx,
|
||||
double min_ty, double max_ty,
|
||||
double min_tz, double max_tz,
|
||||
Mat& R, Mat& tvec,
|
||||
bool random_sign)
|
||||
{
|
||||
Mat axis(3, 1, CV_64FC1);
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
axis.at<double>(i,0) = rng.uniform(-1.0, 1.0);
|
||||
}
|
||||
double theta = rng.uniform(min_theta, max_theta);
|
||||
if (random_sign)
|
||||
{
|
||||
theta *= sign_double(rng.uniform(-1.0, 1.0));
|
||||
}
|
||||
|
||||
Mat rvec(3, 1, CV_64FC1);
|
||||
rvec.at<double>(0,0) = theta*axis.at<double>(0,0);
|
||||
rvec.at<double>(1,0) = theta*axis.at<double>(1,0);
|
||||
rvec.at<double>(2,0) = theta*axis.at<double>(2,0);
|
||||
|
||||
tvec.create(3, 1, CV_64FC1);
|
||||
tvec.at<double>(0,0) = rng.uniform(min_tx, max_tx);
|
||||
tvec.at<double>(1,0) = rng.uniform(min_ty, max_ty);
|
||||
tvec.at<double>(2,0) = rng.uniform(min_tz, max_tz);
|
||||
|
||||
if (random_sign)
|
||||
{
|
||||
tvec.at<double>(0,0) *= sign_double(rng.uniform(-1.0, 1.0));
|
||||
tvec.at<double>(1,0) *= sign_double(rng.uniform(-1.0, 1.0));
|
||||
tvec.at<double>(2,0) *= sign_double(rng.uniform(-1.0, 1.0));
|
||||
}
|
||||
|
||||
cv::Rodrigues(rvec, R);
|
||||
}
|
||||
|
||||
void CV_CalibrateHandEyeTest::simulateData(RNG& rng, int nPoses,
|
||||
std::vector<Mat> &R_gripper2base, std::vector<Mat> &t_gripper2base,
|
||||
std::vector<Mat> &R_target2cam, std::vector<Mat> &t_target2cam,
|
||||
bool noise, Mat& R_cam2gripper, Mat& t_cam2gripper)
|
||||
{
|
||||
//to avoid generating values close to zero,
|
||||
//we use positive range values and randomize the sign
|
||||
const bool random_sign = true;
|
||||
generatePose(rng, 10.0*CV_PI/180.0, 50.0*CV_PI/180.0,
|
||||
0.05, 0.5, 0.05, 0.5, 0.05, 0.5,
|
||||
R_cam2gripper, t_cam2gripper, random_sign);
|
||||
|
||||
Mat R_target2base, t_target2base;
|
||||
generatePose(rng, 5.0*CV_PI/180.0, 85.0*CV_PI/180.0,
|
||||
0.5, 3.5, 0.5, 3.5, 0.5, 3.5,
|
||||
R_target2base, t_target2base, random_sign);
|
||||
|
||||
for (int i = 0; i < nPoses; i++)
|
||||
{
|
||||
Mat R_gripper2base_, t_gripper2base_;
|
||||
generatePose(rng, 5.0*CV_PI/180.0, 45.0*CV_PI/180.0,
|
||||
0.5, 1.5, 0.5, 1.5, 0.5, 1.5,
|
||||
R_gripper2base_, t_gripper2base_, random_sign);
|
||||
|
||||
R_gripper2base.push_back(R_gripper2base_);
|
||||
t_gripper2base.push_back(t_gripper2base_);
|
||||
|
||||
Mat T_cam2gripper = Mat::eye(4, 4, CV_64FC1);
|
||||
R_cam2gripper.copyTo(T_cam2gripper(Rect(0, 0, 3, 3)));
|
||||
t_cam2gripper.copyTo(T_cam2gripper(Rect(3, 0, 1, 3)));
|
||||
|
||||
Mat T_gripper2base = Mat::eye(4, 4, CV_64FC1);
|
||||
R_gripper2base_.copyTo(T_gripper2base(Rect(0, 0, 3, 3)));
|
||||
t_gripper2base_.copyTo(T_gripper2base(Rect(3, 0, 1, 3)));
|
||||
|
||||
Mat T_base2cam = homogeneousInverse(T_cam2gripper) * homogeneousInverse(T_gripper2base);
|
||||
Mat T_target2base = Mat::eye(4, 4, CV_64FC1);
|
||||
R_target2base.copyTo(T_target2base(Rect(0, 0, 3, 3)));
|
||||
t_target2base.copyTo(T_target2base(Rect(3, 0, 1, 3)));
|
||||
Mat T_target2cam = T_base2cam * T_target2base;
|
||||
|
||||
if (noise)
|
||||
{
|
||||
//Add some noise for the transformation between the target and the camera
|
||||
Mat R_target2cam_noise = T_target2cam(Rect(0, 0, 3, 3));
|
||||
Mat rvec_target2cam_noise;
|
||||
cv::Rodrigues(R_target2cam_noise, rvec_target2cam_noise);
|
||||
rvec_target2cam_noise.at<double>(0,0) += rng.gaussian(0.002);
|
||||
rvec_target2cam_noise.at<double>(1,0) += rng.gaussian(0.002);
|
||||
rvec_target2cam_noise.at<double>(2,0) += rng.gaussian(0.002);
|
||||
|
||||
cv::Rodrigues(rvec_target2cam_noise, R_target2cam_noise);
|
||||
|
||||
Mat t_target2cam_noise = T_target2cam(Rect(3, 0, 1, 3));
|
||||
t_target2cam_noise.at<double>(0,0) += rng.gaussian(0.005);
|
||||
t_target2cam_noise.at<double>(1,0) += rng.gaussian(0.005);
|
||||
t_target2cam_noise.at<double>(2,0) += rng.gaussian(0.005);
|
||||
|
||||
//Add some noise for the transformation between the gripper and the robot base
|
||||
Mat R_gripper2base_noise = T_gripper2base(Rect(0, 0, 3, 3));
|
||||
Mat rvec_gripper2base_noise;
|
||||
cv::Rodrigues(R_gripper2base_noise, rvec_gripper2base_noise);
|
||||
rvec_gripper2base_noise.at<double>(0,0) += rng.gaussian(0.001);
|
||||
rvec_gripper2base_noise.at<double>(1,0) += rng.gaussian(0.001);
|
||||
rvec_gripper2base_noise.at<double>(2,0) += rng.gaussian(0.001);
|
||||
|
||||
cv::Rodrigues(rvec_gripper2base_noise, R_gripper2base_noise);
|
||||
|
||||
Mat t_gripper2base_noise = T_gripper2base(Rect(3, 0, 1, 3));
|
||||
t_gripper2base_noise.at<double>(0,0) += rng.gaussian(0.001);
|
||||
t_gripper2base_noise.at<double>(1,0) += rng.gaussian(0.001);
|
||||
t_gripper2base_noise.at<double>(2,0) += rng.gaussian(0.001);
|
||||
}
|
||||
|
||||
R_target2cam.push_back(T_target2cam(Rect(0, 0, 3, 3)));
|
||||
t_target2cam.push_back(T_target2cam(Rect(3, 0, 1, 3)));
|
||||
}
|
||||
}
|
||||
|
||||
Mat CV_CalibrateHandEyeTest::homogeneousInverse(const Mat& T)
|
||||
{
|
||||
CV_Assert( T.rows == 4 && T.cols == 4 );
|
||||
|
||||
Mat R = T(Rect(0, 0, 3, 3));
|
||||
Mat t = T(Rect(3, 0, 1, 3));
|
||||
Mat Rt = R.t();
|
||||
Mat tinv = -Rt * t;
|
||||
Mat Tinv = Mat::eye(4, 4, T.type());
|
||||
Rt.copyTo(Tinv(Rect(0, 0, 3, 3)));
|
||||
tinv.copyTo(Tinv(Rect(3, 0, 1, 3)));
|
||||
|
||||
return Tinv;
|
||||
}
|
||||
|
||||
std::string CV_CalibrateHandEyeTest::getMethodName(HandEyeCalibrationMethod method)
|
||||
{
|
||||
std::string method_name = "";
|
||||
switch (method)
|
||||
{
|
||||
case CALIB_HAND_EYE_TSAI:
|
||||
method_name = "Tsai";
|
||||
break;
|
||||
|
||||
case CALIB_HAND_EYE_PARK:
|
||||
method_name = "Park";
|
||||
break;
|
||||
|
||||
case CALIB_HAND_EYE_HORAUD:
|
||||
method_name = "Horaud";
|
||||
break;
|
||||
|
||||
case CALIB_HAND_EYE_ANDREFF:
|
||||
method_name = "Andreff";
|
||||
break;
|
||||
|
||||
case CALIB_HAND_EYE_DANIILIDIS:
|
||||
method_name = "Daniilidis";
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return method_name;
|
||||
}
|
||||
|
||||
double CV_CalibrateHandEyeTest::sign_double(double val)
|
||||
{
|
||||
return (0 < val) - (val < 0);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TEST(Calib3d_CalibrateHandEye, regression) { CV_CalibrateHandEyeTest test; test.safe_run(); }
|
||||
|
||||
}} // namespace
|
@ -1610,6 +1610,16 @@ inline v_int16x16 v_pack_triplets(const v_int16x16& vec)
|
||||
}
|
||||
inline v_uint16x16 v_pack_triplets(const v_uint16x16& vec) { return v_reinterpret_as_u16(v_pack_triplets(v_reinterpret_as_s16(vec))); }
|
||||
|
||||
inline v_int32x8 v_pack_triplets(const v_int32x8& vec)
|
||||
{
|
||||
return v_int32x8(_mm256_permutevar8x32_epi32(vec.val, _mm256_set_epi64x(0x0000000700000007, 0x0000000600000005, 0x0000000400000002, 0x0000000100000000)));
|
||||
}
|
||||
inline v_uint32x8 v_pack_triplets(const v_uint32x8& vec) { return v_reinterpret_as_u32(v_pack_triplets(v_reinterpret_as_s32(vec))); }
|
||||
inline v_float32x8 v_pack_triplets(const v_float32x8& vec)
|
||||
{
|
||||
return v_float32x8(_mm256_permutevar8x32_ps(vec.val, _mm256_set_epi64x(0x0000000700000007, 0x0000000600000005, 0x0000000400000002, 0x0000000100000000)));
|
||||
}
|
||||
|
||||
////////// Matrix operations /////////
|
||||
|
||||
inline v_int32x8 v_dotprod(const v_int16x16& a, const v_int16x16& b)
|
||||
|
@ -1908,7 +1908,6 @@ template<typename _Tp, int n> inline v_reg<_Tp, n> v_interleave_quads(const v_re
|
||||
template<typename _Tp, int n> inline v_reg<_Tp, n> v_pack_triplets(const v_reg<_Tp, n>& vec)
|
||||
{
|
||||
v_reg<float, n> c;
|
||||
int j = 0;
|
||||
for (int i = 0; i < n/4; i++)
|
||||
{
|
||||
c.s[3*i ] = vec.s[4*i ];
|
||||
|
@ -1597,29 +1597,49 @@ inline v_int8x16 v_lut(const schar* tab, const int* idx)
|
||||
}
|
||||
inline v_int8x16 v_lut_pairs(const schar* tab, const int* idx)
|
||||
{
|
||||
short CV_DECL_ALIGNED(32) elems[8] =
|
||||
schar CV_DECL_ALIGNED(32) elems[16] =
|
||||
{
|
||||
*(short*)(tab+idx[0]),
|
||||
*(short*)(tab+idx[1]),
|
||||
*(short*)(tab+idx[2]),
|
||||
*(short*)(tab+idx[3]),
|
||||
*(short*)(tab+idx[4]),
|
||||
*(short*)(tab+idx[5]),
|
||||
*(short*)(tab+idx[6]),
|
||||
*(short*)(tab+idx[7])
|
||||
tab[idx[0]],
|
||||
tab[idx[0] + 1],
|
||||
tab[idx[1]],
|
||||
tab[idx[1] + 1],
|
||||
tab[idx[2]],
|
||||
tab[idx[2] + 1],
|
||||
tab[idx[3]],
|
||||
tab[idx[3] + 1],
|
||||
tab[idx[4]],
|
||||
tab[idx[4] + 1],
|
||||
tab[idx[5]],
|
||||
tab[idx[5] + 1],
|
||||
tab[idx[6]],
|
||||
tab[idx[6] + 1],
|
||||
tab[idx[7]],
|
||||
tab[idx[7] + 1]
|
||||
};
|
||||
return v_int8x16(vreinterpretq_s8_s16(vld1q_s16(elems)));
|
||||
return v_int8x16(vld1q_s8(elems));
|
||||
}
|
||||
inline v_int8x16 v_lut_quads(const schar* tab, const int* idx)
|
||||
{
|
||||
int CV_DECL_ALIGNED(32) elems[4] =
|
||||
schar CV_DECL_ALIGNED(32) elems[16] =
|
||||
{
|
||||
*(int*)(tab + idx[0]),
|
||||
*(int*)(tab + idx[1]),
|
||||
*(int*)(tab + idx[2]),
|
||||
*(int*)(tab + idx[3])
|
||||
tab[idx[0]],
|
||||
tab[idx[0] + 1],
|
||||
tab[idx[0] + 2],
|
||||
tab[idx[0] + 3],
|
||||
tab[idx[1]],
|
||||
tab[idx[1] + 1],
|
||||
tab[idx[1] + 2],
|
||||
tab[idx[1] + 3],
|
||||
tab[idx[2]],
|
||||
tab[idx[2] + 1],
|
||||
tab[idx[2] + 2],
|
||||
tab[idx[2] + 3],
|
||||
tab[idx[3]],
|
||||
tab[idx[3] + 1],
|
||||
tab[idx[3] + 2],
|
||||
tab[idx[3] + 3]
|
||||
};
|
||||
return v_int8x16(vreinterpretq_s8_s32(vld1q_s32(elems)));
|
||||
return v_int8x16(vld1q_s8(elems));
|
||||
}
|
||||
inline v_uint8x16 v_lut(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut((schar*)tab, idx)); }
|
||||
inline v_uint8x16 v_lut_pairs(const uchar* tab, const int* idx) { return v_reinterpret_as_u8(v_lut_pairs((schar*)tab, idx)); }
|
||||
@ -1642,23 +1662,22 @@ inline v_int16x8 v_lut(const short* tab, const int* idx)
|
||||
}
|
||||
inline v_int16x8 v_lut_pairs(const short* tab, const int* idx)
|
||||
{
|
||||
int CV_DECL_ALIGNED(32) elems[4] =
|
||||
short CV_DECL_ALIGNED(32) elems[8] =
|
||||
{
|
||||
*(int*)(tab + idx[0]),
|
||||
*(int*)(tab + idx[1]),
|
||||
*(int*)(tab + idx[2]),
|
||||
*(int*)(tab + idx[3])
|
||||
tab[idx[0]],
|
||||
tab[idx[0] + 1],
|
||||
tab[idx[1]],
|
||||
tab[idx[1] + 1],
|
||||
tab[idx[2]],
|
||||
tab[idx[2] + 1],
|
||||
tab[idx[3]],
|
||||
tab[idx[3] + 1]
|
||||
};
|
||||
return v_int16x8(vreinterpretq_s16_s32(vld1q_s32(elems)));
|
||||
return v_int16x8(vld1q_s16(elems));
|
||||
}
|
||||
inline v_int16x8 v_lut_quads(const short* tab, const int* idx)
|
||||
{
|
||||
int64 CV_DECL_ALIGNED(32) elems[2] =
|
||||
{
|
||||
*(int64*)(tab + idx[0]),
|
||||
*(int64*)(tab + idx[1])
|
||||
};
|
||||
return v_int16x8(vreinterpretq_s16_s64(vld1q_s64(elems)));
|
||||
return v_int16x8(vcombine_s16(vld1_s16(tab + idx[0]), vld1_s16(tab + idx[1])));
|
||||
}
|
||||
inline v_uint16x8 v_lut(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut((short*)tab, idx)); }
|
||||
inline v_uint16x8 v_lut_pairs(const ushort* tab, const int* idx) { return v_reinterpret_as_u16(v_lut_pairs((short*)tab, idx)); }
|
||||
@ -1677,12 +1696,7 @@ inline v_int32x4 v_lut(const int* tab, const int* idx)
|
||||
}
|
||||
inline v_int32x4 v_lut_pairs(const int* tab, const int* idx)
|
||||
{
|
||||
int64 CV_DECL_ALIGNED(32) elems[2] =
|
||||
{
|
||||
*(int64*)(tab + idx[0]),
|
||||
*(int64*)(tab + idx[1])
|
||||
};
|
||||
return v_int32x4(vreinterpretq_s32_s64(vld1q_s64(elems)));
|
||||
return v_int32x4(vcombine_s32(vld1_s32(tab + idx[0]), vld1_s32(tab + idx[1])));
|
||||
}
|
||||
inline v_int32x4 v_lut_quads(const int* tab, const int* idx)
|
||||
{
|
||||
@ -1800,7 +1814,8 @@ inline v_int16x8 v_interleave_pairs(const v_int16x8& vec)
|
||||
inline v_uint16x8 v_interleave_pairs(const v_uint16x8& vec) { return v_reinterpret_as_u16(v_interleave_pairs(v_reinterpret_as_s16(vec))); }
|
||||
inline v_int16x8 v_interleave_quads(const v_int16x8& vec)
|
||||
{
|
||||
return v_int16x8(vreinterpretq_s16_s8(vcombine_s8(vtbl1_s8(vget_low_s8(vreinterpretq_s8_s16(vec.val)), vcreate_s8(0x0b0a030209080100)), vtbl1_s8(vget_high_s8(vreinterpretq_s8_s16(vec.val)), vcreate_s8(0x0b0a030209080100)))));
|
||||
int16x4x2_t res = vzip_s16(vget_low_s16(vec.val), vget_high_s16(vec.val));
|
||||
return v_int16x8(vcombine_s16(res.val[0], res.val[1]));
|
||||
}
|
||||
inline v_uint16x8 v_interleave_quads(const v_uint16x8& vec) { return v_reinterpret_as_u16(v_interleave_quads(v_reinterpret_as_s16(vec))); }
|
||||
|
||||
@ -1824,6 +1839,10 @@ inline v_int16x8 v_pack_triplets(const v_int16x8& vec)
|
||||
}
|
||||
inline v_uint16x8 v_pack_triplets(const v_uint16x8& vec) { return v_reinterpret_as_u16(v_pack_triplets(v_reinterpret_as_s16(vec))); }
|
||||
|
||||
inline v_int32x4 v_pack_triplets(const v_int32x4& vec) { return vec; }
|
||||
inline v_uint32x4 v_pack_triplets(const v_uint32x4& vec) { return vec; }
|
||||
inline v_float32x4 v_pack_triplets(const v_float32x4& vec) { return vec; }
|
||||
|
||||
#if CV_SIMD128_64F
|
||||
inline v_float64x2 v_lut(const double* tab, const int* idx)
|
||||
{
|
||||
|
@ -2789,7 +2789,7 @@ inline v_int32x4 v_lut_pairs(const int* tab, const int* idx)
|
||||
}
|
||||
inline v_int32x4 v_lut_quads(const int* tab, const int* idx)
|
||||
{
|
||||
return v_int32x4(_mm_load_si128((const __m128i*)(tab + idx[0])));
|
||||
return v_int32x4(_mm_loadu_si128((const __m128i*)(tab + idx[0])));
|
||||
}
|
||||
inline v_uint32x4 v_lut(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut((const int *)tab, idx)); }
|
||||
inline v_uint32x4 v_lut_pairs(const unsigned* tab, const int* idx) { return v_reinterpret_as_u32(v_lut_pairs((const int *)tab, idx)); }
|
||||
@ -2801,7 +2801,7 @@ inline v_int64x2 v_lut(const int64_t* tab, const int* idx)
|
||||
}
|
||||
inline v_int64x2 v_lut_pairs(const int64_t* tab, const int* idx)
|
||||
{
|
||||
return v_int64x2(_mm_load_si128((const __m128i*)(tab + idx[0])));
|
||||
return v_int64x2(_mm_loadu_si128((const __m128i*)(tab + idx[0])));
|
||||
}
|
||||
inline v_uint64x2 v_lut(const uint64_t* tab, const int* idx) { return v_reinterpret_as_u64(v_lut((const int64_t *)tab, idx)); }
|
||||
inline v_uint64x2 v_lut_pairs(const uint64_t* tab, const int* idx) { return v_reinterpret_as_u64(v_lut_pairs((const int64_t *)tab, idx)); }
|
||||
@ -2817,7 +2817,7 @@ inline v_float64x2 v_lut(const double* tab, const int* idx)
|
||||
{
|
||||
return v_float64x2(_mm_setr_pd(tab[idx[0]], tab[idx[1]]));
|
||||
}
|
||||
inline v_float64x2 v_lut_pairs(const double* tab, const int* idx) { return v_float64x2(_mm_castsi128_pd(_mm_load_si128((const __m128i*)(tab + idx[0])))); }
|
||||
inline v_float64x2 v_lut_pairs(const double* tab, const int* idx) { return v_float64x2(_mm_castsi128_pd(_mm_loadu_si128((const __m128i*)(tab + idx[0])))); }
|
||||
|
||||
inline v_int32x4 v_lut(const int* tab, const v_int32x4& idxvec)
|
||||
{
|
||||
@ -2932,7 +2932,7 @@ inline v_int8x16 v_pack_triplets(const v_int8x16& vec)
|
||||
return v_int8x16(_mm_shuffle_epi8(vec.val, _mm_set_epi64x(0xffffff0f0e0d0c0a, 0x0908060504020100)));
|
||||
#else
|
||||
__m128i mask = _mm_set1_epi64x(0x00000000FFFFFFFF);
|
||||
__m128i a = _mm_or_si128(_mm_andnot_si128(mask, vec.val), _mm_and_si128(mask, _mm_sll_epi32(vec.val, _mm_set_epi64x(0, 8))));
|
||||
__m128i a = _mm_srli_si128(_mm_or_si128(_mm_andnot_si128(mask, vec.val), _mm_and_si128(mask, _mm_sll_epi32(vec.val, _mm_set_epi64x(0, 8)))), 1);
|
||||
return v_int8x16(_mm_srli_si128(_mm_shufflelo_epi16(a, _MM_SHUFFLE(2, 1, 0, 3)), 2));
|
||||
#endif
|
||||
}
|
||||
@ -2948,6 +2948,10 @@ inline v_int16x8 v_pack_triplets(const v_int16x8& vec)
|
||||
}
|
||||
inline v_uint16x8 v_pack_triplets(const v_uint16x8& vec) { return v_reinterpret_as_u16(v_pack_triplets(v_reinterpret_as_s16(vec))); }
|
||||
|
||||
inline v_int32x4 v_pack_triplets(const v_int32x4& vec) { return vec; }
|
||||
inline v_uint32x4 v_pack_triplets(const v_uint32x4& vec) { return vec; }
|
||||
inline v_float32x4 v_pack_triplets(const v_float32x4& vec) { return vec; }
|
||||
|
||||
////////////// FP16 support ///////////////////////////
|
||||
|
||||
inline v_float32x4 v_load_expand(const float16_t* ptr)
|
||||
|
@ -1160,6 +1160,10 @@ inline v_int16x8 v_pack_triplets(const v_int16x8& vec)
|
||||
}
|
||||
inline v_uint16x8 v_pack_triplets(const v_uint16x8& vec) { return v_reinterpret_as_u16(v_pack_triplets(v_reinterpret_as_s16(vec))); }
|
||||
|
||||
inline v_int32x4 v_pack_triplets(const v_int32x4& vec) { return vec; }
|
||||
inline v_uint32x4 v_pack_triplets(const v_uint32x4& vec) { return vec; }
|
||||
inline v_float32x4 v_pack_triplets(const v_float32x4& vec) { return vec; }
|
||||
|
||||
/////// FP16 support ////////
|
||||
|
||||
// [TODO] implement these 2 using VSX or universal intrinsics (copy from intrin_sse.cpp and adopt)
|
||||
|
@ -3589,9 +3589,9 @@ CV_EXPORTS MatExpr operator * (const MatExpr& e, double s);
|
||||
CV_EXPORTS MatExpr operator * (double s, const MatExpr& e);
|
||||
CV_EXPORTS MatExpr operator * (const MatExpr& e1, const MatExpr& e2);
|
||||
template<typename _Tp, int m, int n> static inline
|
||||
MatExpr operator * (const Mat& a, const Matx<_Tp, m, n>& b) { return a + Mat(b); }
|
||||
MatExpr operator * (const Mat& a, const Matx<_Tp, m, n>& b) { return a * Mat(b); }
|
||||
template<typename _Tp, int m, int n> static inline
|
||||
MatExpr operator * (const Matx<_Tp, m, n>& a, const Mat& b) { return Mat(a) + b; }
|
||||
MatExpr operator * (const Matx<_Tp, m, n>& a, const Mat& b) { return Mat(a) * b; }
|
||||
|
||||
CV_EXPORTS MatExpr operator / (const Mat& a, const Mat& b);
|
||||
CV_EXPORTS MatExpr operator / (const Mat& a, double s);
|
||||
|
@ -22,37 +22,37 @@
|
||||
|
||||
typedef __vector unsigned char vec_uchar16;
|
||||
#define vec_uchar16_set(...) (vec_uchar16){__VA_ARGS__}
|
||||
#define vec_uchar16_sp(c) (__VSX_S16__(vec_uchar16, c))
|
||||
#define vec_uchar16_sp(c) (__VSX_S16__(vec_uchar16, (unsigned char)c))
|
||||
#define vec_uchar16_c(v) ((vec_uchar16)(v))
|
||||
#define vec_uchar16_z vec_uchar16_sp(0)
|
||||
|
||||
typedef __vector signed char vec_char16;
|
||||
#define vec_char16_set(...) (vec_char16){__VA_ARGS__}
|
||||
#define vec_char16_sp(c) (__VSX_S16__(vec_char16, c))
|
||||
#define vec_char16_sp(c) (__VSX_S16__(vec_char16, (signed char)c))
|
||||
#define vec_char16_c(v) ((vec_char16)(v))
|
||||
#define vec_char16_z vec_char16_sp(0)
|
||||
|
||||
typedef __vector unsigned short vec_ushort8;
|
||||
#define vec_ushort8_set(...) (vec_ushort8){__VA_ARGS__}
|
||||
#define vec_ushort8_sp(c) (__VSX_S8__(vec_ushort8, c))
|
||||
#define vec_ushort8_sp(c) (__VSX_S8__(vec_ushort8, (unsigned short)c))
|
||||
#define vec_ushort8_c(v) ((vec_ushort8)(v))
|
||||
#define vec_ushort8_z vec_ushort8_sp(0)
|
||||
|
||||
typedef __vector signed short vec_short8;
|
||||
#define vec_short8_set(...) (vec_short8){__VA_ARGS__}
|
||||
#define vec_short8_sp(c) (__VSX_S8__(vec_short8, c))
|
||||
#define vec_short8_sp(c) (__VSX_S8__(vec_short8, (signed short)c))
|
||||
#define vec_short8_c(v) ((vec_short8)(v))
|
||||
#define vec_short8_z vec_short8_sp(0)
|
||||
|
||||
typedef __vector unsigned int vec_uint4;
|
||||
#define vec_uint4_set(...) (vec_uint4){__VA_ARGS__}
|
||||
#define vec_uint4_sp(c) (__VSX_S4__(vec_uint4, c))
|
||||
#define vec_uint4_sp(c) (__VSX_S4__(vec_uint4, (unsigned int)c))
|
||||
#define vec_uint4_c(v) ((vec_uint4)(v))
|
||||
#define vec_uint4_z vec_uint4_sp(0)
|
||||
|
||||
typedef __vector signed int vec_int4;
|
||||
#define vec_int4_set(...) (vec_int4){__VA_ARGS__}
|
||||
#define vec_int4_sp(c) (__VSX_S4__(vec_int4, c))
|
||||
#define vec_int4_sp(c) (__VSX_S4__(vec_int4, (signed int)c))
|
||||
#define vec_int4_c(v) ((vec_int4)(v))
|
||||
#define vec_int4_z vec_int4_sp(0)
|
||||
|
||||
@ -64,13 +64,13 @@ typedef __vector float vec_float4;
|
||||
|
||||
typedef __vector unsigned long long vec_udword2;
|
||||
#define vec_udword2_set(...) (vec_udword2){__VA_ARGS__}
|
||||
#define vec_udword2_sp(c) (__VSX_S2__(vec_udword2, c))
|
||||
#define vec_udword2_sp(c) (__VSX_S2__(vec_udword2, (unsigned long long)c))
|
||||
#define vec_udword2_c(v) ((vec_udword2)(v))
|
||||
#define vec_udword2_z vec_udword2_sp(0)
|
||||
|
||||
typedef __vector signed long long vec_dword2;
|
||||
#define vec_dword2_set(...) (vec_dword2){__VA_ARGS__}
|
||||
#define vec_dword2_sp(c) (__VSX_S2__(vec_dword2, c))
|
||||
#define vec_dword2_sp(c) (__VSX_S2__(vec_dword2, (signed long long)c))
|
||||
#define vec_dword2_c(v) ((vec_dword2)(v))
|
||||
#define vec_dword2_z vec_dword2_sp(0)
|
||||
|
||||
|
@ -12,7 +12,7 @@ typedef TestBaseWithParam<Size_MatType_Str_t> Size_Mat_StrType;
|
||||
#define FILE_EXTENSION String(".xml"), String(".yml"), String(".json")
|
||||
|
||||
|
||||
PERF_TEST_P(Size_Mat_StrType, fs_text,
|
||||
PERF_TEST_P(Size_Mat_StrType, DISABLED_fs_text,
|
||||
testing::Combine(testing::Values(MAT_SIZES),
|
||||
testing::Values(MAT_TYPES),
|
||||
testing::Values(FILE_EXTENSION))
|
||||
@ -48,7 +48,7 @@ PERF_TEST_P(Size_Mat_StrType, fs_text,
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
PERF_TEST_P(Size_Mat_StrType, fs_base64,
|
||||
PERF_TEST_P(Size_Mat_StrType, DISABLED_fs_base64,
|
||||
testing::Combine(testing::Values(MAT_SIZES),
|
||||
testing::Values(MAT_TYPES),
|
||||
testing::Values(FILE_EXTENSION))
|
||||
|
@ -1726,7 +1726,7 @@ void convertToGLTexture2D(InputArray src, Texture2D& texture)
|
||||
CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueAcquireGLObjects failed");
|
||||
size_t offset = 0; // TODO
|
||||
size_t dst_origin[3] = {0, 0, 0};
|
||||
size_t region[3] = {u.cols, u.rows, 1};
|
||||
size_t region[3] = { (size_t)u.cols, (size_t)u.rows, 1};
|
||||
status = clEnqueueCopyBufferToImage(q, clBuffer, clImage, offset, dst_origin, region, 0, NULL, NULL);
|
||||
if (status != CL_SUCCESS)
|
||||
CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueCopyBufferToImage failed");
|
||||
@ -1786,7 +1786,7 @@ void convertFromGLTexture2D(const Texture2D& texture, OutputArray dst)
|
||||
CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueAcquireGLObjects failed");
|
||||
size_t offset = 0; // TODO
|
||||
size_t src_origin[3] = {0, 0, 0};
|
||||
size_t region[3] = {u.cols, u.rows, 1};
|
||||
size_t region[3] = { (size_t)u.cols, (size_t)u.rows, 1};
|
||||
status = clEnqueueCopyImageToBuffer(q, clImage, clBuffer, src_origin, region, offset, 0, NULL, NULL);
|
||||
if (status != CL_SUCCESS)
|
||||
CV_Error(cv::Error::OpenCLApiCallError, "OpenCL: clEnqueueCopyImageToBuffer failed");
|
||||
|
@ -1991,6 +1991,22 @@ TEST(Core_Vectors, issue_13078_workaround)
|
||||
ASSERT_EQ(7, ints[3]);
|
||||
}
|
||||
|
||||
TEST(Core_MatExpr, issue_13926)
|
||||
{
|
||||
Mat M1 = (Mat_<double>(4,4,CV_64FC1) << 1, 2, 3, 4,
|
||||
5, 6, 7, 8,
|
||||
9, 10, 11, 12,
|
||||
13, 14, 15, 16);
|
||||
|
||||
Matx44d M2(1, 2, 3, 4,
|
||||
5, 6, 7, 8,
|
||||
9, 10, 11, 12,
|
||||
13, 14, 15, 16);
|
||||
|
||||
EXPECT_GE(1e-6, cvtest::norm(M1*M2, M1*M1, NORM_INF)) << Mat(M1*M2) << std::endl << Mat(M1*M1);
|
||||
EXPECT_GE(1e-6, cvtest::norm(M2*M1, M2*M2, NORM_INF)) << Mat(M2*M1) << std::endl << Mat(M2*M2);
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_EIGEN
|
||||
TEST(Core_Eigen, eigen2cv_check_Mat_type)
|
||||
|
@ -1826,7 +1826,7 @@ void OCL4DNNConvSpatial<float>::setupConvolution(const UMat &bottom,
|
||||
}
|
||||
else
|
||||
{
|
||||
CV_LOG_VERBOSE(NULL, "Kernel " << config->kernelName << " pass verification");
|
||||
CV_LOG_VERBOSE(NULL, 0, "Kernel " << config->kernelName << " pass verification");
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
|
@ -103,7 +103,7 @@ CV_IMPL CvFont cvFontQt(const char* nameFont, int pointSize,CvScalar color,int w
|
||||
float dx;//spacing letter in Qt (0 default) in pixel
|
||||
int line_type;//<- pointSize in Qt
|
||||
*/
|
||||
CvFont f = {nameFont,color,style,NULL,NULL,NULL,0,0,0,weight,spacing,pointSize};
|
||||
CvFont f = {nameFont,color,style,NULL,NULL,NULL,0,0,0,weight, (float)spacing, pointSize};
|
||||
return f;
|
||||
}
|
||||
|
||||
|
@ -1248,14 +1248,12 @@ protected:
|
||||
//! @addtogroup imgproc_feature
|
||||
//! @{
|
||||
|
||||
/** @example samples/cpp/lsd_lines.cpp
|
||||
An example using the LineSegmentDetector
|
||||
\image html building_lsd.png "Sample output image" width=434 height=300
|
||||
*/
|
||||
|
||||
/** @brief Line segment detector class
|
||||
|
||||
following the algorithm described at @cite Rafael12 .
|
||||
|
||||
@note Implementation has been removed due original code license conflict
|
||||
|
||||
*/
|
||||
class CV_EXPORTS_W LineSegmentDetector : public Algorithm
|
||||
{
|
||||
@ -1319,6 +1317,8 @@ to edit those, as to tailor it for their own application.
|
||||
is chosen.
|
||||
@param _density_th Minimal density of aligned region points in the enclosing rectangle.
|
||||
@param _n_bins Number of bins in pseudo-ordering of gradient modulus.
|
||||
|
||||
@note Implementation has been removed due original code license conflict
|
||||
*/
|
||||
CV_EXPORTS_W Ptr<LineSegmentDetector> createLineSegmentDetector(
|
||||
int _refine = LSD_REFINE_STD, double _scale = 0.8,
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -485,7 +485,7 @@ void hlineResizeCn<uint8_t, ufixedpoint16, 2, true, 3>(uint8_t* src, int, int *o
|
||||
v_store(ofst3, vx_load(ofst + i) * vx_setall_s32(3));
|
||||
v_uint8 v_src01, v_src23;
|
||||
v_uint16 v_src0, v_src1, v_src2, v_src3;
|
||||
v_zip(vx_lut_quads(src, ofst3), vx_lut_quads(src+3, ofst3), v_src01, v_src23);
|
||||
v_zip(vx_lut_quads(src, ofst3), v_reinterpret_as_u8(v_reinterpret_as_u32(vx_lut_quads(src+2, ofst3)) >> 8), v_src01, v_src23);
|
||||
v_expand(v_src01, v_src0, v_src1);
|
||||
v_expand(v_src23, v_src2, v_src3);
|
||||
|
||||
|
@ -5,6 +5,8 @@
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
#if 0 // LSD implementation has been removed due original code license issues
|
||||
|
||||
const Size img_size(640, 480);
|
||||
const int LSD_TEST_SEED = 0x134679;
|
||||
const int EPOCHS = 20;
|
||||
@ -402,4 +404,6 @@ TEST_F(Imgproc_LSD_Common, compareSegmentsVec4i)
|
||||
ASSERT_EQ(result2, 11);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}} // namespace
|
||||
|
@ -953,11 +953,19 @@ JNIEXPORT $rtype JNICALL Java_org_opencv_${module}_${clazz}_$fname
|
||||
def gen_class(self, ci):
|
||||
logging.info("%s", ci)
|
||||
# constants
|
||||
consts_map = {c.name: c for c in ci.private_consts}
|
||||
consts_map.update({c.name: c for c in ci.consts})
|
||||
def const_value(v):
|
||||
if v in consts_map:
|
||||
target = consts_map[v]
|
||||
assert target.value != v
|
||||
return const_value(target.value)
|
||||
return v
|
||||
if ci.private_consts:
|
||||
logging.info("%s", ci.private_consts)
|
||||
ci.j_code.write("""
|
||||
private static final int
|
||||
%s;\n\n""" % (",\n"+" "*12).join(["%s = %s" % (c.name, c.value) for c in ci.private_consts])
|
||||
%s;\n\n""" % (",\n"+" "*12).join(["%s = %s" % (c.name, const_value(c.value)) for c in ci.private_consts])
|
||||
)
|
||||
if ci.consts:
|
||||
enumTypes = set(map(lambda c: c.enumType, ci.consts))
|
||||
|
@ -205,11 +205,14 @@ public:
|
||||
for( j = 0; j < vcount; j++ )
|
||||
{
|
||||
Qfloat t = results[j];
|
||||
Qfloat e = std::exp(std::abs(t));
|
||||
if( t > 0 )
|
||||
results[j] = (Qfloat)((e - 1.)/(e + 1.));
|
||||
else
|
||||
results[j] = (Qfloat)((1. - e)/(1. + e));
|
||||
Qfloat e = std::exp(std::abs(t)); // Inf value is possible here
|
||||
Qfloat r = (Qfloat)((e - 1.) / (e + 1.)); // NaN value is possible here (Inf/Inf or similar)
|
||||
if (cvIsNaN(r))
|
||||
r = std::numeric_limits<Qfloat>::infinity();
|
||||
if (t < 0)
|
||||
r = -r;
|
||||
CV_DbgAssert(!cvIsNaN(r));
|
||||
results[j] = r;
|
||||
}
|
||||
}
|
||||
|
||||
@ -327,7 +330,7 @@ public:
|
||||
const Qfloat max_val = (Qfloat)(FLT_MAX*1e-3);
|
||||
for( int j = 0; j < vcount; j++ )
|
||||
{
|
||||
if( results[j] > max_val )
|
||||
if (!(results[j] <= max_val)) // handle NaNs too
|
||||
results[j] = max_val;
|
||||
}
|
||||
}
|
||||
@ -1949,6 +1952,7 @@ public:
|
||||
const DecisionFunc& df = svm->decision_func[dfi];
|
||||
sum = -df.rho;
|
||||
int sv_count = svm->getSVCount(dfi);
|
||||
CV_DbgAssert(sv_count > 0);
|
||||
const double* alpha = &svm->df_alpha[df.ofs];
|
||||
const int* sv_index = &svm->df_index[df.ofs];
|
||||
for( k = 0; k < sv_count; k++ )
|
||||
|
@ -92,7 +92,7 @@ bool pyopencv_to(PyObject* dst, TYPE& src, const char* name)
|
||||
{ \
|
||||
if (!dst || dst == Py_None) \
|
||||
return true; \
|
||||
std::underlying_type<TYPE>::type underlying = 0; \
|
||||
int underlying = 0; \
|
||||
\
|
||||
if (!pyopencv_to(dst, underlying, name)) return false; \
|
||||
src = static_cast<TYPE>(underlying); \
|
||||
@ -103,7 +103,7 @@ bool pyopencv_to(PyObject* dst, TYPE& src, const char* name)
|
||||
template<> \
|
||||
PyObject* pyopencv_from(const TYPE& src) \
|
||||
{ \
|
||||
return pyopencv_from(static_cast<std::underlying_type<TYPE>::type>(src)); \
|
||||
return pyopencv_from(static_cast<int>(src)); \
|
||||
}
|
||||
|
||||
#include "pyopencv_generated_include.h"
|
||||
@ -847,6 +847,40 @@ bool pyopencv_to(PyObject* obj, Range& r, const char* name)
|
||||
CV_UNUSED(name);
|
||||
if(!obj || obj == Py_None)
|
||||
return true;
|
||||
while (PySequence_Check(obj))
|
||||
{
|
||||
PyObject *fi = PySequence_Fast(obj, name);
|
||||
if (fi == NULL)
|
||||
break;
|
||||
if (2 != PySequence_Fast_GET_SIZE(fi))
|
||||
{
|
||||
failmsg("Range value for argument '%s' is longer than 2", name);
|
||||
Py_DECREF(fi);
|
||||
return false;
|
||||
}
|
||||
{
|
||||
PyObject *item = PySequence_Fast_GET_ITEM(fi, 0);
|
||||
if (PyInt_Check(item)) {
|
||||
r.start = (int)PyInt_AsLong(item);
|
||||
} else {
|
||||
failmsg("Range.start value for argument '%s' is not integer", name);
|
||||
Py_DECREF(fi);
|
||||
break;
|
||||
}
|
||||
}
|
||||
{
|
||||
PyObject *item = PySequence_Fast_GET_ITEM(fi, 1);
|
||||
if (PyInt_Check(item)) {
|
||||
r.end = (int)PyInt_AsLong(item);
|
||||
} else {
|
||||
failmsg("Range.end value for argument '%s' is not integer", name);
|
||||
Py_DECREF(fi);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Py_DECREF(fi);
|
||||
return true;
|
||||
}
|
||||
if(PyObject_Size(obj) == 0)
|
||||
{
|
||||
r = Range::all();
|
||||
|
@ -243,6 +243,14 @@ make & enjoy!
|
||||
#define V4L2_CID_ISO_SENSITIVITY (V4L2_CID_CAMERA_CLASS_BASE+23)
|
||||
#endif
|
||||
|
||||
// https://github.com/opencv/opencv/issues/13929
|
||||
#ifndef V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT
|
||||
#define V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT (V4L2_CID_MPEG_BASE+364)
|
||||
#endif
|
||||
#ifndef V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH
|
||||
#define V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH (V4L2_CID_MPEG_BASE+365)
|
||||
#endif
|
||||
|
||||
/* Defaults - If your board can do better, set it here. Set for the most common type inputs. */
|
||||
#define DEFAULT_V4L_WIDTH 640
|
||||
#define DEFAULT_V4L_HEIGHT 480
|
||||
|
@ -50,12 +50,12 @@ def check_dir(d, create=False, clean=False):
|
||||
|
||||
def check_executable(cmd):
|
||||
try:
|
||||
FNULL = open(os.devnull, 'w')
|
||||
retcode = subprocess.call(cmd, stdout=FNULL, stderr=subprocess.STDOUT)
|
||||
if retcode < 0:
|
||||
return False
|
||||
log.debug("Executing: %s" % cmd)
|
||||
result = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
|
||||
log.debug("Result: %s" % (result+'\n').split('\n')[0])
|
||||
return True
|
||||
except:
|
||||
except Exception as e:
|
||||
log.debug('Failed: %s' % e)
|
||||
return False
|
||||
|
||||
def determine_opencv_version(version_hpp_path):
|
||||
@ -140,9 +140,11 @@ class Builder:
|
||||
self.extra_packs = []
|
||||
self.opencv_version = determine_opencv_version(os.path.join(self.opencvdir, "modules", "core", "include", "opencv2", "core", "version.hpp"))
|
||||
self.use_ccache = False if config.no_ccache else True
|
||||
self.cmake_path = self.get_cmake()
|
||||
self.ninja_path = self.get_ninja()
|
||||
|
||||
def get_cmake(self):
|
||||
if check_executable(['cmake', '--version']):
|
||||
if not self.config.use_android_buildtools and check_executable(['cmake', '--version']):
|
||||
log.info("Using cmake from PATH")
|
||||
return 'cmake'
|
||||
# look to see if Android SDK's cmake is installed
|
||||
@ -157,7 +159,7 @@ class Builder:
|
||||
raise Fail("Can't find cmake")
|
||||
|
||||
def get_ninja(self):
|
||||
if check_executable(['ninja', '--version']):
|
||||
if not self.config.use_android_buildtools and check_executable(['ninja', '--version']):
|
||||
log.info("Using ninja from PATH")
|
||||
return 'ninja'
|
||||
# Android SDK's cmake includes a copy of ninja - look to see if its there
|
||||
@ -195,7 +197,7 @@ class Builder:
|
||||
rm_one(d)
|
||||
|
||||
def build_library(self, abi, do_install):
|
||||
cmd = [self.get_cmake(), "-GNinja"]
|
||||
cmd = [self.cmake_path, "-GNinja"]
|
||||
cmake_vars = dict(
|
||||
CMAKE_TOOLCHAIN_FILE=self.get_toolchain_file(),
|
||||
INSTALL_CREATE_DISTRIB="ON",
|
||||
@ -208,8 +210,9 @@ class Builder:
|
||||
BUILD_DOCS="OFF",
|
||||
BUILD_ANDROID_EXAMPLES="ON",
|
||||
INSTALL_ANDROID_EXAMPLES="ON",
|
||||
CMAKE_MAKE_PROGRAM=self.get_ninja(),
|
||||
)
|
||||
if self.ninja_path != 'ninja':
|
||||
cmake_vars['CMAKE_MAKE_PROGRAM'] = self.ninja_path
|
||||
|
||||
if self.config.extra_modules_path is not None:
|
||||
cmd.append("-DOPENCV_EXTRA_MODULES_PATH='%s'" % self.config.extra_modules_path)
|
||||
@ -223,7 +226,7 @@ class Builder:
|
||||
cmd += [ "-D%s='%s'" % (k, v) for (k, v) in cmake_vars.items() if v is not None]
|
||||
cmd.append(self.opencvdir)
|
||||
execute(cmd)
|
||||
execute([self.get_ninja(), "install/strip"])
|
||||
execute([self.ninja_path, "install/strip"])
|
||||
|
||||
def build_javadoc(self):
|
||||
classpaths = []
|
||||
@ -273,6 +276,7 @@ if __name__ == "__main__":
|
||||
parser.add_argument('--config', default='ndk-18.config.py', type=str, help="Package build configuration", )
|
||||
parser.add_argument('--ndk_path', help="Path to Android NDK to use for build")
|
||||
parser.add_argument('--sdk_path', help="Path to Android SDK to use for build")
|
||||
parser.add_argument('--use_android_buildtools', action="store_true", help='Use cmake/ninja build tools from Android SDK')
|
||||
parser.add_argument("--extra_modules_path", help="Path to extra modules to use for build")
|
||||
parser.add_argument('--sign_with', help="Certificate to sign the Manager apk")
|
||||
parser.add_argument('--build_doc', action="store_true", help="Build javadoc")
|
||||
@ -303,7 +307,7 @@ if __name__ == "__main__":
|
||||
raise Fail("NDK location not set. Either pass --ndk_path or set ANDROID_NDK environment variable")
|
||||
|
||||
if not check_executable(['ccache', '--version']):
|
||||
log.info("ccache not found - disabling ccache supprt")
|
||||
log.info("ccache not found - disabling ccache support")
|
||||
args.no_ccache = True
|
||||
|
||||
if os.path.realpath(args.work_dir) == os.path.realpath(SCRIPT_DIR):
|
||||
|
@ -1,73 +0,0 @@
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
cv::CommandLineParser parser(argc, argv,
|
||||
"{input i|building.jpg|input image}"
|
||||
"{refine r|false|if true use LSD_REFINE_STD method, if false use LSD_REFINE_NONE method}"
|
||||
"{canny c|false|use Canny edge detector}"
|
||||
"{overlay o|false|show result on input image}"
|
||||
"{help h|false|show help message}");
|
||||
|
||||
if (parser.get<bool>("help"))
|
||||
{
|
||||
parser.printMessage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
parser.printMessage();
|
||||
|
||||
String filename = samples::findFile(parser.get<String>("input"));
|
||||
bool useRefine = parser.get<bool>("refine");
|
||||
bool useCanny = parser.get<bool>("canny");
|
||||
bool overlay = parser.get<bool>("overlay");
|
||||
|
||||
Mat image = imread(filename, IMREAD_GRAYSCALE);
|
||||
|
||||
if( image.empty() )
|
||||
{
|
||||
cout << "Unable to load " << filename;
|
||||
return 1;
|
||||
}
|
||||
|
||||
imshow("Source Image", image);
|
||||
|
||||
if (useCanny)
|
||||
{
|
||||
Canny(image, image, 50, 200, 3); // Apply Canny edge detector
|
||||
}
|
||||
|
||||
// Create and LSD detector with standard or no refinement.
|
||||
Ptr<LineSegmentDetector> ls = useRefine ? createLineSegmentDetector(LSD_REFINE_STD) : createLineSegmentDetector(LSD_REFINE_NONE);
|
||||
|
||||
double start = double(getTickCount());
|
||||
vector<Vec4f> lines_std;
|
||||
|
||||
// Detect the lines
|
||||
ls->detect(image, lines_std);
|
||||
|
||||
double duration_ms = (double(getTickCount()) - start) * 1000 / getTickFrequency();
|
||||
std::cout << "It took " << duration_ms << " ms." << std::endl;
|
||||
|
||||
// Show found lines
|
||||
if (!overlay || useCanny)
|
||||
{
|
||||
image = Scalar(0, 0, 0);
|
||||
}
|
||||
|
||||
ls->drawSegments(image, lines_std);
|
||||
|
||||
String window_name = useRefine ? "Result - standard refinement" : "Result - no refinement";
|
||||
window_name += useCanny ? " - Canny edge detector used" : "";
|
||||
|
||||
imshow(window_name, image);
|
||||
|
||||
waitKey();
|
||||
return 0;
|
||||
}
|
@ -324,6 +324,6 @@ def writeTextGraph(modelPath, outputPath, outNodes):
|
||||
for node in graph_def.node:
|
||||
if node.op == 'Const':
|
||||
if 'value' in node.attr and node.attr['value'].tensor.tensor_content:
|
||||
node.attr['value'].tensor.tensor_content = ''
|
||||
node.attr['value'].tensor.tensor_content = b''
|
||||
|
||||
tf.train.write_graph(graph_def, "", outputPath, as_text=True)
|
||||
|
Loading…
Reference in New Issue
Block a user