mirror of https://github.com/opencv/opencv.git
parent
47188b7c7e
commit
285108e2e1
16 changed files with 1661 additions and 1608 deletions
@ -0,0 +1,596 @@ |
||||
// 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 { |
||||
|
||||
struct JacobianRow |
||||
{ |
||||
Vec2d df, dc; |
||||
Vec4d dk; |
||||
Vec3d dom, dT; |
||||
double dalpha; |
||||
}; |
||||
|
||||
void cv::fisheye::projectPoints(InputArray objectPoints, OutputArray imagePoints, const Affine3d& affine, |
||||
InputArray K, InputArray D, double alpha, OutputArray jacobian) |
||||
{ |
||||
CV_INSTRUMENT_REGION(); |
||||
|
||||
projectPoints(objectPoints, imagePoints, affine.rvec(), affine.translation(), K, D, alpha, jacobian); |
||||
} |
||||
|
||||
void cv::fisheye::projectPoints(InputArray objectPoints, OutputArray imagePoints, InputArray _rvec, |
||||
InputArray _tvec, InputArray _K, InputArray _D, double alpha, OutputArray jacobian) |
||||
{ |
||||
CV_INSTRUMENT_REGION(); |
||||
|
||||
// will support only 3-channel data now for points
|
||||
CV_Assert(objectPoints.type() == CV_32FC3 || objectPoints.type() == CV_64FC3); |
||||
imagePoints.create(objectPoints.size(), CV_MAKETYPE(objectPoints.depth(), 2)); |
||||
size_t n = objectPoints.total(); |
||||
|
||||
CV_Assert(_rvec.total() * _rvec.channels() == 3 && (_rvec.depth() == CV_32F || _rvec.depth() == CV_64F)); |
||||
CV_Assert(_tvec.total() * _tvec.channels() == 3 && (_tvec.depth() == CV_32F || _tvec.depth() == CV_64F)); |
||||
CV_Assert(_tvec.getMat().isContinuous() && _rvec.getMat().isContinuous()); |
||||
|
||||
Vec3d om = _rvec.depth() == CV_32F ? (Vec3d)*_rvec.getMat().ptr<Vec3f>() : *_rvec.getMat().ptr<Vec3d>(); |
||||
Vec3d T = _tvec.depth() == CV_32F ? (Vec3d)*_tvec.getMat().ptr<Vec3f>() : *_tvec.getMat().ptr<Vec3d>(); |
||||
|
||||
CV_Assert(_K.size() == Size(3,3) && (_K.type() == CV_32F || _K.type() == CV_64F) && _D.type() == _K.type() && _D.total() == 4); |
||||
|
||||
Vec2d f, c; |
||||
if (_K.depth() == CV_32F) |
||||
{ |
||||
|
||||
Matx33f K = _K.getMat(); |
||||
f = Vec2f(K(0, 0), K(1, 1)); |
||||
c = Vec2f(K(0, 2), K(1, 2)); |
||||
} |
||||
else |
||||
{ |
||||
Matx33d K = _K.getMat(); |
||||
f = Vec2d(K(0, 0), K(1, 1)); |
||||
c = Vec2d(K(0, 2), K(1, 2)); |
||||
} |
||||
|
||||
Vec4d k = _D.depth() == CV_32F ? (Vec4d)*_D.getMat().ptr<Vec4f>(): *_D.getMat().ptr<Vec4d>(); |
||||
|
||||
const bool isJacobianNeeded = jacobian.needed(); |
||||
JacobianRow *Jn = 0; |
||||
if (isJacobianNeeded) |
||||
{ |
||||
int nvars = 2 + 2 + 1 + 4 + 3 + 3; // f, c, alpha, k, om, T,
|
||||
jacobian.create(2*(int)n, nvars, CV_64F); |
||||
Jn = jacobian.getMat().ptr<JacobianRow>(0); |
||||
} |
||||
|
||||
Matx33d R; |
||||
Matx<double, 3, 9> dRdom; |
||||
Rodrigues(om, R, dRdom); |
||||
Affine3d aff(om, T); |
||||
|
||||
const Vec3f* Xf = objectPoints.getMat().ptr<Vec3f>(); |
||||
const Vec3d* Xd = objectPoints.getMat().ptr<Vec3d>(); |
||||
Vec2f *xpf = imagePoints.getMat().ptr<Vec2f>(); |
||||
Vec2d *xpd = imagePoints.getMat().ptr<Vec2d>(); |
||||
|
||||
for(size_t i = 0; i < n; ++i) |
||||
{ |
||||
Vec3d Xi = objectPoints.depth() == CV_32F ? (Vec3d)Xf[i] : Xd[i]; |
||||
Vec3d Y = aff*Xi; |
||||
if (fabs(Y[2]) < DBL_MIN) |
||||
Y[2] = 1; |
||||
Vec2d x(Y[0]/Y[2], Y[1]/Y[2]); |
||||
|
||||
double r2 = x.dot(x); |
||||
double r = std::sqrt(r2); |
||||
|
||||
// Angle of the incoming ray:
|
||||
double theta = std::atan(r); |
||||
|
||||
double theta2 = theta*theta, theta3 = theta2*theta, theta4 = theta2*theta2, theta5 = theta4*theta, |
||||
theta6 = theta3*theta3, theta7 = theta6*theta, theta8 = theta4*theta4, theta9 = theta8*theta; |
||||
|
||||
double theta_d = theta + k[0]*theta3 + k[1]*theta5 + k[2]*theta7 + k[3]*theta9; |
||||
|
||||
double inv_r = r > 1e-8 ? 1.0/r : 1; |
||||
double cdist = r > 1e-8 ? theta_d * inv_r : 1; |
||||
|
||||
Vec2d xd1 = x * cdist; |
||||
Vec2d xd3(xd1[0] + alpha*xd1[1], xd1[1]); |
||||
Vec2d final_point(xd3[0] * f[0] + c[0], xd3[1] * f[1] + c[1]); |
||||
|
||||
if (objectPoints.depth() == CV_32F) |
||||
xpf[i] = final_point; |
||||
else |
||||
xpd[i] = final_point; |
||||
|
||||
if (isJacobianNeeded) |
||||
{ |
||||
//Vec3d Xi = pdepth == CV_32F ? (Vec3d)Xf[i] : Xd[i];
|
||||
//Vec3d Y = aff*Xi;
|
||||
double dYdR[] = { Xi[0], Xi[1], Xi[2], 0, 0, 0, 0, 0, 0, |
||||
0, 0, 0, Xi[0], Xi[1], Xi[2], 0, 0, 0, |
||||
0, 0, 0, 0, 0, 0, Xi[0], Xi[1], Xi[2] }; |
||||
|
||||
Matx33d dYdom_data = Matx<double, 3, 9>(dYdR) * dRdom.t(); |
||||
const Vec3d *dYdom = (Vec3d*)dYdom_data.val; |
||||
|
||||
Matx33d dYdT_data = Matx33d::eye(); |
||||
const Vec3d *dYdT = (Vec3d*)dYdT_data.val; |
||||
|
||||
//Vec2d x(Y[0]/Y[2], Y[1]/Y[2]);
|
||||
Vec3d dxdom[2]; |
||||
dxdom[0] = (1.0/Y[2]) * dYdom[0] - x[0]/Y[2] * dYdom[2]; |
||||
dxdom[1] = (1.0/Y[2]) * dYdom[1] - x[1]/Y[2] * dYdom[2]; |
||||
|
||||
Vec3d dxdT[2]; |
||||
dxdT[0] = (1.0/Y[2]) * dYdT[0] - x[0]/Y[2] * dYdT[2]; |
||||
dxdT[1] = (1.0/Y[2]) * dYdT[1] - x[1]/Y[2] * dYdT[2]; |
||||
|
||||
//double r2 = x.dot(x);
|
||||
Vec3d dr2dom = 2 * x[0] * dxdom[0] + 2 * x[1] * dxdom[1]; |
||||
Vec3d dr2dT = 2 * x[0] * dxdT[0] + 2 * x[1] * dxdT[1]; |
||||
|
||||
//double r = std::sqrt(r2);
|
||||
double drdr2 = r > 1e-8 ? 1.0/(2*r) : 1; |
||||
Vec3d drdom = drdr2 * dr2dom; |
||||
Vec3d drdT = drdr2 * dr2dT; |
||||
|
||||
// Angle of the incoming ray:
|
||||
//double theta = atan(r);
|
||||
double dthetadr = 1.0/(1+r2); |
||||
Vec3d dthetadom = dthetadr * drdom; |
||||
Vec3d dthetadT = dthetadr * drdT; |
||||
|
||||
//double theta_d = theta + k[0]*theta3 + k[1]*theta5 + k[2]*theta7 + k[3]*theta9;
|
||||
double dtheta_ddtheta = 1 + 3*k[0]*theta2 + 5*k[1]*theta4 + 7*k[2]*theta6 + 9*k[3]*theta8; |
||||
Vec3d dtheta_ddom = dtheta_ddtheta * dthetadom; |
||||
Vec3d dtheta_ddT = dtheta_ddtheta * dthetadT; |
||||
Vec4d dtheta_ddk = Vec4d(theta3, theta5, theta7, theta9); |
||||
|
||||
//double inv_r = r > 1e-8 ? 1.0/r : 1;
|
||||
//double cdist = r > 1e-8 ? theta_d / r : 1;
|
||||
Vec3d dcdistdom = inv_r * (dtheta_ddom - cdist*drdom); |
||||
Vec3d dcdistdT = inv_r * (dtheta_ddT - cdist*drdT); |
||||
Vec4d dcdistdk = inv_r * dtheta_ddk; |
||||
|
||||
//Vec2d xd1 = x * cdist;
|
||||
Vec4d dxd1dk[2]; |
||||
Vec3d dxd1dom[2], dxd1dT[2]; |
||||
dxd1dom[0] = x[0] * dcdistdom + cdist * dxdom[0]; |
||||
dxd1dom[1] = x[1] * dcdistdom + cdist * dxdom[1]; |
||||
dxd1dT[0] = x[0] * dcdistdT + cdist * dxdT[0]; |
||||
dxd1dT[1] = x[1] * dcdistdT + cdist * dxdT[1]; |
||||
dxd1dk[0] = x[0] * dcdistdk; |
||||
dxd1dk[1] = x[1] * dcdistdk; |
||||
|
||||
//Vec2d xd3(xd1[0] + alpha*xd1[1], xd1[1]);
|
||||
Vec4d dxd3dk[2]; |
||||
Vec3d dxd3dom[2], dxd3dT[2]; |
||||
dxd3dom[0] = dxd1dom[0] + alpha * dxd1dom[1]; |
||||
dxd3dom[1] = dxd1dom[1]; |
||||
dxd3dT[0] = dxd1dT[0] + alpha * dxd1dT[1]; |
||||
dxd3dT[1] = dxd1dT[1]; |
||||
dxd3dk[0] = dxd1dk[0] + alpha * dxd1dk[1]; |
||||
dxd3dk[1] = dxd1dk[1]; |
||||
|
||||
Vec2d dxd3dalpha(xd1[1], 0); |
||||
|
||||
//final jacobian
|
||||
Jn[0].dom = f[0] * dxd3dom[0]; |
||||
Jn[1].dom = f[1] * dxd3dom[1]; |
||||
|
||||
Jn[0].dT = f[0] * dxd3dT[0]; |
||||
Jn[1].dT = f[1] * dxd3dT[1]; |
||||
|
||||
Jn[0].dk = f[0] * dxd3dk[0]; |
||||
Jn[1].dk = f[1] * dxd3dk[1]; |
||||
|
||||
Jn[0].dalpha = f[0] * dxd3dalpha[0]; |
||||
Jn[1].dalpha = 0; //f[1] * dxd3dalpha[1];
|
||||
|
||||
Jn[0].df = Vec2d(xd3[0], 0); |
||||
Jn[1].df = Vec2d(0, xd3[1]); |
||||
|
||||
Jn[0].dc = Vec2d(1, 0); |
||||
Jn[1].dc = Vec2d(0, 1); |
||||
|
||||
//step to jacobian rows for next point
|
||||
Jn += 2; |
||||
} |
||||
} |
||||
} |
||||
|
||||
void cv::fisheye::distortPoints(InputArray undistorted, OutputArray distorted, InputArray K, InputArray D, double alpha) |
||||
{ |
||||
CV_INSTRUMENT_REGION(); |
||||
|
||||
// will support only 2-channel data now for points
|
||||
CV_Assert(undistorted.type() == CV_32FC2 || undistorted.type() == CV_64FC2); |
||||
distorted.create(undistorted.size(), undistorted.type()); |
||||
size_t n = undistorted.total(); |
||||
|
||||
CV_Assert(K.size() == Size(3,3) && (K.type() == CV_32F || K.type() == CV_64F) && D.total() == 4); |
||||
|
||||
Vec2d f, c; |
||||
if (K.depth() == CV_32F) |
||||
{ |
||||
Matx33f camMat = K.getMat(); |
||||
f = Vec2f(camMat(0, 0), camMat(1, 1)); |
||||
c = Vec2f(camMat(0, 2), camMat(1, 2)); |
||||
} |
||||
else |
||||
{ |
||||
Matx33d camMat = K.getMat(); |
||||
f = Vec2d(camMat(0, 0), camMat(1, 1)); |
||||
c = Vec2d(camMat(0 ,2), camMat(1, 2)); |
||||
} |
||||
|
||||
Vec4d k = D.depth() == CV_32F ? (Vec4d)*D.getMat().ptr<Vec4f>(): *D.getMat().ptr<Vec4d>(); |
||||
|
||||
const Vec2f* Xf = undistorted.getMat().ptr<Vec2f>(); |
||||
const Vec2d* Xd = undistorted.getMat().ptr<Vec2d>(); |
||||
Vec2f *xpf = distorted.getMat().ptr<Vec2f>(); |
||||
Vec2d *xpd = distorted.getMat().ptr<Vec2d>(); |
||||
|
||||
for(size_t i = 0; i < n; ++i) |
||||
{ |
||||
Vec2d x = undistorted.depth() == CV_32F ? (Vec2d)Xf[i] : Xd[i]; |
||||
|
||||
double r2 = x.dot(x); |
||||
double r = std::sqrt(r2); |
||||
|
||||
// Angle of the incoming ray:
|
||||
double theta = std::atan(r); |
||||
|
||||
double theta2 = theta*theta, theta3 = theta2*theta, theta4 = theta2*theta2, theta5 = theta4*theta, |
||||
theta6 = theta3*theta3, theta7 = theta6*theta, theta8 = theta4*theta4, theta9 = theta8*theta; |
||||
|
||||
double theta_d = theta + k[0]*theta3 + k[1]*theta5 + k[2]*theta7 + k[3]*theta9; |
||||
|
||||
double inv_r = r > 1e-8 ? 1.0/r : 1; |
||||
double cdist = r > 1e-8 ? theta_d * inv_r : 1; |
||||
|
||||
Vec2d xd1 = x * cdist; |
||||
Vec2d xd3(xd1[0] + alpha*xd1[1], xd1[1]); |
||||
Vec2d final_point(xd3[0] * f[0] + c[0], xd3[1] * f[1] + c[1]); |
||||
|
||||
if (undistorted.depth() == CV_32F) |
||||
xpf[i] = final_point; |
||||
else |
||||
xpd[i] = final_point; |
||||
} |
||||
} |
||||
|
||||
void cv::fisheye::undistortPoints( InputArray distorted, OutputArray undistorted, InputArray K, InputArray D, |
||||
InputArray R, InputArray P, TermCriteria criteria) |
||||
{ |
||||
CV_INSTRUMENT_REGION(); |
||||
|
||||
// will support only 2-channel data now for points
|
||||
CV_Assert(distorted.type() == CV_32FC2 || distorted.type() == CV_64FC2); |
||||
undistorted.create(distorted.size(), distorted.type()); |
||||
|
||||
CV_Assert(P.empty() || P.size() == Size(3, 3) || P.size() == Size(4, 3)); |
||||
CV_Assert(R.empty() || R.size() == Size(3, 3) || R.total() * R.channels() == 3); |
||||
CV_Assert(D.total() == 4 && K.size() == Size(3, 3) && (K.depth() == CV_32F || K.depth() == CV_64F)); |
||||
|
||||
CV_Assert(criteria.isValid()); |
||||
|
||||
Vec2d f, c; |
||||
if (K.depth() == CV_32F) |
||||
{ |
||||
Matx33f camMat = K.getMat(); |
||||
f = Vec2f(camMat(0, 0), camMat(1, 1)); |
||||
c = Vec2f(camMat(0, 2), camMat(1, 2)); |
||||
} |
||||
else |
||||
{ |
||||
Matx33d camMat = K.getMat(); |
||||
f = Vec2d(camMat(0, 0), camMat(1, 1)); |
||||
c = Vec2d(camMat(0, 2), camMat(1, 2)); |
||||
} |
||||
|
||||
Vec4d k = D.depth() == CV_32F ? (Vec4d)*D.getMat().ptr<Vec4f>(): *D.getMat().ptr<Vec4d>(); |
||||
|
||||
Matx33d RR = Matx33d::eye(); |
||||
if (!R.empty() && R.total() * R.channels() == 3) |
||||
{ |
||||
Vec3d rvec; |
||||
R.getMat().convertTo(rvec, CV_64F); |
||||
RR = cv::Affine3d(rvec).rotation(); |
||||
} |
||||
else if (!R.empty() && R.size() == Size(3, 3)) |
||||
R.getMat().convertTo(RR, CV_64F); |
||||
|
||||
if(!P.empty()) |
||||
{ |
||||
Matx33d PP; |
||||
P.getMat().colRange(0, 3).convertTo(PP, CV_64F); |
||||
RR = PP * RR; |
||||
} |
||||
|
||||
// start undistorting
|
||||
const Vec2f* srcf = distorted.getMat().ptr<Vec2f>(); |
||||
const Vec2d* srcd = distorted.getMat().ptr<Vec2d>(); |
||||
Vec2f* dstf = undistorted.getMat().ptr<Vec2f>(); |
||||
Vec2d* dstd = undistorted.getMat().ptr<Vec2d>(); |
||||
|
||||
size_t n = distorted.total(); |
||||
int sdepth = distorted.depth(); |
||||
|
||||
const bool isEps = (criteria.type & TermCriteria::EPS) != 0; |
||||
|
||||
/* Define max count for solver iterations */ |
||||
int maxCount = std::numeric_limits<int>::max(); |
||||
if (criteria.type & TermCriteria::MAX_ITER) { |
||||
maxCount = criteria.maxCount; |
||||
} |
||||
|
||||
|
||||
for(size_t i = 0; i < n; i++ ) |
||||
{ |
||||
Vec2d pi = sdepth == CV_32F ? (Vec2d)srcf[i] : srcd[i]; // image point
|
||||
Vec2d pw((pi[0] - c[0])/f[0], (pi[1] - c[1])/f[1]); // world point
|
||||
|
||||
double theta_d = sqrt(pw[0]*pw[0] + pw[1]*pw[1]); |
||||
|
||||
// the current camera model is only valid up to 180 FOV
|
||||
// for larger FOV the loop below does not converge
|
||||
// clip values so we still get plausible results for super fisheye images > 180 grad
|
||||
theta_d = min(max(-CV_PI/2., theta_d), CV_PI/2.); |
||||
|
||||
bool converged = false; |
||||
double theta = theta_d; |
||||
|
||||
double scale = 0.0; |
||||
|
||||
if (!isEps || fabs(theta_d) > criteria.epsilon) |
||||
{ |
||||
// compensate distortion iteratively using Newton method
|
||||
|
||||
for (int j = 0; j < maxCount; j++) |
||||
{ |
||||
double theta2 = theta*theta, theta4 = theta2*theta2, theta6 = theta4*theta2, theta8 = theta6*theta2; |
||||
double k0_theta2 = k[0] * theta2, k1_theta4 = k[1] * theta4, k2_theta6 = k[2] * theta6, k3_theta8 = k[3] * theta8; |
||||
/* new_theta = theta - theta_fix, theta_fix = f0(theta) / f0'(theta) */ |
||||
double theta_fix = (theta * (1 + k0_theta2 + k1_theta4 + k2_theta6 + k3_theta8) - theta_d) / |
||||
(1 + 3*k0_theta2 + 5*k1_theta4 + 7*k2_theta6 + 9*k3_theta8); |
||||
theta = theta - theta_fix; |
||||
|
||||
if (isEps && (fabs(theta_fix) < criteria.epsilon)) |
||||
{ |
||||
converged = true; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
scale = std::tan(theta) / theta_d; |
||||
} |
||||
else |
||||
{ |
||||
converged = true; |
||||
} |
||||
|
||||
// theta is monotonously increasing or decreasing depending on the sign of theta
|
||||
// if theta has flipped, it might converge due to symmetry but on the opposite of the camera center
|
||||
// so we can check whether theta has changed the sign during the optimization
|
||||
bool theta_flipped = ((theta_d < 0 && theta > 0) || (theta_d > 0 && theta < 0)); |
||||
|
||||
if ((converged || !isEps) && !theta_flipped) |
||||
{ |
||||
Vec2d pu = pw * scale; //undistorted point
|
||||
|
||||
// reproject
|
||||
Vec3d pr = RR * Vec3d(pu[0], pu[1], 1.0); // rotated point optionally multiplied by new camera matrix
|
||||
Vec2d fi(pr[0]/pr[2], pr[1]/pr[2]); // final
|
||||
|
||||
if( sdepth == CV_32F ) |
||||
dstf[i] = fi; |
||||
else |
||||
dstd[i] = fi; |
||||
} |
||||
else |
||||
{ |
||||
// Vec2d fi(std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN());
|
||||
Vec2d fi(-1000000.0, -1000000.0); |
||||
|
||||
if( sdepth == CV_32F ) |
||||
dstf[i] = fi; |
||||
else |
||||
dstd[i] = fi; |
||||
} |
||||
} |
||||
} |
||||
|
||||
void cv::fisheye::initUndistortRectifyMap( InputArray K, InputArray D, InputArray R, InputArray P, |
||||
const cv::Size& size, int m1type, OutputArray map1, OutputArray map2 ) |
||||
{ |
||||
CV_INSTRUMENT_REGION(); |
||||
|
||||
CV_Assert( m1type == CV_16SC2 || m1type == CV_32F || m1type <=0 ); |
||||
map1.create( size, m1type <= 0 ? CV_16SC2 : m1type ); |
||||
map2.create( size, map1.type() == CV_16SC2 ? CV_16UC1 : CV_32F ); |
||||
|
||||
CV_Assert((K.depth() == CV_32F || K.depth() == CV_64F) && (D.depth() == CV_32F || D.depth() == CV_64F)); |
||||
CV_Assert((P.empty() || P.depth() == CV_32F || P.depth() == CV_64F) && (R.empty() || R.depth() == CV_32F || R.depth() == CV_64F)); |
||||
CV_Assert(K.size() == Size(3, 3) && (D.empty() || D.total() == 4)); |
||||
CV_Assert(R.empty() || R.size() == Size(3, 3) || R.total() * R.channels() == 3); |
||||
CV_Assert(P.empty() || P.size() == Size(3, 3) || P.size() == Size(4, 3)); |
||||
|
||||
Vec2d f, c; |
||||
if (K.depth() == CV_32F) |
||||
{ |
||||
Matx33f camMat = K.getMat(); |
||||
f = Vec2f(camMat(0, 0), camMat(1, 1)); |
||||
c = Vec2f(camMat(0, 2), camMat(1, 2)); |
||||
} |
||||
else |
||||
{ |
||||
Matx33d camMat = K.getMat(); |
||||
f = Vec2d(camMat(0, 0), camMat(1, 1)); |
||||
c = Vec2d(camMat(0, 2), camMat(1, 2)); |
||||
} |
||||
|
||||
Vec4d k = Vec4d::all(0); |
||||
if (!D.empty()) |
||||
k = D.depth() == CV_32F ? (Vec4d)*D.getMat().ptr<Vec4f>(): *D.getMat().ptr<Vec4d>(); |
||||
|
||||
Matx33d RR = Matx33d::eye(); |
||||
if (!R.empty() && R.total() * R.channels() == 3) |
||||
{ |
||||
Vec3d rvec; |
||||
R.getMat().convertTo(rvec, CV_64F); |
||||
RR = Affine3d(rvec).rotation(); |
||||
} |
||||
else if (!R.empty() && R.size() == Size(3, 3)) |
||||
R.getMat().convertTo(RR, CV_64F); |
||||
|
||||
Matx33d PP = Matx33d::eye(); |
||||
if (!P.empty()) |
||||
P.getMat().colRange(0, 3).convertTo(PP, CV_64F); |
||||
|
||||
Matx33d iR = (PP * RR).inv(cv::DECOMP_SVD); |
||||
|
||||
for( int i = 0; i < size.height; ++i) |
||||
{ |
||||
float* m1f = map1.getMat().ptr<float>(i); |
||||
float* m2f = map2.getMat().ptr<float>(i); |
||||
short* m1 = (short*)m1f; |
||||
ushort* m2 = (ushort*)m2f; |
||||
|
||||
double _x = i*iR(0, 1) + iR(0, 2), |
||||
_y = i*iR(1, 1) + iR(1, 2), |
||||
_w = i*iR(2, 1) + iR(2, 2); |
||||
|
||||
for( int j = 0; j < size.width; ++j) |
||||
{ |
||||
double u, v; |
||||
if( _w <= 0) |
||||
{ |
||||
u = (_x > 0) ? -std::numeric_limits<double>::infinity() : std::numeric_limits<double>::infinity(); |
||||
v = (_y > 0) ? -std::numeric_limits<double>::infinity() : std::numeric_limits<double>::infinity(); |
||||
} |
||||
else |
||||
{ |
||||
double x = _x/_w, y = _y/_w; |
||||
|
||||
double r = sqrt(x*x + y*y); |
||||
double theta = std::atan(r); |
||||
|
||||
double theta2 = theta*theta, theta4 = theta2*theta2, theta6 = theta4*theta2, theta8 = theta4*theta4; |
||||
double theta_d = theta * (1 + k[0]*theta2 + k[1]*theta4 + k[2]*theta6 + k[3]*theta8); |
||||
|
||||
double scale = (r == 0) ? 1.0 : theta_d / r; |
||||
u = f[0]*x*scale + c[0]; |
||||
v = f[1]*y*scale + c[1]; |
||||
} |
||||
|
||||
if( m1type == CV_16SC2 ) |
||||
{ |
||||
int iu = cv::saturate_cast<int>(u*cv::INTER_TAB_SIZE); |
||||
int iv = cv::saturate_cast<int>(v*cv::INTER_TAB_SIZE); |
||||
m1[j*2+0] = (short)(iu >> cv::INTER_BITS); |
||||
m1[j*2+1] = (short)(iv >> cv::INTER_BITS); |
||||
m2[j] = (ushort)((iv & (cv::INTER_TAB_SIZE-1))*cv::INTER_TAB_SIZE + (iu & (cv::INTER_TAB_SIZE-1))); |
||||
} |
||||
else if( m1type == CV_32FC1 ) |
||||
{ |
||||
m1f[j] = (float)u; |
||||
m2f[j] = (float)v; |
||||
} |
||||
|
||||
_x += iR(0, 0); |
||||
_y += iR(1, 0); |
||||
_w += iR(2, 0); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void cv::fisheye::estimateNewCameraMatrixForUndistortRectify(InputArray K, InputArray D, const Size &image_size, InputArray R, |
||||
OutputArray P, double balance, const Size& new_size, double fov_scale) |
||||
{ |
||||
CV_INSTRUMENT_REGION(); |
||||
|
||||
CV_Assert( K.size() == Size(3, 3) && (K.depth() == CV_32F || K.depth() == CV_64F)); |
||||
CV_Assert(D.empty() || ((D.total() == 4) && (D.depth() == CV_32F || D.depth() == CV_64F))); |
||||
|
||||
int w = image_size.width, h = image_size.height; |
||||
balance = std::min(std::max(balance, 0.0), 1.0); |
||||
|
||||
Mat points(1, 4, CV_64FC2); |
||||
Vec2d* pptr = points.ptr<Vec2d>(); |
||||
pptr[0] = Vec2d(w/2, 0); |
||||
pptr[1] = Vec2d(w, h/2); |
||||
pptr[2] = Vec2d(w/2, h); |
||||
pptr[3] = Vec2d(0, h/2); |
||||
|
||||
fisheye::undistortPoints(points, points, K, D, R); |
||||
cv::Scalar center_mass = mean(points); |
||||
Vec2d cn(center_mass.val); |
||||
|
||||
double aspect_ratio = (K.depth() == CV_32F) ? K.getMat().at<float >(0,0)/K.getMat().at<float> (1,1) |
||||
: K.getMat().at<double>(0,0)/K.getMat().at<double>(1,1); |
||||
|
||||
// convert to identity ratio
|
||||
cn[1] *= aspect_ratio; |
||||
for(size_t i = 0; i < points.total(); ++i) |
||||
pptr[i][1] *= aspect_ratio; |
||||
|
||||
double minx = DBL_MAX, miny = DBL_MAX, maxx = -DBL_MAX, maxy = -DBL_MAX; |
||||
for(size_t i = 0; i < points.total(); ++i) |
||||
{ |
||||
miny = std::min(miny, pptr[i][1]); |
||||
maxy = std::max(maxy, pptr[i][1]); |
||||
minx = std::min(minx, pptr[i][0]); |
||||
maxx = std::max(maxx, pptr[i][0]); |
||||
} |
||||
|
||||
double f1 = w * 0.5/(cn[0] - minx); |
||||
double f2 = w * 0.5/(maxx - cn[0]); |
||||
double f3 = h * 0.5 * aspect_ratio/(cn[1] - miny); |
||||
double f4 = h * 0.5 * aspect_ratio/(maxy - cn[1]); |
||||
|
||||
double fmin = std::min(f1, std::min(f2, std::min(f3, f4))); |
||||
double fmax = std::max(f1, std::max(f2, std::max(f3, f4))); |
||||
|
||||
double f = balance * fmin + (1.0 - balance) * fmax; |
||||
f *= fov_scale > 0 ? 1.0/fov_scale : 1.0; |
||||
|
||||
Vec2d new_f(f, f), new_c = -cn * f + Vec2d(w, h * aspect_ratio) * 0.5; |
||||
|
||||
// restore aspect ratio
|
||||
new_f[1] /= aspect_ratio; |
||||
new_c[1] /= aspect_ratio; |
||||
|
||||
if (!new_size.empty()) |
||||
{ |
||||
double rx = new_size.width /(double)image_size.width; |
||||
double ry = new_size.height/(double)image_size.height; |
||||
|
||||
new_f[0] *= rx; new_f[1] *= ry; |
||||
new_c[0] *= rx; new_c[1] *= ry; |
||||
} |
||||
|
||||
Mat(Matx33d(new_f[0], 0, new_c[0], |
||||
0, new_f[1], new_c[1], |
||||
0, 0, 1)).convertTo(P, P.empty() ? K.type() : P.type()); |
||||
} |
||||
|
||||
void cv::fisheye::undistortImage(InputArray distorted, OutputArray undistorted, |
||||
InputArray K, InputArray D, InputArray Knew, const Size& new_size) |
||||
{ |
||||
CV_INSTRUMENT_REGION(); |
||||
|
||||
Size size = !new_size.empty() ? new_size : distorted.size(); |
||||
|
||||
Mat map1, map2; |
||||
fisheye::initUndistortRectifyMap(K, D, Matx33d::eye(), Knew, size, CV_16SC2, map1, map2 ); |
||||
cv::remap(distorted, undistorted, map1, map2, INTER_LINEAR, BORDER_CONSTANT); |
||||
} |
||||
|
||||
} // namespace cv
|
@ -0,0 +1,388 @@ |
||||
// 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/videoio.hpp" |
||||
|
||||
namespace opencv_test { namespace { |
||||
|
||||
class fisheyeTest : public ::testing::Test { |
||||
|
||||
protected: |
||||
const static cv::Size imageSize; |
||||
const static cv::Matx33d K; |
||||
const static cv::Vec4d D; |
||||
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); |
||||
}; |
||||
|
||||
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); |
||||
|
||||
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; |
||||
} |
||||
|
||||
TEST_F(fisheyeTest, projectPoints) |
||||
{ |
||||
double cols = this->imageSize.width, |
||||
rows = this->imageSize.height; |
||||
|
||||
const int N = 20; |
||||
cv::Mat distorted0(1, N*N, CV_64FC2), undist1, undist2, distorted1, distorted2; |
||||
undist2.create(distorted0.size(), CV_MAKETYPE(distorted0.depth(), 3)); |
||||
cv::Vec2d* pts = distorted0.ptr<cv::Vec2d>(); |
||||
|
||||
cv::Vec2d c(this->K(0, 2), this->K(1, 2)); |
||||
for(int y = 0, k = 0; y < N; ++y) |
||||
for(int x = 0; x < N; ++x) |
||||
{ |
||||
cv::Vec2d point(x*cols/(N-1.f), y*rows/(N-1.f)); |
||||
pts[k++] = (point - c) * 0.85 + c; |
||||
} |
||||
|
||||
cv::fisheye::undistortPoints(distorted0, undist1, this->K, this->D); |
||||
|
||||
cv::Vec2d* u1 = undist1.ptr<cv::Vec2d>(); |
||||
cv::Vec3d* u2 = undist2.ptr<cv::Vec3d>(); |
||||
for(int i = 0; i < (int)distorted0.total(); ++i) |
||||
u2[i] = cv::Vec3d(u1[i][0], u1[i][1], 1.0); |
||||
|
||||
cv::fisheye::distortPoints(undist1, distorted1, this->K, this->D); |
||||
cv::fisheye::projectPoints(undist2, distorted2, cv::Vec3d::all(0), cv::Vec3d::all(0), this->K, this->D); |
||||
|
||||
EXPECT_MAT_NEAR(distorted0, distorted1, 1e-10); |
||||
EXPECT_MAT_NEAR(distorted0, distorted2, 1e-10); |
||||
} |
||||
|
||||
TEST_F(fisheyeTest, distortUndistortPoints) |
||||
{ |
||||
int width = imageSize.width; |
||||
int height = imageSize.height; |
||||
|
||||
/* Create test points */ |
||||
std::vector<cv::Point2d> points0Vector; |
||||
cv::Mat principalPoints = (cv::Mat_<double>(5, 2) << K(0, 2), K(1, 2), // (cx, cy)
|
||||
/* Image corners */ |
||||
0, 0, |
||||
0, height, |
||||
width, 0, |
||||
width, height |
||||
); |
||||
|
||||
/* Random points inside image */ |
||||
cv::Mat xy[2] = {}; |
||||
xy[0].create(100, 1, CV_64F); |
||||
theRNG().fill(xy[0], cv::RNG::UNIFORM, 0, width); // x
|
||||
xy[1].create(100, 1, CV_64F); |
||||
theRNG().fill(xy[1], cv::RNG::UNIFORM, 0, height); // y
|
||||
|
||||
cv::Mat randomPoints; |
||||
merge(xy, 2, randomPoints); |
||||
|
||||
cv::Mat points0; |
||||
cv::vconcat(principalPoints.reshape(2), randomPoints, points0); |
||||
|
||||
/* Test with random D set */ |
||||
for (size_t i = 0; i < 10; ++i) { |
||||
cv::Mat distortion(1, 4, CV_64F); |
||||
theRNG().fill(distortion, cv::RNG::UNIFORM, -0.00001, 0.00001); |
||||
|
||||
/* Distort -> Undistort */ |
||||
cv::Mat distortedPoints; |
||||
cv::fisheye::distortPoints(points0, distortedPoints, K, distortion); |
||||
cv::Mat undistortedPoints; |
||||
cv::fisheye::undistortPoints(distortedPoints, undistortedPoints, K, distortion); |
||||
|
||||
EXPECT_MAT_NEAR(points0, undistortedPoints, 1e-8); |
||||
|
||||
/* Undistort -> Distort */ |
||||
cv::fisheye::undistortPoints(points0, undistortedPoints, K, distortion); |
||||
cv::fisheye::distortPoints(undistortedPoints, distortedPoints, K, distortion); |
||||
|
||||
EXPECT_MAT_NEAR(points0, distortedPoints, 1e-8); |
||||
} |
||||
} |
||||
|
||||
TEST_F(fisheyeTest, undistortImage) |
||||
{ |
||||
// we use it to reduce patch size for images in testdata
|
||||
auto throwAwayHalf = [](Mat img) |
||||
{ |
||||
int whalf = img.cols / 2, hhalf = img.rows / 2; |
||||
Rect tl(0, 0, whalf, hhalf), br(whalf, hhalf, whalf, hhalf); |
||||
img(tl) = 0; |
||||
img(br) = 0; |
||||
}; |
||||
|
||||
cv::Matx33d theK = this->K; |
||||
cv::Mat theD = cv::Mat(this->D); |
||||
std::string file = combine(datasets_repository_path, "/calib-3_stereo_from_JY/left/stereo_pair_014.jpg"); |
||||
cv::Matx33d newK = theK; |
||||
cv::Mat distorted = cv::imread(file), undistorted; |
||||
{ |
||||
newK(0, 0) = 100; |
||||
newK(1, 1) = 100; |
||||
cv::fisheye::undistortImage(distorted, undistorted, theK, theD, newK); |
||||
std::string imageFilename = combine(datasets_repository_path, "new_f_100.png"); |
||||
cv::Mat correct = cv::imread(imageFilename); |
||||
ASSERT_FALSE(correct.empty()) << "Correct image " << imageFilename.c_str() << " can not be read" << std::endl; |
||||
|
||||
throwAwayHalf(correct); |
||||
throwAwayHalf(undistorted); |
||||
|
||||
EXPECT_MAT_NEAR(correct, undistorted, 1e-10); |
||||
} |
||||
{ |
||||
double balance = 1.0; |
||||
cv::fisheye::estimateNewCameraMatrixForUndistortRectify(theK, theD, distorted.size(), cv::noArray(), newK, balance); |
||||
cv::fisheye::undistortImage(distorted, undistorted, theK, theD, newK); |
||||
std::string imageFilename = combine(datasets_repository_path, "balance_1.0.png"); |
||||
cv::Mat correct = cv::imread(imageFilename); |
||||
ASSERT_FALSE(correct.empty()) << "Correct image " << imageFilename.c_str() << " can not be read" << std::endl; |
||||
|
||||
throwAwayHalf(correct); |
||||
throwAwayHalf(undistorted); |
||||
|
||||
EXPECT_MAT_NEAR(correct, undistorted, 1e-10); |
||||
} |
||||
|
||||
{ |
||||
double balance = 0.0; |
||||
cv::fisheye::estimateNewCameraMatrixForUndistortRectify(theK, theD, distorted.size(), cv::noArray(), newK, balance); |
||||
cv::fisheye::undistortImage(distorted, undistorted, theK, theD, newK); |
||||
std::string imageFilename = combine(datasets_repository_path, "balance_0.0.png"); |
||||
cv::Mat correct = cv::imread(imageFilename); |
||||
ASSERT_FALSE(correct.empty()) << "Correct image " << imageFilename.c_str() << " can not be read" << std::endl; |
||||
|
||||
throwAwayHalf(correct); |
||||
throwAwayHalf(undistorted); |
||||
|
||||
EXPECT_MAT_NEAR(correct, undistorted, 1e-10); |
||||
} |
||||
} |
||||
|
||||
TEST_F(fisheyeTest, undistortAndDistortImage) |
||||
{ |
||||
cv::Matx33d K_src = this->K; |
||||
cv::Mat D_src = cv::Mat(this->D); |
||||
std::string file = combine(datasets_repository_path, "/calib-3_stereo_from_JY/left/stereo_pair_014.jpg"); |
||||
cv::Matx33d K_dst = K_src; |
||||
cv::Mat image = cv::imread(file), image_projected; |
||||
cv::Vec4d D_dst_vec (-1.0, 0.0, 0.0, 0.0); |
||||
cv::Mat D_dst = cv::Mat(D_dst_vec); |
||||
|
||||
int imageWidth = (int)this->imageSize.width; |
||||
int imageHeight = (int)this->imageSize.height; |
||||
|
||||
cv::Mat imagePoints(imageHeight, imageWidth, CV_32FC2), undPoints, distPoints; |
||||
cv::Vec2f* pts = imagePoints.ptr<cv::Vec2f>(); |
||||
|
||||
for(int y = 0, k = 0; y < imageHeight; ++y) |
||||
{ |
||||
for(int x = 0; x < imageWidth; ++x) |
||||
{ |
||||
cv::Vec2f point((float)x, (float)y); |
||||
pts[k++] = point; |
||||
} |
||||
} |
||||
|
||||
cv::fisheye::undistortPoints(imagePoints, undPoints, K_dst, D_dst); |
||||
cv::fisheye::distortPoints(undPoints, distPoints, K_src, D_src); |
||||
cv::remap(image, image_projected, distPoints, cv::noArray(), cv::INTER_LINEAR); |
||||
|
||||
float dx, dy, r_sq; |
||||
float R_MAX = 250; |
||||
float imageCenterX = (float)imageWidth / 2; |
||||
float imageCenterY = (float)imageHeight / 2; |
||||
|
||||
cv::Mat undPointsGt(imageHeight, imageWidth, CV_32FC2); |
||||
cv::Mat imageGt(imageHeight, imageWidth, CV_8UC3); |
||||
|
||||
for(int y = 0; y < imageHeight; ++y) |
||||
{ |
||||
for(int x = 0; x < imageWidth; ++x) |
||||
{ |
||||
dx = x - imageCenterX; |
||||
dy = y - imageCenterY; |
||||
r_sq = dy * dy + dx * dx; |
||||
|
||||
Vec2f & und_vec = undPoints.at<Vec2f>(y,x); |
||||
Vec3b & pixel = image_projected.at<Vec3b>(y,x); |
||||
|
||||
Vec2f & undist_vec_gt = undPointsGt.at<Vec2f>(y,x); |
||||
Vec3b & pixel_gt = imageGt.at<Vec3b>(y,x); |
||||
|
||||
if (r_sq > R_MAX * R_MAX) |
||||
{ |
||||
|
||||
undist_vec_gt[0] = -1e6; |
||||
undist_vec_gt[1] = -1e6; |
||||
|
||||
pixel_gt[0] = 0; |
||||
pixel_gt[1] = 0; |
||||
pixel_gt[2] = 0; |
||||
} |
||||
else |
||||
{ |
||||
undist_vec_gt[0] = und_vec[0]; |
||||
undist_vec_gt[1] = und_vec[1]; |
||||
|
||||
pixel_gt[0] = pixel[0]; |
||||
pixel_gt[1] = pixel[1]; |
||||
pixel_gt[2] = pixel[2]; |
||||
} |
||||
|
||||
} |
||||
} |
||||
|
||||
EXPECT_MAT_NEAR(undPoints, undPointsGt, 1e-10); |
||||
EXPECT_MAT_NEAR(image_projected, imageGt, 1e-10); |
||||
|
||||
Vec2f dist_point_1 = distPoints.at<Vec2f>(400, 640); |
||||
Vec2f dist_point_1_gt(640.044f, 400.041f); |
||||
|
||||
Vec2f dist_point_2 = distPoints.at<Vec2f>(400, 440); |
||||
Vec2f dist_point_2_gt(409.731f, 403.029f); |
||||
|
||||
Vec2f dist_point_3 = distPoints.at<Vec2f>(200, 640); |
||||
Vec2f dist_point_3_gt(643.341f, 168.896f); |
||||
|
||||
Vec2f dist_point_4 = distPoints.at<Vec2f>(300, 480); |
||||
Vec2f dist_point_4_gt(463.402f, 290.317f); |
||||
|
||||
Vec2f dist_point_5 = distPoints.at<Vec2f>(550, 750); |
||||
Vec2f dist_point_5_gt(797.51f, 611.637f); |
||||
|
||||
EXPECT_MAT_NEAR(dist_point_1, dist_point_1_gt, 1e-2); |
||||
EXPECT_MAT_NEAR(dist_point_2, dist_point_2_gt, 1e-2); |
||||
EXPECT_MAT_NEAR(dist_point_3, dist_point_3_gt, 1e-2); |
||||
EXPECT_MAT_NEAR(dist_point_4, dist_point_4_gt, 1e-2); |
||||
EXPECT_MAT_NEAR(dist_point_5, dist_point_5_gt, 1e-2); |
||||
|
||||
// Add the "--test_debug" to arguments for file output
|
||||
if (cvtest::debugLevel > 0) |
||||
cv::imwrite(combine(datasets_repository_path, "new_distortion.png"), image_projected); |
||||
} |
||||
|
||||
TEST_F(fisheyeTest, jacobians) |
||||
{ |
||||
int n = 10; |
||||
cv::Mat X(1, n, CV_64FC3); |
||||
cv::Mat om(3, 1, CV_64F), theT(3, 1, CV_64F); |
||||
cv::Mat f(2, 1, CV_64F), c(2, 1, CV_64F); |
||||
cv::Mat k(4, 1, CV_64F); |
||||
double alpha; |
||||
|
||||
cv::RNG r; |
||||
|
||||
r.fill(X, cv::RNG::NORMAL, 2, 1); |
||||
X = cv::abs(X) * 10; |
||||
|
||||
r.fill(om, cv::RNG::NORMAL, 0, 1); |
||||
om = cv::abs(om); |
||||
|
||||
r.fill(theT, cv::RNG::NORMAL, 0, 1); |
||||
theT = cv::abs(theT); theT.at<double>(2) = 4; theT *= 10; |
||||
|
||||
r.fill(f, cv::RNG::NORMAL, 0, 1); |
||||
f = cv::abs(f) * 1000; |
||||
|
||||
r.fill(c, cv::RNG::NORMAL, 0, 1); |
||||
c = cv::abs(c) * 1000; |
||||
|
||||
r.fill(k, cv::RNG::NORMAL, 0, 1); |
||||
k*= 0.5; |
||||
|
||||
alpha = 0.01*r.gaussian(1); |
||||
|
||||
cv::Mat x1, x2, xpred; |
||||
cv::Matx33d theK(f.at<double>(0), alpha * f.at<double>(0), c.at<double>(0), |
||||
0, f.at<double>(1), c.at<double>(1), |
||||
0, 0, 1); |
||||
|
||||
cv::Mat jacobians; |
||||
cv::fisheye::projectPoints(X, x1, om, theT, theK, k, alpha, jacobians); |
||||
|
||||
//test on T:
|
||||
cv::Mat dT(3, 1, CV_64FC1); |
||||
r.fill(dT, cv::RNG::NORMAL, 0, 1); |
||||
dT *= 1e-9*cv::norm(theT); |
||||
cv::Mat T2 = theT + dT; |
||||
cv::fisheye::projectPoints(X, x2, om, T2, theK, k, alpha, cv::noArray()); |
||||
xpred = x1 + cv::Mat(jacobians.colRange(11,14) * dT).reshape(2, 1); |
||||
CV_Assert (cv::norm(x2 - xpred) < 1e-10); |
||||
|
||||
//test on om:
|
||||
cv::Mat dom(3, 1, CV_64FC1); |
||||
r.fill(dom, cv::RNG::NORMAL, 0, 1); |
||||
dom *= 1e-9*cv::norm(om); |
||||
cv::Mat om2 = om + dom; |
||||
cv::fisheye::projectPoints(X, x2, om2, theT, theK, k, alpha, cv::noArray()); |
||||
xpred = x1 + cv::Mat(jacobians.colRange(8,11) * dom).reshape(2, 1); |
||||
CV_Assert (cv::norm(x2 - xpred) < 1e-10); |
||||
|
||||
//test on f:
|
||||
cv::Mat df(2, 1, CV_64FC1); |
||||
r.fill(df, cv::RNG::NORMAL, 0, 1); |
||||
df *= 1e-9*cv::norm(f); |
||||
cv::Matx33d K2 = theK + cv::Matx33d(df.at<double>(0), df.at<double>(0) * alpha, 0, 0, df.at<double>(1), 0, 0, 0, 0); |
||||
cv::fisheye::projectPoints(X, x2, om, theT, K2, k, alpha, cv::noArray()); |
||||
xpred = x1 + cv::Mat(jacobians.colRange(0,2) * df).reshape(2, 1); |
||||
CV_Assert (cv::norm(x2 - xpred) < 1e-10); |
||||
|
||||
//test on c:
|
||||
cv::Mat dc(2, 1, CV_64FC1); |
||||
r.fill(dc, cv::RNG::NORMAL, 0, 1); |
||||
dc *= 1e-9*cv::norm(c); |
||||
K2 = theK + cv::Matx33d(0, 0, dc.at<double>(0), 0, 0, dc.at<double>(1), 0, 0, 0); |
||||
cv::fisheye::projectPoints(X, x2, om, theT, K2, k, alpha, cv::noArray()); |
||||
xpred = x1 + cv::Mat(jacobians.colRange(2,4) * dc).reshape(2, 1); |
||||
CV_Assert (cv::norm(x2 - xpred) < 1e-10); |
||||
|
||||
//test on k:
|
||||
cv::Mat dk(4, 1, CV_64FC1); |
||||
r.fill(dk, cv::RNG::NORMAL, 0, 1); |
||||
dk *= 1e-9*cv::norm(k); |
||||
cv::Mat k2 = k + dk; |
||||
cv::fisheye::projectPoints(X, x2, om, theT, theK, k2, alpha, cv::noArray()); |
||||
xpred = x1 + cv::Mat(jacobians.colRange(4,8) * dk).reshape(2, 1); |
||||
CV_Assert (cv::norm(x2 - xpred) < 1e-10); |
||||
|
||||
//test on alpha:
|
||||
cv::Mat dalpha(1, 1, CV_64FC1); |
||||
r.fill(dalpha, cv::RNG::NORMAL, 0, 1); |
||||
dalpha *= 1e-9*cv::norm(f); |
||||
double alpha2 = alpha + dalpha.at<double>(0); |
||||
K2 = theK + cv::Matx33d(0, f.at<double>(0) * dalpha.at<double>(0), 0, 0, 0, 0, 0, 0, 0); |
||||
cv::fisheye::projectPoints(X, x2, om, theT, theK, k, alpha2, cv::noArray()); |
||||
xpred = x1 + cv::Mat(jacobians.col(14) * dalpha).reshape(2, 1); |
||||
CV_Assert (cv::norm(x2 - xpred) < 1e-10); |
||||
} |
||||
|
||||
}} |
@ -1,40 +0,0 @@ |
||||
// 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 "perf_precomp.hpp" |
||||
|
||||
namespace opencv_test { namespace { |
||||
|
||||
PERF_TEST(Undistort, fisheye_undistortPoints_100k_10iter) |
||||
{ |
||||
const int pointsNumber = 100000; |
||||
const Size imageSize(1280, 800); |
||||
|
||||
/* Set camera matrix */ |
||||
const Matx33d K(558.478087865323, 0, 620.458515360843, |
||||
0, 560.506767351568, 381.939424848348, |
||||
0, 0, 1); |
||||
|
||||
/* Set distortion coefficients */ |
||||
const Matx14d D(2.81e-06, 1.31e-06, -4.42e-06, -1.25e-06); |
||||
|
||||
/* Create two-channel points matrix */ |
||||
Mat xy[2] = {}; |
||||
xy[0].create(pointsNumber, 1, CV_64F); |
||||
theRNG().fill(xy[0], RNG::UNIFORM, 0, imageSize.width); // x
|
||||
xy[1].create(pointsNumber, 1, CV_64F); |
||||
theRNG().fill(xy[1], RNG::UNIFORM, 0, imageSize.height); // y
|
||||
|
||||
Mat points; |
||||
merge(xy, 2, points); |
||||
|
||||
/* Set fixed iteration number to check only c++ code, not algo convergence */ |
||||
TermCriteria termCriteria(TermCriteria::MAX_ITER, 10, 0); |
||||
|
||||
Mat undistortedPoints; |
||||
TEST_CYCLE() fisheye::undistortPoints(points, undistortedPoints, K, D, noArray(), noArray(), termCriteria); |
||||
|
||||
SANITY_CHECK_NOTHING(); |
||||
} |
||||
|
||||
}} // namespace
|
@ -0,0 +1,5 @@ |
||||
{ |
||||
"namespaces_dict": { |
||||
"cv.fisheye": "fisheye" |
||||
} |
||||
} |
@ -0,0 +1,347 @@ |
||||
// 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/3d.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; |
||||
} |
||||
|
||||
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); |
||||
} |
||||
} |
||||
|
||||
}} |
Loading…
Reference in new issue