|
|
/*M/////////////////////////////////////////////////////////////////////////////////////// |
|
|
// |
|
|
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. |
|
|
// |
|
|
// By downloading, copying, installing or using the software you agree to this license. |
|
|
// If you do not agree to this license, do not download, install, |
|
|
// copy or use the software. |
|
|
// |
|
|
// |
|
|
// License Agreement |
|
|
// For Open Source Computer Vision Library |
|
|
// |
|
|
// Copyright (C) 2015, Baisheng Lai (laibaisheng@gmail.com), Zhejiang University, |
|
|
// all rights reserved. |
|
|
// |
|
|
// Redistribution and use in source and binary forms, with or without modification, |
|
|
// are permitted provided that the following conditions are met: |
|
|
// |
|
|
// * Redistribution's of source code must retain the above copyright notice, |
|
|
// this list of conditions and the following disclaimer. |
|
|
// |
|
|
// * Redistribution's in binary form must reproduce the above copyright notice, |
|
|
// this list of conditions and the following disclaimer in the documentation |
|
|
// and/or other materials provided with the distribution. |
|
|
// |
|
|
// * The name of the copyright holders may not be used to endorse or promote products |
|
|
// derived from this software without specific prior written permission. |
|
|
// |
|
|
// This software is provided by the copyright holders and contributors "as is" and |
|
|
// any express or implied warranties, including, but not limited to, the implied |
|
|
// warranties of merchantability and fitness for a particular purpose are disclaimed. |
|
|
// In no event shall the Intel Corporation or contributors be liable for any direct, |
|
|
// indirect, incidental, special, exemplary, or consequential damages |
|
|
// (including, but not limited to, procurement of substitute goods or services; |
|
|
// loss of use, data, or profits; or business interruption) however caused |
|
|
// and on any theory of liability, whether in contract, strict liability, |
|
|
// or tort (including negligence or otherwise) arising in any way out of |
|
|
// the use of this software, even if advised of the possibility of such damage. |
|
|
// |
|
|
//M*/ |
|
|
|
|
|
/** |
|
|
* This module was accepted as a GSoC 2015 project for OpenCV, authored by |
|
|
* Baisheng Lai, mentored by Bo Li. |
|
|
* |
|
|
* The omnidirectional camera in this module is denoted by the catadioptric |
|
|
* model. Please refer to Mei's paper for details of the camera model: |
|
|
* |
|
|
* C. Mei and P. Rives, "Single view point omnidirectional camera |
|
|
* calibration from planar grids", in ICRA 2007. |
|
|
* |
|
|
* The implementation of the calibration part is based on Li's calibration |
|
|
* toolbox: |
|
|
* |
|
|
* B. Li, L. Heng, K. Kevin and M. Pollefeys, "A Multiple-Camera System |
|
|
* Calibration Toolbox Using A Feature Descriptor-Based Calibration |
|
|
* Pattern", in IROS 2013. |
|
|
*/ |
|
|
#include "precomp.hpp" |
|
|
#include "opencv2/ccalib/omnidir.hpp" |
|
|
#include <fstream> |
|
|
#include <iostream> |
|
|
namespace cv { namespace |
|
|
{ |
|
|
struct JacobianRow |
|
|
{ |
|
|
Matx13d dom,dT; |
|
|
Matx12d df; |
|
|
double ds; |
|
|
Matx12d dc; |
|
|
double dxi; |
|
|
Matx14d dkp; // distortion k1,k2,p1,p2 |
|
|
}; |
|
|
}} |
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////// |
|
|
//////// projectPoints |
|
|
void cv::omnidir::projectPoints(InputArray objectPoints, OutputArray imagePoints, |
|
|
InputArray rvec, InputArray tvec, InputArray K, double xi, InputArray D, OutputArray jacobian) |
|
|
{ |
|
|
|
|
|
CV_Assert(objectPoints.type() == CV_64FC3 || objectPoints.type() == CV_32FC3); |
|
|
CV_Assert((rvec.depth() == CV_64F || rvec.depth() == CV_32F) && rvec.total() == 3); |
|
|
CV_Assert((tvec.depth() == CV_64F || tvec.depth() == CV_32F) && tvec.total() == 3); |
|
|
CV_Assert((K.type() == CV_64F || K.type() == CV_32F) && K.size() == Size(3,3)); |
|
|
CV_Assert((D.type() == CV_64F || D.type() == CV_32F) && D.total() == 4); |
|
|
|
|
|
imagePoints.create(objectPoints.size(), CV_MAKETYPE(objectPoints.depth(), 2)); |
|
|
|
|
|
int n = (int)objectPoints.total(); |
|
|
|
|
|
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>(); |
|
|
|
|
|
Vec2d f,c; |
|
|
double s; |
|
|
if (K.depth() == CV_32F) |
|
|
{ |
|
|
Matx33f Kc = K.getMat(); |
|
|
f = Vec2f(Kc(0,0), Kc(1,1)); |
|
|
c = Vec2f(Kc(0,2),Kc(1,2)); |
|
|
s = (double)Kc(0,1); |
|
|
} |
|
|
else |
|
|
{ |
|
|
Matx33d Kc = K.getMat(); |
|
|
f = Vec2d(Kc(0,0), Kc(1,1)); |
|
|
c = Vec2d(Kc(0,2),Kc(1,2)); |
|
|
s = Kc(0,1); |
|
|
} |
|
|
|
|
|
Vec4d kp = D.depth() == CV_32F ? (Vec4d)*D.getMat().ptr<Vec4f>() : *D.getMat().ptr<Vec4d>(); |
|
|
//Vec<double, 4> kp= (Vec<double,4>)*D.getMat().ptr<Vec<double,4> >(); |
|
|
|
|
|
const Vec3d* Xw_alld = objectPoints.getMat().ptr<Vec3d>(); |
|
|
const Vec3f* Xw_allf = objectPoints.getMat().ptr<Vec3f>(); |
|
|
Vec2d* xpd = imagePoints.getMat().ptr<Vec2d>(); |
|
|
Vec2f* xpf = imagePoints.getMat().ptr<Vec2f>(); |
|
|
|
|
|
Matx33d R; |
|
|
Matx<double, 3, 9> dRdom; |
|
|
Rodrigues(om, R, dRdom); |
|
|
|
|
|
JacobianRow *Jn = 0; |
|
|
if (jacobian.needed()) |
|
|
{ |
|
|
int nvars = 2+2+1+4+3+3+1; // f,c,s,kp,om,T,xi |
|
|
jacobian.create(2*int(n), nvars, CV_64F); |
|
|
Jn = jacobian.getMat().ptr<JacobianRow>(0); |
|
|
} |
|
|
|
|
|
double k1=kp[0],k2=kp[1]; |
|
|
double p1 = kp[2], p2 = kp[3]; |
|
|
|
|
|
for (int i = 0; i < n; i++) |
|
|
{ |
|
|
// convert to camera coordinate |
|
|
Vec3d Xw = objectPoints.depth() == CV_32F ? (Vec3d)Xw_allf[i] : Xw_alld[i]; |
|
|
|
|
|
Vec3d Xc = (Vec3d)(R*Xw + T); |
|
|
|
|
|
// convert to unit sphere |
|
|
Vec3d Xs = Xc/cv::norm(Xc); |
|
|
|
|
|
// convert to normalized image plane |
|
|
Vec2d xu = Vec2d(Xs[0]/(Xs[2]+xi), Xs[1]/(Xs[2]+xi)); |
|
|
|
|
|
// add distortion |
|
|
Vec2d xd; |
|
|
double r2 = xu[0]*xu[0]+xu[1]*xu[1]; |
|
|
double r4 = r2*r2; |
|
|
|
|
|
xd[0] = xu[0]*(1+k1*r2+k2*r4) + 2*p1*xu[0]*xu[1] + p2*(r2+2*xu[0]*xu[0]); |
|
|
xd[1] = xu[1]*(1+k1*r2+k2*r4) + p1*(r2+2*xu[1]*xu[1]) + 2*p2*xu[0]*xu[1]; |
|
|
|
|
|
// convert to pixel coordinate |
|
|
Vec2d final; |
|
|
final[0] = f[0]*xd[0]+s*xd[1]+c[0]; |
|
|
final[1] = f[1]*xd[1]+c[1]; |
|
|
|
|
|
if (objectPoints.depth() == CV_32F) |
|
|
{ |
|
|
xpf[i] = final; |
|
|
} |
|
|
else |
|
|
{ |
|
|
xpd[i] = final; |
|
|
} |
|
|
/*xpd[i][0] = f[0]*xd[0]+s*xd[1]+c[0]; |
|
|
xpd[i][1] = f[1]*xd[1]+c[1];*/ |
|
|
|
|
|
if (jacobian.needed()) |
|
|
{ |
|
|
double dXcdR_a[] = {Xw[0],Xw[1],Xw[2],0,0,0,0,0,0, |
|
|
0,0,0,Xw[0],Xw[1],Xw[2],0,0,0, |
|
|
0,0,0,0,0,0,Xw[0],Xw[1],Xw[2]}; |
|
|
Matx<double,3, 9> dXcdR(dXcdR_a); |
|
|
Matx33d dXcdom = dXcdR * dRdom.t(); |
|
|
double r_1 = 1.0/norm(Xc); |
|
|
double r_3 = pow(r_1,3); |
|
|
Matx33d dXsdXc(r_1-Xc[0]*Xc[0]*r_3, -(Xc[0]*Xc[1])*r_3, -(Xc[0]*Xc[2])*r_3, |
|
|
-(Xc[0]*Xc[1])*r_3, r_1-Xc[1]*Xc[1]*r_3, -(Xc[1]*Xc[2])*r_3, |
|
|
-(Xc[0]*Xc[2])*r_3, -(Xc[1]*Xc[2])*r_3, r_1-Xc[2]*Xc[2]*r_3); |
|
|
Matx23d dxudXs(1/(Xs[2]+xi), 0, -Xs[0]/(Xs[2]+xi)/(Xs[2]+xi), |
|
|
0, 1/(Xs[2]+xi), -Xs[1]/(Xs[2]+xi)/(Xs[2]+xi)); |
|
|
// pre-compute some reusable things |
|
|
double temp1 = 2*k1*xu[0] + 4*k2*xu[0]*r2; |
|
|
double temp2 = 2*k1*xu[1] + 4*k2*xu[1]*r2; |
|
|
Matx22d dxddxu(k2*r4+6*p2*xu[0]+2*p1*xu[1]+xu[0]*temp1+k1*r2+1, 2*p1*xu[0]+2*p2*xu[1]+xu[0]*temp2, |
|
|
2*p1*xu[0]+2*p2*xu[1]+xu[1]*temp1, k2*r4+2*p2*xu[0]+6*p1*xu[1]+xu[1]*temp2+k1*r2+1); |
|
|
Matx22d dxpddxd(f[0], s, |
|
|
0, f[1]); |
|
|
Matx23d dxpddXc = dxpddxd * dxddxu * dxudXs * dXsdXc; |
|
|
|
|
|
// derivative of xpd respect to om |
|
|
Matx23d dxpddom = dxpddXc * dXcdom; |
|
|
Matx33d dXcdT(1.0,0.0,0.0, |
|
|
0.0,1.0,0.0, |
|
|
0.0,0.0,1.0); |
|
|
// derivative of xpd respect to T |
|
|
|
|
|
Matx23d dxpddT = dxpddXc * dXcdT; |
|
|
Matx21d dxudxi(-Xs[0]/(Xs[2]+xi)/(Xs[2]+xi), |
|
|
-Xs[1]/(Xs[2]+xi)/(Xs[2]+xi)); |
|
|
|
|
|
// derivative of xpd respect to xi |
|
|
Matx21d dxpddxi = dxpddxd * dxddxu * dxudxi; |
|
|
Matx<double,2,4> dxddkp(xu[0]*r2, xu[0]*r4, 2*xu[0]*xu[1], r2+2*xu[0]*xu[0], |
|
|
xu[1]*r2, xu[1]*r4, r2+2*xu[1]*xu[1], 2*xu[0]*xu[1]); |
|
|
|
|
|
// derivative of xpd respect to kp |
|
|
Matx<double,2,4> dxpddkp = dxpddxd * dxddkp; |
|
|
|
|
|
// derivative of xpd respect to f |
|
|
Matx22d dxpddf(xd[0], 0, |
|
|
0, xd[1]); |
|
|
|
|
|
// derivative of xpd respect to c |
|
|
Matx22d dxpddc(1, 0, |
|
|
0, 1); |
|
|
|
|
|
Jn[0].dom = dxpddom.row(0); |
|
|
Jn[1].dom = dxpddom.row(1); |
|
|
Jn[0].dT = dxpddT.row(0); |
|
|
Jn[1].dT = dxpddT.row(1); |
|
|
Jn[0].dkp = dxpddkp.row(0); |
|
|
Jn[1].dkp = dxpddkp.row(1); |
|
|
Jn[0].dxi = dxpddxi(0,0); |
|
|
Jn[1].dxi = dxpddxi(1,0); |
|
|
Jn[0].df = dxpddf.row(0); |
|
|
Jn[1].df = dxpddf.row(1); |
|
|
Jn[0].dc = dxpddc.row(0); |
|
|
Jn[1].dc = dxpddc.row(1); |
|
|
Jn[0].ds = xd[1]; |
|
|
Jn[1].ds = 0; |
|
|
Jn += 2; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////// |
|
|
//////// undistortPoints |
|
|
void cv::omnidir::undistortPoints( InputArray distorted, OutputArray undistorted, |
|
|
InputArray K, InputArray D, InputArray xi, InputArray R) |
|
|
{ |
|
|
CV_Assert(distorted.type() == CV_64FC2 || distorted.type() == CV_32FC2); |
|
|
CV_Assert(R.empty() || (!R.empty() && (R.size() == Size(3, 3) || R.total() * R.channels() == 3) |
|
|
&& (R.depth() == CV_64F || R.depth() == CV_32F))); |
|
|
CV_Assert((D.depth() == CV_64F || D.depth() == CV_32F) && D.total() == 4); |
|
|
CV_Assert(K.size() == Size(3, 3) && (K.depth() == CV_64F || K.depth() == CV_32F)); |
|
|
CV_Assert(xi.total() == 1 && (xi.depth() == CV_64F || xi.depth() == CV_32F)); |
|
|
|
|
|
undistorted.create(distorted.size(), distorted.type()); |
|
|
|
|
|
cv::Vec2d f, c; |
|
|
double s = 0.0; |
|
|
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)); |
|
|
s = (double)camMat(0,1); |
|
|
} |
|
|
else if (K.depth() == CV_64F) |
|
|
{ |
|
|
Matx33d camMat = K.getMat(); |
|
|
f = Vec2d(camMat(0,0), camMat(1,1)); |
|
|
c = Vec2d(camMat(0,2), camMat(1,2)); |
|
|
s = camMat(0,1); |
|
|
} |
|
|
|
|
|
Vec4d kp = D.depth()==CV_32F ? (Vec4d)*D.getMat().ptr<Vec4f>():(Vec4d)*D.getMat().ptr<Vec4d>(); |
|
|
Vec2d k = Vec2d(kp[0], kp[1]); |
|
|
Vec2d p = Vec2d(kp[2], kp[3]); |
|
|
|
|
|
double _xi = xi.depth() == CV_32F ? (double)*xi.getMat().ptr<float>() : *xi.getMat().ptr<double>(); |
|
|
cv::Matx33d RR = cv::Matx33d::eye(); |
|
|
// R is om |
|
|
if(!R.empty() && R.total()*R.channels() == 3) |
|
|
{ |
|
|
cv::Vec3d rvec; |
|
|
R.getMat().convertTo(rvec, CV_64F); |
|
|
cv::Rodrigues(rvec, RR); |
|
|
} |
|
|
else if (!R.empty() && R.size() == Size(3,3)) |
|
|
{ |
|
|
R.getMat().convertTo(RR, CV_64F); |
|
|
} |
|
|
|
|
|
const cv::Vec2d *srcd = distorted.getMat().ptr<cv::Vec2d>(); |
|
|
const cv::Vec2f *srcf = distorted.getMat().ptr<cv::Vec2f>(); |
|
|
|
|
|
cv::Vec2d *dstd = undistorted.getMat().ptr<cv::Vec2d>(); |
|
|
cv::Vec2f *dstf = undistorted.getMat().ptr<cv::Vec2f>(); |
|
|
|
|
|
int n = (int)distorted.total(); |
|
|
for (int i = 0; i < n; i++) |
|
|
{ |
|
|
Vec2d pi = distorted.depth() == CV_32F ? (Vec2d)srcf[i]:(Vec2d)srcd[i]; // image point |
|
|
Vec2d pp((pi[0]*f[1]-c[0]*f[1]-s*(pi[1]-c[1]))/(f[0]*f[1]), (pi[1]-c[1])/f[1]); //plane |
|
|
Vec2d pu = pp; // points without distortion |
|
|
|
|
|
// remove distortion iteratively |
|
|
for (int j = 0; j < 20; j++) |
|
|
{ |
|
|
double r2 = pu[0]*pu[0] + pu[1]*pu[1]; |
|
|
double r4 = r2*r2; |
|
|
pu[0] = (pp[0] - 2*p[0]*pu[0]*pu[1] - p[1]*(r2+2*pu[0]*pu[0])) / (1 + k[0]*r2 + k[1]*r4); |
|
|
pu[1] = (pp[1] - 2*p[1]*pu[0]*pu[1] - p[0]*(r2+2*pu[1]*pu[1])) / (1 + k[0]*r2 + k[1]*r4); |
|
|
} |
|
|
|
|
|
// project to unit sphere |
|
|
double r2 = pu[0]*pu[0] + pu[1]*pu[1]; |
|
|
double a = (r2 + 1); |
|
|
double b = 2*_xi*r2; |
|
|
double cc = r2*_xi*_xi-1; |
|
|
double Zs = (-b + sqrt(b*b - 4*a*cc))/(2*a); |
|
|
Vec3d Xw = Vec3d(pu[0]*(Zs + _xi), pu[1]*(Zs +_xi), Zs); |
|
|
|
|
|
// rotate |
|
|
Xw = RR * Xw; |
|
|
|
|
|
// project back to sphere |
|
|
Vec3d Xs = Xw / cv::norm(Xw); |
|
|
|
|
|
// reproject to camera plane |
|
|
Vec3d ppu = Vec3d(Xs[0]/(Xs[2]+_xi), Xs[1]/(Xs[2]+_xi), 1.0); |
|
|
if (undistorted.depth() == CV_32F) |
|
|
{ |
|
|
dstf[i] = Vec2f((float)ppu[0], (float)ppu[1]); |
|
|
} |
|
|
else if (undistorted.depth() == CV_64F) |
|
|
{ |
|
|
dstd[i] = Vec2d(ppu[0], ppu[1]); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////// |
|
|
//////// cv::omnidir::initUndistortRectifyMap |
|
|
void cv::omnidir::initUndistortRectifyMap(InputArray K, InputArray D, InputArray xi, InputArray R, InputArray P, |
|
|
const cv::Size& size, int m1type, OutputArray map1, OutputArray map2, int flags) |
|
|
{ |
|
|
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(K.size() == Size(3, 3) && (D.empty() || D.total() == 4)); |
|
|
CV_Assert(P.empty()|| (P.depth() == CV_32F || P.depth() == CV_64F)); |
|
|
CV_Assert(P.empty() || P.size() == Size(3, 3) || P.size() == Size(4, 3)); |
|
|
CV_Assert(R.empty() || (R.depth() == CV_32F || R.depth() == CV_64F)); |
|
|
CV_Assert(R.empty() || R.size() == Size(3, 3) || R.total() * R.channels() == 3); |
|
|
CV_Assert(flags == RECTIFY_PERSPECTIVE || flags == RECTIFY_CYLINDRICAL || flags == RECTIFY_LONGLATI |
|
|
|| flags == RECTIFY_STEREOGRAPHIC); |
|
|
CV_Assert(xi.total() == 1 && (xi.depth() == CV_32F || xi.depth() == CV_64F)); |
|
|
|
|
|
cv::Vec2d f, c; |
|
|
double s; |
|
|
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)); |
|
|
s = (double)camMat(0,1); |
|
|
} |
|
|
else |
|
|
{ |
|
|
Matx33d camMat = K.getMat(); |
|
|
f = Vec2d(camMat(0, 0), camMat(1, 1)); |
|
|
c = Vec2d(camMat(0, 2), camMat(1, 2)); |
|
|
s = camMat(0,1); |
|
|
} |
|
|
|
|
|
Vec4d kp = Vec4d::all(0); |
|
|
if (!D.empty()) |
|
|
kp = D.depth() == CV_32F ? (Vec4d)*D.getMat().ptr<Vec4f>(): *D.getMat().ptr<Vec4d>(); |
|
|
double _xi = xi.depth() == CV_32F ? (double)*xi.getMat().ptr<float>() : *xi.getMat().ptr<double>(); |
|
|
Vec2d k = Vec2d(kp[0], kp[1]); |
|
|
Vec2d p = Vec2d(kp[2], kp[3]); |
|
|
cv::Matx33d RR = cv::Matx33d::eye(); |
|
|
if (!R.empty() && R.total() * R.channels() == 3) |
|
|
{ |
|
|
cv::Vec3d rvec; |
|
|
R.getMat().convertTo(rvec, CV_64F); |
|
|
cv::Rodrigues(rvec, RR); |
|
|
} |
|
|
else if (!R.empty() && R.size() == Size(3, 3)) |
|
|
R.getMat().convertTo(RR, CV_64F); |
|
|
|
|
|
cv::Matx33d PP = cv::Matx33d::eye(); |
|
|
if (!P.empty()) |
|
|
P.getMat().colRange(0, 3).convertTo(PP, CV_64F); |
|
|
else |
|
|
PP = K.getMat(); |
|
|
|
|
|
cv::Matx33d iKR = (PP*RR).inv(cv::DECOMP_SVD); |
|
|
cv::Matx33d iK = PP.inv(cv::DECOMP_SVD); |
|
|
cv::Matx33d iR = RR.inv(cv::DECOMP_SVD); |
|
|
|
|
|
if (flags == omnidir::RECTIFY_PERSPECTIVE) |
|
|
{ |
|
|
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*iKR(0, 1) + iKR(0, 2), |
|
|
_y = i*iKR(1, 1) + iKR(1, 2), |
|
|
_w = i*iKR(2, 1) + iKR(2, 2); |
|
|
for(int j = 0; j < size.width; ++j, _x+=iKR(0,0), _y+=iKR(1,0), _w+=iKR(2,0)) |
|
|
{ |
|
|
// project back to unit sphere |
|
|
double r = sqrt(_x*_x + _y*_y + _w*_w); |
|
|
double Xs = _x / r; |
|
|
double Ys = _y / r; |
|
|
double Zs = _w / r; |
|
|
// project to image plane |
|
|
double xu = Xs / (Zs + _xi), |
|
|
yu = Ys / (Zs + _xi); |
|
|
// add distortion |
|
|
double r2 = xu*xu + yu*yu; |
|
|
double r4 = r2*r2; |
|
|
double xd = (1+k[0]*r2+k[1]*r4)*xu + 2*p[0]*xu*yu + p[1]*(r2+2*xu*xu); |
|
|
double yd = (1+k[0]*r2+k[1]*r4)*yu + p[0]*(r2+2*yu*yu) + 2*p[1]*xu*yu; |
|
|
// to image pixel |
|
|
double u = f[0]*xd + s*yd + c[0]; |
|
|
double v = f[1]*yd + 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; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
else if(flags == omnidir::RECTIFY_CYLINDRICAL || flags == omnidir::RECTIFY_LONGLATI || |
|
|
flags == omnidir::RECTIFY_STEREOGRAPHIC) |
|
|
{ |
|
|
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; |
|
|
|
|
|
// for RECTIFY_LONGLATI, theta and h are longittude and latitude |
|
|
double theta = i*iK(0, 1) + iK(0, 2), |
|
|
h = i*iK(1, 1) + iK(1, 2); |
|
|
|
|
|
for (int j = 0; j < size.width; ++j, theta+=iK(0,0), h+=iK(1,0)) |
|
|
{ |
|
|
double _xt = 0.0, _yt = 0.0, _wt = 0.0; |
|
|
if (flags == omnidir::RECTIFY_CYLINDRICAL) |
|
|
{ |
|
|
//_xt = std::sin(theta); |
|
|
//_yt = h; |
|
|
//_wt = std::cos(theta); |
|
|
_xt = std::cos(theta); |
|
|
_yt = std::sin(theta); |
|
|
_wt = h; |
|
|
} |
|
|
else if (flags == omnidir::RECTIFY_LONGLATI) |
|
|
{ |
|
|
_xt = -std::cos(theta); |
|
|
_yt = -std::sin(theta) * std::cos(h); |
|
|
_wt = std::sin(theta) * std::sin(h); |
|
|
} |
|
|
else if (flags == omnidir::RECTIFY_STEREOGRAPHIC) |
|
|
{ |
|
|
double a = theta*theta + h*h + 4; |
|
|
double b = -2*theta*theta - 2*h*h; |
|
|
double c2 = theta*theta + h*h -4; |
|
|
|
|
|
_yt = (-b-std::sqrt(b*b - 4*a*c2))/(2*a); |
|
|
_xt = theta*(1 - _yt) / 2; |
|
|
_wt = h*(1 - _yt) / 2; |
|
|
} |
|
|
double _x = iR(0,0)*_xt + iR(0,1)*_yt + iR(0,2)*_wt; |
|
|
double _y = iR(1,0)*_xt + iR(1,1)*_yt + iR(1,2)*_wt; |
|
|
double _w = iR(2,0)*_xt + iR(2,1)*_yt + iR(2,2)*_wt; |
|
|
|
|
|
double r = sqrt(_x*_x + _y*_y + _w*_w); |
|
|
double Xs = _x / r; |
|
|
double Ys = _y / r; |
|
|
double Zs = _w / r; |
|
|
// project to image plane |
|
|
double xu = Xs / (Zs + _xi), |
|
|
yu = Ys / (Zs + _xi); |
|
|
// add distortion |
|
|
double r2 = xu*xu + yu*yu; |
|
|
double r4 = r2*r2; |
|
|
double xd = (1+k[0]*r2+k[1]*r4)*xu + 2*p[0]*xu*yu + p[1]*(r2+2*xu*xu); |
|
|
double yd = (1+k[0]*r2+k[1]*r4)*yu + p[0]*(r2+2*yu*yu) + 2*p[1]*xu*yu; |
|
|
// to image pixel |
|
|
double u = f[0]*xd + s*yd + c[0]; |
|
|
double v = f[1]*yd + 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; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
|
/// cv::omnidir::undistortImage |
|
|
|
|
|
void cv::omnidir::undistortImage(InputArray distorted, OutputArray undistorted, |
|
|
InputArray K, InputArray D, InputArray xi, int flags, InputArray Knew, const Size& new_size, InputArray R) |
|
|
{ |
|
|
Size size = new_size.area() != 0 ? new_size : distorted.size(); |
|
|
|
|
|
cv::Mat map1, map2; |
|
|
omnidir::initUndistortRectifyMap(K, D, xi, R, Knew, size, CV_16SC2, map1, map2, flags); |
|
|
cv::remap(distorted, undistorted, map1, map2, INTER_LINEAR, BORDER_CONSTANT); |
|
|
} |
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
|
/// cv::omnidir::internal::initializeCalibration |
|
|
|
|
|
void cv::omnidir::internal::initializeCalibration(InputArrayOfArrays patternPoints, InputArrayOfArrays imagePoints, Size size, |
|
|
OutputArrayOfArrays omAll, OutputArrayOfArrays tAll, OutputArray K, double& xi, OutputArray idx) |
|
|
{ |
|
|
// For details please refer to Section III from Li's IROS 2013 paper |
|
|
|
|
|
double u0 = size.width / 2; |
|
|
double v0 = size.height / 2; |
|
|
|
|
|
int n_img = (int)imagePoints.total(); |
|
|
|
|
|
std::vector<cv::Vec3d> v_omAll(n_img), v_tAll(n_img); |
|
|
|
|
|
std::vector<double> gammaAll(n_img); |
|
|
|
|
|
K.create(3, 3, CV_64F); |
|
|
Mat _K; |
|
|
for (int image_idx = 0; image_idx < n_img; ++image_idx) |
|
|
{ |
|
|
cv::Mat objPoints, imgPoints; |
|
|
patternPoints.getMat(image_idx).copyTo(objPoints); |
|
|
imagePoints.getMat(image_idx).copyTo(imgPoints); |
|
|
|
|
|
int n_point = imgPoints.rows * imgPoints.cols; |
|
|
if (objPoints.rows != n_point) |
|
|
objPoints = objPoints.reshape(3, n_point); |
|
|
if (imgPoints.rows != n_point) |
|
|
imgPoints = imgPoints.reshape(2, n_point); |
|
|
|
|
|
// objectPoints should be 3-channel data, imagePoints should be 2-channel data |
|
|
CV_Assert(objPoints.type() == CV_64FC3 && imgPoints.type() == CV_64FC2 ); |
|
|
|
|
|
std::vector<cv::Mat> xy, uv; |
|
|
cv::split(objPoints, xy); |
|
|
cv::split(imgPoints, uv); |
|
|
|
|
|
|
|
|
cv::Mat x = xy[0].reshape(1, n_point), y = xy[1].reshape(1, n_point), |
|
|
u = uv[0].reshape(1, n_point) - u0, v = uv[1].reshape(1, n_point) - v0; |
|
|
|
|
|
cv::Mat sqrRho = u.mul(u) + v.mul(v); |
|
|
// compute extrinsic parameters |
|
|
cv::Mat M(n_point, 6, CV_64F); |
|
|
Mat(-v.mul(x)).copyTo(M.col(0)); |
|
|
Mat(-v.mul(y)).copyTo(M.col(1)); |
|
|
Mat(u.mul(x)).copyTo(M.col(2)); |
|
|
Mat(u.mul(y)).copyTo(M.col(3)); |
|
|
Mat(-v).copyTo(M.col(4)); |
|
|
Mat(u).copyTo(M.col(5)); |
|
|
|
|
|
Mat W,U,V; |
|
|
cv::SVD::compute(M, W, U, V,SVD::FULL_UV); |
|
|
V = V.t(); |
|
|
|
|
|
double miniReprojectError = 1e5; |
|
|
// the signs of r1, r2, r3 are unknown, so they can be flipped. |
|
|
for (int coef = 1; coef >= -1; coef-=2) |
|
|
{ |
|
|
double r11 = V.at<double>(0, 5) * coef; |
|
|
double r12 = V.at<double>(1, 5) * coef; |
|
|
double r21 = V.at<double>(2, 5) * coef; |
|
|
double r22 = V.at<double>(3, 5) * coef; |
|
|
double t1 = V.at<double>(4, 5) * coef; |
|
|
double t2 = V.at<double>(5, 5) * coef; |
|
|
|
|
|
Mat roots; |
|
|
double r31s; |
|
|
solvePoly(Matx13d(-(r11*r12+r21*r22)*(r11*r12+r21*r22), r11*r11+r21*r21-r12*r12-r22*r22, 1), roots); |
|
|
|
|
|
if (roots.at<Vec2d>(0)[0] > 0) |
|
|
r31s = sqrt(roots.at<Vec2d>(0)[0]); |
|
|
else |
|
|
r31s = sqrt(roots.at<Vec2d>(1)[0]); |
|
|
|
|
|
for (int coef2 = 1; coef2 >= -1; coef2-=2) |
|
|
{ |
|
|
double r31 = r31s * coef2; |
|
|
double r32 = -(r11*r12 + r21*r22) / r31; |
|
|
|
|
|
cv::Vec3d r1(r11, r21, r31); |
|
|
cv::Vec3d r2(r12, r22, r32); |
|
|
cv::Vec3d t(t1, t2, 0); |
|
|
double scale = 1 / cv::norm(r1); |
|
|
r1 = r1 * scale; |
|
|
r2 = r2 * scale; |
|
|
t = t * scale; |
|
|
|
|
|
// compute intrisic parameters |
|
|
// Form equations in Scaramuzza's paper |
|
|
// A Toolbox for Easily Calibrating Omnidirectional Cameras |
|
|
Mat A(n_point*2, 3, CV_64F); |
|
|
Mat((r1[1]*x + r2[1]*y + t[1])/2).copyTo(A.rowRange(0, n_point).col(0)); |
|
|
Mat((r1[0]*x + r2[0]*y + t[0])/2).copyTo(A.rowRange(n_point, 2*n_point).col(0)); |
|
|
Mat(-A.col(0).rowRange(0, n_point).mul(sqrRho)).copyTo(A.col(1).rowRange(0, n_point)); |
|
|
Mat(-A.col(0).rowRange(n_point, 2*n_point).mul(sqrRho)).copyTo(A.col(1).rowRange(n_point, 2*n_point)); |
|
|
Mat(-v).copyTo(A.rowRange(0, n_point).col(2)); |
|
|
Mat(-u).copyTo(A.rowRange(n_point, 2*n_point).col(2)); |
|
|
|
|
|
// Operation to avoid bad numerical-condition of A |
|
|
Vec3d maxA, minA; |
|
|
for (int j = 0; j < A.cols; j++) |
|
|
{ |
|
|
cv::minMaxLoc(cv::abs(A.col(j)), &minA[j], &maxA[j]); |
|
|
A.col(j) = A.col(j) / maxA[j]; |
|
|
} |
|
|
|
|
|
Mat B(n_point*2 , 1, CV_64F); |
|
|
Mat(v.mul(r1[2]*x + r2[2]*y)).copyTo(B.rowRange(0, n_point)); |
|
|
Mat(u.mul(r1[2]*x + r2[2]*y)).copyTo(B.rowRange(n_point, 2*n_point)); |
|
|
|
|
|
Mat res = A.inv(DECOMP_SVD) * B; |
|
|
res = res.mul(1/Mat(maxA)); |
|
|
|
|
|
double gamma = sqrt(res.at<double>(0) / res.at<double>(1)); |
|
|
t[2] = res.at<double>(2); |
|
|
|
|
|
cv::Vec3d r3 = r1.cross(r2); |
|
|
|
|
|
Matx33d R(r1[0], r2[0], r3[0], |
|
|
r1[1], r2[1], r3[1], |
|
|
r1[2], r2[2], r3[2]); |
|
|
Vec3d om; |
|
|
Rodrigues(R, om); |
|
|
|
|
|
// project pattern points to images |
|
|
Mat projedImgPoints; |
|
|
Matx33d Kc(gamma, 0, u0, 0, gamma, v0, 0, 0, 1); |
|
|
|
|
|
// reproject error |
|
|
cv::omnidir::projectPoints(objPoints, projedImgPoints, om, t, Kc, 1, Matx14d(0, 0, 0, 0), cv::noArray()); |
|
|
double reprojectError = omnidir::internal::computeMeanReproErr(imgPoints, projedImgPoints); |
|
|
|
|
|
// if this reproject error is smaller |
|
|
if (reprojectError < miniReprojectError) |
|
|
{ |
|
|
miniReprojectError = reprojectError; |
|
|
v_omAll[image_idx] = om; |
|
|
v_tAll[image_idx] = t; |
|
|
gammaAll[image_idx] = gamma; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
// filter initial results whose reproject errors are too large |
|
|
std::vector<double> reProjErrorFilter,v_gammaFilter; |
|
|
std::vector<Vec3d> omFilter, tFilter; |
|
|
double gammaFinal = 0; |
|
|
|
|
|
// choose median value |
|
|
size_t n = gammaAll.size() / 2; |
|
|
std::nth_element(gammaAll.begin(), gammaAll.begin()+n, gammaAll.end()); |
|
|
gammaFinal = gammaAll[n]; |
|
|
|
|
|
_K = Mat(Matx33d(gammaFinal, 0, u0, 0, gammaFinal, v0, 0, 0, 1)); |
|
|
_K.convertTo(K, CV_64F); |
|
|
std::vector<int> _idx; |
|
|
// recompute reproject error using the final gamma |
|
|
for (int i = 0; i< n_img; i++) |
|
|
{ |
|
|
Mat _projected; |
|
|
cv::omnidir::projectPoints(patternPoints.getMat(i), _projected, v_omAll[i], v_tAll[i], _K, 1, Matx14d(0, 0, 0, 0), cv::noArray()); |
|
|
double _error = omnidir::internal::computeMeanReproErr(imagePoints.getMat(i), _projected); |
|
|
if(_error < 100) |
|
|
{ |
|
|
_idx.push_back(i); |
|
|
omFilter.push_back(v_omAll[i]); |
|
|
tFilter.push_back(v_tAll[i]); |
|
|
} |
|
|
} |
|
|
|
|
|
if (idx.needed()) |
|
|
{ |
|
|
idx.create(1, (int)_idx.size(), CV_32S); |
|
|
Mat idx_m = idx.getMat(); |
|
|
for (int j = 0; j < (int)idx_m.total(); j++) |
|
|
{ |
|
|
idx_m.at<int>(j) = _idx[j]; |
|
|
} |
|
|
} |
|
|
|
|
|
if(omAll.kind() == _InputArray::STD_VECTOR_MAT) |
|
|
{ |
|
|
for (int i = 0; i < (int)omFilter.size(); ++i) |
|
|
{ |
|
|
omAll.getMat(i) = Mat(omFilter[i]); |
|
|
tAll.getMat(i) = Mat(tFilter[i]); |
|
|
} |
|
|
} |
|
|
else |
|
|
{ |
|
|
cv::Mat(omFilter).convertTo(omAll, CV_64FC3); |
|
|
cv::Mat(tFilter).convertTo(tAll, CV_64FC3); |
|
|
} |
|
|
xi = 1; |
|
|
} |
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
|
/// cv::omnidir::internal::initializeStereoCalibration |
|
|
|
|
|
void cv::omnidir::internal::initializeStereoCalibration(InputArrayOfArrays objectPoints, InputArrayOfArrays imagePoints1, InputArrayOfArrays imagePoints2, |
|
|
const Size& size1, const Size& size2, OutputArray om, OutputArray T, OutputArrayOfArrays omL, OutputArrayOfArrays tL, OutputArray K1, OutputArray D1, OutputArray K2, OutputArray D2, |
|
|
double &xi1, double &xi2, int flags, OutputArray idx) |
|
|
{ |
|
|
Mat idx1, idx2; |
|
|
Matx33d _K1, _K2; |
|
|
Matx14d _D1, _D2; |
|
|
Mat _xi1m, _xi2m; |
|
|
|
|
|
std::vector<Vec3d> omAllTemp1, omAllTemp2, tAllTemp1, tAllTemp2; |
|
|
|
|
|
omnidir::calibrate(objectPoints, imagePoints1, size1, _K1, _xi1m, _D1, omAllTemp1, tAllTemp1, flags, TermCriteria(3, 100, 1e-6), idx1); |
|
|
omnidir::calibrate(objectPoints, imagePoints2, size2, _K2, _xi2m, _D2, omAllTemp2, tAllTemp2, flags, TermCriteria(3, 100, 1e-6), idx2); |
|
|
|
|
|
// find the intersection idx |
|
|
Mat interIdx1, interIdx2, interOri; |
|
|
|
|
|
getInterset(idx1, idx2, interIdx1, interIdx2, interOri); |
|
|
if (idx.empty()) |
|
|
idx.create(1, (int)interOri.total(), CV_32S); |
|
|
interOri.copyTo(idx.getMat()); |
|
|
|
|
|
int n_inter = (int)interIdx1.total(); |
|
|
|
|
|
std::vector<Vec3d> omAll1(n_inter), omAll2(n_inter), tAll1(n_inter), tAll2(n_inter); |
|
|
for(int i = 0; i < (int)interIdx1.total(); ++i) |
|
|
{ |
|
|
omAll1[i] = omAllTemp1[interIdx1.at<int>(i)]; |
|
|
tAll1[i] = tAllTemp1[interIdx1.at<int>(i)]; |
|
|
omAll2[i] = omAllTemp2[interIdx2.at<int>(i)]; |
|
|
tAll2[i] = tAllTemp2[interIdx2.at<int>(i)]; |
|
|
} |
|
|
|
|
|
// initialize R,T |
|
|
Mat omEstAll(1, n_inter, CV_64FC3), tEstAll(1, n_inter, CV_64FC3); |
|
|
Mat R1, R2, T1, T2, omLR, TLR, RLR; |
|
|
for (int i = 0; i < n_inter; ++i) |
|
|
{ |
|
|
Rodrigues(omAll1[i], R1); |
|
|
Rodrigues(omAll2[i], R2); |
|
|
T1 = Mat(tAll1[i]).reshape(1, 3); |
|
|
T2 = Mat(tAll2[i]).reshape(1, 3); |
|
|
RLR = R2 * R1.t(); |
|
|
TLR = T2 - RLR*T1; |
|
|
Rodrigues(RLR, omLR); |
|
|
omLR.reshape(3, 1).copyTo(omEstAll.col(i)); |
|
|
TLR.reshape(3, 1).copyTo(tEstAll.col(i)); |
|
|
} |
|
|
Vec3d omEst = internal::findMedian3(omEstAll); |
|
|
Vec3d tEst = internal::findMedian3(tEstAll); |
|
|
|
|
|
Mat(omEst).copyTo(om.getMat()); |
|
|
Mat(tEst).copyTo(T.getMat()); |
|
|
|
|
|
if (omL.empty()) |
|
|
{ |
|
|
omL.create((int)omAll1.size(), 1, CV_64FC3); |
|
|
} |
|
|
if (tL.empty()) |
|
|
{ |
|
|
tL.create((int)tAll1.size(), 1, CV_64FC3); |
|
|
} |
|
|
|
|
|
if(omL.kind() == _InputArray::STD_VECTOR_MAT) |
|
|
{ |
|
|
for(int i = 0; i < n_inter; ++i) |
|
|
{ |
|
|
omL.create(3, 1, CV_64F, i, true); |
|
|
tL.create(3, 1, CV_64F, i, true); |
|
|
omL.getMat(i) = Mat(omAll1[i]); |
|
|
tL.getMat(i) = Mat(tAll1[i]); |
|
|
} |
|
|
} |
|
|
else |
|
|
{ |
|
|
cv::Mat(omAll1).convertTo(omL, CV_64FC3); |
|
|
cv::Mat(tAll1).convertTo(tL, CV_64FC3); |
|
|
} |
|
|
if (K1.empty()) |
|
|
{ |
|
|
K1.create(3, 3, CV_64F); |
|
|
K2.create(3, 3, CV_64F); |
|
|
} |
|
|
if (D1.empty()) |
|
|
{ |
|
|
D1.create(1, 4, CV_64F); |
|
|
D2.create(1, 4, CV_64F); |
|
|
} |
|
|
Mat(_K1).copyTo(K1.getMat()); |
|
|
Mat(_K2).copyTo(K2.getMat()); |
|
|
|
|
|
Mat(_D1).copyTo(D1.getMat()); |
|
|
Mat(_D2).copyTo(D2.getMat()); |
|
|
|
|
|
xi1 = _xi1m.at<double>(0); |
|
|
xi2 = _xi2m.at<double>(0); |
|
|
} |
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
|
|
/// cv::omnidir::internal::computeJacobian |
|
|
|
|
|
void cv::omnidir::internal::computeJacobian(InputArrayOfArrays objectPoints, InputArrayOfArrays imagePoints, |
|
|
InputArray parameters, Mat& JTJ_inv, Mat& JTE, int flags, double epsilon) |
|
|
{ |
|
|
CV_Assert(!objectPoints.empty() && objectPoints.type() == CV_64FC3); |
|
|
CV_Assert(!imagePoints.empty() && imagePoints.type() == CV_64FC2); |
|
|
|
|
|
int n = (int)objectPoints.total(); |
|
|
|
|
|
Mat JTJ = Mat::zeros(10 + 6*n, 10 + 6*n, CV_64F); |
|
|
JTJ_inv = Mat::zeros(10 + 6*n, 10 + 6*n, CV_64F); |
|
|
JTE = Mat::zeros(10 + 6*n, 1, CV_64F); |
|
|
|
|
|
int nPointsAll = 0; |
|
|
for (int i = 0; i < n; ++i) |
|
|
{ |
|
|
nPointsAll += (int)objectPoints.getMat(i).total(); |
|
|
} |
|
|
|
|
|
Mat J = Mat::zeros(2*nPointsAll, 10+6*n, CV_64F); |
|
|
Mat exAll = Mat::zeros(2*nPointsAll, 10+6*n, CV_64F); |
|
|
double *para = parameters.getMat().ptr<double>(); |
|
|
Matx33d K(para[6*n], para[6*n+2], para[6*n+3], |
|
|
0, para[6*n+1], para[6*n+4], |
|
|
0, 0, 1); |
|
|
Matx14d D(para[6*n+6], para[6*n+7], para[6*n+8], para[6*n+9]); |
|
|
double xi = para[6*n+5]; |
|
|
for (int i = 0; i < n; i++) |
|
|
{ |
|
|
Mat objPoints, imgPoints, om, T; |
|
|
objectPoints.getMat(i).copyTo(objPoints); |
|
|
imagePoints.getMat(i).copyTo(imgPoints); |
|
|
objPoints = objPoints.reshape(3, objPoints.rows*objPoints.cols); |
|
|
imgPoints = imgPoints.reshape(2, imgPoints.rows*imgPoints.cols); |
|
|
|
|
|
om = parameters.getMat().colRange(i*6, i*6+3); |
|
|
T = parameters.getMat().colRange(i*6+3, (i+1)*6); |
|
|
Mat imgProj, jacobian; |
|
|
omnidir::projectPoints(objPoints, imgProj, om, T, K, xi, D, jacobian); |
|
|
Mat projError = imgPoints - imgProj; |
|
|
|
|
|
// The intrinsic part of Jacobian |
|
|
Mat JIn(jacobian.rows, 10, CV_64F); |
|
|
Mat JEx(jacobian.rows, 6, CV_64F); |
|
|
|
|
|
jacobian.colRange(6, 16).copyTo(JIn); |
|
|
jacobian.colRange(0, 6).copyTo(JEx); |
|
|
|
|
|
JTJ(Rect(6*n, 6*n, 10, 10)) = JTJ(Rect(6*n, 6*n, 10, 10)) + JIn.t()*JIn; |
|
|
|
|
|
JTJ(Rect(i*6, i*6, 6, 6)) = JEx.t() * JEx; |
|
|
|
|
|
Mat JExTIn = JEx.t() * JIn; |
|
|
|
|
|
JExTIn.copyTo(JTJ(Rect(6*n, i*6, 10, 6))); |
|
|
|
|
|
Mat(JIn.t()*JEx).copyTo(JTJ(Rect(i*6, 6*n, 6, 10))); |
|
|
|
|
|
JTE(Rect(0, 6*n, 1, 10)) = JTE(Rect(0, 6*n,1, 10)) + JIn.t() * projError.reshape(1, 2*(int)projError.total()); |
|
|
JTE(Rect(0, i*6, 1, 6)) = JEx.t() * projError.reshape(1, 2*(int)projError.total()); |
|
|
|
|
|
//int nPoints = objectPoints.getMat(i).total(); |
|
|
//JIn.copyTo(J(Rect(6*n, i*nPoints*2, 10, nPoints*2))); |
|
|
//JEx.copyTo(J(Rect(6*i, i*nPoints*2, 6, nPoints*2))); |
|
|
//projError.reshape(1, 2*projError.rows).copyTo(exAll.rowRange(i*2*nPoints, (i+1)*2*nPoints)); |
|
|
} |
|
|
//JTJ = J.t()*J; |
|
|
//JTE = J.t()*exAll; |
|
|
std::vector<int> _idx(6*n+10, 1); |
|
|
flags2idx(flags, _idx, n); |
|
|
|
|
|
subMatrix(JTJ, JTJ, _idx, _idx); |
|
|
subMatrix(JTE, JTE, std::vector<int>(1, 1), _idx); |
|
|
// in case JTJ is singular |
|
|
//SVD svd(JTJ, SVD::NO_UV); |
|
|
//double cond = svd.w.at<double>(0)/svd.w.at<double>(5); |
|
|
|
|
|
//if (cond_JTJ.needed()) |
|
|
//{ |
|
|
// cond_JTJ.create(1, 1, CV_64F); |
|
|
// cond_JTJ.getMat().at<double>(0) = cond; |
|
|
//} |
|
|
|
|
|
//double epsilon = 1e-4*std::exp(cond); |
|
|
JTJ_inv = Mat(JTJ+epsilon).inv(); |
|
|
} |
|
|
|
|
|
void cv::omnidir::internal::computeJacobianStereo(InputArrayOfArrays objectPoints, InputArrayOfArrays imagePoints1, InputArrayOfArrays imagePoints2, |
|
|
InputArray parameters, Mat& JTJ_inv, Mat& JTE, int flags, double epsilon) |
|
|
{ |
|
|
CV_Assert(!objectPoints.empty() && objectPoints.type() == CV_64FC3); |
|
|
CV_Assert(!imagePoints1.empty() && imagePoints1.type() == CV_64FC2); |
|
|
CV_Assert(!imagePoints2.empty() && imagePoints2.type() == CV_64FC2); |
|
|
CV_Assert((imagePoints1.total() == imagePoints2.total()) && (imagePoints1.total() == objectPoints.total())); |
|
|
|
|
|
// compute Jacobian matrix by naive way |
|
|
int n_img = (int)objectPoints.total(); |
|
|
int n_points = (int)objectPoints.getMat(0).total(); |
|
|
Mat J = Mat::zeros(4 * n_points * n_img, 20 + 6 * (n_img + 1), CV_64F); |
|
|
Mat exAll = Mat::zeros(4 * n_points * n_img, 1, CV_64F); |
|
|
double *para = parameters.getMat().ptr<double>(); |
|
|
int offset1 = (n_img + 1) * 6; |
|
|
int offset2 = offset1 + 10; |
|
|
Matx33d K1(para[offset1], para[offset1+2], para[offset1+3], |
|
|
0, para[offset1+1], para[offset1+4], |
|
|
0, 0, 1); |
|
|
Matx14d D1(para[offset1+6], para[offset1+7], para[offset1+8], para[offset1+9]); |
|
|
double xi1 = para[offset1+5]; |
|
|
|
|
|
Matx33d K2(para[offset2], para[offset2+2], para[offset2+3], |
|
|
0, para[offset2+1], para[offset2+4], |
|
|
0, 0, 1); |
|
|
Matx14d D2(para[offset2+6], para[offset2+7], para[offset2+8], para[offset2+9]); |
|
|
double xi2 = para[offset2+5]; |
|
|
|
|
|
Mat om = parameters.getMat().reshape(1, 1).colRange(0, 3); |
|
|
Mat T = parameters.getMat().reshape(1, 1).colRange(3, 6); |
|
|
|
|
|
for (int i = 0; i < n_img; i++) |
|
|
{ |
|
|
Mat objPointsi, imgPoints1i, imgPoints2i, om1, T1; |
|
|
objectPoints.getMat(i).copyTo(objPointsi); |
|
|
imagePoints1.getMat(i).copyTo(imgPoints1i); |
|
|
imagePoints2.getMat(i).copyTo(imgPoints2i); |
|
|
objPointsi = objPointsi.reshape(3, objPointsi.rows*objPointsi.cols); |
|
|
imgPoints1i = imgPoints1i.reshape(2, imgPoints1i.rows*imgPoints1i.cols); |
|
|
imgPoints2i = imgPoints2i.reshape(2, imgPoints2i.rows*imgPoints2i.cols); |
|
|
|
|
|
om1 = parameters.getMat().reshape(1, 1).colRange((1 + i) * 6, (1 + i) * 6 + 3); |
|
|
T1 = parameters.getMat().reshape(1, 1).colRange((1 + i) * 6 + 3, (i + 1) * 6 + 6); |
|
|
|
|
|
|
|
|
Mat imgProj1, imgProj2, jacobian1, jacobian2; |
|
|
|
|
|
// jacobian for left image |
|
|
cv::omnidir::projectPoints(objPointsi, imgProj1, om1, T1, K1, xi1, D1, jacobian1); |
|
|
Mat projError1 = imgPoints1i - imgProj1; |
|
|
//Mat JIn1(jacobian1.rows, 10, CV_64F); |
|
|
//Mat JEx1(jacobian1.rows, 6, CV_64F); |
|
|
jacobian1.colRange(6, 16).copyTo(J(Rect(6*(n_img+1), i*n_points*4, 10, n_points*2))); |
|
|
jacobian1.colRange(0, 6).copyTo(J(Rect(6+i*6, i*n_points*4, 6, n_points*2))); |
|
|
projError1.reshape(1, 2*n_points).copyTo(exAll.rowRange(i*4*n_points, (i*4+2)*n_points)); |
|
|
|
|
|
//jacobian for right image |
|
|
Mat om2, T2, dom2dom1, dom2dT1, dom2dom, dom2dT, dT2dom1, dT2dT1, dT2dom, dT2dT; |
|
|
cv::omnidir::internal::compose_motion(om1, T1, om, T, om2, T2, dom2dom1, dom2dT1, dom2dom, dom2dT, dT2dom1, dT2dT1, dT2dom, dT2dT); |
|
|
cv::omnidir::projectPoints(objPointsi, imgProj2, om2, T2, K2, xi2, D2, jacobian2); |
|
|
Mat projError2 = imgPoints2i - imgProj2; |
|
|
projError2.reshape(1, 2*n_points).copyTo(exAll.rowRange((i*4+2)*n_points, (i*4+4)*n_points)); |
|
|
Mat dxrdom = jacobian2.colRange(0, 3) * dom2dom + jacobian2.colRange(3, 6) * dT2dom; |
|
|
Mat dxrdT = jacobian2.colRange(0, 3) * dom2dT + jacobian2.colRange(3, 6) * dT2dT; |
|
|
Mat dxrdom1 = jacobian2.colRange(0, 3) * dom2dom1 + jacobian2.colRange(3, 6) * dT2dom1; |
|
|
Mat dxrdT1 = jacobian2.colRange(0, 3) * dom2dT1 + jacobian2.colRange(3, 6) * dT2dT1; |
|
|
|
|
|
dxrdom.copyTo(J(Rect(0, (i*4+2)*n_points, 3, n_points*2))); |
|
|
dxrdT.copyTo(J(Rect(3, (i*4+2)*n_points, 3, n_points*2))); |
|
|
dxrdom1.copyTo(J(Rect(6+i*6, (i*4+2)*n_points, 3, n_points*2))); |
|
|
dxrdT1.copyTo(J(Rect(6+i*6+3, (i*4+2)*n_points, 3, n_points*2))); |
|
|
jacobian2.colRange(6, 16).copyTo(J(Rect(6*(n_img+1)+10, (4*i+2)*n_points, 10, n_points*2))); |
|
|
} |
|
|
|
|
|
std::vector<int> _idx(6*(n_img+1)+20, 1); |
|
|
flags2idxStereo(flags, _idx, n_img); |
|
|
|
|
|
Mat JTJ = J.t()*J; |
|
|
JTE = J.t()*exAll; |
|
|
subMatrix(JTJ, JTJ, _idx, _idx); |
|
|
subMatrix(JTE, JTE, std::vector<int>(1, 1), _idx); |
|
|
|
|
|
JTJ_inv = Mat(JTJ+epsilon).inv(); |
|
|
} |
|
|
|
|
|
// This function is from fisheye.cpp |
|
|
void cv::omnidir::internal::compose_motion(InputArray _om1, InputArray _T1, InputArray _om2, InputArray _T2, Mat& om3, Mat& T3, Mat& dom3dom1, |
|
|
Mat& dom3dT1, Mat& dom3dom2, Mat& dom3dT2, Mat& dT3dom1, Mat& dT3dT1, Mat& dT3dom2, Mat& dT3dT2) |
|
|
{ |
|
|
Mat om1 = _om1.getMat(); |
|
|
Mat om2 = _om2.getMat(); |
|
|
Mat T1 = _T1.getMat().reshape(1, 3); |
|
|
Mat T2 = _T2.getMat().reshape(1, 3); |
|
|
|
|
|
//% Rotations: |
|
|
Mat R1, R2, R3, dR1dom1(9, 3, CV_64FC1), dR2dom2; |
|
|
Rodrigues(om1, R1, dR1dom1); |
|
|
Rodrigues(om2, R2, dR2dom2); |
|
|
//JRodriguesMatlab(dR1dom1, dR1dom1); |
|
|
//JRodriguesMatlab(dR2dom2, dR2dom2); |
|
|
dR1dom1 = dR1dom1.t(); |
|
|
dR2dom2 = dR2dom2.t(); |
|
|
|
|
|
R3 = R2 * R1; |
|
|
Mat dR3dR2, dR3dR1; |
|
|
//dAB(R2, R1, dR3dR2, dR3dR1); |
|
|
matMulDeriv(R2, R1, dR3dR2, dR3dR1); |
|
|
|
|
|
Mat dom3dR3; |
|
|
Rodrigues(R3, om3, dom3dR3); |
|
|
//JRodriguesMatlab(dom3dR3, dom3dR3); |
|
|
dom3dR3 = dom3dR3.t(); |
|
|
dom3dom1 = dom3dR3 * dR3dR1 * dR1dom1; |
|
|
dom3dom2 = dom3dR3 * dR3dR2 * dR2dom2; |
|
|
dom3dT1 = Mat::zeros(3, 3, CV_64FC1); |
|
|
dom3dT2 = Mat::zeros(3, 3, CV_64FC1); |
|
|
|
|
|
//% Translations: |
|
|
Mat T3t = R2 * T1; |
|
|
Mat dT3tdR2, dT3tdT1; |
|
|
//dAB(R2, T1, dT3tdR2, dT3tdT1); |
|
|
matMulDeriv(R2, T1, dT3tdR2, dT3tdT1); |
|
|
Mat dT3tdom2 = dT3tdR2 * dR2dom2; |
|
|
T3 = T3t + T2; |
|
|
dT3dT1 = dT3tdT1; |
|
|
dT3dT2 = Mat::eye(3, 3, CV_64FC1); |
|
|
dT3dom2 = dT3tdom2; |
|
|
dT3dom1 = Mat::zeros(3, 3, CV_64FC1); |
|
|
} |
|
|
|
|
|
double cv::omnidir::calibrate(InputArrayOfArrays patternPoints, InputArrayOfArrays imagePoints, Size size, |
|
|
InputOutputArray K, InputOutputArray xi, InputOutputArray D, OutputArrayOfArrays omAll, OutputArrayOfArrays tAll, |
|
|
int flags, TermCriteria criteria, OutputArray idx) |
|
|
{ |
|
|
CV_Assert(!patternPoints.empty() && !imagePoints.empty() && patternPoints.total() == imagePoints.total()); |
|
|
CV_Assert((patternPoints.type() == CV_64FC3 && imagePoints.type() == CV_64FC2) || |
|
|
(patternPoints.type() == CV_32FC3 && imagePoints.type() == CV_32FC2)); |
|
|
CV_Assert(patternPoints.getMat(0).channels() == 3 && imagePoints.getMat(0).channels() == 2); |
|
|
CV_Assert((!K.empty() && K.size() == Size(3,3)) || K.empty()); |
|
|
CV_Assert((!D.empty() && D.total() == 4) || D.empty()); |
|
|
CV_Assert((!xi.empty() && xi.total() == 1) || xi.empty()); |
|
|
CV_Assert((!omAll.empty() && omAll.depth() == patternPoints.depth()) || omAll.empty()); |
|
|
CV_Assert((!tAll.empty() && tAll.depth() == patternPoints.depth()) || tAll.empty()); |
|
|
int depth = patternPoints.depth(); |
|
|
|
|
|
std::vector<Mat> _patternPoints, _imagePoints; |
|
|
|
|
|
for (int i = 0; i < (int)patternPoints.total(); ++i) |
|
|
{ |
|
|
_patternPoints.push_back(patternPoints.getMat(i)); |
|
|
_imagePoints.push_back(imagePoints.getMat(i)); |
|
|
if (depth == CV_32F) |
|
|
{ |
|
|
_patternPoints[i].convertTo(_patternPoints[i], CV_64FC3); |
|
|
_imagePoints[i].convertTo(_imagePoints[i], CV_64FC2); |
|
|
} |
|
|
} |
|
|
|
|
|
double _xi; |
|
|
// initialization |
|
|
std::vector<Vec3d> _omAll, _tAll; |
|
|
Matx33d _K; |
|
|
Matx14d _D; |
|
|
Mat _idx; |
|
|
cv::omnidir::internal::initializeCalibration(_patternPoints, _imagePoints, size, _omAll, _tAll, _K, _xi, _idx); |
|
|
std::vector<Mat> _patternPointsTmp = _patternPoints; |
|
|
std::vector<Mat> _imagePointsTmp = _imagePoints; |
|
|
|
|
|
_patternPoints.clear(); |
|
|
_imagePoints.clear(); |
|
|
// erase |
|
|
for (int i = 0; i < (int)_idx.total(); i++) |
|
|
{ |
|
|
_patternPoints.push_back(_patternPointsTmp[_idx.at<int>(i)]); |
|
|
_imagePoints.push_back(_imagePointsTmp[_idx.at<int>(i)]); |
|
|
} |
|
|
|
|
|
int n = (int)_patternPoints.size(); |
|
|
Mat finalParam(1, 10 + 6*n, CV_64F); |
|
|
Mat currentParam(1, 10 + 6*n, CV_64F); |
|
|
cv::omnidir::internal::encodeParameters(_K, _omAll, _tAll, Mat::zeros(1,4,CV_64F), _xi, currentParam); |
|
|
|
|
|
// optimization |
|
|
const double alpha_smooth = 0.01; |
|
|
//const double thresh_cond = 1e6; |
|
|
double change = 1; |
|
|
for(int iter = 0; ; ++iter) |
|
|
{ |
|
|
if ((criteria.type == 1 && iter >= criteria.maxCount) || |
|
|
(criteria.type == 2 && change <= criteria.epsilon) || |
|
|
(criteria.type == 3 && (change <= criteria.epsilon || iter >= criteria.maxCount))) |
|
|
break; |
|
|
double alpha_smooth2 = 1 - std::pow(1 - alpha_smooth, (double)iter + 1.0); |
|
|
Mat JTJ_inv, JTError; |
|
|
double epsilon = 0.01 * std::pow(0.9, (double)iter/10); |
|
|
cv::omnidir::internal::computeJacobian(_patternPoints, _imagePoints, currentParam, JTJ_inv, JTError, flags, epsilon); |
|
|
|
|
|
// Gauss<EFBFBD>CNewton |
|
|
Mat G = alpha_smooth2*JTJ_inv * JTError; |
|
|
|
|
|
omnidir::internal::fillFixed(G, flags, n); |
|
|
|
|
|
finalParam = currentParam + G.t(); |
|
|
|
|
|
change = norm(G) / norm(currentParam); |
|
|
|
|
|
currentParam = finalParam.clone(); |
|
|
|
|
|
cv::omnidir::internal::decodeParameters(currentParam, _K, _omAll, _tAll, _D, _xi); |
|
|
//double repr = internal::computeMeanReproErr(_patternPoints, _imagePoints, _K, _D, _xi, _omAll, _tAll); |
|
|
} |
|
|
cv::omnidir::internal::decodeParameters(currentParam, _K, _omAll, _tAll, _D, _xi); |
|
|
|
|
|
//double repr = internal::computeMeanReproErr(_patternPoints, _imagePoints, _K, _D, _xi, _omAll, _tAll); |
|
|
|
|
|
if (omAll.needed()) |
|
|
{ |
|
|
omAll.create((int)_omAll.size(), 1, CV_64FC3); |
|
|
} |
|
|
if (tAll.needed()) |
|
|
{ |
|
|
tAll.create((int)_tAll.size(), 1, CV_64FC3); |
|
|
} |
|
|
if (omAll.kind() == _InputArray::STD_VECTOR_MAT) |
|
|
{ |
|
|
for (int i = 0; i < n; ++i) |
|
|
{ |
|
|
omAll.create(3, 1, CV_64F, i, true); |
|
|
tAll.create(3, 1, CV_64F, i, true); |
|
|
Mat tmpom = Mat(_omAll[i]); |
|
|
Mat tmpt = Mat(_tAll[i]); |
|
|
tmpom.convertTo(tmpom, CV_64F); |
|
|
tmpt.convertTo(tmpt, CV_64F); |
|
|
tmpom.copyTo(omAll.getMat(i)); |
|
|
tmpt.copyTo(tAll.getMat(i)); |
|
|
} |
|
|
} |
|
|
else |
|
|
{ |
|
|
Mat(_omAll).convertTo(omAll, CV_64FC3); |
|
|
Mat(_tAll).convertTo(tAll, CV_64FC3); |
|
|
} |
|
|
|
|
|
if(K.empty()) |
|
|
{ |
|
|
K.create(3, 3, CV_64F); |
|
|
} |
|
|
if (D.empty()) |
|
|
{ |
|
|
D.create(1, 4, CV_64F); |
|
|
} |
|
|
|
|
|
Mat(_K).convertTo(K.getMat(), K.empty()? CV_64F : K.type()); |
|
|
Mat(_D).convertTo(D.getMat(), D.empty() ? CV_64F: D.type()); |
|
|
|
|
|
if (xi.empty()) |
|
|
{ |
|
|
xi.create(1, 1, CV_64F); |
|
|
} |
|
|
Mat xi_m = Mat(1, 1, CV_64F); |
|
|
xi_m.at<double>(0) = _xi; |
|
|
xi_m.convertTo(xi.getMat(), xi.empty() ? CV_64F : xi.type()); |
|
|
|
|
|
if (idx.needed()) |
|
|
{ |
|
|
idx.create(1, (int)_idx.total(), CV_32S); |
|
|
_idx.copyTo(idx.getMat()); |
|
|
} |
|
|
|
|
|
Vec2d std_error; |
|
|
double rms; |
|
|
Mat errors; |
|
|
cv::omnidir::internal::estimateUncertainties(_patternPoints, _imagePoints, finalParam, errors, std_error, rms, flags); |
|
|
return rms; |
|
|
} |
|
|
|
|
|
double cv::omnidir::stereoCalibrate(InputOutputArrayOfArrays objectPoints, InputOutputArrayOfArrays imagePoints1, InputOutputArrayOfArrays imagePoints2, |
|
|
const Size& imageSize1, const Size& imageSize2, InputOutputArray K1, InputOutputArray xi1, InputOutputArray D1, InputOutputArray K2, InputOutputArray xi2, |
|
|
InputOutputArray D2, OutputArray om, OutputArray T, OutputArrayOfArrays omL, OutputArrayOfArrays tL, int flags, TermCriteria criteria, OutputArray idx) |
|
|
{ |
|
|
CV_Assert(!objectPoints.empty() && (objectPoints.type() == CV_64FC3 || objectPoints.type() == CV_32FC3)); |
|
|
CV_Assert(!imagePoints1.empty() && (imagePoints1.type() == CV_64FC2 || imagePoints1.type() == CV_32FC2)); |
|
|
CV_Assert(!imagePoints2.empty() && (imagePoints2.type() == CV_64FC2 || imagePoints2.type() == CV_32FC2)); |
|
|
|
|
|
CV_Assert(((flags & CALIB_USE_GUESS) && !K1.empty() && !D1.empty() && !K2.empty() && !D2.empty()) || !(flags & CALIB_USE_GUESS)); |
|
|
|
|
|
int depth = objectPoints.depth(); |
|
|
|
|
|
std::vector<Mat> _objectPoints, _imagePoints1, _imagePoints2, |
|
|
_objectPointsFilt, _imagePoints1Filt, _imagePoints2Filt; |
|
|
for (int i = 0; i < (int)objectPoints.total(); ++i) |
|
|
{ |
|
|
_objectPoints.push_back(objectPoints.getMat(i)); |
|
|
_imagePoints1.push_back(imagePoints1.getMat(i)); |
|
|
_imagePoints2.push_back(imagePoints2.getMat(i)); |
|
|
if (depth == CV_32F) |
|
|
{ |
|
|
_objectPoints[i].convertTo(_objectPoints[i], CV_64FC3); |
|
|
_imagePoints1[i].convertTo(_imagePoints1[i], CV_64FC2); |
|
|
_imagePoints2[i].convertTo(_imagePoints2[i], CV_64FC2); |
|
|
} |
|
|
} |
|
|
|
|
|
Matx33d _K1, _K2; |
|
|
Matx14d _D1, _D2; |
|
|
|
|
|
double _xi1, _xi2; |
|
|
|
|
|
std::vector<Vec3d> _omL, _TL; |
|
|
Vec3d _om, _T; |
|
|
|
|
|
// initializaition |
|
|
Mat _idx; |
|
|
internal::initializeStereoCalibration(_objectPoints, _imagePoints1, _imagePoints2, imageSize1, imageSize2, _om, _T, _omL, _TL, _K1, _D1, _K2, _D2, _xi1, _xi2, flags, _idx); |
|
|
if(idx.needed()) |
|
|
{ |
|
|
idx.create(1, (int)_idx.total(), CV_32S); |
|
|
_idx.copyTo(idx.getMat()); |
|
|
} |
|
|
for (int i = 0; i < (int)_idx.total(); ++i) |
|
|
{ |
|
|
_objectPointsFilt.push_back(_objectPoints[_idx.at<int>(i)]); |
|
|
_imagePoints1Filt.push_back(_imagePoints1[_idx.at<int>(i)]); |
|
|
_imagePoints2Filt.push_back(_imagePoints2[_idx.at<int>(i)]); |
|
|
} |
|
|
|
|
|
int n = (int)_objectPointsFilt.size(); |
|
|
Mat finalParam(1, 10 + 6*n, CV_64F); |
|
|
Mat currentParam(1, 10 + 6*n, CV_64F); |
|
|
|
|
|
//double repr1 = internal::computeMeanReproErrStereo(_objectPoints, _imagePoints1, _imagePoints2, _K1, _K2, _D1, _D2, _xi1, _xi2, _om, |
|
|
// _T, _omL, _TL); |
|
|
cv::omnidir::internal::encodeParametersStereo(_K1, _K2, _om, _T, _omL, _TL, _D1, _D2, _xi1, _xi2, currentParam); |
|
|
|
|
|
// optimization |
|
|
const double alpha_smooth = 0.01; |
|
|
double change = 1; |
|
|
for(int iter = 0; ; ++iter) |
|
|
{ |
|
|
if ((criteria.type == 1 && iter >= criteria.maxCount) || |
|
|
(criteria.type == 2 && change <= criteria.epsilon) || |
|
|
(criteria.type == 3 && (change <= criteria.epsilon || iter >= criteria.maxCount))) |
|
|
break; |
|
|
double alpha_smooth2 = 1 - std::pow(1 - alpha_smooth, (double)iter + 1.0); |
|
|
Mat JTJ_inv, JTError; |
|
|
double epsilon = 0.01 * std::pow(0.9, (double)iter/10); |
|
|
|
|
|
cv::omnidir::internal::computeJacobianStereo(_objectPointsFilt, _imagePoints1Filt, _imagePoints2Filt, currentParam, |
|
|
JTJ_inv, JTError, flags, epsilon); |
|
|
|
|
|
// Gauss<EFBFBD>CNewton |
|
|
Mat G = alpha_smooth2*JTJ_inv * JTError; |
|
|
|
|
|
omnidir::internal::fillFixedStereo(G, flags, n); |
|
|
|
|
|
finalParam = currentParam + G.t(); |
|
|
|
|
|
change = norm(G) / norm(currentParam); |
|
|
|
|
|
currentParam = finalParam.clone(); |
|
|
cv::omnidir::internal::decodeParametersStereo(currentParam, _K1, _K2, _om, _T, _omL, _TL, _D1, _D2, _xi1, _xi2); |
|
|
//double repr = internal::computeMeanReproErrStereo(_objectPoints, _imagePoints1, _imagePoints2, _K1, _K2, _D1, _D2, _xi1, _xi2, _om, |
|
|
// _T, _omL, _TL); |
|
|
|
|
|
} |
|
|
cv::omnidir::internal::decodeParametersStereo(finalParam, _K1, _K2, _om, _T, _omL, _TL, _D1, _D2, _xi1, _xi2); |
|
|
//double repr = internal::computeMeanReproErrStereo(_objectPoints, _imagePoints1, _imagePoints2, _K1, _K2, _D1, _D2, _xi1, _xi2, _om, |
|
|
// _T, _omL, _TL); |
|
|
|
|
|
if (K1.empty()) |
|
|
{ |
|
|
K1.create(3, 3, CV_64F); |
|
|
D1.create(1, 4, CV_64F); |
|
|
K2.create(3, 3, CV_64F); |
|
|
D2.create(1, 4, CV_64F); |
|
|
} |
|
|
if (om.empty()) |
|
|
{ |
|
|
om.create(3, 1, CV_64F); |
|
|
T.create(3, 1, CV_64F); |
|
|
} |
|
|
if (omL.empty()) |
|
|
{ |
|
|
omL.create(1, n, CV_64FC3); |
|
|
tL.create(1, n, CV_64FC3); |
|
|
} |
|
|
|
|
|
Mat(_K1).convertTo(K1.getMat(), K1.empty() ? CV_64F : K1.type()); |
|
|
Mat(_D1).convertTo(D1.getMat(), D1.empty() ? CV_64F : D1.type()); |
|
|
Mat(_K2).convertTo(K2.getMat(), K2.empty() ? CV_64F : K2.type()); |
|
|
Mat(_D2).convertTo(D2.getMat(), D2.empty() ? CV_64F : D2.type()); |
|
|
|
|
|
Mat(_om).convertTo(om.getMat(), om.empty() ? CV_64F: om.type()); |
|
|
Mat(_T).convertTo(T.getMat(), T.empty() ? CV_64F: T.type()); |
|
|
|
|
|
if (omL.needed()) |
|
|
{ |
|
|
omL.create((int)_omL.size(), 1, CV_64FC3); |
|
|
} |
|
|
if (tL.needed()) |
|
|
{ |
|
|
tL.create((int)_TL.size(), 1, CV_64FC3); |
|
|
} |
|
|
|
|
|
if (omL.kind() == _InputArray::STD_VECTOR_MAT) |
|
|
{ |
|
|
for (int i = 0; i < n; ++i) |
|
|
{ |
|
|
omL.create(3, 1, CV_64F, i, true); |
|
|
tL.create(3, 1, CV_64F, i, true); |
|
|
Mat(_omL[i]).copyTo(omL.getMat(i)); |
|
|
Mat(_TL[i]).copyTo(tL.getMat(i)); |
|
|
} |
|
|
} |
|
|
else |
|
|
{ |
|
|
Mat(_omL).convertTo(omL, omL.empty() ? CV_64FC3 : omL.type()); |
|
|
Mat(_TL).convertTo(tL, tL.empty() ? CV_64FC3 : tL.type()); |
|
|
} |
|
|
|
|
|
Mat xi1_m = Mat(1, 1, CV_64F), |
|
|
xi2_m = Mat(1, 1, CV_64F); |
|
|
xi1_m.at<double>(0) = _xi1; |
|
|
xi2_m.at<double>(0) = _xi2; |
|
|
|
|
|
if (xi1.empty()) |
|
|
{ |
|
|
xi1.create(1, 1, CV_64F); |
|
|
} |
|
|
if (xi2.empty()) |
|
|
{ |
|
|
xi2.create(1, 1, CV_64F); |
|
|
} |
|
|
xi1_m.convertTo(xi1, xi1.empty() ? CV_64F : xi1.type()); |
|
|
xi2_m.convertTo(xi2, xi2.empty() ? CV_64F : xi2.type()); |
|
|
|
|
|
// compute uncertainty |
|
|
Vec2d std_error; |
|
|
double rms; |
|
|
Mat errors; |
|
|
|
|
|
cv::omnidir::internal::estimateUncertaintiesStereo(_objectPointsFilt, _imagePoints1Filt, _imagePoints2Filt, |
|
|
finalParam, errors, std_error, rms, flags); |
|
|
return rms; |
|
|
} |
|
|
|
|
|
void cv::omnidir::stereoReconstruct(InputArray image1, InputArray image2, InputArray K1, InputArray D1, |
|
|
InputArray xi1, InputArray K2, InputArray D2, InputArray xi2, InputArray R, InputArray T, int flag, |
|
|
int numDisparities, int SADWindowSize, OutputArray disparity, OutputArray image1Rec, OutputArray image2Rec, |
|
|
const Size& newSize, InputArray Knew, OutputArray pointCloud, int pointType) |
|
|
{ |
|
|
CV_Assert(!K1.empty() && K1.size() == Size(3,3) && (K1.type() == CV_64F || K1.type() == CV_32F)); |
|
|
CV_Assert(!K2.empty() && K2.size() == Size(3,3) && (K2.type() == CV_64F || K2.type() == CV_32F)); |
|
|
CV_Assert(!D1.empty() && D1.total() == 4 && (D1.type() == CV_64F || D1.type() == CV_32F)); |
|
|
CV_Assert(!D2.empty() && D2.total() == 4 && (D2.type() == CV_64F || D2.type() == CV_32F)); |
|
|
CV_Assert(!R.empty() && (R.size() == Size(3,3) || R.total() == 3) && (R.type() == CV_64F || R.type() == CV_32F)); |
|
|
CV_Assert(!T.empty() && T.total() == 3 && (T.type() == CV_64F || T.type() == CV_32F)); |
|
|
CV_Assert(!image1.empty() && (image1.type() == CV_8U || image1.type() == CV_8UC3)); |
|
|
CV_Assert(!image2.empty() && (image2.type() == CV_8U || image2.type() == CV_8UC3)); |
|
|
CV_Assert(flag == omnidir::RECTIFY_LONGLATI || flag == omnidir::RECTIFY_PERSPECTIVE); |
|
|
|
|
|
Mat _K1, _D1, _K2, _D2, _R, _T; |
|
|
|
|
|
K1.getMat().convertTo(_K1, CV_64F); |
|
|
K2.getMat().convertTo(_K2, CV_64F); |
|
|
D1.getMat().convertTo(_D1, CV_64F); |
|
|
D2.getMat().convertTo(_D2, CV_64F); |
|
|
T.getMat().reshape(1, 3).convertTo(_T, CV_64F); |
|
|
|
|
|
if (R.size() == Size(3, 3)) |
|
|
{ |
|
|
R.getMat().convertTo(_R, CV_64F); |
|
|
} |
|
|
else if (R.total() == 3) |
|
|
{ |
|
|
Rodrigues(R.getMat(), _R); |
|
|
_R.convertTo(_R, CV_64F); |
|
|
} |
|
|
// stereo rectify so that stereo matching can be applied in one line |
|
|
Mat R1, R2; |
|
|
stereoRectify(_R, _T, R1, R2); |
|
|
Mat undis1, undis2; |
|
|
Matx33d _Knew = Matx33d(_K1); |
|
|
if (!Knew.empty()) |
|
|
{ |
|
|
Knew.getMat().convertTo(_Knew, CV_64F); |
|
|
} |
|
|
|
|
|
undistortImage(image1.getMat(), undis1, _K1, _D1, xi1, flag, _Knew, newSize, R1); |
|
|
undistortImage(image2.getMat(), undis2, _K2, _D2, xi2, flag, _Knew, newSize, R2); |
|
|
|
|
|
undis1.copyTo(image1Rec); |
|
|
undis2.copyTo(image2Rec); |
|
|
|
|
|
// stereo matching by semi-global |
|
|
Mat _disMap; |
|
|
int channel = image1.channels(); |
|
|
|
|
|
//cv::StereoSGBM matching(0, numDisparities, SADWindowSize, 8*channel*SADWindowSize*SADWindowSize, 32*channel*SADWindowSize*SADWindowSize); |
|
|
//matching(undis1, undis2, _depthMap); |
|
|
Ptr<StereoSGBM> sgbm = StereoSGBM::create(0, numDisparities, SADWindowSize, 8 * channel*SADWindowSize*SADWindowSize, 32 * channel*SADWindowSize*SADWindowSize); |
|
|
|
|
|
sgbm->compute(undis1, undis2, _disMap); |
|
|
|
|
|
// some regions of image1 is black, the corresponding regions of disparity map is also invalid. |
|
|
Mat realDis; |
|
|
_disMap.convertTo(_disMap, CV_32F); |
|
|
Mat(_disMap/16.0f).convertTo(realDis, CV_32F); |
|
|
|
|
|
Mat grayImg, binaryImg, idx; |
|
|
if (undis1.channels() == 3) |
|
|
{ |
|
|
cvtColor(undis1, grayImg, COLOR_RGB2GRAY); |
|
|
} |
|
|
else |
|
|
{ |
|
|
grayImg = undis1; |
|
|
} |
|
|
|
|
|
binaryImg = (grayImg <= 0); |
|
|
findNonZero(binaryImg, idx); |
|
|
for (int i = 0; i < (int)idx.total(); ++i) |
|
|
{ |
|
|
Vec2i _idx = idx.at<Vec2i>(i); |
|
|
realDis.at<float>(_idx[1], _idx[0]) = 0.0f; |
|
|
} |
|
|
|
|
|
disparity.create(realDis.size(), realDis.type()); |
|
|
realDis.copyTo(disparity.getMat()); |
|
|
|
|
|
std::vector<Vec3f> _pointCloud; |
|
|
std::vector<Vec6f> _pointCloudColor; |
|
|
double baseline = cv::norm(T); |
|
|
double f = _Knew(0, 0); |
|
|
Matx33d K_inv = _Knew.inv(); |
|
|
|
|
|
std::vector<Mat> rgb; |
|
|
if (undis1.channels() == 3) |
|
|
{ |
|
|
split(undis1, rgb); |
|
|
} |
|
|
|
|
|
if (pointCloud.needed()) |
|
|
{ |
|
|
for (int i = 0; i < newSize.width; ++i) |
|
|
{ |
|
|
for(int j = 0; j < newSize.height; ++j) |
|
|
{ |
|
|
Vec3f point; |
|
|
Vec6f pointColor; |
|
|
if (realDis.at<float>(j, i) > 15) |
|
|
{ |
|
|
float depth = float(baseline * f /realDis.at<float>(j, i)); |
|
|
// for RECTIFY_PERSPECTIVE, (x,y) are image plane points, |
|
|
// for RECTIFY_LONGLATI, (x,y) are (theta, phi) angles |
|
|
float x = float(K_inv(0,0) * i + K_inv(0,1) * j + K_inv(0,2)); |
|
|
float y = float(K_inv(1,0) * i + K_inv(1,1) * j + K_inv(1,2)); |
|
|
if (flag == omnidir::RECTIFY_LONGLATI) |
|
|
{ |
|
|
point = Vec3f((float)-std::cos(x), (float)(-std::sin(x)*std::cos(y)), (float)(std::sin(x)*std::sin(y))) * depth; |
|
|
} |
|
|
else if(flag == omnidir::RECTIFY_PERSPECTIVE) |
|
|
{ |
|
|
point = Vec3f(float(x), float(y), 1.0f) * depth; |
|
|
} |
|
|
if (pointType == XYZ) |
|
|
{ |
|
|
_pointCloud.push_back(point); |
|
|
} |
|
|
else if (pointType == XYZRGB) |
|
|
{ |
|
|
pointColor[0] = point[0]; |
|
|
pointColor[1] = point[1]; |
|
|
pointColor[2] = point[2]; |
|
|
|
|
|
if (undis1.channels() == 1) |
|
|
{ |
|
|
pointColor[3] = float(undis1.at<uchar>(j, i)); |
|
|
pointColor[4] = pointColor[3]; |
|
|
pointColor[5] = pointColor[3]; |
|
|
} |
|
|
else if (undis1.channels() == 3) |
|
|
{ |
|
|
pointColor[3] = rgb[0].at<uchar>(j, i); |
|
|
pointColor[4] = rgb[1].at<uchar>(j, i); |
|
|
pointColor[5] = rgb[2].at<uchar>(j, i); |
|
|
} |
|
|
_pointCloudColor.push_back(pointColor); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
if (pointType == XYZ) |
|
|
{ |
|
|
Mat(_pointCloud).convertTo(pointCloud, CV_MAKE_TYPE(CV_32F, 3)); |
|
|
} |
|
|
else if (pointType == XYZRGB) |
|
|
{ |
|
|
Mat(_pointCloudColor).convertTo(pointCloud, CV_MAKE_TYPE(CV_32F, 6)); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
void cv::omnidir::internal::encodeParameters(InputArray K, InputArrayOfArrays omAll, InputArrayOfArrays tAll, InputArray distoaration, double xi, OutputArray parameters) |
|
|
{ |
|
|
CV_Assert(K.type() == CV_64F && K.size() == Size(3,3)); |
|
|
CV_Assert(distoaration.total() == 4 && distoaration.type() == CV_64F); |
|
|
int n = (int)omAll.total(); |
|
|
Mat _omAll = omAll.getMat(), _tAll = tAll.getMat(); |
|
|
|
|
|
Matx33d _K = K.getMat(); |
|
|
Vec4d _D = (Vec4d)distoaration.getMat(); |
|
|
parameters.create(1, 10+6*n,CV_64F); |
|
|
Mat _params = parameters.getMat(); |
|
|
for (int i = 0; i < n; i++) |
|
|
{ |
|
|
Mat(_omAll.at<Vec3d>(i)).reshape(1, 1).copyTo(_params.colRange(i*6, i*6+3)); |
|
|
Mat(_tAll.at<Vec3d>(i)).reshape(1, 1).copyTo(_params.colRange(i*6+3, (i+1)*6)); |
|
|
} |
|
|
|
|
|
_params.at<double>(0, 6*n) = _K(0,0); |
|
|
_params.at<double>(0, 6*n+1) = _K(1,1); |
|
|
_params.at<double>(0, 6*n+2) = _K(0,1); |
|
|
_params.at<double>(0, 6*n+3) = _K(0,2); |
|
|
_params.at<double>(0, 6*n+4) = _K(1,2); |
|
|
_params.at<double>(0, 6*n+5) = xi; |
|
|
_params.at<double>(0, 6*n+6) = _D[0]; |
|
|
_params.at<double>(0, 6*n+7) = _D[1]; |
|
|
_params.at<double>(0, 6*n+8) = _D[2]; |
|
|
_params.at<double>(0, 6*n+9) = _D[3]; |
|
|
} |
|
|
|
|
|
void cv::omnidir::internal::encodeParametersStereo(InputArray K1, InputArray K2, InputArray om, InputArray T, InputArrayOfArrays omL, InputArrayOfArrays tL, |
|
|
InputArray D1, InputArray D2, double xi1, double xi2, OutputArray parameters) |
|
|
{ |
|
|
CV_Assert(!K1.empty() && K1.type() == CV_64F && K1.size() == Size(3,3)); |
|
|
CV_Assert(!K2.empty() && K2.type() == CV_64F && K2.size() == Size(3,3)); |
|
|
CV_Assert(!om.empty() && om.type() == CV_64F && om.total() == 3); |
|
|
CV_Assert(!T.empty() && T.type() == CV_64F && T.total() == 3); |
|
|
CV_Assert(omL.total() == tL.total() && omL.type() == CV_64FC3 && tL.type() == CV_64FC3); |
|
|
CV_Assert(D1.type() == CV_64F && D1.total() == 4 && D2.type() == CV_64F && D2.total() == 4); |
|
|
|
|
|
int n = (int)omL.total(); |
|
|
// om, T, omL, tL, intrinsic left, intrinsic right |
|
|
parameters.create(1, 20 + 6 * (n + 1), CV_64F); |
|
|
|
|
|
Mat _params = parameters.getMat(); |
|
|
|
|
|
om.getMat().reshape(1, 1).copyTo(_params.colRange(0, 3)); |
|
|
T.getMat().reshape(1, 1).copyTo(_params.colRange(3, 6)); |
|
|
for(int i = 0; i < n; ++i) |
|
|
{ |
|
|
Mat(omL.getMat().at<Vec3d>(i)).reshape(1, 1).copyTo(_params.colRange(6 + i*6, 6 + i*6 + 3)); |
|
|
Mat(tL.getMat().at<Vec3d>(i)).reshape(1, 1).copyTo(_params.colRange(6 + i*6 + 3, 6 + i*6 + 6)); |
|
|
} |
|
|
|
|
|
Matx33d _K1 = K1.getMat(); |
|
|
Matx33d _K2 = K2.getMat(); |
|
|
Vec4d _D1 = D1.getMat(); |
|
|
Vec4d _D2 = D2.getMat(); |
|
|
_params.at<double>(0, 6*(n+1)) = _K1(0,0); |
|
|
_params.at<double>(0, 6*(n+1)+1) = _K1(1,1); |
|
|
_params.at<double>(0, 6*(n+1)+2) = _K1(0,1); |
|
|
_params.at<double>(0, 6*(n+1)+3) = _K1(0,2); |
|
|
_params.at<double>(0, 6*(n+1)+4) = _K1(1,2); |
|
|
_params.at<double>(0, 6*(n+1)+5) = xi1; |
|
|
_params.at<double>(0, 6*(n+1)+6) = _D1[0]; |
|
|
_params.at<double>(0, 6*(n+1)+7) = _D1[1]; |
|
|
_params.at<double>(0, 6*(n+1)+8) = _D1[2]; |
|
|
_params.at<double>(0, 6*(n+1)+9) = _D1[3]; |
|
|
|
|
|
_params.at<double>(0, 6*(n+1)+10) = _K2(0,0); |
|
|
_params.at<double>(0, 6*(n+1)+11) = _K2(1,1); |
|
|
_params.at<double>(0, 6*(n+1)+12) = _K2(0,1); |
|
|
_params.at<double>(0, 6*(n+1)+13) = _K2(0,2); |
|
|
_params.at<double>(0, 6*(n+1)+14) = _K2(1,2); |
|
|
_params.at<double>(0, 6*(n+1)+15) = xi2; |
|
|
_params.at<double>(0, 6*(n+1)+16) = _D2[0]; |
|
|
_params.at<double>(0, 6*(n+1)+17) = _D2[1]; |
|
|
_params.at<double>(0, 6*(n+1)+18) = _D2[2]; |
|
|
_params.at<double>(0, 6*(n+1)+19) = _D2[3]; |
|
|
} |
|
|
|
|
|
|
|
|
void cv::omnidir::internal::decodeParameters(InputArray parameters, OutputArray K, OutputArrayOfArrays omAll, OutputArrayOfArrays tAll, OutputArray distoration, double& xi) |
|
|
{ |
|
|
if(K.empty()) |
|
|
K.create(3,3,CV_64F); |
|
|
Matx33d _K; |
|
|
int n = (int)(parameters.total()-10)/6; |
|
|
if(omAll.empty()) |
|
|
omAll.create(1, n, CV_64FC3); |
|
|
if(tAll.empty()) |
|
|
tAll.create(1, n, CV_64FC3); |
|
|
if(distoration.empty()) |
|
|
distoration.create(1, 4, CV_64F); |
|
|
Matx14d _D = distoration.getMat(); |
|
|
Mat param = parameters.getMat(); |
|
|
double *para = param.ptr<double>(); |
|
|
_K = Matx33d(para[6*n], para[6*n+2], para[6*n+3], |
|
|
0, para[6*n+1], para[6*n+4], |
|
|
0, 0, 1); |
|
|
_D = Matx14d(para[6*n+6], para[6*n+7], para[6*n+8], para[6*n+9]); |
|
|
xi = para[6*n+5]; |
|
|
std::vector<Vec3d> _omAll(n), _tAll(n); |
|
|
for (int i = 0; i < n; i++) |
|
|
{ |
|
|
_omAll[i] = Vec3d(param.colRange(i*6, i*6+3)); |
|
|
_tAll[i] = Vec3d(param.colRange(i*6+3, i*6+6)); |
|
|
} |
|
|
Mat(_D).convertTo(distoration, CV_64F); |
|
|
Mat(_K).convertTo(K, CV_64F); |
|
|
|
|
|
if (omAll.kind() == _InputArray::STD_VECTOR_MAT) |
|
|
{ |
|
|
for (int i = 0; i < n; ++i) |
|
|
{ |
|
|
Mat(_omAll[i]).copyTo(omAll.getMat(i)); |
|
|
Mat(_tAll[i]).copyTo(tAll.getMat(i)); |
|
|
} |
|
|
} |
|
|
else |
|
|
{ |
|
|
Mat(_omAll).convertTo(omAll, CV_64FC3); |
|
|
Mat(_tAll).convertTo(tAll, CV_64FC3); |
|
|
} |
|
|
} |
|
|
|
|
|
void cv::omnidir::internal::decodeParametersStereo(InputArray parameters, OutputArray K1, OutputArray K2, OutputArray om, OutputArray T, OutputArrayOfArrays omL, |
|
|
OutputArrayOfArrays tL, OutputArray D1, OutputArray D2, double& xi1, double& xi2) |
|
|
{ |
|
|
if(K1.empty()) |
|
|
K1.create(3, 3, CV_64F); |
|
|
if(K2.empty()) |
|
|
K2.create(3, 3, CV_64F); |
|
|
if(om.empty()) |
|
|
om.create(3, 1, CV_64F); |
|
|
if(T.empty()) |
|
|
T.create(3, 1, CV_64F); |
|
|
|
|
|
int n = ((int)parameters.total() - 20) / 6 - 1; |
|
|
|
|
|
if(omL.empty()) |
|
|
omL.create(1, n, CV_64FC3); |
|
|
if(tL.empty()) |
|
|
tL.create(1, n, CV_64FC3); |
|
|
if(D1.empty()) |
|
|
D1.create(1, 4, CV_64F); |
|
|
if(D2.empty()) |
|
|
D2.create(1, 4, CV_64F); |
|
|
|
|
|
Mat param = parameters.getMat().reshape(1, 1); |
|
|
param.colRange(0, 3).reshape(1, 3).copyTo(om.getMat()); |
|
|
param.colRange(3, 6).reshape(1, 3).copyTo(T.getMat()); |
|
|
std::vector<Vec3d> _omL, _tL; |
|
|
|
|
|
for(int i = 0; i < n; i++) |
|
|
{ |
|
|
_omL.push_back(Vec3d(param.colRange(6 + i*6, 6 + i*6 + 3))); |
|
|
_tL.push_back(Vec3d(param.colRange(6 + i*6 + 3, 6 + i*6 + 6))); |
|
|
} |
|
|
|
|
|
double* para = param.ptr<double>(); |
|
|
int offset1 = (n + 1)*6; |
|
|
Matx33d _K1(para[offset1], para[offset1+2], para[offset1+3], |
|
|
0, para[offset1+1], para[offset1+4], |
|
|
0, 0, 1); |
|
|
xi1 = para[offset1+5]; |
|
|
Matx14d _D1(para[offset1+6], para[offset1+7], para[offset1+8], para[offset1+9]); |
|
|
|
|
|
int offset2 = (n + 1)*6 + 10; |
|
|
Matx33d _K2(para[offset2], para[offset2+2], para[offset2+3], |
|
|
0, para[offset2+1], para[offset2+4], |
|
|
0, 0, 1); |
|
|
xi2 = para[offset2+5]; |
|
|
Matx14d _D2(para[offset2+6], para[offset2+7], para[offset2+8], para[offset2+9]); |
|
|
|
|
|
Mat(_K1).convertTo(K1, CV_64F); |
|
|
Mat(_D1).convertTo(D1, CV_64F); |
|
|
Mat(_K2).convertTo(K2, CV_64F); |
|
|
Mat(_D2).convertTo(D2, CV_64F); |
|
|
if(omL.kind() == _InputArray::STD_VECTOR_MAT) |
|
|
{ |
|
|
for(int i = 0; i < n; ++i) |
|
|
{ |
|
|
Mat(_omL[i]).copyTo(omL.getMat(i)); |
|
|
Mat(_tL[i]).copyTo(tL.getMat(i)); |
|
|
} |
|
|
} |
|
|
else |
|
|
{ |
|
|
Mat(_omL).convertTo(omL, CV_64FC3); |
|
|
Mat(_tL).convertTo(tL, CV_64FC3); |
|
|
} |
|
|
} |
|
|
|
|
|
void cv::omnidir::internal::estimateUncertainties(InputArrayOfArrays objectPoints, InputArrayOfArrays imagePoints, InputArray parameters, |
|
|
Mat& errors, Vec2d& std_error, double& rms, int flags) |
|
|
{ |
|
|
CV_Assert(!objectPoints.empty() && objectPoints.type() == CV_64FC3); |
|
|
CV_Assert(!imagePoints.empty() && imagePoints.type() == CV_64FC2); |
|
|
CV_Assert(!parameters.empty() && parameters.type() == CV_64F); |
|
|
|
|
|
int n = (int) objectPoints.total(); |
|
|
// assume every image has the same number of objectpoints |
|
|
int nPointsAll = 0; |
|
|
for (int i = 0; i < n; ++i) |
|
|
{ |
|
|
nPointsAll += (int)objectPoints.getMat(i).total(); |
|
|
} |
|
|
|
|
|
Mat reprojError = Mat(nPointsAll, 1, CV_64FC2); |
|
|
|
|
|
double* para = parameters.getMat().ptr<double>(); |
|
|
Matx33d K(para[6*n], para[6*n+2], para[6*n+3], |
|
|
0, para[6*n+1], para[6*n+4], |
|
|
0, 0, 1); |
|
|
Matx14d D(para[6*n+6], para[6*n+7], para[6*n+8], para[6*n+9]); |
|
|
double xi = para[6*n+5]; |
|
|
int nPointsAccu = 0; |
|
|
|
|
|
for(int i=0; i < n; ++i) |
|
|
{ |
|
|
Mat imgPoints, objPoints; |
|
|
imagePoints.getMat(i).copyTo(imgPoints); |
|
|
objectPoints.getMat(i).copyTo(objPoints); |
|
|
imgPoints = imgPoints.reshape(2, imgPoints.rows*imgPoints.cols); |
|
|
objPoints = objPoints.reshape(3, objPoints.rows*objPoints.cols); |
|
|
|
|
|
Mat om = parameters.getMat().colRange(i*6, i*6+3); |
|
|
Mat T = parameters.getMat().colRange(i*6+3, (i+1)*6); |
|
|
|
|
|
Mat x; |
|
|
omnidir::projectPoints(objPoints, x, om, T, K, xi, D, cv::noArray()); |
|
|
|
|
|
Mat errorx = (imgPoints - x); |
|
|
|
|
|
//reprojError.rowRange(errorx.rows*i, errorx.rows*(i+1)) = errorx.clone(); |
|
|
errorx.copyTo(reprojError.rowRange(nPointsAccu, nPointsAccu + (int)errorx.total())); |
|
|
nPointsAccu += (int)errorx.total(); |
|
|
} |
|
|
|
|
|
meanStdDev(reprojError, noArray(), std_error); |
|
|
std_error *= sqrt((double)reprojError.total()/((double)reprojError.total() - 1.0)); |
|
|
|
|
|
Mat sigma_x; |
|
|
meanStdDev(reprojError.reshape(1,1), noArray(), sigma_x); |
|
|
sigma_x *= sqrt(2.0*(double)reprojError.total()/(2.0*(double)reprojError.total() - 1.0)); |
|
|
double s = sigma_x.at<double>(0); |
|
|
|
|
|
Mat _JTJ_inv, _JTE; |
|
|
computeJacobian(objectPoints, imagePoints, parameters, _JTJ_inv, _JTE, flags, 0.0); |
|
|
sqrt(_JTJ_inv, _JTJ_inv); |
|
|
|
|
|
errors = 3 * s * _JTJ_inv.diag(); |
|
|
|
|
|
checkFixed(errors, flags, n); |
|
|
|
|
|
rms = 0; |
|
|
const Vec2d* ptr_ex = reprojError.ptr<Vec2d>(); |
|
|
for (int i = 0; i < (int)reprojError.total(); i++) |
|
|
{ |
|
|
rms += ptr_ex[i][0] * ptr_ex[i][0] + ptr_ex[i][1] * ptr_ex[i][1]; |
|
|
} |
|
|
|
|
|
rms /= (double)reprojError.total(); |
|
|
rms = sqrt(rms); |
|
|
} |
|
|
|
|
|
// estimateUncertaintiesStereo |
|
|
void cv::omnidir::internal::estimateUncertaintiesStereo(InputArrayOfArrays objectPoints, InputArrayOfArrays imagePoints1, InputArrayOfArrays imagePoints2, |
|
|
InputArray parameters, Mat& errors, Vec2d& std_error, double& rms, int flags) |
|
|
{ |
|
|
CV_Assert(!objectPoints.empty() && objectPoints.type() == CV_64FC3); |
|
|
CV_Assert(!imagePoints1.empty() && imagePoints1.type() == CV_64FC2 && imagePoints1.total() == objectPoints.total()); |
|
|
CV_Assert(!imagePoints2.empty() && imagePoints2.type() == CV_64FC2 && imagePoints1.total() == imagePoints2.total()); |
|
|
int n_img = (int)objectPoints.total(); |
|
|
CV_Assert((int)parameters.total() == (6*(n_img+1)+20)); |
|
|
|
|
|
Mat _K1, _K2, _D1, _D2; |
|
|
Vec3d _om, _T; |
|
|
std::vector<Vec3d> _omL(n_img), _tL(n_img); |
|
|
Mat _parameters = parameters.getMat().reshape(1, 1); |
|
|
double _xi1, _xi2; |
|
|
internal::decodeParametersStereo(_parameters, _K1, _K2, _om, _T, _omL, _tL, _D1, _D2, _xi1, _xi2); |
|
|
|
|
|
int n_points = (int)objectPoints.getMat(0).total(); |
|
|
Mat reprojErrorAll = Mat::zeros(2*n_points*n_img, 1, CV_64FC2); |
|
|
|
|
|
// error for left image |
|
|
for (int i = 0; i < n_img; ++i) |
|
|
{ |
|
|
Mat objPointsi, imgPointsi; |
|
|
objectPoints.getMat(i).copyTo(objPointsi); |
|
|
imagePoints1.getMat(i).copyTo(imgPointsi); |
|
|
objPointsi = objPointsi.reshape(3, objPointsi.rows*objPointsi.cols); |
|
|
imgPointsi = imgPointsi.reshape(2, imgPointsi.rows*imgPointsi.cols); |
|
|
|
|
|
Mat x; |
|
|
omnidir::projectPoints(objPointsi, x, _omL[i], _tL[i], _K1, _xi1, _D1, cv::noArray()); |
|
|
|
|
|
Mat errorx = imgPointsi - x; |
|
|
|
|
|
errorx.copyTo(reprojErrorAll.rowRange(i*2*n_points, (i*2+1)*n_points)); |
|
|
} |
|
|
// error for right image |
|
|
for (int i = 0; i < n_img; ++i) |
|
|
{ |
|
|
Mat objPointsi, imgPointsi; |
|
|
objectPoints.getMat(i).copyTo(objPointsi); |
|
|
imagePoints2.getMat(i).copyTo(imgPointsi); |
|
|
objPointsi = objPointsi.reshape(3, objPointsi.rows*objPointsi.cols); |
|
|
imgPointsi = imgPointsi.reshape(2, imgPointsi.rows*imgPointsi.cols); |
|
|
|
|
|
Mat x; |
|
|
Mat _R, _R2, _R1, _T2, _T1, _om2; |
|
|
Rodrigues(_om, _R); |
|
|
Rodrigues(_omL[i], _R1); |
|
|
_T1 = Mat(_tL[i]); |
|
|
_R2 = _R * _R1; |
|
|
_T2 = _R * _T1 + Mat(_T); |
|
|
Rodrigues(_R2, _om2); |
|
|
|
|
|
omnidir::projectPoints(objPointsi, x, _om2, _T2, _K2, _xi2, _D2, cv::noArray()); |
|
|
|
|
|
Mat errorx = imgPointsi - x; |
|
|
|
|
|
errorx.copyTo(reprojErrorAll.rowRange((i*2+1)*n_points, (i+1)*2*n_points)); |
|
|
} |
|
|
|
|
|
meanStdDev(reprojErrorAll, cv::noArray(), std_error); |
|
|
std_error *= sqrt((double)reprojErrorAll.total()/((double)reprojErrorAll.total() - 1.0)); |
|
|
|
|
|
Mat sigma_x; |
|
|
meanStdDev(reprojErrorAll.reshape(1,1), noArray(), sigma_x); |
|
|
sigma_x *= sqrt(2.0*(double)reprojErrorAll.total()/(2.0*(double)reprojErrorAll.total() - 1.0)); |
|
|
double s = sigma_x.at<double>(0); |
|
|
|
|
|
Mat _JTJ_inv, _JTE; |
|
|
computeJacobianStereo(objectPoints, imagePoints1, imagePoints2, _parameters, _JTJ_inv, _JTE, flags, 0.0); |
|
|
cv::sqrt(_JTJ_inv, _JTJ_inv); |
|
|
|
|
|
errors = 3 * s * _JTJ_inv.diag(); |
|
|
|
|
|
rms = 0; |
|
|
|
|
|
const Vec2d* ptr_ex = reprojErrorAll.ptr<Vec2d>(); |
|
|
for (int i = 0; i < (int)reprojErrorAll.total(); i++) |
|
|
{ |
|
|
rms += ptr_ex[i][0] * ptr_ex[i][0] + ptr_ex[i][1] * ptr_ex[i][1]; |
|
|
} |
|
|
rms /= (double)reprojErrorAll.total(); |
|
|
rms = sqrt(rms); |
|
|
} |
|
|
|
|
|
// |
|
|
double cv::omnidir::internal::computeMeanReproErr(InputArrayOfArrays imagePoints, InputArrayOfArrays proImagePoints) |
|
|
{ |
|
|
CV_Assert(!imagePoints.empty() && imagePoints.type()==CV_64FC2); |
|
|
CV_Assert(!proImagePoints.empty() && proImagePoints.type() == CV_64FC2); |
|
|
CV_Assert(imagePoints.total() == proImagePoints.total()); |
|
|
|
|
|
int n = (int)imagePoints.total(); |
|
|
double reprojError = 0; |
|
|
int totalPoints = 0; |
|
|
if (imagePoints.kind() == _InputArray::STD_VECTOR_MAT) |
|
|
{ |
|
|
for (int i = 0; i < n; i++) |
|
|
{ |
|
|
Mat x, proj_x; |
|
|
imagePoints.getMat(i).copyTo(x); |
|
|
proImagePoints.getMat(i).copyTo(proj_x); |
|
|
Mat errorI = x.reshape(2, x.rows*x.cols) - proj_x.reshape(2, proj_x.rows*proj_x.cols); |
|
|
//Mat errorI = imagePoints.getMat(i) - proImagePoints.getMat(i); |
|
|
totalPoints += (int)errorI.total(); |
|
|
Vec2d* ptr_err = errorI.ptr<Vec2d>(); |
|
|
for (int j = 0; j < (int)errorI.total(); j++) |
|
|
{ |
|
|
reprojError += sqrt(ptr_err[j][0]*ptr_err[j][0] + ptr_err[j][1]*ptr_err[j][1]); |
|
|
} |
|
|
} |
|
|
} |
|
|
else |
|
|
{ |
|
|
Mat x, proj_x; |
|
|
imagePoints.getMat().copyTo(x); |
|
|
proImagePoints.getMat().copyTo(proj_x); |
|
|
Mat errorI = x.reshape(2, x.rows*x.cols) - proj_x.reshape(2, proj_x.rows*proj_x.cols); |
|
|
//Mat errorI = imagePoints.getMat() - proImagePoints.getMat(); |
|
|
totalPoints += (int)errorI.total(); |
|
|
Vec2d* ptr_err = errorI.ptr<Vec2d>(); |
|
|
for (int j = 0; j < (int)errorI.total(); j++) |
|
|
{ |
|
|
reprojError += sqrt(ptr_err[j][0]*ptr_err[j][0] + ptr_err[j][1]*ptr_err[j][1]); |
|
|
} |
|
|
} |
|
|
double meanReprojError = reprojError / totalPoints; |
|
|
return meanReprojError; |
|
|
} |
|
|
|
|
|
double cv::omnidir::internal::computeMeanReproErr(InputArrayOfArrays objectPoints, InputArrayOfArrays imagePoints, InputArray K, InputArray D, double xi, InputArrayOfArrays omAll, |
|
|
InputArrayOfArrays tAll) |
|
|
{ |
|
|
CV_Assert(objectPoints.total() == imagePoints.total()); |
|
|
CV_Assert(!objectPoints.empty() && objectPoints.type() == CV_64FC3); |
|
|
CV_Assert(!imagePoints.empty() && imagePoints.type() == CV_64FC2); |
|
|
std::vector<Mat> proImagePoints; |
|
|
int n = (int)objectPoints.total(); |
|
|
Mat _omAll = omAll.getMat(); |
|
|
Mat _tAll = tAll.getMat(); |
|
|
for(int i = 0; i < n; ++i) |
|
|
{ |
|
|
Mat imgPoint; |
|
|
//cv::omnidir::projectPoints(objetPoints.getMat(i), imgPoint, omAll.getMat(i), tAll.getMat(i), K.getMat(), xi, D.getMat(), noArray()); |
|
|
cv::omnidir::projectPoints(objectPoints.getMat(i), imgPoint, _omAll.at<Vec3d>(i), _tAll.at<Vec3d>(i), K.getMat(), xi, D.getMat(), noArray()); |
|
|
proImagePoints.push_back(imgPoint); |
|
|
} |
|
|
|
|
|
return internal::computeMeanReproErr(imagePoints, proImagePoints); |
|
|
} |
|
|
|
|
|
double cv::omnidir::internal::computeMeanReproErrStereo(InputArrayOfArrays objectPoints, InputArrayOfArrays imagePoints1, InputArrayOfArrays imagePoints2, InputArray K1, InputArray K2, |
|
|
InputArray D1, InputArray D2, double xi1, double xi2, InputArray om, InputArray T, InputArrayOfArrays omL, InputArrayOfArrays TL) |
|
|
{ |
|
|
CV_Assert(objectPoints.total() == imagePoints1.total() && imagePoints1.total() == imagePoints2.total()); |
|
|
CV_Assert(!objectPoints.empty() && objectPoints.type() == CV_64FC3); |
|
|
CV_Assert(!imagePoints1.empty() && imagePoints1.type() == CV_64FC2); |
|
|
CV_Assert(!imagePoints2.empty() && imagePoints2.type() == CV_64FC2); |
|
|
|
|
|
std::vector<Mat> proImagePoints1, proImagePoints2; |
|
|
int n = (int)objectPoints.total(); |
|
|
Mat _omL = omL.getMat(), _TL = TL.getMat(); |
|
|
Mat _om = om.getMat(), _R, _T = T.getMat(); |
|
|
Rodrigues(_om, _R); |
|
|
Mat _K1 = K1.getMat(), _K2 = K2.getMat(); |
|
|
Mat _D1 = D1.getMat(), _D2 = D2.getMat(); |
|
|
|
|
|
// reprojection error for left image |
|
|
for (int i = 0; i < n; ++i) |
|
|
{ |
|
|
Mat imgPoints; |
|
|
cv::omnidir::projectPoints(objectPoints.getMat(i), imgPoints, _omL.at<Vec3d>(i), _TL.at<Vec3d>(i), _K1, xi1, _D1, cv::noArray()); |
|
|
proImagePoints1.push_back(imgPoints); |
|
|
} |
|
|
|
|
|
// reprojection error for right image |
|
|
for (int i = 0; i < n; ++i) |
|
|
{ |
|
|
Mat imgPoints; |
|
|
Mat _omRi,_RRi,_TRi,_RLi, _TLi; |
|
|
Rodrigues(_omL.at<Vec3d>(i), _RLi); |
|
|
_TLi = Mat(_TL.at<Vec3d>(i)).reshape(1, 3); |
|
|
_RRi = _R * _RLi; |
|
|
_TRi = _R * _TLi + _T; |
|
|
Rodrigues(_RRi, _omRi); |
|
|
cv::omnidir::projectPoints(objectPoints.getMat(i), imgPoints, _omRi, _TRi, _K2, xi2, _D2, cv::noArray()); |
|
|
proImagePoints2.push_back(imgPoints); |
|
|
} |
|
|
double reProErr1 = internal::computeMeanReproErr(imagePoints1, proImagePoints1); |
|
|
double reProErr2 = internal::computeMeanReproErr(imagePoints2, proImagePoints2); |
|
|
|
|
|
double reProErr = (reProErr1 + reProErr2) / 2.0; |
|
|
//double reProErr = reProErr1*reProErr1 + reProErr2* reProErr2; |
|
|
return reProErr; |
|
|
} |
|
|
|
|
|
void cv::omnidir::internal::checkFixed(Mat& G, int flags, int n) |
|
|
{ |
|
|
int _flags = flags; |
|
|
if(_flags >= omnidir::CALIB_FIX_CENTER) |
|
|
{ |
|
|
G.at<double>(6*n+3) = 0; |
|
|
G.at<double>(6*n+4) = 0; |
|
|
_flags -= omnidir::CALIB_FIX_CENTER; |
|
|
} |
|
|
if(_flags >= omnidir::CALIB_FIX_GAMMA) |
|
|
{ |
|
|
G.at<double>(6*n) = 0; |
|
|
G.at<double>(6*n+1) = 0; |
|
|
_flags -= omnidir::CALIB_FIX_GAMMA; |
|
|
} |
|
|
if(_flags >= omnidir::CALIB_FIX_XI) |
|
|
{ |
|
|
G.at<double>(6*n + 5) = 0; |
|
|
_flags -= omnidir::CALIB_FIX_XI; |
|
|
} |
|
|
if(_flags >= omnidir::CALIB_FIX_P2) |
|
|
{ |
|
|
G.at<double>(6*n + 9) = 0; |
|
|
_flags -= omnidir::CALIB_FIX_P2; |
|
|
} |
|
|
if(_flags >= omnidir::CALIB_FIX_P1) |
|
|
{ |
|
|
G.at<double>(6*n + 8) = 0; |
|
|
_flags -= omnidir::CALIB_FIX_P1; |
|
|
} |
|
|
if(_flags >= omnidir::CALIB_FIX_K2) |
|
|
{ |
|
|
G.at<double>(6*n + 7) = 0; |
|
|
_flags -= omnidir::CALIB_FIX_K2; |
|
|
} |
|
|
if(_flags >= omnidir::CALIB_FIX_K1) |
|
|
{ |
|
|
G.at<double>(6*n + 6) = 0; |
|
|
_flags -= omnidir::CALIB_FIX_K1; |
|
|
} |
|
|
if(_flags >= omnidir::CALIB_FIX_SKEW) |
|
|
{ |
|
|
G.at<double>(6*n + 2) = 0; |
|
|
} |
|
|
} |
|
|
|
|
|
// This function is from fisheye.cpp |
|
|
void cv::omnidir::internal::subMatrix(const Mat& src, Mat& dst, const std::vector<int>& cols, const std::vector<int>& rows) |
|
|
{ |
|
|
CV_Assert(src.type() == CV_64FC1); |
|
|
|
|
|
int nonzeros_cols = cv::countNonZero(cols); |
|
|
Mat tmp(src.rows, nonzeros_cols, CV_64FC1); |
|
|
|
|
|
for (int i = 0, j = 0; i < (int)cols.size(); i++) |
|
|
{ |
|
|
if (cols[i]) |
|
|
{ |
|
|
src.col(i).copyTo(tmp.col(j++)); |
|
|
} |
|
|
} |
|
|
|
|
|
int nonzeros_rows = cv::countNonZero(rows); |
|
|
Mat tmp1(nonzeros_rows, nonzeros_cols, CV_64FC1); |
|
|
for (int i = 0, j = 0; i < (int)rows.size(); i++) |
|
|
{ |
|
|
if (rows[i]) |
|
|
{ |
|
|
tmp.row(i).copyTo(tmp1.row(j++)); |
|
|
} |
|
|
} |
|
|
|
|
|
dst = tmp1.clone(); |
|
|
} |
|
|
|
|
|
void cv::omnidir::internal::flags2idx(int flags, std::vector<int>& idx, int n) |
|
|
{ |
|
|
idx = std::vector<int>(6*n+10,1); |
|
|
int _flags = flags; |
|
|
if(_flags >= omnidir::CALIB_FIX_CENTER) |
|
|
{ |
|
|
idx[6*n+3] = 0; |
|
|
idx[6*n+4] = 0; |
|
|
_flags -= omnidir::CALIB_FIX_CENTER; |
|
|
} |
|
|
if(_flags >= omnidir::CALIB_FIX_GAMMA) |
|
|
{ |
|
|
idx[6*n] = 0; |
|
|
idx[6*n+1] = 0; |
|
|
_flags -= omnidir::CALIB_FIX_GAMMA; |
|
|
} |
|
|
if(_flags >= omnidir::CALIB_FIX_XI) |
|
|
{ |
|
|
idx[6*n + 5] = 0; |
|
|
_flags -= omnidir::CALIB_FIX_XI; |
|
|
} |
|
|
if(_flags >= omnidir::CALIB_FIX_P2) |
|
|
{ |
|
|
idx[6*n + 9] = 0; |
|
|
_flags -= omnidir::CALIB_FIX_P2; |
|
|
} |
|
|
if(_flags >= omnidir::CALIB_FIX_P1) |
|
|
{ |
|
|
idx[6*n + 8] = 0; |
|
|
_flags -= omnidir::CALIB_FIX_P1; |
|
|
} |
|
|
if(_flags >= omnidir::CALIB_FIX_K2) |
|
|
{ |
|
|
idx[6*n + 7] = 0; |
|
|
_flags -= omnidir::CALIB_FIX_K2; |
|
|
} |
|
|
if(_flags >= omnidir::CALIB_FIX_K1) |
|
|
{ |
|
|
idx[6*n + 6] = 0; |
|
|
_flags -= omnidir::CALIB_FIX_K1; |
|
|
} |
|
|
if(_flags >= omnidir::CALIB_FIX_SKEW) |
|
|
{ |
|
|
idx[6*n + 2] = 0; |
|
|
} |
|
|
} |
|
|
|
|
|
void cv::omnidir::internal::flags2idxStereo(int flags, std::vector<int>& idx, int n) |
|
|
{ |
|
|
idx = std::vector<int>(6*(n+1)+20, 1); |
|
|
int _flags = flags; |
|
|
int offset1 = 6*(n+1); |
|
|
int offset2 = offset1 + 10; |
|
|
if(_flags >= omnidir::CALIB_FIX_CENTER) |
|
|
{ |
|
|
idx[offset1+3] = 0; |
|
|
idx[offset1+4] = 0; |
|
|
idx[offset2+3] = 0; |
|
|
idx[offset2+4] = 0; |
|
|
_flags -= omnidir::CALIB_FIX_CENTER; |
|
|
} |
|
|
if(_flags >= omnidir::CALIB_FIX_GAMMA) |
|
|
{ |
|
|
idx[offset1] = 0; |
|
|
idx[offset1+1] = 0; |
|
|
idx[offset2] = 0; |
|
|
idx[offset2+1] = 0; |
|
|
_flags -= omnidir::CALIB_FIX_GAMMA; |
|
|
} |
|
|
if(_flags >= omnidir::CALIB_FIX_XI) |
|
|
{ |
|
|
idx[offset1 + 5] = 0; |
|
|
idx[offset2 + 5] = 0; |
|
|
_flags -= omnidir::CALIB_FIX_XI; |
|
|
} |
|
|
if(_flags >= omnidir::CALIB_FIX_P2) |
|
|
{ |
|
|
idx[offset1 + 9] = 0; |
|
|
idx[offset2 + 9] = 0; |
|
|
_flags -= omnidir::CALIB_FIX_P2; |
|
|
} |
|
|
if(_flags >= omnidir::CALIB_FIX_P1) |
|
|
{ |
|
|
idx[offset1 + 8] = 0; |
|
|
idx[offset2 + 8] = 0; |
|
|
_flags -= omnidir::CALIB_FIX_P1; |
|
|
} |
|
|
if(_flags >= omnidir::CALIB_FIX_K2) |
|
|
{ |
|
|
idx[offset1 + 7] = 0; |
|
|
idx[offset2 + 7] = 0; |
|
|
_flags -= omnidir::CALIB_FIX_K2; |
|
|
} |
|
|
if(_flags >= omnidir::CALIB_FIX_K1) |
|
|
{ |
|
|
idx[offset1 + 6] = 0; |
|
|
idx[offset2 + 6] = 0; |
|
|
_flags -= omnidir::CALIB_FIX_K1; |
|
|
} |
|
|
if(_flags >= omnidir::CALIB_FIX_SKEW) |
|
|
{ |
|
|
idx[offset1 + 2] = 0; |
|
|
idx[offset2 + 2] = 0; |
|
|
} |
|
|
} |
|
|
|
|
|
// fill in zerso for fixed parameters |
|
|
void cv::omnidir::internal::fillFixed(Mat&G, int flags, int n) |
|
|
{ |
|
|
Mat tmp = G.clone(); |
|
|
std::vector<int> idx(6*n + 10, 1); |
|
|
flags2idx(flags, idx, n); |
|
|
G.release(); |
|
|
G.create(6*n +10, 1, CV_64F); |
|
|
G = cv::Mat::zeros(6*n +10, 1, CV_64F); |
|
|
for (int i = 0,j=0; i < (int)idx.size(); i++) |
|
|
{ |
|
|
if (idx[i]) |
|
|
{ |
|
|
G.at<double>(i) = tmp.at<double>(j++); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
void cv::omnidir::internal::fillFixedStereo(Mat& G, int flags, int n) |
|
|
{ |
|
|
Mat tmp = G.clone(); |
|
|
std::vector<int> idx(6*(n+1)+20, 1); |
|
|
flags2idxStereo(flags, idx, n); |
|
|
G.release(); |
|
|
G.create(6 * (n+1) + 20, 1, CV_64F); |
|
|
G = cv::Mat::zeros(6* (n + 1) + 20, 1, CV_64F); |
|
|
for (int i = 0,j=0; i < (int)idx.size(); i++) |
|
|
{ |
|
|
if (idx[i]) |
|
|
{ |
|
|
G.at<double>(i) = tmp.at<double>(j++); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
double cv::omnidir::internal::findMedian(const Mat& row) |
|
|
{ |
|
|
CV_Assert(!row.empty() && row.rows == 1 && row.type() == CV_64F); |
|
|
Mat tmp = row.clone(); |
|
|
cv::sort(tmp, tmp, 0); |
|
|
if((int)tmp.total()%2 == 0) |
|
|
return tmp.at<double>((int)tmp.total() / 2); |
|
|
else |
|
|
return 0.5 * (tmp.at<double>((int)tmp.total() / 2) + tmp.at<double>((int)tmp.total()/2 - 1)); |
|
|
} |
|
|
|
|
|
cv::Vec3d cv::omnidir::internal::findMedian3(InputArray mat) |
|
|
{ |
|
|
CV_Assert(mat.depth() == CV_64F && mat.getMat().rows == 1); |
|
|
Mat M = Mat(mat.getMat().t()).reshape(1).t(); |
|
|
return Vec3d(findMedian(M.row(0)), findMedian(M.row(1)), findMedian(M.row(2))); |
|
|
} |
|
|
|
|
|
void cv::omnidir::stereoRectify(InputArray R, InputArray T, OutputArray R1, OutputArray R2) |
|
|
{ |
|
|
CV_Assert((R.size() == Size(3,3) || R.total() == 3) && (R.depth() == CV_32F || R.depth() == CV_64F)); |
|
|
CV_Assert(T.total() == 3 && (T.depth() == CV_32F || T.depth() == CV_64F)); |
|
|
|
|
|
Mat _R, _T; |
|
|
if (R.size() == Size(3, 3)) |
|
|
{ |
|
|
R.getMat().convertTo(_R, CV_64F); |
|
|
} |
|
|
else if (R.total() == 3) |
|
|
{ |
|
|
Rodrigues(R.getMat(), _R); |
|
|
_R.convertTo(_R, CV_64F); |
|
|
} |
|
|
|
|
|
T.getMat().reshape(1, 3).convertTo(_T, CV_64F); |
|
|
|
|
|
R1.create(3, 3, CV_64F); |
|
|
Mat _R1 = R1.getMat(); |
|
|
R2.create(3, 3, CV_64F); |
|
|
Mat _R2 = R2.getMat(); |
|
|
|
|
|
Mat R21 = _R.t(); |
|
|
Mat T21 = -_R.t() * _T; |
|
|
|
|
|
Mat e1, e2, e3; |
|
|
e1 = T21.t() / norm(T21); |
|
|
e2 = Mat(Matx13d(T21.at<double>(1)*-1, T21.at<double>(0), 0.0)); |
|
|
e2 = e2 / norm(e2); |
|
|
e3 = e1.cross(e2); |
|
|
e3 = e3 / norm(e3); |
|
|
e1.copyTo(_R1.row(0)); |
|
|
e2.copyTo(_R1.row(1)); |
|
|
e3.copyTo(_R1.row(2)); |
|
|
_R2 = R21 * _R1; |
|
|
|
|
|
} |
|
|
|
|
|
void cv::omnidir::internal::getInterset(InputArray idx1, InputArray idx2, OutputArray inter1, OutputArray inter2, |
|
|
OutputArray inter_ori) |
|
|
{ |
|
|
Mat _idx1 = idx1.getMat(); |
|
|
Mat _idx2 = idx2.getMat(); |
|
|
|
|
|
int n1 = (int)idx1.total(); |
|
|
int n2 = (int)idx2.total(); |
|
|
|
|
|
std::vector<int> _inter1, _inter2, _inter_ori; |
|
|
for (int i = 0; i < n1; ++i) |
|
|
{ |
|
|
for (int j = 0; j < n2; ++j) |
|
|
{ |
|
|
if(_idx1.at<int>(i) == _idx2.at<int>(j)) |
|
|
{ |
|
|
_inter1.push_back(i); |
|
|
_inter2.push_back(j); |
|
|
_inter_ori.push_back(_idx1.at<int>(i)); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
inter1.create(1, (int)_inter1.size(), CV_32S); |
|
|
inter2.create(1, (int)_inter2.size(), CV_32S); |
|
|
inter_ori.create(1, (int)_inter_ori.size(), CV_32S); |
|
|
|
|
|
for (int i = 0; i < (int)_inter1.size(); ++i) |
|
|
{ |
|
|
inter1.getMat().at<int>(i) = _inter1[i]; |
|
|
} |
|
|
for (int i = 0; i < (int)_inter2.size(); ++i) |
|
|
{ |
|
|
inter2.getMat().at<int>(i) = _inter2[i]; |
|
|
} |
|
|
for (int i = 0; i < (int)_inter_ori.size(); ++i) |
|
|
{ |
|
|
inter_ori.getMat().at<int>(i) = _inter_ori[i]; |
|
|
} |
|
|
}
|
|
|
|