1 /*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4 //
5 // By downloading, copying, installing or using the software you agree to this license.
6 // If you do not agree to this license, do not download, install,
7 // copy or use the software.
8 //
9 //
10 // License Agreement
11 // For Open Source Computer Vision Library
12 //
13 // Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
14 // Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved.
15 // Third party copyrights are property of their respective owners.
16 //
17 // Redistribution and use in source and binary forms, with or without modification,
18 // are permitted provided that the following conditions are met:
19 //
20 // * Redistribution's of source code must retain the above copyright notice,
21 // this list of conditions and the following disclaimer.
22 //
23 // * Redistribution's in binary form must reproduce the above copyright notice,
24 // this list of conditions and the following disclaimer in the documentation
25 // and/or other materials provided with the distribution.
26 //
27 // * The name of the copyright holders may not be used to endorse or promote products
28 // derived from this software without specific prior written permission.
29 //
30 // This software is provided by the copyright holders and contributors "as is" and
31 // any express or implied warranties, including, but not limited to, the implied
32 // warranties of merchantability and fitness for a particular purpose are disclaimed.
33 // In no event shall the Intel Corporation or contributors be liable for any direct,
34 // indirect, incidental, special, exemplary, or consequential damages
35 // (including, but not limited to, procurement of substitute goods or services;
36 // loss of use, data, or profits; or business interruption) however caused
37 // and on any theory of liability, whether in contract, strict liability,
38 // or tort (including negligence or otherwise) arising in any way out of
39 // the use of this software, even if advised of the possibility of such damage.
40 //
41 //M*/
42
43 #include "precomp.hpp"
44 #include "fisheye.hpp"
45
46 namespace cv { namespace
47 {
48 struct JacobianRow
49 {
50 Vec2d df, dc;
51 Vec4d dk;
52 Vec3d dom, dT;
53 double dalpha;
54 };
55
56 void subMatrix(const Mat& src, Mat& dst, const std::vector<int>& cols, const std::vector<int>& rows);
57 }}
58
59 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
60 /// cv::fisheye::projectPoints
61
projectPoints(InputArray objectPoints,OutputArray imagePoints,const Affine3d & affine,InputArray K,InputArray D,double alpha,OutputArray jacobian)62 void cv::fisheye::projectPoints(InputArray objectPoints, OutputArray imagePoints, const Affine3d& affine,
63 InputArray K, InputArray D, double alpha, OutputArray jacobian)
64 {
65 projectPoints(objectPoints, imagePoints, affine.rvec(), affine.translation(), K, D, alpha, jacobian);
66 }
67
projectPoints(InputArray objectPoints,OutputArray imagePoints,InputArray _rvec,InputArray _tvec,InputArray _K,InputArray _D,double alpha,OutputArray jacobian)68 void cv::fisheye::projectPoints(InputArray objectPoints, OutputArray imagePoints, InputArray _rvec,
69 InputArray _tvec, InputArray _K, InputArray _D, double alpha, OutputArray jacobian)
70 {
71 // will support only 3-channel data now for points
72 CV_Assert(objectPoints.type() == CV_32FC3 || objectPoints.type() == CV_64FC3);
73 imagePoints.create(objectPoints.size(), CV_MAKETYPE(objectPoints.depth(), 2));
74 size_t n = objectPoints.total();
75
76 CV_Assert(_rvec.total() * _rvec.channels() == 3 && (_rvec.depth() == CV_32F || _rvec.depth() == CV_64F));
77 CV_Assert(_tvec.total() * _tvec.channels() == 3 && (_tvec.depth() == CV_32F || _tvec.depth() == CV_64F));
78 CV_Assert(_tvec.getMat().isContinuous() && _rvec.getMat().isContinuous());
79
80 Vec3d om = _rvec.depth() == CV_32F ? (Vec3d)*_rvec.getMat().ptr<Vec3f>() : *_rvec.getMat().ptr<Vec3d>();
81 Vec3d T = _tvec.depth() == CV_32F ? (Vec3d)*_tvec.getMat().ptr<Vec3f>() : *_tvec.getMat().ptr<Vec3d>();
82
83 CV_Assert(_K.size() == Size(3,3) && (_K.type() == CV_32F || _K.type() == CV_64F) && _D.type() == _K.type() && _D.total() == 4);
84
85 cv::Vec2d f, c;
86 if (_K.depth() == CV_32F)
87 {
88
89 Matx33f K = _K.getMat();
90 f = Vec2f(K(0, 0), K(1, 1));
91 c = Vec2f(K(0, 2), K(1, 2));
92 }
93 else
94 {
95 Matx33d K = _K.getMat();
96 f = Vec2d(K(0, 0), K(1, 1));
97 c = Vec2d(K(0, 2), K(1, 2));
98 }
99
100 Vec4d k = _D.depth() == CV_32F ? (Vec4d)*_D.getMat().ptr<Vec4f>(): *_D.getMat().ptr<Vec4d>();
101
102 JacobianRow *Jn = 0;
103 if (jacobian.needed())
104 {
105 int nvars = 2 + 2 + 1 + 4 + 3 + 3; // f, c, alpha, k, om, T,
106 jacobian.create(2*(int)n, nvars, CV_64F);
107 Jn = jacobian.getMat().ptr<JacobianRow>(0);
108 }
109
110 Matx33d R;
111 Matx<double, 3, 9> dRdom;
112 Rodrigues(om, R, dRdom);
113 Affine3d aff(om, T);
114
115 const Vec3f* Xf = objectPoints.getMat().ptr<Vec3f>();
116 const Vec3d* Xd = objectPoints.getMat().ptr<Vec3d>();
117 Vec2f *xpf = imagePoints.getMat().ptr<Vec2f>();
118 Vec2d *xpd = imagePoints.getMat().ptr<Vec2d>();
119
120 for(size_t i = 0; i < n; ++i)
121 {
122 Vec3d Xi = objectPoints.depth() == CV_32F ? (Vec3d)Xf[i] : Xd[i];
123 Vec3d Y = aff*Xi;
124
125 Vec2d x(Y[0]/Y[2], Y[1]/Y[2]);
126
127 double r2 = x.dot(x);
128 double r = std::sqrt(r2);
129
130 // Angle of the incoming ray:
131 double theta = atan(r);
132
133 double theta2 = theta*theta, theta3 = theta2*theta, theta4 = theta2*theta2, theta5 = theta4*theta,
134 theta6 = theta3*theta3, theta7 = theta6*theta, theta8 = theta4*theta4, theta9 = theta8*theta;
135
136 double theta_d = theta + k[0]*theta3 + k[1]*theta5 + k[2]*theta7 + k[3]*theta9;
137
138 double inv_r = r > 1e-8 ? 1.0/r : 1;
139 double cdist = r > 1e-8 ? theta_d * inv_r : 1;
140
141 Vec2d xd1 = x * cdist;
142 Vec2d xd3(xd1[0] + alpha*xd1[1], xd1[1]);
143 Vec2d final_point(xd3[0] * f[0] + c[0], xd3[1] * f[1] + c[1]);
144
145 if (objectPoints.depth() == CV_32F)
146 xpf[i] = final_point;
147 else
148 xpd[i] = final_point;
149
150 if (jacobian.needed())
151 {
152 //Vec3d Xi = pdepth == CV_32F ? (Vec3d)Xf[i] : Xd[i];
153 //Vec3d Y = aff*Xi;
154 double dYdR[] = { Xi[0], Xi[1], Xi[2], 0, 0, 0, 0, 0, 0,
155 0, 0, 0, Xi[0], Xi[1], Xi[2], 0, 0, 0,
156 0, 0, 0, 0, 0, 0, Xi[0], Xi[1], Xi[2] };
157
158 Matx33d dYdom_data = Matx<double, 3, 9>(dYdR) * dRdom.t();
159 const Vec3d *dYdom = (Vec3d*)dYdom_data.val;
160
161 Matx33d dYdT_data = Matx33d::eye();
162 const Vec3d *dYdT = (Vec3d*)dYdT_data.val;
163
164 //Vec2d x(Y[0]/Y[2], Y[1]/Y[2]);
165 Vec3d dxdom[2];
166 dxdom[0] = (1.0/Y[2]) * dYdom[0] - x[0]/Y[2] * dYdom[2];
167 dxdom[1] = (1.0/Y[2]) * dYdom[1] - x[1]/Y[2] * dYdom[2];
168
169 Vec3d dxdT[2];
170 dxdT[0] = (1.0/Y[2]) * dYdT[0] - x[0]/Y[2] * dYdT[2];
171 dxdT[1] = (1.0/Y[2]) * dYdT[1] - x[1]/Y[2] * dYdT[2];
172
173 //double r2 = x.dot(x);
174 Vec3d dr2dom = 2 * x[0] * dxdom[0] + 2 * x[1] * dxdom[1];
175 Vec3d dr2dT = 2 * x[0] * dxdT[0] + 2 * x[1] * dxdT[1];
176
177 //double r = std::sqrt(r2);
178 double drdr2 = r > 1e-8 ? 1.0/(2*r) : 1;
179 Vec3d drdom = drdr2 * dr2dom;
180 Vec3d drdT = drdr2 * dr2dT;
181
182 // Angle of the incoming ray:
183 //double theta = atan(r);
184 double dthetadr = 1.0/(1+r2);
185 Vec3d dthetadom = dthetadr * drdom;
186 Vec3d dthetadT = dthetadr * drdT;
187
188 //double theta_d = theta + k[0]*theta3 + k[1]*theta5 + k[2]*theta7 + k[3]*theta9;
189 double dtheta_ddtheta = 1 + 3*k[0]*theta2 + 5*k[1]*theta4 + 7*k[2]*theta6 + 9*k[3]*theta8;
190 Vec3d dtheta_ddom = dtheta_ddtheta * dthetadom;
191 Vec3d dtheta_ddT = dtheta_ddtheta * dthetadT;
192 Vec4d dtheta_ddk = Vec4d(theta3, theta5, theta7, theta9);
193
194 //double inv_r = r > 1e-8 ? 1.0/r : 1;
195 //double cdist = r > 1e-8 ? theta_d / r : 1;
196 Vec3d dcdistdom = inv_r * (dtheta_ddom - cdist*drdom);
197 Vec3d dcdistdT = inv_r * (dtheta_ddT - cdist*drdT);
198 Vec4d dcdistdk = inv_r * dtheta_ddk;
199
200 //Vec2d xd1 = x * cdist;
201 Vec4d dxd1dk[2];
202 Vec3d dxd1dom[2], dxd1dT[2];
203 dxd1dom[0] = x[0] * dcdistdom + cdist * dxdom[0];
204 dxd1dom[1] = x[1] * dcdistdom + cdist * dxdom[1];
205 dxd1dT[0] = x[0] * dcdistdT + cdist * dxdT[0];
206 dxd1dT[1] = x[1] * dcdistdT + cdist * dxdT[1];
207 dxd1dk[0] = x[0] * dcdistdk;
208 dxd1dk[1] = x[1] * dcdistdk;
209
210 //Vec2d xd3(xd1[0] + alpha*xd1[1], xd1[1]);
211 Vec4d dxd3dk[2];
212 Vec3d dxd3dom[2], dxd3dT[2];
213 dxd3dom[0] = dxd1dom[0] + alpha * dxd1dom[1];
214 dxd3dom[1] = dxd1dom[1];
215 dxd3dT[0] = dxd1dT[0] + alpha * dxd1dT[1];
216 dxd3dT[1] = dxd1dT[1];
217 dxd3dk[0] = dxd1dk[0] + alpha * dxd1dk[1];
218 dxd3dk[1] = dxd1dk[1];
219
220 Vec2d dxd3dalpha(xd1[1], 0);
221
222 //final jacobian
223 Jn[0].dom = f[0] * dxd3dom[0];
224 Jn[1].dom = f[1] * dxd3dom[1];
225
226 Jn[0].dT = f[0] * dxd3dT[0];
227 Jn[1].dT = f[1] * dxd3dT[1];
228
229 Jn[0].dk = f[0] * dxd3dk[0];
230 Jn[1].dk = f[1] * dxd3dk[1];
231
232 Jn[0].dalpha = f[0] * dxd3dalpha[0];
233 Jn[1].dalpha = 0; //f[1] * dxd3dalpha[1];
234
235 Jn[0].df = Vec2d(xd3[0], 0);
236 Jn[1].df = Vec2d(0, xd3[1]);
237
238 Jn[0].dc = Vec2d(1, 0);
239 Jn[1].dc = Vec2d(0, 1);
240
241 //step to jacobian rows for next point
242 Jn += 2;
243 }
244 }
245 }
246
247 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
248 /// cv::fisheye::distortPoints
249
distortPoints(InputArray undistorted,OutputArray distorted,InputArray K,InputArray D,double alpha)250 void cv::fisheye::distortPoints(InputArray undistorted, OutputArray distorted, InputArray K, InputArray D, double alpha)
251 {
252 // will support only 2-channel data now for points
253 CV_Assert(undistorted.type() == CV_32FC2 || undistorted.type() == CV_64FC2);
254 distorted.create(undistorted.size(), undistorted.type());
255 size_t n = undistorted.total();
256
257 CV_Assert(K.size() == Size(3,3) && (K.type() == CV_32F || K.type() == CV_64F) && D.total() == 4);
258
259 cv::Vec2d f, c;
260 if (K.depth() == CV_32F)
261 {
262 Matx33f camMat = K.getMat();
263 f = Vec2f(camMat(0, 0), camMat(1, 1));
264 c = Vec2f(camMat(0, 2), camMat(1, 2));
265 }
266 else
267 {
268 Matx33d camMat = K.getMat();
269 f = Vec2d(camMat(0, 0), camMat(1, 1));
270 c = Vec2d(camMat(0 ,2), camMat(1, 2));
271 }
272
273 Vec4d k = D.depth() == CV_32F ? (Vec4d)*D.getMat().ptr<Vec4f>(): *D.getMat().ptr<Vec4d>();
274
275 const Vec2f* Xf = undistorted.getMat().ptr<Vec2f>();
276 const Vec2d* Xd = undistorted.getMat().ptr<Vec2d>();
277 Vec2f *xpf = distorted.getMat().ptr<Vec2f>();
278 Vec2d *xpd = distorted.getMat().ptr<Vec2d>();
279
280 for(size_t i = 0; i < n; ++i)
281 {
282 Vec2d x = undistorted.depth() == CV_32F ? (Vec2d)Xf[i] : Xd[i];
283
284 double r2 = x.dot(x);
285 double r = std::sqrt(r2);
286
287 // Angle of the incoming ray:
288 double theta = atan(r);
289
290 double theta2 = theta*theta, theta3 = theta2*theta, theta4 = theta2*theta2, theta5 = theta4*theta,
291 theta6 = theta3*theta3, theta7 = theta6*theta, theta8 = theta4*theta4, theta9 = theta8*theta;
292
293 double theta_d = theta + k[0]*theta3 + k[1]*theta5 + k[2]*theta7 + k[3]*theta9;
294
295 double inv_r = r > 1e-8 ? 1.0/r : 1;
296 double cdist = r > 1e-8 ? theta_d * inv_r : 1;
297
298 Vec2d xd1 = x * cdist;
299 Vec2d xd3(xd1[0] + alpha*xd1[1], xd1[1]);
300 Vec2d final_point(xd3[0] * f[0] + c[0], xd3[1] * f[1] + c[1]);
301
302 if (undistorted.depth() == CV_32F)
303 xpf[i] = final_point;
304 else
305 xpd[i] = final_point;
306 }
307 }
308
309 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
310 /// cv::fisheye::undistortPoints
311
undistortPoints(InputArray distorted,OutputArray undistorted,InputArray K,InputArray D,InputArray R,InputArray P)312 void cv::fisheye::undistortPoints( InputArray distorted, OutputArray undistorted, InputArray K, InputArray D, InputArray R, InputArray P)
313 {
314 // will support only 2-channel data now for points
315 CV_Assert(distorted.type() == CV_32FC2 || distorted.type() == CV_64FC2);
316 undistorted.create(distorted.size(), distorted.type());
317
318 CV_Assert(P.empty() || P.size() == Size(3, 3) || P.size() == Size(4, 3));
319 CV_Assert(R.empty() || R.size() == Size(3, 3) || R.total() * R.channels() == 3);
320 CV_Assert(D.total() == 4 && K.size() == Size(3, 3) && (K.depth() == CV_32F || K.depth() == CV_64F));
321
322 cv::Vec2d f, c;
323 if (K.depth() == CV_32F)
324 {
325 Matx33f camMat = K.getMat();
326 f = Vec2f(camMat(0, 0), camMat(1, 1));
327 c = Vec2f(camMat(0, 2), camMat(1, 2));
328 }
329 else
330 {
331 Matx33d camMat = K.getMat();
332 f = Vec2d(camMat(0, 0), camMat(1, 1));
333 c = Vec2d(camMat(0, 2), camMat(1, 2));
334 }
335
336 Vec4d k = D.depth() == CV_32F ? (Vec4d)*D.getMat().ptr<Vec4f>(): *D.getMat().ptr<Vec4d>();
337
338 cv::Matx33d RR = cv::Matx33d::eye();
339 if (!R.empty() && R.total() * R.channels() == 3)
340 {
341 cv::Vec3d rvec;
342 R.getMat().convertTo(rvec, CV_64F);
343 RR = cv::Affine3d(rvec).rotation();
344 }
345 else if (!R.empty() && R.size() == Size(3, 3))
346 R.getMat().convertTo(RR, CV_64F);
347
348 if(!P.empty())
349 {
350 cv::Matx33d PP;
351 P.getMat().colRange(0, 3).convertTo(PP, CV_64F);
352 RR = PP * RR;
353 }
354
355 // start undistorting
356 const cv::Vec2f* srcf = distorted.getMat().ptr<cv::Vec2f>();
357 const cv::Vec2d* srcd = distorted.getMat().ptr<cv::Vec2d>();
358 cv::Vec2f* dstf = undistorted.getMat().ptr<cv::Vec2f>();
359 cv::Vec2d* dstd = undistorted.getMat().ptr<cv::Vec2d>();
360
361 size_t n = distorted.total();
362 int sdepth = distorted.depth();
363
364 for(size_t i = 0; i < n; i++ )
365 {
366 Vec2d pi = sdepth == CV_32F ? (Vec2d)srcf[i] : srcd[i]; // image point
367 Vec2d pw((pi[0] - c[0])/f[0], (pi[1] - c[1])/f[1]); // world point
368
369 double scale = 1.0;
370
371 double theta_d = sqrt(pw[0]*pw[0] + pw[1]*pw[1]);
372 if (theta_d > 1e-8)
373 {
374 // compensate distortion iteratively
375 double theta = theta_d;
376 for(int j = 0; j < 10; j++ )
377 {
378 double theta2 = theta*theta, theta4 = theta2*theta2, theta6 = theta4*theta2, theta8 = theta6*theta2;
379 theta = theta_d / (1 + k[0] * theta2 + k[1] * theta4 + k[2] * theta6 + k[3] * theta8);
380 }
381
382 scale = std::tan(theta) / theta_d;
383 }
384
385 Vec2d pu = pw * scale; //undistorted point
386
387 // reproject
388 Vec3d pr = RR * Vec3d(pu[0], pu[1], 1.0); // rotated point optionally multiplied by new camera matrix
389 Vec2d fi(pr[0]/pr[2], pr[1]/pr[2]); // final
390
391 if( sdepth == CV_32F )
392 dstf[i] = fi;
393 else
394 dstd[i] = fi;
395 }
396 }
397
398 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
399 /// cv::fisheye::undistortPoints
400
initUndistortRectifyMap(InputArray K,InputArray D,InputArray R,InputArray P,const cv::Size & size,int m1type,OutputArray map1,OutputArray map2)401 void cv::fisheye::initUndistortRectifyMap( InputArray K, InputArray D, InputArray R, InputArray P,
402 const cv::Size& size, int m1type, OutputArray map1, OutputArray map2 )
403 {
404 CV_Assert( m1type == CV_16SC2 || m1type == CV_32F || m1type <=0 );
405 map1.create( size, m1type <= 0 ? CV_16SC2 : m1type );
406 map2.create( size, map1.type() == CV_16SC2 ? CV_16UC1 : CV_32F );
407
408 CV_Assert((K.depth() == CV_32F || K.depth() == CV_64F) && (D.depth() == CV_32F || D.depth() == CV_64F));
409 CV_Assert((P.depth() == CV_32F || P.depth() == CV_64F) && (R.depth() == CV_32F || R.depth() == CV_64F));
410 CV_Assert(K.size() == Size(3, 3) && (D.empty() || D.total() == 4));
411 CV_Assert(R.empty() || R.size() == Size(3, 3) || R.total() * R.channels() == 3);
412 CV_Assert(P.empty() || P.size() == Size(3, 3) || P.size() == Size(4, 3));
413
414 cv::Vec2d f, c;
415 if (K.depth() == CV_32F)
416 {
417 Matx33f camMat = K.getMat();
418 f = Vec2f(camMat(0, 0), camMat(1, 1));
419 c = Vec2f(camMat(0, 2), camMat(1, 2));
420 }
421 else
422 {
423 Matx33d camMat = K.getMat();
424 f = Vec2d(camMat(0, 0), camMat(1, 1));
425 c = Vec2d(camMat(0, 2), camMat(1, 2));
426 }
427
428 Vec4d k = Vec4d::all(0);
429 if (!D.empty())
430 k = D.depth() == CV_32F ? (Vec4d)*D.getMat().ptr<Vec4f>(): *D.getMat().ptr<Vec4d>();
431
432 cv::Matx33d RR = cv::Matx33d::eye();
433 if (!R.empty() && R.total() * R.channels() == 3)
434 {
435 cv::Vec3d rvec;
436 R.getMat().convertTo(rvec, CV_64F);
437 RR = Affine3d(rvec).rotation();
438 }
439 else if (!R.empty() && R.size() == Size(3, 3))
440 R.getMat().convertTo(RR, CV_64F);
441
442 cv::Matx33d PP = cv::Matx33d::eye();
443 if (!P.empty())
444 P.getMat().colRange(0, 3).convertTo(PP, CV_64F);
445
446 cv::Matx33d iR = (PP * RR).inv(cv::DECOMP_SVD);
447
448 for( int i = 0; i < size.height; ++i)
449 {
450 float* m1f = map1.getMat().ptr<float>(i);
451 float* m2f = map2.getMat().ptr<float>(i);
452 short* m1 = (short*)m1f;
453 ushort* m2 = (ushort*)m2f;
454
455 double _x = i*iR(0, 1) + iR(0, 2),
456 _y = i*iR(1, 1) + iR(1, 2),
457 _w = i*iR(2, 1) + iR(2, 2);
458
459 for( int j = 0; j < size.width; ++j)
460 {
461 double x = _x/_w, y = _y/_w;
462
463 double r = sqrt(x*x + y*y);
464 double theta = atan(r);
465
466 double theta2 = theta*theta, theta4 = theta2*theta2, theta6 = theta4*theta2, theta8 = theta4*theta4;
467 double theta_d = theta * (1 + k[0]*theta2 + k[1]*theta4 + k[2]*theta6 + k[3]*theta8);
468
469 double scale = (r == 0) ? 1.0 : theta_d / r;
470 double u = f[0]*x*scale + c[0];
471 double v = f[1]*y*scale + c[1];
472
473 if( m1type == CV_16SC2 )
474 {
475 int iu = cv::saturate_cast<int>(u*cv::INTER_TAB_SIZE);
476 int iv = cv::saturate_cast<int>(v*cv::INTER_TAB_SIZE);
477 m1[j*2+0] = (short)(iu >> cv::INTER_BITS);
478 m1[j*2+1] = (short)(iv >> cv::INTER_BITS);
479 m2[j] = (ushort)((iv & (cv::INTER_TAB_SIZE-1))*cv::INTER_TAB_SIZE + (iu & (cv::INTER_TAB_SIZE-1)));
480 }
481 else if( m1type == CV_32FC1 )
482 {
483 m1f[j] = (float)u;
484 m2f[j] = (float)v;
485 }
486
487 _x += iR(0, 0);
488 _y += iR(1, 0);
489 _w += iR(2, 0);
490 }
491 }
492 }
493
494 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
495 /// cv::fisheye::undistortImage
496
undistortImage(InputArray distorted,OutputArray undistorted,InputArray K,InputArray D,InputArray Knew,const Size & new_size)497 void cv::fisheye::undistortImage(InputArray distorted, OutputArray undistorted,
498 InputArray K, InputArray D, InputArray Knew, const Size& new_size)
499 {
500 Size size = new_size.area() != 0 ? new_size : distorted.size();
501
502 cv::Mat map1, map2;
503 fisheye::initUndistortRectifyMap(K, D, cv::Matx33d::eye(), Knew, size, CV_16SC2, map1, map2 );
504 cv::remap(distorted, undistorted, map1, map2, INTER_LINEAR, BORDER_CONSTANT);
505 }
506
507
508 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
509 /// cv::fisheye::estimateNewCameraMatrixForUndistortRectify
510
estimateNewCameraMatrixForUndistortRectify(InputArray K,InputArray D,const Size & image_size,InputArray R,OutputArray P,double balance,const Size & new_size,double fov_scale)511 void cv::fisheye::estimateNewCameraMatrixForUndistortRectify(InputArray K, InputArray D, const Size &image_size, InputArray R,
512 OutputArray P, double balance, const Size& new_size, double fov_scale)
513 {
514 CV_Assert( K.size() == Size(3, 3) && (K.depth() == CV_32F || K.depth() == CV_64F));
515 CV_Assert((D.empty() || D.total() == 4) && (D.depth() == CV_32F || D.depth() == CV_64F || D.empty()));
516
517 int w = image_size.width, h = image_size.height;
518 balance = std::min(std::max(balance, 0.0), 1.0);
519
520 cv::Mat points(1, 4, CV_64FC2);
521 Vec2d* pptr = points.ptr<Vec2d>();
522 pptr[0] = Vec2d(w/2, 0);
523 pptr[1] = Vec2d(w, h/2);
524 pptr[2] = Vec2d(w/2, h);
525 pptr[3] = Vec2d(0, h/2);
526
527 #if 0
528 const int N = 10;
529 cv::Mat points(1, N * 4, CV_64FC2);
530 Vec2d* pptr = points.ptr<Vec2d>();
531 for(int i = 0, k = 0; i < 10; ++i)
532 {
533 pptr[k++] = Vec2d(w/2, 0) - Vec2d(w/8, 0) + Vec2d(w/4/N*i, 0);
534 pptr[k++] = Vec2d(w/2, h-1) - Vec2d(w/8, h-1) + Vec2d(w/4/N*i, h-1);
535
536 pptr[k++] = Vec2d(0, h/2) - Vec2d(0, h/8) + Vec2d(0, h/4/N*i);
537 pptr[k++] = Vec2d(w-1, h/2) - Vec2d(w-1, h/8) + Vec2d(w-1, h/4/N*i);
538 }
539 #endif
540
541 fisheye::undistortPoints(points, points, K, D, R);
542 cv::Scalar center_mass = mean(points);
543 cv::Vec2d cn(center_mass.val);
544
545 double aspect_ratio = (K.depth() == CV_32F) ? K.getMat().at<float >(0,0)/K.getMat().at<float> (1,1)
546 : K.getMat().at<double>(0,0)/K.getMat().at<double>(1,1);
547
548 // convert to identity ratio
549 cn[0] *= aspect_ratio;
550 for(size_t i = 0; i < points.total(); ++i)
551 pptr[i][1] *= aspect_ratio;
552
553 double minx = DBL_MAX, miny = DBL_MAX, maxx = -DBL_MAX, maxy = -DBL_MAX;
554 for(size_t i = 0; i < points.total(); ++i)
555 {
556 miny = std::min(miny, pptr[i][1]);
557 maxy = std::max(maxy, pptr[i][1]);
558 minx = std::min(minx, pptr[i][0]);
559 maxx = std::max(maxx, pptr[i][0]);
560 }
561
562 #if 0
563 double minx = -DBL_MAX, miny = -DBL_MAX, maxx = DBL_MAX, maxy = DBL_MAX;
564 for(size_t i = 0; i < points.total(); ++i)
565 {
566 if (i % 4 == 0) miny = std::max(miny, pptr[i][1]);
567 if (i % 4 == 1) maxy = std::min(maxy, pptr[i][1]);
568 if (i % 4 == 2) minx = std::max(minx, pptr[i][0]);
569 if (i % 4 == 3) maxx = std::min(maxx, pptr[i][0]);
570 }
571 #endif
572
573 double f1 = w * 0.5/(cn[0] - minx);
574 double f2 = w * 0.5/(maxx - cn[0]);
575 double f3 = h * 0.5 * aspect_ratio/(cn[1] - miny);
576 double f4 = h * 0.5 * aspect_ratio/(maxy - cn[1]);
577
578 double fmin = std::min(f1, std::min(f2, std::min(f3, f4)));
579 double fmax = std::max(f1, std::max(f2, std::max(f3, f4)));
580
581 double f = balance * fmin + (1.0 - balance) * fmax;
582 f *= fov_scale > 0 ? 1.0/fov_scale : 1.0;
583
584 cv::Vec2d new_f(f, f), new_c = -cn * f + Vec2d(w, h * aspect_ratio) * 0.5;
585
586 // restore aspect ratio
587 new_f[1] /= aspect_ratio;
588 new_c[1] /= aspect_ratio;
589
590 if (new_size.area() > 0)
591 {
592 double rx = new_size.width /(double)image_size.width;
593 double ry = new_size.height/(double)image_size.height;
594
595 new_f[0] *= rx; new_f[1] *= ry;
596 new_c[0] *= rx; new_c[1] *= ry;
597 }
598
599 Mat(Matx33d(new_f[0], 0, new_c[0],
600 0, new_f[1], new_c[1],
601 0, 0, 1)).convertTo(P, P.empty() ? K.type() : P.type());
602 }
603
604
605 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
606 /// cv::fisheye::stereoRectify
607
stereoRectify(InputArray K1,InputArray D1,InputArray K2,InputArray D2,const Size & imageSize,InputArray _R,InputArray _tvec,OutputArray R1,OutputArray R2,OutputArray P1,OutputArray P2,OutputArray Q,int flags,const Size & newImageSize,double balance,double fov_scale)608 void cv::fisheye::stereoRectify( InputArray K1, InputArray D1, InputArray K2, InputArray D2, const Size& imageSize,
609 InputArray _R, InputArray _tvec, OutputArray R1, OutputArray R2, OutputArray P1, OutputArray P2,
610 OutputArray Q, int flags, const Size& newImageSize, double balance, double fov_scale)
611 {
612 CV_Assert((_R.size() == Size(3, 3) || _R.total() * _R.channels() == 3) && (_R.depth() == CV_32F || _R.depth() == CV_64F));
613 CV_Assert(_tvec.total() * _tvec.channels() == 3 && (_tvec.depth() == CV_32F || _tvec.depth() == CV_64F));
614
615
616 cv::Mat aaa = _tvec.getMat().reshape(3, 1);
617
618 Vec3d rvec; // Rodrigues vector
619 if (_R.size() == Size(3, 3))
620 {
621 cv::Matx33d rmat;
622 _R.getMat().convertTo(rmat, CV_64F);
623 rvec = Affine3d(rmat).rvec();
624 }
625 else if (_R.total() * _R.channels() == 3)
626 _R.getMat().convertTo(rvec, CV_64F);
627
628 Vec3d tvec;
629 _tvec.getMat().convertTo(tvec, CV_64F);
630
631 // rectification algorithm
632 rvec *= -0.5; // get average rotation
633
634 Matx33d r_r;
635 Rodrigues(rvec, r_r); // rotate cameras to same orientation by averaging
636
637 Vec3d t = r_r * tvec;
638 Vec3d uu(t[0] > 0 ? 1 : -1, 0, 0);
639
640 // calculate global Z rotation
641 Vec3d ww = t.cross(uu);
642 double nw = norm(ww);
643 if (nw > 0.0)
644 ww *= acos(fabs(t[0])/cv::norm(t))/nw;
645
646 Matx33d wr;
647 Rodrigues(ww, wr);
648
649 // apply to both views
650 Matx33d ri1 = wr * r_r.t();
651 Mat(ri1, false).convertTo(R1, R1.empty() ? CV_64F : R1.type());
652 Matx33d ri2 = wr * r_r;
653 Mat(ri2, false).convertTo(R2, R2.empty() ? CV_64F : R2.type());
654 Vec3d tnew = ri2 * tvec;
655
656 // calculate projection/camera matrices. these contain the relevant rectified image internal params (fx, fy=fx, cx, cy)
657 Matx33d newK1, newK2;
658 estimateNewCameraMatrixForUndistortRectify(K1, D1, imageSize, R1, newK1, balance, newImageSize, fov_scale);
659 estimateNewCameraMatrixForUndistortRectify(K2, D2, imageSize, R2, newK2, balance, newImageSize, fov_scale);
660
661 double fc_new = std::min(newK1(1,1), newK2(1,1));
662 Point2d cc_new[2] = { Vec2d(newK1(0, 2), newK1(1, 2)), Vec2d(newK2(0, 2), newK2(1, 2)) };
663
664 // Vertical focal length must be the same for both images to keep the epipolar constraint use fy for fx also.
665 // For simplicity, set the principal points for both cameras to be the average
666 // of the two principal points (either one of or both x- and y- coordinates)
667 if( flags & cv::CALIB_ZERO_DISPARITY )
668 cc_new[0] = cc_new[1] = (cc_new[0] + cc_new[1]) * 0.5;
669 else
670 cc_new[0].y = cc_new[1].y = (cc_new[0].y + cc_new[1].y)*0.5;
671
672 Mat(Matx34d(fc_new, 0, cc_new[0].x, 0,
673 0, fc_new, cc_new[0].y, 0,
674 0, 0, 1, 0), false).convertTo(P1, P1.empty() ? CV_64F : P1.type());
675
676 Mat(Matx34d(fc_new, 0, cc_new[1].x, tnew[0]*fc_new, // baseline * focal length;,
677 0, fc_new, cc_new[1].y, 0,
678 0, 0, 1, 0), false).convertTo(P2, P2.empty() ? CV_64F : P2.type());
679
680 if (Q.needed())
681 Mat(Matx44d(1, 0, 0, -cc_new[0].x,
682 0, 1, 0, -cc_new[0].y,
683 0, 0, 0, fc_new,
684 0, 0, -1./tnew[0], (cc_new[0].x - cc_new[1].x)/tnew[0]), false).convertTo(Q, Q.empty() ? CV_64F : Q.depth());
685 }
686
687 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
688 /// cv::fisheye::calibrate
689
calibrate(InputArrayOfArrays objectPoints,InputArrayOfArrays imagePoints,const Size & image_size,InputOutputArray K,InputOutputArray D,OutputArrayOfArrays rvecs,OutputArrayOfArrays tvecs,int flags,cv::TermCriteria criteria)690 double cv::fisheye::calibrate(InputArrayOfArrays objectPoints, InputArrayOfArrays imagePoints, const Size& image_size,
691 InputOutputArray K, InputOutputArray D, OutputArrayOfArrays rvecs, OutputArrayOfArrays tvecs,
692 int flags , cv::TermCriteria criteria)
693 {
694 CV_Assert(!objectPoints.empty() && !imagePoints.empty() && objectPoints.total() == imagePoints.total());
695 CV_Assert(objectPoints.type() == CV_32FC3 || objectPoints.type() == CV_64FC3);
696 CV_Assert(imagePoints.type() == CV_32FC2 || imagePoints.type() == CV_64FC2);
697 CV_Assert((!K.empty() && K.size() == Size(3,3)) || K.empty());
698 CV_Assert((!D.empty() && D.total() == 4) || D.empty());
699 CV_Assert((!rvecs.empty() && rvecs.channels() == 3) || rvecs.empty());
700 CV_Assert((!tvecs.empty() && tvecs.channels() == 3) || tvecs.empty());
701
702 CV_Assert(((flags & CALIB_USE_INTRINSIC_GUESS) && !K.empty() && !D.empty()) || !(flags & CALIB_USE_INTRINSIC_GUESS));
703
704 using namespace cv::internal;
705 //-------------------------------Initialization
706 IntrinsicParams finalParam;
707 IntrinsicParams currentParam;
708 IntrinsicParams errors;
709
710 finalParam.isEstimate[0] = 1;
711 finalParam.isEstimate[1] = 1;
712 finalParam.isEstimate[2] = 1;
713 finalParam.isEstimate[3] = 1;
714 finalParam.isEstimate[4] = flags & CALIB_FIX_SKEW ? 0 : 1;
715 finalParam.isEstimate[5] = flags & CALIB_FIX_K1 ? 0 : 1;
716 finalParam.isEstimate[6] = flags & CALIB_FIX_K2 ? 0 : 1;
717 finalParam.isEstimate[7] = flags & CALIB_FIX_K3 ? 0 : 1;
718 finalParam.isEstimate[8] = flags & CALIB_FIX_K4 ? 0 : 1;
719
720 const int recompute_extrinsic = flags & CALIB_RECOMPUTE_EXTRINSIC ? 1: 0;
721 const int check_cond = flags & CALIB_CHECK_COND ? 1 : 0;
722
723 const double alpha_smooth = 0.4;
724 const double thresh_cond = 1e6;
725 double change = 1;
726 Vec2d err_std;
727
728 Matx33d _K;
729 Vec4d _D;
730 if (flags & CALIB_USE_INTRINSIC_GUESS)
731 {
732 K.getMat().convertTo(_K, CV_64FC1);
733 D.getMat().convertTo(_D, CV_64FC1);
734 finalParam.Init(Vec2d(_K(0,0), _K(1, 1)),
735 Vec2d(_K(0,2), _K(1, 2)),
736 Vec4d(flags & CALIB_FIX_K1 ? 0 : _D[0],
737 flags & CALIB_FIX_K2 ? 0 : _D[1],
738 flags & CALIB_FIX_K3 ? 0 : _D[2],
739 flags & CALIB_FIX_K4 ? 0 : _D[3]),
740 _K(0, 1) / _K(0, 0));
741 }
742 else
743 {
744 finalParam.Init(Vec2d(max(image_size.width, image_size.height) / CV_PI, max(image_size.width, image_size.height) / CV_PI),
745 Vec2d(image_size.width / 2.0 - 0.5, image_size.height / 2.0 - 0.5));
746 }
747
748 errors.isEstimate = finalParam.isEstimate;
749
750 std::vector<Vec3d> omc(objectPoints.total()), Tc(objectPoints.total());
751
752 CalibrateExtrinsics(objectPoints, imagePoints, finalParam, check_cond, thresh_cond, omc, Tc);
753
754
755 //-------------------------------Optimization
756 for(int iter = 0; ; ++iter)
757 {
758 if ((criteria.type == 1 && iter >= criteria.maxCount) ||
759 (criteria.type == 2 && change <= criteria.epsilon) ||
760 (criteria.type == 3 && (change <= criteria.epsilon || iter >= criteria.maxCount)))
761 break;
762
763 double alpha_smooth2 = 1 - std::pow(1 - alpha_smooth, iter + 1.0);
764
765 Mat JJ2_inv, ex3;
766 ComputeJacobians(objectPoints, imagePoints, finalParam, omc, Tc, check_cond,thresh_cond, JJ2_inv, ex3);
767
768 Mat G = alpha_smooth2 * JJ2_inv * ex3;
769
770 currentParam = finalParam + G;
771
772 change = norm(Vec4d(currentParam.f[0], currentParam.f[1], currentParam.c[0], currentParam.c[1]) -
773 Vec4d(finalParam.f[0], finalParam.f[1], finalParam.c[0], finalParam.c[1]))
774 / norm(Vec4d(currentParam.f[0], currentParam.f[1], currentParam.c[0], currentParam.c[1]));
775
776 finalParam = currentParam;
777
778 if (recompute_extrinsic)
779 {
780 CalibrateExtrinsics(objectPoints, imagePoints, finalParam, check_cond,
781 thresh_cond, omc, Tc);
782 }
783 }
784
785 //-------------------------------Validation
786 double rms;
787 EstimateUncertainties(objectPoints, imagePoints, finalParam, omc, Tc, errors, err_std, thresh_cond,
788 check_cond, rms);
789
790 //-------------------------------
791 _K = Matx33d(finalParam.f[0], finalParam.f[0] * finalParam.alpha, finalParam.c[0],
792 0, finalParam.f[1], finalParam.c[1],
793 0, 0, 1);
794
795 if (K.needed()) cv::Mat(_K).convertTo(K, K.empty() ? CV_64FC1 : K.type());
796 if (D.needed()) cv::Mat(finalParam.k).convertTo(D, D.empty() ? CV_64FC1 : D.type());
797 if (rvecs.kind()==_InputArray::STD_VECTOR_MAT)
798 {
799 int i;
800 for( i = 0; i < (int)objectPoints.total(); i++ )
801 {
802 rvecs.getMat(i)=omc[i];
803 tvecs.getMat(i)=Tc[i];
804 }
805 }
806 else
807 {
808 if (rvecs.needed()) cv::Mat(omc).convertTo(rvecs, rvecs.empty() ? CV_64FC3 : rvecs.type());
809 if (tvecs.needed()) cv::Mat(Tc).convertTo(tvecs, tvecs.empty() ? CV_64FC3 : tvecs.type());
810 }
811
812 return rms;
813 }
814
815 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
816 /// cv::fisheye::stereoCalibrate
817
stereoCalibrate(InputArrayOfArrays objectPoints,InputArrayOfArrays imagePoints1,InputArrayOfArrays imagePoints2,InputOutputArray K1,InputOutputArray D1,InputOutputArray K2,InputOutputArray D2,Size imageSize,OutputArray R,OutputArray T,int flags,TermCriteria criteria)818 double cv::fisheye::stereoCalibrate(InputArrayOfArrays objectPoints, InputArrayOfArrays imagePoints1, InputArrayOfArrays imagePoints2,
819 InputOutputArray K1, InputOutputArray D1, InputOutputArray K2, InputOutputArray D2, Size imageSize,
820 OutputArray R, OutputArray T, int flags, TermCriteria criteria)
821 {
822 CV_Assert(!objectPoints.empty() && !imagePoints1.empty() && !imagePoints2.empty());
823 CV_Assert(objectPoints.total() == imagePoints1.total() || imagePoints1.total() == imagePoints2.total());
824 CV_Assert(objectPoints.type() == CV_32FC3 || objectPoints.type() == CV_64FC3);
825 CV_Assert(imagePoints1.type() == CV_32FC2 || imagePoints1.type() == CV_64FC2);
826 CV_Assert(imagePoints2.type() == CV_32FC2 || imagePoints2.type() == CV_64FC2);
827
828 CV_Assert((!K1.empty() && K1.size() == Size(3,3)) || K1.empty());
829 CV_Assert((!D1.empty() && D1.total() == 4) || D1.empty());
830 CV_Assert((!K2.empty() && K1.size() == Size(3,3)) || K2.empty());
831 CV_Assert((!D2.empty() && D1.total() == 4) || D2.empty());
832
833 CV_Assert(((flags & CALIB_FIX_INTRINSIC) && !K1.empty() && !K2.empty() && !D1.empty() && !D2.empty()) || !(flags & CALIB_FIX_INTRINSIC));
834
835 //-------------------------------Initialization
836
837 const int threshold = 50;
838 const double thresh_cond = 1e6;
839 const int check_cond = 1;
840
841 int n_points = (int)objectPoints.getMat(0).total();
842 int n_images = (int)objectPoints.total();
843
844 double change = 1;
845
846 cv::internal::IntrinsicParams intrinsicLeft;
847 cv::internal::IntrinsicParams intrinsicRight;
848
849 cv::internal::IntrinsicParams intrinsicLeft_errors;
850 cv::internal::IntrinsicParams intrinsicRight_errors;
851
852 Matx33d _K1, _K2;
853 Vec4d _D1, _D2;
854 if (!K1.empty()) K1.getMat().convertTo(_K1, CV_64FC1);
855 if (!D1.empty()) D1.getMat().convertTo(_D1, CV_64FC1);
856 if (!K2.empty()) K2.getMat().convertTo(_K2, CV_64FC1);
857 if (!D2.empty()) D2.getMat().convertTo(_D2, CV_64FC1);
858
859 std::vector<Vec3d> rvecs1(n_images), tvecs1(n_images), rvecs2(n_images), tvecs2(n_images);
860
861 if (!(flags & CALIB_FIX_INTRINSIC))
862 {
863 calibrate(objectPoints, imagePoints1, imageSize, _K1, _D1, rvecs1, tvecs1, flags, TermCriteria(3, 20, 1e-6));
864 calibrate(objectPoints, imagePoints2, imageSize, _K2, _D2, rvecs2, tvecs2, flags, TermCriteria(3, 20, 1e-6));
865 }
866
867 intrinsicLeft.Init(Vec2d(_K1(0,0), _K1(1, 1)), Vec2d(_K1(0,2), _K1(1, 2)),
868 Vec4d(_D1[0], _D1[1], _D1[2], _D1[3]), _K1(0, 1) / _K1(0, 0));
869
870 intrinsicRight.Init(Vec2d(_K2(0,0), _K2(1, 1)), Vec2d(_K2(0,2), _K2(1, 2)),
871 Vec4d(_D2[0], _D2[1], _D2[2], _D2[3]), _K2(0, 1) / _K2(0, 0));
872
873 if ((flags & CALIB_FIX_INTRINSIC))
874 {
875 cv::internal::CalibrateExtrinsics(objectPoints, imagePoints1, intrinsicLeft, check_cond, thresh_cond, rvecs1, tvecs1);
876 cv::internal::CalibrateExtrinsics(objectPoints, imagePoints2, intrinsicRight, check_cond, thresh_cond, rvecs2, tvecs2);
877 }
878
879 intrinsicLeft.isEstimate[0] = flags & CALIB_FIX_INTRINSIC ? 0 : 1;
880 intrinsicLeft.isEstimate[1] = flags & CALIB_FIX_INTRINSIC ? 0 : 1;
881 intrinsicLeft.isEstimate[2] = flags & CALIB_FIX_INTRINSIC ? 0 : 1;
882 intrinsicLeft.isEstimate[3] = flags & CALIB_FIX_INTRINSIC ? 0 : 1;
883 intrinsicLeft.isEstimate[4] = flags & (CALIB_FIX_SKEW | CALIB_FIX_INTRINSIC) ? 0 : 1;
884 intrinsicLeft.isEstimate[5] = flags & (CALIB_FIX_K1 | CALIB_FIX_INTRINSIC) ? 0 : 1;
885 intrinsicLeft.isEstimate[6] = flags & (CALIB_FIX_K2 | CALIB_FIX_INTRINSIC) ? 0 : 1;
886 intrinsicLeft.isEstimate[7] = flags & (CALIB_FIX_K3 | CALIB_FIX_INTRINSIC) ? 0 : 1;
887 intrinsicLeft.isEstimate[8] = flags & (CALIB_FIX_K4 | CALIB_FIX_INTRINSIC) ? 0 : 1;
888
889 intrinsicRight.isEstimate[0] = flags & CALIB_FIX_INTRINSIC ? 0 : 1;
890 intrinsicRight.isEstimate[1] = flags & CALIB_FIX_INTRINSIC ? 0 : 1;
891 intrinsicRight.isEstimate[2] = flags & CALIB_FIX_INTRINSIC ? 0 : 1;
892 intrinsicRight.isEstimate[3] = flags & CALIB_FIX_INTRINSIC ? 0 : 1;
893 intrinsicRight.isEstimate[4] = flags & (CALIB_FIX_SKEW | CALIB_FIX_INTRINSIC) ? 0 : 1;
894 intrinsicRight.isEstimate[5] = flags & (CALIB_FIX_K1 | CALIB_FIX_INTRINSIC) ? 0 : 1;
895 intrinsicRight.isEstimate[6] = flags & (CALIB_FIX_K2 | CALIB_FIX_INTRINSIC) ? 0 : 1;
896 intrinsicRight.isEstimate[7] = flags & (CALIB_FIX_K3 | CALIB_FIX_INTRINSIC) ? 0 : 1;
897 intrinsicRight.isEstimate[8] = flags & (CALIB_FIX_K4 | CALIB_FIX_INTRINSIC) ? 0 : 1;
898
899 intrinsicLeft_errors.isEstimate = intrinsicLeft.isEstimate;
900 intrinsicRight_errors.isEstimate = intrinsicRight.isEstimate;
901
902 std::vector<int> selectedParams;
903 std::vector<int> tmp(6 * (n_images + 1), 1);
904 selectedParams.insert(selectedParams.end(), intrinsicLeft.isEstimate.begin(), intrinsicLeft.isEstimate.end());
905 selectedParams.insert(selectedParams.end(), intrinsicRight.isEstimate.begin(), intrinsicRight.isEstimate.end());
906 selectedParams.insert(selectedParams.end(), tmp.begin(), tmp.end());
907
908 //Init values for rotation and translation between two views
909 cv::Mat om_list(1, n_images, CV_64FC3), T_list(1, n_images, CV_64FC3);
910 cv::Mat om_ref, R_ref, T_ref, R1, R2;
911 for (int image_idx = 0; image_idx < n_images; ++image_idx)
912 {
913 cv::Rodrigues(rvecs1[image_idx], R1);
914 cv::Rodrigues(rvecs2[image_idx], R2);
915 R_ref = R2 * R1.t();
916 T_ref = cv::Mat(tvecs2[image_idx]) - R_ref * cv::Mat(tvecs1[image_idx]);
917 cv::Rodrigues(R_ref, om_ref);
918 om_ref.reshape(3, 1).copyTo(om_list.col(image_idx));
919 T_ref.reshape(3, 1).copyTo(T_list.col(image_idx));
920 }
921 cv::Vec3d omcur = cv::internal::median3d(om_list);
922 cv::Vec3d Tcur = cv::internal::median3d(T_list);
923
924 cv::Mat J = cv::Mat::zeros(4 * n_points * n_images, 18 + 6 * (n_images + 1), CV_64FC1),
925 e = cv::Mat::zeros(4 * n_points * n_images, 1, CV_64FC1), Jkk, ekk;
926 cv::Mat J2_inv;
927
928 for(int iter = 0; ; ++iter)
929 {
930 if ((criteria.type == 1 && iter >= criteria.maxCount) ||
931 (criteria.type == 2 && change <= criteria.epsilon) ||
932 (criteria.type == 3 && (change <= criteria.epsilon || iter >= criteria.maxCount)))
933 break;
934
935 J.create(4 * n_points * n_images, 18 + 6 * (n_images + 1), CV_64FC1);
936 e.create(4 * n_points * n_images, 1, CV_64FC1);
937 Jkk.create(4 * n_points, 18 + 6 * (n_images + 1), CV_64FC1);
938 ekk.create(4 * n_points, 1, CV_64FC1);
939
940 cv::Mat omr, Tr, domrdomckk, domrdTckk, domrdom, domrdT, dTrdomckk, dTrdTckk, dTrdom, dTrdT;
941
942 for (int image_idx = 0; image_idx < n_images; ++image_idx)
943 {
944 Jkk = cv::Mat::zeros(4 * n_points, 18 + 6 * (n_images + 1), CV_64FC1);
945
946 cv::Mat object = objectPoints.getMat(image_idx).clone();
947 cv::Mat imageLeft = imagePoints1.getMat(image_idx).clone();
948 cv::Mat imageRight = imagePoints2.getMat(image_idx).clone();
949 cv::Mat jacobians, projected;
950
951 //left camera jacobian
952 cv::Mat rvec = cv::Mat(rvecs1[image_idx]);
953 cv::Mat tvec = cv::Mat(tvecs1[image_idx]);
954 cv::internal::projectPoints(object, projected, rvec, tvec, intrinsicLeft, jacobians);
955 cv::Mat(cv::Mat((imageLeft - projected).t()).reshape(1, 1).t()).copyTo(ekk.rowRange(0, 2 * n_points));
956 jacobians.colRange(8, 11).copyTo(Jkk.colRange(24 + image_idx * 6, 27 + image_idx * 6).rowRange(0, 2 * n_points));
957 jacobians.colRange(11, 14).copyTo(Jkk.colRange(27 + image_idx * 6, 30 + image_idx * 6).rowRange(0, 2 * n_points));
958 jacobians.colRange(0, 2).copyTo(Jkk.colRange(0, 2).rowRange(0, 2 * n_points));
959 jacobians.colRange(2, 4).copyTo(Jkk.colRange(2, 4).rowRange(0, 2 * n_points));
960 jacobians.colRange(4, 8).copyTo(Jkk.colRange(5, 9).rowRange(0, 2 * n_points));
961 jacobians.col(14).copyTo(Jkk.col(4).rowRange(0, 2 * n_points));
962
963 //right camera jacobian
964 cv::internal::compose_motion(rvec, tvec, omcur, Tcur, omr, Tr, domrdomckk, domrdTckk, domrdom, domrdT, dTrdomckk, dTrdTckk, dTrdom, dTrdT);
965 rvec = cv::Mat(rvecs2[image_idx]);
966 tvec = cv::Mat(tvecs2[image_idx]);
967
968 cv::internal::projectPoints(object, projected, omr, Tr, intrinsicRight, jacobians);
969 cv::Mat(cv::Mat((imageRight - projected).t()).reshape(1, 1).t()).copyTo(ekk.rowRange(2 * n_points, 4 * n_points));
970 cv::Mat dxrdom = jacobians.colRange(8, 11) * domrdom + jacobians.colRange(11, 14) * dTrdom;
971 cv::Mat dxrdT = jacobians.colRange(8, 11) * domrdT + jacobians.colRange(11, 14)* dTrdT;
972 cv::Mat dxrdomckk = jacobians.colRange(8, 11) * domrdomckk + jacobians.colRange(11, 14) * dTrdomckk;
973 cv::Mat dxrdTckk = jacobians.colRange(8, 11) * domrdTckk + jacobians.colRange(11, 14) * dTrdTckk;
974
975 dxrdom.copyTo(Jkk.colRange(18, 21).rowRange(2 * n_points, 4 * n_points));
976 dxrdT.copyTo(Jkk.colRange(21, 24).rowRange(2 * n_points, 4 * n_points));
977 dxrdomckk.copyTo(Jkk.colRange(24 + image_idx * 6, 27 + image_idx * 6).rowRange(2 * n_points, 4 * n_points));
978 dxrdTckk.copyTo(Jkk.colRange(27 + image_idx * 6, 30 + image_idx * 6).rowRange(2 * n_points, 4 * n_points));
979 jacobians.colRange(0, 2).copyTo(Jkk.colRange(9 + 0, 9 + 2).rowRange(2 * n_points, 4 * n_points));
980 jacobians.colRange(2, 4).copyTo(Jkk.colRange(9 + 2, 9 + 4).rowRange(2 * n_points, 4 * n_points));
981 jacobians.colRange(4, 8).copyTo(Jkk.colRange(9 + 5, 9 + 9).rowRange(2 * n_points, 4 * n_points));
982 jacobians.col(14).copyTo(Jkk.col(9 + 4).rowRange(2 * n_points, 4 * n_points));
983
984 //check goodness of sterepair
985 double abs_max = 0;
986 for (int i = 0; i < 4 * n_points; i++)
987 {
988 if (fabs(ekk.at<double>(i)) > abs_max)
989 {
990 abs_max = fabs(ekk.at<double>(i));
991 }
992 }
993
994 CV_Assert(abs_max < threshold); // bad stereo pair
995
996 Jkk.copyTo(J.rowRange(image_idx * 4 * n_points, (image_idx + 1) * 4 * n_points));
997 ekk.copyTo(e.rowRange(image_idx * 4 * n_points, (image_idx + 1) * 4 * n_points));
998 }
999
1000 cv::Vec6d oldTom(Tcur[0], Tcur[1], Tcur[2], omcur[0], omcur[1], omcur[2]);
1001
1002 //update all parameters
1003 cv::subMatrix(J, J, selectedParams, std::vector<int>(J.rows, 1));
1004 cv::Mat J2 = J.t() * J;
1005 J2_inv = J2.inv();
1006 int a = cv::countNonZero(intrinsicLeft.isEstimate);
1007 int b = cv::countNonZero(intrinsicRight.isEstimate);
1008 cv::Mat deltas = J2_inv * J.t() * e;
1009 intrinsicLeft = intrinsicLeft + deltas.rowRange(0, a);
1010 intrinsicRight = intrinsicRight + deltas.rowRange(a, a + b);
1011 omcur = omcur + cv::Vec3d(deltas.rowRange(a + b, a + b + 3));
1012 Tcur = Tcur + cv::Vec3d(deltas.rowRange(a + b + 3, a + b + 6));
1013 for (int image_idx = 0; image_idx < n_images; ++image_idx)
1014 {
1015 rvecs1[image_idx] = cv::Mat(cv::Mat(rvecs1[image_idx]) + deltas.rowRange(a + b + 6 + image_idx * 6, a + b + 9 + image_idx * 6));
1016 tvecs1[image_idx] = cv::Mat(cv::Mat(tvecs1[image_idx]) + deltas.rowRange(a + b + 9 + image_idx * 6, a + b + 12 + image_idx * 6));
1017 }
1018
1019 cv::Vec6d newTom(Tcur[0], Tcur[1], Tcur[2], omcur[0], omcur[1], omcur[2]);
1020 change = cv::norm(newTom - oldTom) / cv::norm(newTom);
1021 }
1022
1023 double rms = 0;
1024 const Vec2d* ptr_e = e.ptr<Vec2d>();
1025 for (size_t i = 0; i < e.total() / 2; i++)
1026 {
1027 rms += ptr_e[i][0] * ptr_e[i][0] + ptr_e[i][1] * ptr_e[i][1];
1028 }
1029
1030 rms /= ((double)e.total() / 2.0);
1031 rms = sqrt(rms);
1032
1033 _K1 = Matx33d(intrinsicLeft.f[0], intrinsicLeft.f[0] * intrinsicLeft.alpha, intrinsicLeft.c[0],
1034 0, intrinsicLeft.f[1], intrinsicLeft.c[1],
1035 0, 0, 1);
1036
1037 _K2 = Matx33d(intrinsicRight.f[0], intrinsicRight.f[0] * intrinsicRight.alpha, intrinsicRight.c[0],
1038 0, intrinsicRight.f[1], intrinsicRight.c[1],
1039 0, 0, 1);
1040
1041 Mat _R;
1042 Rodrigues(omcur, _R);
1043
1044 if (K1.needed()) cv::Mat(_K1).convertTo(K1, K1.empty() ? CV_64FC1 : K1.type());
1045 if (K2.needed()) cv::Mat(_K2).convertTo(K2, K2.empty() ? CV_64FC1 : K2.type());
1046 if (D1.needed()) cv::Mat(intrinsicLeft.k).convertTo(D1, D1.empty() ? CV_64FC1 : D1.type());
1047 if (D2.needed()) cv::Mat(intrinsicRight.k).convertTo(D2, D2.empty() ? CV_64FC1 : D2.type());
1048 if (R.needed()) _R.convertTo(R, R.empty() ? CV_64FC1 : R.type());
1049 if (T.needed()) cv::Mat(Tcur).convertTo(T, T.empty() ? CV_64FC1 : T.type());
1050
1051 return rms;
1052 }
1053
1054 namespace cv{ namespace {
subMatrix(const Mat & src,Mat & dst,const std::vector<int> & cols,const std::vector<int> & rows)1055 void subMatrix(const Mat& src, Mat& dst, const std::vector<int>& cols, const std::vector<int>& rows)
1056 {
1057 CV_Assert(src.type() == CV_64FC1);
1058
1059 int nonzeros_cols = cv::countNonZero(cols);
1060 Mat tmp(src.rows, nonzeros_cols, CV_64FC1);
1061
1062 for (int i = 0, j = 0; i < (int)cols.size(); i++)
1063 {
1064 if (cols[i])
1065 {
1066 src.col(i).copyTo(tmp.col(j++));
1067 }
1068 }
1069
1070 int nonzeros_rows = cv::countNonZero(rows);
1071 Mat tmp1(nonzeros_rows, nonzeros_cols, CV_64FC1);
1072 for (int i = 0, j = 0; i < (int)rows.size(); i++)
1073 {
1074 if (rows[i])
1075 {
1076 tmp.row(i).copyTo(tmp1.row(j++));
1077 }
1078 }
1079
1080 dst = tmp1.clone();
1081 }
1082
1083 }}
1084
IntrinsicParams()1085 cv::internal::IntrinsicParams::IntrinsicParams():
1086 f(Vec2d::all(0)), c(Vec2d::all(0)), k(Vec4d::all(0)), alpha(0), isEstimate(9,0)
1087 {
1088 }
1089
IntrinsicParams(Vec2d _f,Vec2d _c,Vec4d _k,double _alpha)1090 cv::internal::IntrinsicParams::IntrinsicParams(Vec2d _f, Vec2d _c, Vec4d _k, double _alpha):
1091 f(_f), c(_c), k(_k), alpha(_alpha), isEstimate(9,0)
1092 {
1093 }
1094
operator +(const Mat & a)1095 cv::internal::IntrinsicParams cv::internal::IntrinsicParams::operator+(const Mat& a)
1096 {
1097 CV_Assert(a.type() == CV_64FC1);
1098 IntrinsicParams tmp;
1099 const double* ptr = a.ptr<double>();
1100
1101 int j = 0;
1102 tmp.f[0] = this->f[0] + (isEstimate[0] ? ptr[j++] : 0);
1103 tmp.f[1] = this->f[1] + (isEstimate[1] ? ptr[j++] : 0);
1104 tmp.c[0] = this->c[0] + (isEstimate[2] ? ptr[j++] : 0);
1105 tmp.alpha = this->alpha + (isEstimate[4] ? ptr[j++] : 0);
1106 tmp.c[1] = this->c[1] + (isEstimate[3] ? ptr[j++] : 0);
1107 tmp.k[0] = this->k[0] + (isEstimate[5] ? ptr[j++] : 0);
1108 tmp.k[1] = this->k[1] + (isEstimate[6] ? ptr[j++] : 0);
1109 tmp.k[2] = this->k[2] + (isEstimate[7] ? ptr[j++] : 0);
1110 tmp.k[3] = this->k[3] + (isEstimate[8] ? ptr[j++] : 0);
1111
1112 tmp.isEstimate = isEstimate;
1113 return tmp;
1114 }
1115
operator =(const Mat & a)1116 cv::internal::IntrinsicParams& cv::internal::IntrinsicParams::operator =(const Mat& a)
1117 {
1118 CV_Assert(a.type() == CV_64FC1);
1119 const double* ptr = a.ptr<double>();
1120
1121 int j = 0;
1122
1123 this->f[0] = isEstimate[0] ? ptr[j++] : 0;
1124 this->f[1] = isEstimate[1] ? ptr[j++] : 0;
1125 this->c[0] = isEstimate[2] ? ptr[j++] : 0;
1126 this->c[1] = isEstimate[3] ? ptr[j++] : 0;
1127 this->alpha = isEstimate[4] ? ptr[j++] : 0;
1128 this->k[0] = isEstimate[5] ? ptr[j++] : 0;
1129 this->k[1] = isEstimate[6] ? ptr[j++] : 0;
1130 this->k[2] = isEstimate[7] ? ptr[j++] : 0;
1131 this->k[3] = isEstimate[8] ? ptr[j++] : 0;
1132
1133 return *this;
1134 }
1135
Init(const cv::Vec2d & _f,const cv::Vec2d & _c,const cv::Vec4d & _k,const double & _alpha)1136 void cv::internal::IntrinsicParams::Init(const cv::Vec2d& _f, const cv::Vec2d& _c, const cv::Vec4d& _k, const double& _alpha)
1137 {
1138 this->c = _c;
1139 this->f = _f;
1140 this->k = _k;
1141 this->alpha = _alpha;
1142 }
1143
projectPoints(cv::InputArray objectPoints,cv::OutputArray imagePoints,cv::InputArray _rvec,cv::InputArray _tvec,const IntrinsicParams & param,cv::OutputArray jacobian)1144 void cv::internal::projectPoints(cv::InputArray objectPoints, cv::OutputArray imagePoints,
1145 cv::InputArray _rvec,cv::InputArray _tvec,
1146 const IntrinsicParams& param, cv::OutputArray jacobian)
1147 {
1148 CV_Assert(!objectPoints.empty() && objectPoints.type() == CV_64FC3);
1149 Matx33d K(param.f[0], param.f[0] * param.alpha, param.c[0],
1150 0, param.f[1], param.c[1],
1151 0, 0, 1);
1152 fisheye::projectPoints(objectPoints, imagePoints, _rvec, _tvec, K, param.k, param.alpha, jacobian);
1153 }
1154
ComputeExtrinsicRefine(const Mat & imagePoints,const Mat & objectPoints,Mat & rvec,Mat & tvec,Mat & J,const int MaxIter,const IntrinsicParams & param,const double thresh_cond)1155 void cv::internal::ComputeExtrinsicRefine(const Mat& imagePoints, const Mat& objectPoints, Mat& rvec,
1156 Mat& tvec, Mat& J, const int MaxIter,
1157 const IntrinsicParams& param, const double thresh_cond)
1158 {
1159 CV_Assert(!objectPoints.empty() && objectPoints.type() == CV_64FC3);
1160 CV_Assert(!imagePoints.empty() && imagePoints.type() == CV_64FC2);
1161 Vec6d extrinsics(rvec.at<double>(0), rvec.at<double>(1), rvec.at<double>(2),
1162 tvec.at<double>(0), tvec.at<double>(1), tvec.at<double>(2));
1163 double change = 1;
1164 int iter = 0;
1165
1166 while (change > 1e-10 && iter < MaxIter)
1167 {
1168 std::vector<Point2d> x;
1169 Mat jacobians;
1170 projectPoints(objectPoints, x, rvec, tvec, param, jacobians);
1171
1172 Mat ex = imagePoints - Mat(x).t();
1173 ex = ex.reshape(1, 2);
1174
1175 J = jacobians.colRange(8, 14).clone();
1176
1177 SVD svd(J, SVD::NO_UV);
1178 double condJJ = svd.w.at<double>(0)/svd.w.at<double>(5);
1179
1180 if (condJJ > thresh_cond)
1181 change = 0;
1182 else
1183 {
1184 Vec6d param_innov;
1185 solve(J, ex.reshape(1, (int)ex.total()), param_innov, DECOMP_SVD + DECOMP_NORMAL);
1186
1187 Vec6d param_up = extrinsics + param_innov;
1188 change = norm(param_innov)/norm(param_up);
1189 extrinsics = param_up;
1190 iter = iter + 1;
1191
1192 rvec = Mat(Vec3d(extrinsics.val));
1193 tvec = Mat(Vec3d(extrinsics.val+3));
1194 }
1195 }
1196 }
1197
ComputeHomography(Mat m,Mat M)1198 cv::Mat cv::internal::ComputeHomography(Mat m, Mat M)
1199 {
1200 int Np = m.cols;
1201
1202 if (m.rows < 3)
1203 {
1204 vconcat(m, Mat::ones(1, Np, CV_64FC1), m);
1205 }
1206 if (M.rows < 3)
1207 {
1208 vconcat(M, Mat::ones(1, Np, CV_64FC1), M);
1209 }
1210
1211 divide(m, Mat::ones(3, 1, CV_64FC1) * m.row(2), m);
1212 divide(M, Mat::ones(3, 1, CV_64FC1) * M.row(2), M);
1213
1214 Mat ax = m.row(0).clone();
1215 Mat ay = m.row(1).clone();
1216
1217 double mxx = mean(ax)[0];
1218 double myy = mean(ay)[0];
1219
1220 ax = ax - mxx;
1221 ay = ay - myy;
1222
1223 double scxx = mean(abs(ax))[0];
1224 double scyy = mean(abs(ay))[0];
1225
1226 Mat Hnorm (Matx33d( 1/scxx, 0.0, -mxx/scxx,
1227 0.0, 1/scyy, -myy/scyy,
1228 0.0, 0.0, 1.0 ));
1229
1230 Mat inv_Hnorm (Matx33d( scxx, 0, mxx,
1231 0, scyy, myy,
1232 0, 0, 1 ));
1233 Mat mn = Hnorm * m;
1234
1235 Mat L = Mat::zeros(2*Np, 9, CV_64FC1);
1236
1237 for (int i = 0; i < Np; ++i)
1238 {
1239 for (int j = 0; j < 3; j++)
1240 {
1241 L.at<double>(2 * i, j) = M.at<double>(j, i);
1242 L.at<double>(2 * i + 1, j + 3) = M.at<double>(j, i);
1243 L.at<double>(2 * i, j + 6) = -mn.at<double>(0,i) * M.at<double>(j, i);
1244 L.at<double>(2 * i + 1, j + 6) = -mn.at<double>(1,i) * M.at<double>(j, i);
1245 }
1246 }
1247
1248 if (Np > 4) L = L.t() * L;
1249 SVD svd(L);
1250 Mat hh = svd.vt.row(8) / svd.vt.row(8).at<double>(8);
1251 Mat Hrem = hh.reshape(1, 3);
1252 Mat H = inv_Hnorm * Hrem;
1253
1254 if (Np > 4)
1255 {
1256 Mat hhv = H.reshape(1, 9)(Rect(0, 0, 1, 8)).clone();
1257 for (int iter = 0; iter < 10; iter++)
1258 {
1259 Mat mrep = H * M;
1260 Mat J = Mat::zeros(2 * Np, 8, CV_64FC1);
1261 Mat MMM;
1262 divide(M, Mat::ones(3, 1, CV_64FC1) * mrep(Rect(0, 2, mrep.cols, 1)), MMM);
1263 divide(mrep, Mat::ones(3, 1, CV_64FC1) * mrep(Rect(0, 2, mrep.cols, 1)), mrep);
1264 Mat m_err = m(Rect(0,0, m.cols, 2)) - mrep(Rect(0,0, mrep.cols, 2));
1265 m_err = Mat(m_err.t()).reshape(1, m_err.cols * m_err.rows);
1266 Mat MMM2, MMM3;
1267 multiply(Mat::ones(3, 1, CV_64FC1) * mrep(Rect(0, 0, mrep.cols, 1)), MMM, MMM2);
1268 multiply(Mat::ones(3, 1, CV_64FC1) * mrep(Rect(0, 1, mrep.cols, 1)), MMM, MMM3);
1269
1270 for (int i = 0; i < Np; ++i)
1271 {
1272 for (int j = 0; j < 3; ++j)
1273 {
1274 J.at<double>(2 * i, j) = -MMM.at<double>(j, i);
1275 J.at<double>(2 * i + 1, j + 3) = -MMM.at<double>(j, i);
1276 }
1277
1278 for (int j = 0; j < 2; ++j)
1279 {
1280 J.at<double>(2 * i, j + 6) = MMM2.at<double>(j, i);
1281 J.at<double>(2 * i + 1, j + 6) = MMM3.at<double>(j, i);
1282 }
1283 }
1284 divide(M, Mat::ones(3, 1, CV_64FC1) * mrep(Rect(0,2,mrep.cols,1)), MMM);
1285 Mat hh_innov = (J.t() * J).inv() * (J.t()) * m_err;
1286 Mat hhv_up = hhv - hh_innov;
1287 Mat tmp;
1288 vconcat(hhv_up, Mat::ones(1,1,CV_64FC1), tmp);
1289 Mat H_up = tmp.reshape(1,3);
1290 hhv = hhv_up;
1291 H = H_up;
1292 }
1293 }
1294 return H;
1295 }
1296
NormalizePixels(const Mat & imagePoints,const IntrinsicParams & param)1297 cv::Mat cv::internal::NormalizePixels(const Mat& imagePoints, const IntrinsicParams& param)
1298 {
1299 CV_Assert(!imagePoints.empty() && imagePoints.type() == CV_64FC2);
1300
1301 Mat distorted((int)imagePoints.total(), 1, CV_64FC2), undistorted;
1302 const Vec2d* ptr = imagePoints.ptr<Vec2d>(0);
1303 Vec2d* ptr_d = distorted.ptr<Vec2d>(0);
1304 for (size_t i = 0; i < imagePoints.total(); ++i)
1305 {
1306 ptr_d[i] = (ptr[i] - param.c).mul(Vec2d(1.0 / param.f[0], 1.0 / param.f[1]));
1307 ptr_d[i][0] = ptr_d[i][0] - param.alpha * ptr_d[i][1];
1308 }
1309 cv::fisheye::undistortPoints(distorted, undistorted, Matx33d::eye(), param.k);
1310 return undistorted;
1311 }
1312
InitExtrinsics(const Mat & _imagePoints,const Mat & _objectPoints,const IntrinsicParams & param,Mat & omckk,Mat & Tckk)1313 void cv::internal::InitExtrinsics(const Mat& _imagePoints, const Mat& _objectPoints, const IntrinsicParams& param, Mat& omckk, Mat& Tckk)
1314 {
1315
1316 CV_Assert(!_objectPoints.empty() && _objectPoints.type() == CV_64FC3);
1317 CV_Assert(!_imagePoints.empty() && _imagePoints.type() == CV_64FC2);
1318
1319 Mat imagePointsNormalized = NormalizePixels(_imagePoints.t(), param).reshape(1).t();
1320 Mat objectPoints = Mat(_objectPoints.t()).reshape(1).t();
1321 Mat objectPointsMean, covObjectPoints;
1322 Mat Rckk;
1323 int Np = imagePointsNormalized.cols;
1324 calcCovarMatrix(objectPoints, covObjectPoints, objectPointsMean, COVAR_NORMAL | COVAR_COLS);
1325 SVD svd(covObjectPoints);
1326 Mat R(svd.vt);
1327 if (norm(R(Rect(2, 0, 1, 2))) < 1e-6)
1328 R = Mat::eye(3,3, CV_64FC1);
1329 if (determinant(R) < 0)
1330 R = -R;
1331 Mat T = -R * objectPointsMean;
1332 Mat X_new = R * objectPoints + T * Mat::ones(1, Np, CV_64FC1);
1333 Mat H = ComputeHomography(imagePointsNormalized, X_new(Rect(0,0,X_new.cols,2)));
1334 double sc = .5 * (norm(H.col(0)) + norm(H.col(1)));
1335 H = H / sc;
1336 Mat u1 = H.col(0).clone();
1337 u1 = u1 / norm(u1);
1338 Mat u2 = H.col(1).clone() - u1.dot(H.col(1).clone()) * u1;
1339 u2 = u2 / norm(u2);
1340 Mat u3 = u1.cross(u2);
1341 Mat RRR;
1342 hconcat(u1, u2, RRR);
1343 hconcat(RRR, u3, RRR);
1344 Rodrigues(RRR, omckk);
1345 Rodrigues(omckk, Rckk);
1346 Tckk = H.col(2).clone();
1347 Tckk = Tckk + Rckk * T;
1348 Rckk = Rckk * R;
1349 Rodrigues(Rckk, omckk);
1350 }
1351
CalibrateExtrinsics(InputArrayOfArrays objectPoints,InputArrayOfArrays imagePoints,const IntrinsicParams & param,const int check_cond,const double thresh_cond,InputOutputArray omc,InputOutputArray Tc)1352 void cv::internal::CalibrateExtrinsics(InputArrayOfArrays objectPoints, InputArrayOfArrays imagePoints,
1353 const IntrinsicParams& param, const int check_cond,
1354 const double thresh_cond, InputOutputArray omc, InputOutputArray Tc)
1355 {
1356 CV_Assert(!objectPoints.empty() && (objectPoints.type() == CV_32FC3 || objectPoints.type() == CV_64FC3));
1357 CV_Assert(!imagePoints.empty() && (imagePoints.type() == CV_32FC2 || imagePoints.type() == CV_64FC2));
1358 CV_Assert(omc.type() == CV_64FC3 || Tc.type() == CV_64FC3);
1359
1360 if (omc.empty()) omc.create(1, (int)objectPoints.total(), CV_64FC3);
1361 if (Tc.empty()) Tc.create(1, (int)objectPoints.total(), CV_64FC3);
1362
1363 const int maxIter = 20;
1364
1365 for(int image_idx = 0; image_idx < (int)imagePoints.total(); ++image_idx)
1366 {
1367 Mat omckk, Tckk, JJ_kk;
1368 Mat image, object;
1369
1370 objectPoints.getMat(image_idx).convertTo(object, CV_64FC3);
1371 imagePoints.getMat (image_idx).convertTo(image, CV_64FC2);
1372
1373 InitExtrinsics(image, object, param, omckk, Tckk);
1374
1375 ComputeExtrinsicRefine(image, object, omckk, Tckk, JJ_kk, maxIter, param, thresh_cond);
1376 if (check_cond)
1377 {
1378 SVD svd(JJ_kk, SVD::NO_UV);
1379 CV_Assert(svd.w.at<double>(0) / svd.w.at<double>((int)svd.w.total() - 1) < thresh_cond);
1380 }
1381 omckk.reshape(3,1).copyTo(omc.getMat().col(image_idx));
1382 Tckk.reshape(3,1).copyTo(Tc.getMat().col(image_idx));
1383 }
1384 }
1385
1386
ComputeJacobians(InputArrayOfArrays objectPoints,InputArrayOfArrays imagePoints,const IntrinsicParams & param,InputArray omc,InputArray Tc,const int & check_cond,const double & thresh_cond,Mat & JJ2_inv,Mat & ex3)1387 void cv::internal::ComputeJacobians(InputArrayOfArrays objectPoints, InputArrayOfArrays imagePoints,
1388 const IntrinsicParams& param, InputArray omc, InputArray Tc,
1389 const int& check_cond, const double& thresh_cond, Mat& JJ2_inv, Mat& ex3)
1390 {
1391 CV_Assert(!objectPoints.empty() && (objectPoints.type() == CV_32FC3 || objectPoints.type() == CV_64FC3));
1392 CV_Assert(!imagePoints.empty() && (imagePoints.type() == CV_32FC2 || imagePoints.type() == CV_64FC2));
1393
1394 CV_Assert(!omc.empty() && omc.type() == CV_64FC3);
1395 CV_Assert(!Tc.empty() && Tc.type() == CV_64FC3);
1396
1397 int n = (int)objectPoints.total();
1398
1399 Mat JJ3 = Mat::zeros(9 + 6 * n, 9 + 6 * n, CV_64FC1);
1400 ex3 = Mat::zeros(9 + 6 * n, 1, CV_64FC1 );
1401
1402 for (int image_idx = 0; image_idx < n; ++image_idx)
1403 {
1404 Mat image, object;
1405 objectPoints.getMat(image_idx).convertTo(object, CV_64FC3);
1406 imagePoints.getMat (image_idx).convertTo(image, CV_64FC2);
1407
1408 Mat om(omc.getMat().col(image_idx)), T(Tc.getMat().col(image_idx));
1409
1410 std::vector<Point2d> x;
1411 Mat jacobians;
1412 projectPoints(object, x, om, T, param, jacobians);
1413 Mat exkk = image.t() - Mat(x);
1414
1415 Mat A(jacobians.rows, 9, CV_64FC1);
1416 jacobians.colRange(0, 4).copyTo(A.colRange(0, 4));
1417 jacobians.col(14).copyTo(A.col(4));
1418 jacobians.colRange(4, 8).copyTo(A.colRange(5, 9));
1419
1420 A = A.t();
1421
1422 Mat B = jacobians.colRange(8, 14).clone();
1423 B = B.t();
1424
1425 JJ3(Rect(0, 0, 9, 9)) = JJ3(Rect(0, 0, 9, 9)) + A * A.t();
1426 JJ3(Rect(9 + 6 * image_idx, 9 + 6 * image_idx, 6, 6)) = B * B.t();
1427
1428 Mat AB = A * B.t();
1429 AB.copyTo(JJ3(Rect(9 + 6 * image_idx, 0, 6, 9)));
1430
1431 JJ3(Rect(0, 9 + 6 * image_idx, 9, 6)) = AB.t();
1432 ex3(Rect(0,0,1,9)) = ex3(Rect(0,0,1,9)) + A * exkk.reshape(1, 2 * exkk.rows);
1433
1434 ex3(Rect(0, 9 + 6 * image_idx, 1, 6)) = B * exkk.reshape(1, 2 * exkk.rows);
1435
1436 if (check_cond)
1437 {
1438 Mat JJ_kk = B.t();
1439 SVD svd(JJ_kk, SVD::NO_UV);
1440 CV_Assert(svd.w.at<double>(0) / svd.w.at<double>(svd.w.rows - 1) < thresh_cond);
1441 }
1442 }
1443
1444 std::vector<int> idxs(param.isEstimate);
1445 idxs.insert(idxs.end(), 6 * n, 1);
1446
1447 subMatrix(JJ3, JJ3, idxs, idxs);
1448 subMatrix(ex3, ex3, std::vector<int>(1, 1), idxs);
1449 JJ2_inv = JJ3.inv();
1450 }
1451
EstimateUncertainties(InputArrayOfArrays objectPoints,InputArrayOfArrays imagePoints,const IntrinsicParams & params,InputArray omc,InputArray Tc,IntrinsicParams & errors,Vec2d & std_err,double thresh_cond,int check_cond,double & rms)1452 void cv::internal::EstimateUncertainties(InputArrayOfArrays objectPoints, InputArrayOfArrays imagePoints,
1453 const IntrinsicParams& params, InputArray omc, InputArray Tc,
1454 IntrinsicParams& errors, Vec2d& std_err, double thresh_cond, int check_cond, double& rms)
1455 {
1456 CV_Assert(!objectPoints.empty() && (objectPoints.type() == CV_32FC3 || objectPoints.type() == CV_64FC3));
1457 CV_Assert(!imagePoints.empty() && (imagePoints.type() == CV_32FC2 || imagePoints.type() == CV_64FC2));
1458
1459 CV_Assert(!omc.empty() && omc.type() == CV_64FC3);
1460 CV_Assert(!Tc.empty() && Tc.type() == CV_64FC3);
1461
1462 Mat ex((int)(objectPoints.getMat(0).total() * objectPoints.total()), 1, CV_64FC2);
1463
1464 for (int image_idx = 0; image_idx < (int)objectPoints.total(); ++image_idx)
1465 {
1466 Mat image, object;
1467 objectPoints.getMat(image_idx).convertTo(object, CV_64FC3);
1468 imagePoints.getMat (image_idx).convertTo(image, CV_64FC2);
1469
1470 Mat om(omc.getMat().col(image_idx)), T(Tc.getMat().col(image_idx));
1471
1472 std::vector<Point2d> x;
1473 projectPoints(object, x, om, T, params, noArray());
1474 Mat ex_ = image.t() - Mat(x);
1475 ex_.copyTo(ex.rowRange(ex_.rows * image_idx, ex_.rows * (image_idx + 1)));
1476 }
1477
1478 meanStdDev(ex, noArray(), std_err);
1479 std_err *= sqrt((double)ex.total()/((double)ex.total() - 1.0));
1480
1481 Mat sigma_x;
1482 meanStdDev(ex.reshape(1, 1), noArray(), sigma_x);
1483 sigma_x *= sqrt(2.0 * (double)ex.total()/(2.0 * (double)ex.total() - 1.0));
1484
1485 Mat _JJ2_inv, ex3;
1486 ComputeJacobians(objectPoints, imagePoints, params, omc, Tc, check_cond, thresh_cond, _JJ2_inv, ex3);
1487
1488 Mat_<double>& JJ2_inv = (Mat_<double>&)_JJ2_inv;
1489
1490 sqrt(JJ2_inv, JJ2_inv);
1491
1492 double s = sigma_x.at<double>(0);
1493 Mat r = 3 * s * JJ2_inv.diag();
1494 errors = r;
1495
1496 rms = 0;
1497 const Vec2d* ptr_ex = ex.ptr<Vec2d>();
1498 for (size_t i = 0; i < ex.total(); i++)
1499 {
1500 rms += ptr_ex[i][0] * ptr_ex[i][0] + ptr_ex[i][1] * ptr_ex[i][1];
1501 }
1502
1503 rms /= (double)ex.total();
1504 rms = sqrt(rms);
1505 }
1506
dAB(InputArray A,InputArray B,OutputArray dABdA,OutputArray dABdB)1507 void cv::internal::dAB(InputArray A, InputArray B, OutputArray dABdA, OutputArray dABdB)
1508 {
1509 CV_Assert(A.getMat().cols == B.getMat().rows);
1510 CV_Assert(A.type() == CV_64FC1 && B.type() == CV_64FC1);
1511
1512 int p = A.getMat().rows;
1513 int n = A.getMat().cols;
1514 int q = B.getMat().cols;
1515
1516 dABdA.create(p * q, p * n, CV_64FC1);
1517 dABdB.create(p * q, q * n, CV_64FC1);
1518
1519 dABdA.getMat() = Mat::zeros(p * q, p * n, CV_64FC1);
1520 dABdB.getMat() = Mat::zeros(p * q, q * n, CV_64FC1);
1521
1522 for (int i = 0; i < q; ++i)
1523 {
1524 for (int j = 0; j < p; ++j)
1525 {
1526 int ij = j + i * p;
1527 for (int k = 0; k < n; ++k)
1528 {
1529 int kj = j + k * p;
1530 dABdA.getMat().at<double>(ij, kj) = B.getMat().at<double>(k, i);
1531 }
1532 }
1533 }
1534
1535 for (int i = 0; i < q; ++i)
1536 {
1537 A.getMat().copyTo(dABdB.getMat().rowRange(i * p, i * p + p).colRange(i * n, i * n + n));
1538 }
1539 }
1540
JRodriguesMatlab(const Mat & src,Mat & dst)1541 void cv::internal::JRodriguesMatlab(const Mat& src, Mat& dst)
1542 {
1543 Mat tmp(src.cols, src.rows, src.type());
1544 if (src.rows == 9)
1545 {
1546 Mat(src.row(0).t()).copyTo(tmp.col(0));
1547 Mat(src.row(1).t()).copyTo(tmp.col(3));
1548 Mat(src.row(2).t()).copyTo(tmp.col(6));
1549 Mat(src.row(3).t()).copyTo(tmp.col(1));
1550 Mat(src.row(4).t()).copyTo(tmp.col(4));
1551 Mat(src.row(5).t()).copyTo(tmp.col(7));
1552 Mat(src.row(6).t()).copyTo(tmp.col(2));
1553 Mat(src.row(7).t()).copyTo(tmp.col(5));
1554 Mat(src.row(8).t()).copyTo(tmp.col(8));
1555 }
1556 else
1557 {
1558 Mat(src.col(0).t()).copyTo(tmp.row(0));
1559 Mat(src.col(1).t()).copyTo(tmp.row(3));
1560 Mat(src.col(2).t()).copyTo(tmp.row(6));
1561 Mat(src.col(3).t()).copyTo(tmp.row(1));
1562 Mat(src.col(4).t()).copyTo(tmp.row(4));
1563 Mat(src.col(5).t()).copyTo(tmp.row(7));
1564 Mat(src.col(6).t()).copyTo(tmp.row(2));
1565 Mat(src.col(7).t()).copyTo(tmp.row(5));
1566 Mat(src.col(8).t()).copyTo(tmp.row(8));
1567 }
1568 dst = tmp.clone();
1569 }
1570
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)1571 void cv::internal::compose_motion(InputArray _om1, InputArray _T1, InputArray _om2, InputArray _T2,
1572 Mat& om3, Mat& T3, Mat& dom3dom1, Mat& dom3dT1, Mat& dom3dom2,
1573 Mat& dom3dT2, Mat& dT3dom1, Mat& dT3dT1, Mat& dT3dom2, Mat& dT3dT2)
1574 {
1575 Mat om1 = _om1.getMat();
1576 Mat om2 = _om2.getMat();
1577 Mat T1 = _T1.getMat().reshape(1, 3);
1578 Mat T2 = _T2.getMat().reshape(1, 3);
1579
1580 //% Rotations:
1581 Mat R1, R2, R3, dR1dom1(9, 3, CV_64FC1), dR2dom2;
1582 Rodrigues(om1, R1, dR1dom1);
1583 Rodrigues(om2, R2, dR2dom2);
1584 JRodriguesMatlab(dR1dom1, dR1dom1);
1585 JRodriguesMatlab(dR2dom2, dR2dom2);
1586 R3 = R2 * R1;
1587 Mat dR3dR2, dR3dR1;
1588 dAB(R2, R1, dR3dR2, dR3dR1);
1589 Mat dom3dR3;
1590 Rodrigues(R3, om3, dom3dR3);
1591 JRodriguesMatlab(dom3dR3, dom3dR3);
1592 dom3dom1 = dom3dR3 * dR3dR1 * dR1dom1;
1593 dom3dom2 = dom3dR3 * dR3dR2 * dR2dom2;
1594 dom3dT1 = Mat::zeros(3, 3, CV_64FC1);
1595 dom3dT2 = Mat::zeros(3, 3, CV_64FC1);
1596
1597 //% Translations:
1598 Mat T3t = R2 * T1;
1599 Mat dT3tdR2, dT3tdT1;
1600 dAB(R2, T1, dT3tdR2, dT3tdT1);
1601 Mat dT3tdom2 = dT3tdR2 * dR2dom2;
1602 T3 = T3t + T2;
1603 dT3dT1 = dT3tdT1;
1604 dT3dT2 = Mat::eye(3, 3, CV_64FC1);
1605 dT3dom2 = dT3tdom2;
1606 dT3dom1 = Mat::zeros(3, 3, CV_64FC1);
1607 }
1608
median(const Mat & row)1609 double cv::internal::median(const Mat& row)
1610 {
1611 CV_Assert(row.type() == CV_64FC1);
1612 CV_Assert(!row.empty() && row.rows == 1);
1613 Mat tmp = row.clone();
1614 sort(tmp, tmp, 0);
1615 if ((int)tmp.total() % 2) return tmp.at<double>((int)tmp.total() / 2);
1616 else return 0.5 *(tmp.at<double>((int)tmp.total() / 2) + tmp.at<double>((int)tmp.total() / 2 - 1));
1617 }
1618
median3d(InputArray m)1619 cv::Vec3d cv::internal::median3d(InputArray m)
1620 {
1621 CV_Assert(m.depth() == CV_64F && m.getMat().rows == 1);
1622 Mat M = Mat(m.getMat().t()).reshape(1).t();
1623 return Vec3d(median(M.row(0)), median(M.row(1)), median(M.row(2)));
1624 }
1625