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, Intel Corporation, all rights reserved.
14 // Copyright (C) 2013, OpenCV Foundation, 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 "rho.h"
45 #include <iostream>
46 
47 namespace cv
48 {
49 
haveCollinearPoints(const Mat & m,int count)50 static bool haveCollinearPoints( const Mat& m, int count )
51 {
52     int j, k, i = count-1;
53     const Point2f* ptr = m.ptr<Point2f>();
54 
55     // check that the i-th selected point does not belong
56     // to a line connecting some previously selected points
57     for( j = 0; j < i; j++ )
58     {
59         double dx1 = ptr[j].x - ptr[i].x;
60         double dy1 = ptr[j].y - ptr[i].y;
61         for( k = 0; k < j; k++ )
62         {
63             double dx2 = ptr[k].x - ptr[i].x;
64             double dy2 = ptr[k].y - ptr[i].y;
65             if( fabs(dx2*dy1 - dy2*dx1) <= FLT_EPSILON*(fabs(dx1) + fabs(dy1) + fabs(dx2) + fabs(dy2)))
66                 return true;
67         }
68     }
69     return false;
70 }
71 
72 
73 class HomographyEstimatorCallback : public PointSetRegistrator::Callback
74 {
75 public:
checkSubset(InputArray _ms1,InputArray _ms2,int count) const76     bool checkSubset( InputArray _ms1, InputArray _ms2, int count ) const
77     {
78         Mat ms1 = _ms1.getMat(), ms2 = _ms2.getMat();
79         if( haveCollinearPoints(ms1, count) || haveCollinearPoints(ms2, count) )
80             return false;
81 
82         // We check whether the minimal set of points for the homography estimation
83         // are geometrically consistent. We check if every 3 correspondences sets
84         // fulfills the constraint.
85         //
86         // The usefullness of this constraint is explained in the paper:
87         //
88         // "Speeding-up homography estimation in mobile devices"
89         // Journal of Real-Time Image Processing. 2013. DOI: 10.1007/s11554-012-0314-1
90         // Pablo Marquez-Neila, Javier Lopez-Alberca, Jose M. Buenaposada, Luis Baumela
91         if( count == 4 )
92         {
93             static const int tt[][3] = {{0, 1, 2}, {1, 2, 3}, {0, 2, 3}, {0, 1, 3}};
94             const Point2f* src = ms1.ptr<Point2f>();
95             const Point2f* dst = ms2.ptr<Point2f>();
96             int negative = 0;
97 
98             for( int i = 0; i < 4; i++ )
99             {
100                 const int* t = tt[i];
101                 Matx33d A(src[t[0]].x, src[t[0]].y, 1., src[t[1]].x, src[t[1]].y, 1., src[t[2]].x, src[t[2]].y, 1.);
102                 Matx33d B(dst[t[0]].x, dst[t[0]].y, 1., dst[t[1]].x, dst[t[1]].y, 1., dst[t[2]].x, dst[t[2]].y, 1.);
103 
104                 negative += determinant(A)*determinant(B) < 0;
105             }
106             if( negative != 0 && negative != 4 )
107                 return false;
108         }
109 
110         return true;
111     }
112 
runKernel(InputArray _m1,InputArray _m2,OutputArray _model) const113     int runKernel( InputArray _m1, InputArray _m2, OutputArray _model ) const
114     {
115         Mat m1 = _m1.getMat(), m2 = _m2.getMat();
116         int i, count = m1.checkVector(2);
117         const Point2f* M = m1.ptr<Point2f>();
118         const Point2f* m = m2.ptr<Point2f>();
119 
120         double LtL[9][9], W[9][1], V[9][9];
121         Mat _LtL( 9, 9, CV_64F, &LtL[0][0] );
122         Mat matW( 9, 1, CV_64F, W );
123         Mat matV( 9, 9, CV_64F, V );
124         Mat _H0( 3, 3, CV_64F, V[8] );
125         Mat _Htemp( 3, 3, CV_64F, V[7] );
126         Point2d cM(0,0), cm(0,0), sM(0,0), sm(0,0);
127 
128         for( i = 0; i < count; i++ )
129         {
130             cm.x += m[i].x; cm.y += m[i].y;
131             cM.x += M[i].x; cM.y += M[i].y;
132         }
133 
134         cm.x /= count;
135         cm.y /= count;
136         cM.x /= count;
137         cM.y /= count;
138 
139         for( i = 0; i < count; i++ )
140         {
141             sm.x += fabs(m[i].x - cm.x);
142             sm.y += fabs(m[i].y - cm.y);
143             sM.x += fabs(M[i].x - cM.x);
144             sM.y += fabs(M[i].y - cM.y);
145         }
146 
147         if( fabs(sm.x) < DBL_EPSILON || fabs(sm.y) < DBL_EPSILON ||
148             fabs(sM.x) < DBL_EPSILON || fabs(sM.y) < DBL_EPSILON )
149             return 0;
150         sm.x = count/sm.x; sm.y = count/sm.y;
151         sM.x = count/sM.x; sM.y = count/sM.y;
152 
153         double invHnorm[9] = { 1./sm.x, 0, cm.x, 0, 1./sm.y, cm.y, 0, 0, 1 };
154         double Hnorm2[9] = { sM.x, 0, -cM.x*sM.x, 0, sM.y, -cM.y*sM.y, 0, 0, 1 };
155         Mat _invHnorm( 3, 3, CV_64FC1, invHnorm );
156         Mat _Hnorm2( 3, 3, CV_64FC1, Hnorm2 );
157 
158         _LtL.setTo(Scalar::all(0));
159         for( i = 0; i < count; i++ )
160         {
161             double x = (m[i].x - cm.x)*sm.x, y = (m[i].y - cm.y)*sm.y;
162             double X = (M[i].x - cM.x)*sM.x, Y = (M[i].y - cM.y)*sM.y;
163             double Lx[] = { X, Y, 1, 0, 0, 0, -x*X, -x*Y, -x };
164             double Ly[] = { 0, 0, 0, X, Y, 1, -y*X, -y*Y, -y };
165             int j, k;
166             for( j = 0; j < 9; j++ )
167                 for( k = j; k < 9; k++ )
168                     LtL[j][k] += Lx[j]*Lx[k] + Ly[j]*Ly[k];
169         }
170         completeSymm( _LtL );
171 
172         eigen( _LtL, matW, matV );
173         _Htemp = _invHnorm*_H0;
174         _H0 = _Htemp*_Hnorm2;
175         _H0.convertTo(_model, _H0.type(), 1./_H0.at<double>(2,2) );
176 
177         return 1;
178     }
179 
computeError(InputArray _m1,InputArray _m2,InputArray _model,OutputArray _err) const180     void computeError( InputArray _m1, InputArray _m2, InputArray _model, OutputArray _err ) const
181     {
182         Mat m1 = _m1.getMat(), m2 = _m2.getMat(), model = _model.getMat();
183         int i, count = m1.checkVector(2);
184         const Point2f* M = m1.ptr<Point2f>();
185         const Point2f* m = m2.ptr<Point2f>();
186         const double* H = model.ptr<double>();
187         float Hf[] = { (float)H[0], (float)H[1], (float)H[2], (float)H[3], (float)H[4], (float)H[5], (float)H[6], (float)H[7] };
188 
189         _err.create(count, 1, CV_32F);
190         float* err = _err.getMat().ptr<float>();
191 
192         for( i = 0; i < count; i++ )
193         {
194             float ww = 1.f/(Hf[6]*M[i].x + Hf[7]*M[i].y + 1.f);
195             float dx = (Hf[0]*M[i].x + Hf[1]*M[i].y + Hf[2])*ww - m[i].x;
196             float dy = (Hf[3]*M[i].x + Hf[4]*M[i].y + Hf[5])*ww - m[i].y;
197             err[i] = (float)(dx*dx + dy*dy);
198         }
199     }
200 };
201 
202 
203 class HomographyRefineCallback : public LMSolver::Callback
204 {
205 public:
HomographyRefineCallback(InputArray _src,InputArray _dst)206     HomographyRefineCallback(InputArray _src, InputArray _dst)
207     {
208         src = _src.getMat();
209         dst = _dst.getMat();
210     }
211 
compute(InputArray _param,OutputArray _err,OutputArray _Jac) const212     bool compute(InputArray _param, OutputArray _err, OutputArray _Jac) const
213     {
214         int i, count = src.checkVector(2);
215         Mat param = _param.getMat();
216         _err.create(count*2, 1, CV_64F);
217         Mat err = _err.getMat(), J;
218         if( _Jac.needed())
219         {
220             _Jac.create(count*2, param.rows, CV_64F);
221             J = _Jac.getMat();
222             CV_Assert( J.isContinuous() && J.cols == 8 );
223         }
224 
225         const Point2f* M = src.ptr<Point2f>();
226         const Point2f* m = dst.ptr<Point2f>();
227         const double* h = param.ptr<double>();
228         double* errptr = err.ptr<double>();
229         double* Jptr = J.data ? J.ptr<double>() : 0;
230 
231         for( i = 0; i < count; i++ )
232         {
233             double Mx = M[i].x, My = M[i].y;
234             double ww = h[6]*Mx + h[7]*My + 1.;
235             ww = fabs(ww) > DBL_EPSILON ? 1./ww : 0;
236             double xi = (h[0]*Mx + h[1]*My + h[2])*ww;
237             double yi = (h[3]*Mx + h[4]*My + h[5])*ww;
238             errptr[i*2] = xi - m[i].x;
239             errptr[i*2+1] = yi - m[i].y;
240 
241             if( Jptr )
242             {
243                 Jptr[0] = Mx*ww; Jptr[1] = My*ww; Jptr[2] = ww;
244                 Jptr[3] = Jptr[4] = Jptr[5] = 0.;
245                 Jptr[6] = -Mx*ww*xi; Jptr[7] = -My*ww*xi;
246                 Jptr[8] = Jptr[9] = Jptr[10] = 0.;
247                 Jptr[11] = Mx*ww; Jptr[12] = My*ww; Jptr[13] = ww;
248                 Jptr[14] = -Mx*ww*yi; Jptr[15] = -My*ww*yi;
249 
250                 Jptr += 16;
251             }
252         }
253 
254         return true;
255     }
256 
257     Mat src, dst;
258 };
259 
260 }
261 
262 
263 
264 namespace cv{
createAndRunRHORegistrator(double confidence,int maxIters,double ransacReprojThreshold,int npoints,InputArray _src,InputArray _dst,OutputArray _H,OutputArray _tempMask)265 static bool createAndRunRHORegistrator(double confidence,
266                                        int    maxIters,
267                                        double ransacReprojThreshold,
268                                        int    npoints,
269                                        InputArray  _src,
270                                        InputArray  _dst,
271                                        OutputArray _H,
272                                        OutputArray _tempMask){
273     Mat    src = _src.getMat();
274     Mat    dst = _dst.getMat();
275     Mat    tempMask;
276     bool   result;
277     double beta = 0.35;/* 0.35 is a value that often works. */
278 
279     /* Create temporary output matrix (RHO outputs a single-precision H only). */
280     Mat tmpH = Mat(3, 3, CV_32FC1);
281 
282     /* Create output mask. */
283     tempMask = Mat(npoints, 1, CV_8U);
284 
285     /**
286      * Make use of the RHO estimator API.
287      *
288      * This is where the math happens. A homography estimation context is
289      * initialized, used, then finalized.
290      */
291 
292     Ptr<RHO_HEST> p = rhoInit();
293 
294     /**
295      * Optional. Ideally, the context would survive across calls to
296      * findHomography(), but no clean way appears to exit to do so. The price
297      * to pay is marginally more computational work than strictly needed.
298      */
299 
300     rhoEnsureCapacity(p, npoints, beta);
301 
302     /**
303      * The critical call. All parameters are heavily documented in rhorefc.h.
304      *
305      * Currently, NR (Non-Randomness criterion) and Final Refinement (with
306      * internal, optimized Levenberg-Marquardt method) are enabled. However,
307      * while refinement seems to correctly smooth jitter most of the time, when
308      * refinement fails it tends to make the estimate visually very much worse.
309      * It may be necessary to remove the refinement flags in a future commit if
310      * this behaviour is too problematic.
311      */
312 
313     result = !!rhoHest(p,
314                       (const float*)src.data,
315                       (const float*)dst.data,
316                       (char*)       tempMask.data,
317                       (unsigned)    npoints,
318                       (float)       ransacReprojThreshold,
319                       (unsigned)    maxIters,
320                       (unsigned)    maxIters,
321                       confidence,
322                       4U,
323                       beta,
324                       RHO_FLAG_ENABLE_NR | RHO_FLAG_ENABLE_FINAL_REFINEMENT,
325                       NULL,
326                       (float*)tmpH.data);
327 
328     /* Convert float homography to double precision. */
329     tmpH.convertTo(_H, CV_64FC1);
330 
331     /* Maps non-zero mask elems to 1, for the sake of the testcase. */
332     for(int k=0;k<npoints;k++){
333         tempMask.data[k] = !!tempMask.data[k];
334     }
335     tempMask.copyTo(_tempMask);
336 
337     return result;
338 }
339 }
340 
341 
findHomography(InputArray _points1,InputArray _points2,int method,double ransacReprojThreshold,OutputArray _mask,const int maxIters,const double confidence)342 cv::Mat cv::findHomography( InputArray _points1, InputArray _points2,
343                             int method, double ransacReprojThreshold, OutputArray _mask,
344                             const int maxIters, const double confidence)
345 {
346     const double defaultRANSACReprojThreshold = 3;
347     bool result = false;
348 
349     Mat points1 = _points1.getMat(), points2 = _points2.getMat();
350     Mat src, dst, H, tempMask;
351     int npoints = -1;
352 
353     for( int i = 1; i <= 2; i++ )
354     {
355         Mat& p = i == 1 ? points1 : points2;
356         Mat& m = i == 1 ? src : dst;
357         npoints = p.checkVector(2, -1, false);
358         if( npoints < 0 )
359         {
360             npoints = p.checkVector(3, -1, false);
361             if( npoints < 0 )
362                 CV_Error(Error::StsBadArg, "The input arrays should be 2D or 3D point sets");
363             if( npoints == 0 )
364                 return Mat();
365             convertPointsFromHomogeneous(p, p);
366         }
367         p.reshape(2, npoints).convertTo(m, CV_32F);
368     }
369 
370     CV_Assert( src.checkVector(2) == dst.checkVector(2) );
371 
372     if( ransacReprojThreshold <= 0 )
373         ransacReprojThreshold = defaultRANSACReprojThreshold;
374 
375     Ptr<PointSetRegistrator::Callback> cb = makePtr<HomographyEstimatorCallback>();
376 
377     if( method == 0 || npoints == 4 )
378     {
379         tempMask = Mat::ones(npoints, 1, CV_8U);
380         result = cb->runKernel(src, dst, H) > 0;
381     }
382     else if( method == RANSAC )
383         result = createRANSACPointSetRegistrator(cb, 4, ransacReprojThreshold, confidence, maxIters)->run(src, dst, H, tempMask);
384     else if( method == LMEDS )
385         result = createLMeDSPointSetRegistrator(cb, 4, confidence, maxIters)->run(src, dst, H, tempMask);
386     else if( method == RHO )
387         result = createAndRunRHORegistrator(confidence, maxIters, ransacReprojThreshold, npoints, src, dst, H, tempMask);
388     else
389         CV_Error(Error::StsBadArg, "Unknown estimation method");
390 
391     if( result && npoints > 4 && method != RHO)
392     {
393         compressElems( src.ptr<Point2f>(), tempMask.ptr<uchar>(), 1, npoints );
394         npoints = compressElems( dst.ptr<Point2f>(), tempMask.ptr<uchar>(), 1, npoints );
395         if( npoints > 0 )
396         {
397             Mat src1 = src.rowRange(0, npoints);
398             Mat dst1 = dst.rowRange(0, npoints);
399             src = src1;
400             dst = dst1;
401             if( method == RANSAC || method == LMEDS )
402                 cb->runKernel( src, dst, H );
403             Mat H8(8, 1, CV_64F, H.ptr<double>());
404             createLMSolver(makePtr<HomographyRefineCallback>(src, dst), 10)->run(H8);
405         }
406     }
407 
408     if( result )
409     {
410         if( _mask.needed() )
411             tempMask.copyTo(_mask);
412     }
413     else
414         H.release();
415 
416     return H;
417 }
418 
findHomography(InputArray _points1,InputArray _points2,OutputArray _mask,int method,double ransacReprojThreshold)419 cv::Mat cv::findHomography( InputArray _points1, InputArray _points2,
420                            OutputArray _mask, int method, double ransacReprojThreshold )
421 {
422     return cv::findHomography(_points1, _points2, method, ransacReprojThreshold, _mask);
423 }
424 
425 
426 
427 /* Estimation of Fundamental Matrix from point correspondences.
428    The original code has been written by Valery Mosyagin */
429 
430 /* The algorithms (except for RANSAC) and the notation have been taken from
431    Zhengyou Zhang's research report
432    "Determining the Epipolar Geometry and its Uncertainty: A Review"
433    that can be found at http://www-sop.inria.fr/robotvis/personnel/zzhang/zzhang-eng.html */
434 
435 /************************************** 7-point algorithm *******************************/
436 namespace cv
437 {
438 
run7Point(const Mat & _m1,const Mat & _m2,Mat & _fmatrix)439 static int run7Point( const Mat& _m1, const Mat& _m2, Mat& _fmatrix )
440 {
441     double a[7*9], w[7], u[9*9], v[9*9], c[4], r[3];
442     double* f1, *f2;
443     double t0, t1, t2;
444     Mat A( 7, 9, CV_64F, a );
445     Mat U( 7, 9, CV_64F, u );
446     Mat Vt( 9, 9, CV_64F, v );
447     Mat W( 7, 1, CV_64F, w );
448     Mat coeffs( 1, 4, CV_64F, c );
449     Mat roots( 1, 3, CV_64F, r );
450     const Point2f* m1 = _m1.ptr<Point2f>();
451     const Point2f* m2 = _m2.ptr<Point2f>();
452     double* fmatrix = _fmatrix.ptr<double>();
453     int i, k, n;
454 
455     // form a linear system: i-th row of A(=a) represents
456     // the equation: (m2[i], 1)'*F*(m1[i], 1) = 0
457     for( i = 0; i < 7; i++ )
458     {
459         double x0 = m1[i].x, y0 = m1[i].y;
460         double x1 = m2[i].x, y1 = m2[i].y;
461 
462         a[i*9+0] = x1*x0;
463         a[i*9+1] = x1*y0;
464         a[i*9+2] = x1;
465         a[i*9+3] = y1*x0;
466         a[i*9+4] = y1*y0;
467         a[i*9+5] = y1;
468         a[i*9+6] = x0;
469         a[i*9+7] = y0;
470         a[i*9+8] = 1;
471     }
472 
473     // A*(f11 f12 ... f33)' = 0 is singular (7 equations for 9 variables), so
474     // the solution is linear subspace of dimensionality 2.
475     // => use the last two singular vectors as a basis of the space
476     // (according to SVD properties)
477     SVDecomp( A, W, U, Vt, SVD::MODIFY_A + SVD::FULL_UV );
478     f1 = v + 7*9;
479     f2 = v + 8*9;
480 
481     // f1, f2 is a basis => lambda*f1 + mu*f2 is an arbitrary f. matrix.
482     // as it is determined up to a scale, normalize lambda & mu (lambda + mu = 1),
483     // so f ~ lambda*f1 + (1 - lambda)*f2.
484     // use the additional constraint det(f) = det(lambda*f1 + (1-lambda)*f2) to find lambda.
485     // it will be a cubic equation.
486     // find c - polynomial coefficients.
487     for( i = 0; i < 9; i++ )
488         f1[i] -= f2[i];
489 
490     t0 = f2[4]*f2[8] - f2[5]*f2[7];
491     t1 = f2[3]*f2[8] - f2[5]*f2[6];
492     t2 = f2[3]*f2[7] - f2[4]*f2[6];
493 
494     c[3] = f2[0]*t0 - f2[1]*t1 + f2[2]*t2;
495 
496     c[2] = f1[0]*t0 - f1[1]*t1 + f1[2]*t2 -
497     f1[3]*(f2[1]*f2[8] - f2[2]*f2[7]) +
498     f1[4]*(f2[0]*f2[8] - f2[2]*f2[6]) -
499     f1[5]*(f2[0]*f2[7] - f2[1]*f2[6]) +
500     f1[6]*(f2[1]*f2[5] - f2[2]*f2[4]) -
501     f1[7]*(f2[0]*f2[5] - f2[2]*f2[3]) +
502     f1[8]*(f2[0]*f2[4] - f2[1]*f2[3]);
503 
504     t0 = f1[4]*f1[8] - f1[5]*f1[7];
505     t1 = f1[3]*f1[8] - f1[5]*f1[6];
506     t2 = f1[3]*f1[7] - f1[4]*f1[6];
507 
508     c[1] = f2[0]*t0 - f2[1]*t1 + f2[2]*t2 -
509     f2[3]*(f1[1]*f1[8] - f1[2]*f1[7]) +
510     f2[4]*(f1[0]*f1[8] - f1[2]*f1[6]) -
511     f2[5]*(f1[0]*f1[7] - f1[1]*f1[6]) +
512     f2[6]*(f1[1]*f1[5] - f1[2]*f1[4]) -
513     f2[7]*(f1[0]*f1[5] - f1[2]*f1[3]) +
514     f2[8]*(f1[0]*f1[4] - f1[1]*f1[3]);
515 
516     c[0] = f1[0]*t0 - f1[1]*t1 + f1[2]*t2;
517 
518     // solve the cubic equation; there can be 1 to 3 roots ...
519     n = solveCubic( coeffs, roots );
520 
521     if( n < 1 || n > 3 )
522         return n;
523 
524     for( k = 0; k < n; k++, fmatrix += 9 )
525     {
526         // for each root form the fundamental matrix
527         double lambda = r[k], mu = 1.;
528         double s = f1[8]*r[k] + f2[8];
529 
530         // normalize each matrix, so that F(3,3) (~fmatrix[8]) == 1
531         if( fabs(s) > DBL_EPSILON )
532         {
533             mu = 1./s;
534             lambda *= mu;
535             fmatrix[8] = 1.;
536         }
537         else
538             fmatrix[8] = 0.;
539 
540         for( i = 0; i < 8; i++ )
541             fmatrix[i] = f1[i]*lambda + f2[i]*mu;
542     }
543 
544     return n;
545 }
546 
547 
run8Point(const Mat & _m1,const Mat & _m2,Mat & _fmatrix)548 static int run8Point( const Mat& _m1, const Mat& _m2, Mat& _fmatrix )
549 {
550     double a[9*9], w[9], v[9*9];
551     Mat W( 9, 1, CV_64F, w );
552     Mat V( 9, 9, CV_64F, v );
553     Mat A( 9, 9, CV_64F, a );
554     Mat U, F0, TF;
555 
556     Point2d m1c(0,0), m2c(0,0);
557     double t, scale1 = 0, scale2 = 0;
558 
559     const Point2f* m1 = _m1.ptr<Point2f>();
560     const Point2f* m2 = _m2.ptr<Point2f>();
561     double* fmatrix = _fmatrix.ptr<double>();
562     CV_Assert( (_m1.cols == 1 || _m1.rows == 1) && _m1.size() == _m2.size());
563     int i, j, k, count = _m1.checkVector(2);
564 
565     // compute centers and average distances for each of the two point sets
566     for( i = 0; i < count; i++ )
567     {
568         double x = m1[i].x, y = m1[i].y;
569         m1c.x += x; m1c.y += y;
570 
571         x = m2[i].x, y = m2[i].y;
572         m2c.x += x; m2c.y += y;
573     }
574 
575     // calculate the normalizing transformations for each of the point sets:
576     // after the transformation each set will have the mass center at the coordinate origin
577     // and the average distance from the origin will be ~sqrt(2).
578     t = 1./count;
579     m1c.x *= t; m1c.y *= t;
580     m2c.x *= t; m2c.y *= t;
581 
582     for( i = 0; i < count; i++ )
583     {
584         double x = m1[i].x - m1c.x, y = m1[i].y - m1c.y;
585         scale1 += std::sqrt(x*x + y*y);
586 
587         x = m2[i].x - m2c.x, y = m2[i].y - m2c.y;
588         scale2 += std::sqrt(x*x + y*y);
589     }
590 
591     scale1 *= t;
592     scale2 *= t;
593 
594     if( scale1 < FLT_EPSILON || scale2 < FLT_EPSILON )
595         return 0;
596 
597     scale1 = std::sqrt(2.)/scale1;
598     scale2 = std::sqrt(2.)/scale2;
599 
600     A.setTo(Scalar::all(0));
601 
602     // form a linear system Ax=0: for each selected pair of points m1 & m2,
603     // the row of A(=a) represents the coefficients of equation: (m2, 1)'*F*(m1, 1) = 0
604     // to save computation time, we compute (At*A) instead of A and then solve (At*A)x=0.
605     for( i = 0; i < count; i++ )
606     {
607         double x1 = (m1[i].x - m1c.x)*scale1;
608         double y1 = (m1[i].y - m1c.y)*scale1;
609         double x2 = (m2[i].x - m2c.x)*scale2;
610         double y2 = (m2[i].y - m2c.y)*scale2;
611         double r[9] = { x2*x1, x2*y1, x2, y2*x1, y2*y1, y2, x1, y1, 1 };
612         for( j = 0; j < 9; j++ )
613             for( k = 0; k < 9; k++ )
614                 a[j*9+k] += r[j]*r[k];
615     }
616 
617     eigen(A, W, V);
618 
619     for( i = 0; i < 9; i++ )
620     {
621         if( fabs(w[i]) < DBL_EPSILON )
622             break;
623     }
624 
625     if( i < 8 )
626         return 0;
627 
628     F0 = Mat( 3, 3, CV_64F, v + 9*8 ); // take the last column of v as a solution of Af = 0
629 
630     // make F0 singular (of rank 2) by decomposing it with SVD,
631     // zeroing the last diagonal element of W and then composing the matrices back.
632 
633     // use v as a temporary storage for different 3x3 matrices
634     W = U = V = TF = F0;
635     W = Mat(3, 1, CV_64F, v);
636     U = Mat(3, 3, CV_64F, v + 9);
637     V = Mat(3, 3, CV_64F, v + 18);
638     TF = Mat(3, 3, CV_64F, v + 27);
639 
640     SVDecomp( F0, W, U, V, SVD::MODIFY_A );
641     W.at<double>(2) = 0.;
642 
643     // F0 <- U*diag([W(1), W(2), 0])*V'
644     gemm( U, Mat::diag(W), 1., 0, 0., TF, 0 );
645     gemm( TF, V, 1., 0, 0., F0, 0/*CV_GEMM_B_T*/ );
646 
647     // apply the transformation that is inverse
648     // to what we used to normalize the point coordinates
649     double tt1[] = { scale1, 0, -scale1*m1c.x, 0, scale1, -scale1*m1c.y, 0, 0, 1 };
650     double tt2[] = { scale2, 0, -scale2*m2c.x, 0, scale2, -scale2*m2c.y, 0, 0, 1 };
651     Mat T1(3, 3, CV_64F, tt1), T2(3, 3, CV_64F, tt2);
652 
653     // F0 <- T2'*F0*T1
654     gemm( T2, F0, 1., 0, 0., TF, GEMM_1_T );
655     F0 = Mat(3, 3, CV_64F, fmatrix);
656     gemm( TF, T1, 1., 0, 0., F0, 0 );
657 
658     // make F(3,3) = 1
659     if( fabs(F0.at<double>(2,2)) > FLT_EPSILON )
660         F0 *= 1./F0.at<double>(2,2);
661 
662     return 1;
663 }
664 
665 
666 class FMEstimatorCallback : public PointSetRegistrator::Callback
667 {
668 public:
checkSubset(InputArray _ms1,InputArray _ms2,int count) const669     bool checkSubset( InputArray _ms1, InputArray _ms2, int count ) const
670     {
671         Mat ms1 = _ms1.getMat(), ms2 = _ms2.getMat();
672         return !haveCollinearPoints(ms1, count) && !haveCollinearPoints(ms2, count);
673     }
674 
runKernel(InputArray _m1,InputArray _m2,OutputArray _model) const675     int runKernel( InputArray _m1, InputArray _m2, OutputArray _model ) const
676     {
677         double f[9*3];
678         Mat m1 = _m1.getMat(), m2 = _m2.getMat();
679         int count = m1.checkVector(2);
680         Mat F(count == 7 ? 9 : 3, 3, CV_64F, f);
681         int n = count == 7 ? run7Point(m1, m2, F) : run8Point(m1, m2, F);
682 
683         if( n == 0 )
684             _model.release();
685         else
686             F.rowRange(0, n*3).copyTo(_model);
687 
688         return n;
689     }
690 
computeError(InputArray _m1,InputArray _m2,InputArray _model,OutputArray _err) const691     void computeError( InputArray _m1, InputArray _m2, InputArray _model, OutputArray _err ) const
692     {
693         Mat __m1 = _m1.getMat(), __m2 = _m2.getMat(), __model = _model.getMat();
694         int i, count = __m1.checkVector(2);
695         const Point2f* m1 = __m1.ptr<Point2f>();
696         const Point2f* m2 = __m2.ptr<Point2f>();
697         const double* F = __model.ptr<double>();
698         _err.create(count, 1, CV_32F);
699         float* err = _err.getMat().ptr<float>();
700 
701         for( i = 0; i < count; i++ )
702         {
703             double a, b, c, d1, d2, s1, s2;
704 
705             a = F[0]*m1[i].x + F[1]*m1[i].y + F[2];
706             b = F[3]*m1[i].x + F[4]*m1[i].y + F[5];
707             c = F[6]*m1[i].x + F[7]*m1[i].y + F[8];
708 
709             s2 = 1./(a*a + b*b);
710             d2 = m2[i].x*a + m2[i].y*b + c;
711 
712             a = F[0]*m2[i].x + F[3]*m2[i].y + F[6];
713             b = F[1]*m2[i].x + F[4]*m2[i].y + F[7];
714             c = F[2]*m2[i].x + F[5]*m2[i].y + F[8];
715 
716             s1 = 1./(a*a + b*b);
717             d1 = m1[i].x*a + m1[i].y*b + c;
718 
719             err[i] = (float)std::max(d1*d1*s1, d2*d2*s2);
720         }
721     }
722 };
723 
724 }
725 
findFundamentalMat(InputArray _points1,InputArray _points2,int method,double param1,double param2,OutputArray _mask)726 cv::Mat cv::findFundamentalMat( InputArray _points1, InputArray _points2,
727                                 int method, double param1, double param2,
728                                 OutputArray _mask )
729 {
730     Mat points1 = _points1.getMat(), points2 = _points2.getMat();
731     Mat m1, m2, F;
732     int npoints = -1;
733 
734     for( int i = 1; i <= 2; i++ )
735     {
736         Mat& p = i == 1 ? points1 : points2;
737         Mat& m = i == 1 ? m1 : m2;
738         npoints = p.checkVector(2, -1, false);
739         if( npoints < 0 )
740         {
741             npoints = p.checkVector(3, -1, false);
742             if( npoints < 0 )
743                 CV_Error(Error::StsBadArg, "The input arrays should be 2D or 3D point sets");
744             if( npoints == 0 )
745                 return Mat();
746             convertPointsFromHomogeneous(p, p);
747         }
748         p.reshape(2, npoints).convertTo(m, CV_32F);
749     }
750 
751     CV_Assert( m1.checkVector(2) == m2.checkVector(2) );
752 
753     if( npoints < 7 )
754         return Mat();
755 
756     Ptr<PointSetRegistrator::Callback> cb = makePtr<FMEstimatorCallback>();
757     int result;
758 
759     if( npoints == 7 || method == FM_8POINT )
760     {
761         result = cb->runKernel(m1, m2, F);
762         if( _mask.needed() )
763         {
764             _mask.create(npoints, 1, CV_8U, -1, true);
765             Mat mask = _mask.getMat();
766             CV_Assert( (mask.cols == 1 || mask.rows == 1) && (int)mask.total() == npoints );
767             mask.setTo(Scalar::all(1));
768         }
769     }
770     else
771     {
772         if( param1 <= 0 )
773             param1 = 3;
774         if( param2 < DBL_EPSILON || param2 > 1 - DBL_EPSILON )
775             param2 = 0.99;
776 
777         if( (method & ~3) == FM_RANSAC && npoints >= 15 )
778             result = createRANSACPointSetRegistrator(cb, 7, param1, param2)->run(m1, m2, F, _mask);
779         else
780             result = createLMeDSPointSetRegistrator(cb, 7, param2)->run(m1, m2, F, _mask);
781     }
782 
783     if( result <= 0 )
784         return Mat();
785 
786     return F;
787 }
788 
findFundamentalMat(InputArray _points1,InputArray _points2,OutputArray _mask,int method,double param1,double param2)789 cv::Mat cv::findFundamentalMat( InputArray _points1, InputArray _points2,
790                                OutputArray _mask, int method, double param1, double param2 )
791 {
792     return cv::findFundamentalMat(_points1, _points2, method, param1, param2, _mask);
793 }
794 
795 
computeCorrespondEpilines(InputArray _points,int whichImage,InputArray _Fmat,OutputArray _lines)796 void cv::computeCorrespondEpilines( InputArray _points, int whichImage,
797                                     InputArray _Fmat, OutputArray _lines )
798 {
799     double f[9];
800     Mat tempF(3, 3, CV_64F, f);
801     Mat points = _points.getMat(), F = _Fmat.getMat();
802 
803     if( !points.isContinuous() )
804         points = points.clone();
805 
806     int npoints = points.checkVector(2);
807     if( npoints < 0 )
808     {
809         npoints = points.checkVector(3);
810         if( npoints < 0 )
811             CV_Error( Error::StsBadArg, "The input should be a 2D or 3D point set");
812         Mat temp;
813         convertPointsFromHomogeneous(points, temp);
814         points = temp;
815     }
816     int depth = points.depth();
817     CV_Assert( depth == CV_32F || depth == CV_32S || depth == CV_64F );
818 
819     CV_Assert(F.size() == Size(3,3));
820     F.convertTo(tempF, CV_64F);
821     if( whichImage == 2 )
822         transpose(tempF, tempF);
823 
824     int ltype = CV_MAKETYPE(MAX(depth, CV_32F), 3);
825     _lines.create(npoints, 1, ltype);
826     Mat lines = _lines.getMat();
827     if( !lines.isContinuous() )
828     {
829         _lines.release();
830         _lines.create(npoints, 1, ltype);
831         lines = _lines.getMat();
832     }
833     CV_Assert( lines.isContinuous());
834 
835     if( depth == CV_32S || depth == CV_32F )
836     {
837         const Point* ptsi = points.ptr<Point>();
838         const Point2f* ptsf = points.ptr<Point2f>();
839         Point3f* dstf = lines.ptr<Point3f>();
840         for( int i = 0; i < npoints; i++ )
841         {
842             Point2f pt = depth == CV_32F ? ptsf[i] : Point2f((float)ptsi[i].x, (float)ptsi[i].y);
843             double a = f[0]*pt.x + f[1]*pt.y + f[2];
844             double b = f[3]*pt.x + f[4]*pt.y + f[5];
845             double c = f[6]*pt.x + f[7]*pt.y + f[8];
846             double nu = a*a + b*b;
847             nu = nu ? 1./std::sqrt(nu) : 1.;
848             a *= nu; b *= nu; c *= nu;
849             dstf[i] = Point3f((float)a, (float)b, (float)c);
850         }
851     }
852     else
853     {
854         const Point2d* ptsd = points.ptr<Point2d>();
855         Point3d* dstd = lines.ptr<Point3d>();
856         for( int i = 0; i < npoints; i++ )
857         {
858             Point2d pt = ptsd[i];
859             double a = f[0]*pt.x + f[1]*pt.y + f[2];
860             double b = f[3]*pt.x + f[4]*pt.y + f[5];
861             double c = f[6]*pt.x + f[7]*pt.y + f[8];
862             double nu = a*a + b*b;
863             nu = nu ? 1./std::sqrt(nu) : 1.;
864             a *= nu; b *= nu; c *= nu;
865             dstd[i] = Point3d(a, b, c);
866         }
867     }
868 }
869 
convertPointsFromHomogeneous(InputArray _src,OutputArray _dst)870 void cv::convertPointsFromHomogeneous( InputArray _src, OutputArray _dst )
871 {
872     Mat src = _src.getMat();
873     if( !src.isContinuous() )
874         src = src.clone();
875     int i, npoints = src.checkVector(3), depth = src.depth(), cn = 3;
876     if( npoints < 0 )
877     {
878         npoints = src.checkVector(4);
879         CV_Assert(npoints >= 0);
880         cn = 4;
881     }
882     CV_Assert( npoints >= 0 && (depth == CV_32S || depth == CV_32F || depth == CV_64F));
883 
884     int dtype = CV_MAKETYPE(depth <= CV_32F ? CV_32F : CV_64F, cn-1);
885     _dst.create(npoints, 1, dtype);
886     Mat dst = _dst.getMat();
887     if( !dst.isContinuous() )
888     {
889         _dst.release();
890         _dst.create(npoints, 1, dtype);
891         dst = _dst.getMat();
892     }
893     CV_Assert( dst.isContinuous() );
894 
895     if( depth == CV_32S )
896     {
897         if( cn == 3 )
898         {
899             const Point3i* sptr = src.ptr<Point3i>();
900             Point2f* dptr = dst.ptr<Point2f>();
901             for( i = 0; i < npoints; i++ )
902             {
903                 float scale = sptr[i].z != 0 ? 1.f/sptr[i].z : 1.f;
904                 dptr[i] = Point2f(sptr[i].x*scale, sptr[i].y*scale);
905             }
906         }
907         else
908         {
909             const Vec4i* sptr = src.ptr<Vec4i>();
910             Point3f* dptr = dst.ptr<Point3f>();
911             for( i = 0; i < npoints; i++ )
912             {
913                 float scale = sptr[i][3] != 0 ? 1.f/sptr[i][3] : 1.f;
914                 dptr[i] = Point3f(sptr[i][0]*scale, sptr[i][1]*scale, sptr[i][2]*scale);
915             }
916         }
917     }
918     else if( depth == CV_32F )
919     {
920         if( cn == 3 )
921         {
922             const Point3f* sptr = src.ptr<Point3f>();
923             Point2f* dptr = dst.ptr<Point2f>();
924             for( i = 0; i < npoints; i++ )
925             {
926                 float scale = sptr[i].z != 0.f ? 1.f/sptr[i].z : 1.f;
927                 dptr[i] = Point2f(sptr[i].x*scale, sptr[i].y*scale);
928             }
929         }
930         else
931         {
932             const Vec4f* sptr = src.ptr<Vec4f>();
933             Point3f* dptr = dst.ptr<Point3f>();
934             for( i = 0; i < npoints; i++ )
935             {
936                 float scale = sptr[i][3] != 0.f ? 1.f/sptr[i][3] : 1.f;
937                 dptr[i] = Point3f(sptr[i][0]*scale, sptr[i][1]*scale, sptr[i][2]*scale);
938             }
939         }
940     }
941     else if( depth == CV_64F )
942     {
943         if( cn == 3 )
944         {
945             const Point3d* sptr = src.ptr<Point3d>();
946             Point2d* dptr = dst.ptr<Point2d>();
947             for( i = 0; i < npoints; i++ )
948             {
949                 double scale = sptr[i].z != 0. ? 1./sptr[i].z : 1.;
950                 dptr[i] = Point2d(sptr[i].x*scale, sptr[i].y*scale);
951             }
952         }
953         else
954         {
955             const Vec4d* sptr = src.ptr<Vec4d>();
956             Point3d* dptr = dst.ptr<Point3d>();
957             for( i = 0; i < npoints; i++ )
958             {
959                 double scale = sptr[i][3] != 0.f ? 1./sptr[i][3] : 1.;
960                 dptr[i] = Point3d(sptr[i][0]*scale, sptr[i][1]*scale, sptr[i][2]*scale);
961             }
962         }
963     }
964     else
965         CV_Error(Error::StsUnsupportedFormat, "");
966 }
967 
968 
convertPointsToHomogeneous(InputArray _src,OutputArray _dst)969 void cv::convertPointsToHomogeneous( InputArray _src, OutputArray _dst )
970 {
971     Mat src = _src.getMat();
972     if( !src.isContinuous() )
973         src = src.clone();
974     int i, npoints = src.checkVector(2), depth = src.depth(), cn = 2;
975     if( npoints < 0 )
976     {
977         npoints = src.checkVector(3);
978         CV_Assert(npoints >= 0);
979         cn = 3;
980     }
981     CV_Assert( npoints >= 0 && (depth == CV_32S || depth == CV_32F || depth == CV_64F));
982 
983     int dtype = CV_MAKETYPE(depth <= CV_32F ? CV_32F : CV_64F, cn+1);
984     _dst.create(npoints, 1, dtype);
985     Mat dst = _dst.getMat();
986     if( !dst.isContinuous() )
987     {
988         _dst.release();
989         _dst.create(npoints, 1, dtype);
990         dst = _dst.getMat();
991     }
992     CV_Assert( dst.isContinuous() );
993 
994     if( depth == CV_32S )
995     {
996         if( cn == 2 )
997         {
998             const Point2i* sptr = src.ptr<Point2i>();
999             Point3i* dptr = dst.ptr<Point3i>();
1000             for( i = 0; i < npoints; i++ )
1001                 dptr[i] = Point3i(sptr[i].x, sptr[i].y, 1);
1002         }
1003         else
1004         {
1005             const Point3i* sptr = src.ptr<Point3i>();
1006             Vec4i* dptr = dst.ptr<Vec4i>();
1007             for( i = 0; i < npoints; i++ )
1008                 dptr[i] = Vec4i(sptr[i].x, sptr[i].y, sptr[i].z, 1);
1009         }
1010     }
1011     else if( depth == CV_32F )
1012     {
1013         if( cn == 2 )
1014         {
1015             const Point2f* sptr = src.ptr<Point2f>();
1016             Point3f* dptr = dst.ptr<Point3f>();
1017             for( i = 0; i < npoints; i++ )
1018                 dptr[i] = Point3f(sptr[i].x, sptr[i].y, 1.f);
1019         }
1020         else
1021         {
1022             const Point3f* sptr = src.ptr<Point3f>();
1023             Vec4f* dptr = dst.ptr<Vec4f>();
1024             for( i = 0; i < npoints; i++ )
1025                 dptr[i] = Vec4f(sptr[i].x, sptr[i].y, sptr[i].z, 1.f);
1026         }
1027     }
1028     else if( depth == CV_64F )
1029     {
1030         if( cn == 2 )
1031         {
1032             const Point2d* sptr = src.ptr<Point2d>();
1033             Point3d* dptr = dst.ptr<Point3d>();
1034             for( i = 0; i < npoints; i++ )
1035                 dptr[i] = Point3d(sptr[i].x, sptr[i].y, 1.);
1036         }
1037         else
1038         {
1039             const Point3d* sptr = src.ptr<Point3d>();
1040             Vec4d* dptr = dst.ptr<Vec4d>();
1041             for( i = 0; i < npoints; i++ )
1042                 dptr[i] = Vec4d(sptr[i].x, sptr[i].y, sptr[i].z, 1.);
1043         }
1044     }
1045     else
1046         CV_Error(Error::StsUnsupportedFormat, "");
1047 }
1048 
1049 
convertPointsHomogeneous(InputArray _src,OutputArray _dst)1050 void cv::convertPointsHomogeneous( InputArray _src, OutputArray _dst )
1051 {
1052     int stype = _src.type(), dtype = _dst.type();
1053     CV_Assert( _dst.fixedType() );
1054 
1055     if( CV_MAT_CN(stype) > CV_MAT_CN(dtype) )
1056         convertPointsFromHomogeneous(_src, _dst);
1057     else
1058         convertPointsToHomogeneous(_src, _dst);
1059 }
1060 
1061 /* End of file. */
1062