Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/calib3d/src/ptsetreg.cpp
16354 views
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, Willow Garage Inc., all rights reserved.
15
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
16
// Third party copyrights are property of their respective owners.
17
//
18
// Redistribution and use in source and binary forms, with or without modification,
19
// are permitted provided that the following conditions are met:
20
//
21
// * Redistribution's of source code must retain the above copyright notice,
22
// this list of conditions and the following disclaimer.
23
//
24
// * Redistribution's in binary form must reproduce the above copyright notice,
25
// this list of conditions and the following disclaimer in the documentation
26
// and/or other materials provided with the distribution.
27
//
28
// * The name of the copyright holders may not be used to endorse or promote products
29
// derived from this software without specific prior written permission.
30
//
31
// This software is provided by the copyright holders and contributors "as is" and
32
// any express or implied warranties, including, but not limited to, the implied
33
// warranties of merchantability and fitness for a particular purpose are disclaimed.
34
// In no event shall the Intel Corporation or contributors be liable for any direct,
35
// indirect, incidental, special, exemplary, or consequential damages
36
// (including, but not limited to, procurement of substitute goods or services;
37
// loss of use, data, or profits; or business interruption) however caused
38
// and on any theory of liability, whether in contract, strict liability,
39
// or tort (including negligence or otherwise) arising in any way out of
40
// the use of this software, even if advised of the possibility of such damage.
41
//
42
//M*/
43
44
#include "precomp.hpp"
45
46
#include <algorithm>
47
#include <iterator>
48
#include <limits>
49
50
namespace cv
51
{
52
53
int RANSACUpdateNumIters( double p, double ep, int modelPoints, int maxIters )
54
{
55
if( modelPoints <= 0 )
56
CV_Error( Error::StsOutOfRange, "the number of model points should be positive" );
57
58
p = MAX(p, 0.);
59
p = MIN(p, 1.);
60
ep = MAX(ep, 0.);
61
ep = MIN(ep, 1.);
62
63
// avoid inf's & nan's
64
double num = MAX(1. - p, DBL_MIN);
65
double denom = 1. - std::pow(1. - ep, modelPoints);
66
if( denom < DBL_MIN )
67
return 0;
68
69
num = std::log(num);
70
denom = std::log(denom);
71
72
return denom >= 0 || -num >= maxIters*(-denom) ? maxIters : cvRound(num/denom);
73
}
74
75
76
class RANSACPointSetRegistrator : public PointSetRegistrator
77
{
78
public:
79
RANSACPointSetRegistrator(const Ptr<PointSetRegistrator::Callback>& _cb=Ptr<PointSetRegistrator::Callback>(),
80
int _modelPoints=0, double _threshold=0, double _confidence=0.99, int _maxIters=1000)
81
: cb(_cb), modelPoints(_modelPoints), threshold(_threshold), confidence(_confidence), maxIters(_maxIters) {}
82
83
int findInliers( const Mat& m1, const Mat& m2, const Mat& model, Mat& err, Mat& mask, double thresh ) const
84
{
85
cb->computeError( m1, m2, model, err );
86
mask.create(err.size(), CV_8U);
87
88
CV_Assert( err.isContinuous() && err.type() == CV_32F && mask.isContinuous() && mask.type() == CV_8U);
89
const float* errptr = err.ptr<float>();
90
uchar* maskptr = mask.ptr<uchar>();
91
float t = (float)(thresh*thresh);
92
int i, n = (int)err.total(), nz = 0;
93
for( i = 0; i < n; i++ )
94
{
95
int f = errptr[i] <= t;
96
maskptr[i] = (uchar)f;
97
nz += f;
98
}
99
return nz;
100
}
101
102
bool getSubset( const Mat& m1, const Mat& m2,
103
Mat& ms1, Mat& ms2, RNG& rng,
104
int maxAttempts=1000 ) const
105
{
106
cv::AutoBuffer<int> _idx(modelPoints);
107
int* idx = _idx.data();
108
int i = 0, j, k, iters = 0;
109
int d1 = m1.channels() > 1 ? m1.channels() : m1.cols;
110
int d2 = m2.channels() > 1 ? m2.channels() : m2.cols;
111
int esz1 = (int)m1.elemSize1()*d1, esz2 = (int)m2.elemSize1()*d2;
112
int count = m1.checkVector(d1), count2 = m2.checkVector(d2);
113
const int *m1ptr = m1.ptr<int>(), *m2ptr = m2.ptr<int>();
114
115
ms1.create(modelPoints, 1, CV_MAKETYPE(m1.depth(), d1));
116
ms2.create(modelPoints, 1, CV_MAKETYPE(m2.depth(), d2));
117
118
int *ms1ptr = ms1.ptr<int>(), *ms2ptr = ms2.ptr<int>();
119
120
CV_Assert( count >= modelPoints && count == count2 );
121
CV_Assert( (esz1 % sizeof(int)) == 0 && (esz2 % sizeof(int)) == 0 );
122
esz1 /= sizeof(int);
123
esz2 /= sizeof(int);
124
125
for(; iters < maxAttempts; iters++)
126
{
127
for( i = 0; i < modelPoints && iters < maxAttempts; )
128
{
129
int idx_i = 0;
130
for(;;)
131
{
132
idx_i = idx[i] = rng.uniform(0, count);
133
for( j = 0; j < i; j++ )
134
if( idx_i == idx[j] )
135
break;
136
if( j == i )
137
break;
138
}
139
for( k = 0; k < esz1; k++ )
140
ms1ptr[i*esz1 + k] = m1ptr[idx_i*esz1 + k];
141
for( k = 0; k < esz2; k++ )
142
ms2ptr[i*esz2 + k] = m2ptr[idx_i*esz2 + k];
143
i++;
144
}
145
if( i == modelPoints && !cb->checkSubset(ms1, ms2, i) )
146
continue;
147
break;
148
}
149
150
return i == modelPoints && iters < maxAttempts;
151
}
152
153
bool run(InputArray _m1, InputArray _m2, OutputArray _model, OutputArray _mask) const CV_OVERRIDE
154
{
155
bool result = false;
156
Mat m1 = _m1.getMat(), m2 = _m2.getMat();
157
Mat err, mask, model, bestModel, ms1, ms2;
158
159
int iter, niters = MAX(maxIters, 1);
160
int d1 = m1.channels() > 1 ? m1.channels() : m1.cols;
161
int d2 = m2.channels() > 1 ? m2.channels() : m2.cols;
162
int count = m1.checkVector(d1), count2 = m2.checkVector(d2), maxGoodCount = 0;
163
164
RNG rng((uint64)-1);
165
166
CV_Assert( cb );
167
CV_Assert( confidence > 0 && confidence < 1 );
168
169
CV_Assert( count >= 0 && count2 == count );
170
if( count < modelPoints )
171
return false;
172
173
Mat bestMask0, bestMask;
174
175
if( _mask.needed() )
176
{
177
_mask.create(count, 1, CV_8U, -1, true);
178
bestMask0 = bestMask = _mask.getMat();
179
CV_Assert( (bestMask.cols == 1 || bestMask.rows == 1) && (int)bestMask.total() == count );
180
}
181
else
182
{
183
bestMask.create(count, 1, CV_8U);
184
bestMask0 = bestMask;
185
}
186
187
if( count == modelPoints )
188
{
189
if( cb->runKernel(m1, m2, bestModel) <= 0 )
190
return false;
191
bestModel.copyTo(_model);
192
bestMask.setTo(Scalar::all(1));
193
return true;
194
}
195
196
for( iter = 0; iter < niters; iter++ )
197
{
198
int i, nmodels;
199
if( count > modelPoints )
200
{
201
bool found = getSubset( m1, m2, ms1, ms2, rng, 10000 );
202
if( !found )
203
{
204
if( iter == 0 )
205
return false;
206
break;
207
}
208
}
209
210
nmodels = cb->runKernel( ms1, ms2, model );
211
if( nmodels <= 0 )
212
continue;
213
CV_Assert( model.rows % nmodels == 0 );
214
Size modelSize(model.cols, model.rows/nmodels);
215
216
for( i = 0; i < nmodels; i++ )
217
{
218
Mat model_i = model.rowRange( i*modelSize.height, (i+1)*modelSize.height );
219
int goodCount = findInliers( m1, m2, model_i, err, mask, threshold );
220
221
if( goodCount > MAX(maxGoodCount, modelPoints-1) )
222
{
223
std::swap(mask, bestMask);
224
model_i.copyTo(bestModel);
225
maxGoodCount = goodCount;
226
niters = RANSACUpdateNumIters( confidence, (double)(count - goodCount)/count, modelPoints, niters );
227
}
228
}
229
}
230
231
if( maxGoodCount > 0 )
232
{
233
if( bestMask.data != bestMask0.data )
234
{
235
if( bestMask.size() == bestMask0.size() )
236
bestMask.copyTo(bestMask0);
237
else
238
transpose(bestMask, bestMask0);
239
}
240
bestModel.copyTo(_model);
241
result = true;
242
}
243
else
244
_model.release();
245
246
return result;
247
}
248
249
void setCallback(const Ptr<PointSetRegistrator::Callback>& _cb) CV_OVERRIDE { cb = _cb; }
250
251
Ptr<PointSetRegistrator::Callback> cb;
252
int modelPoints;
253
double threshold;
254
double confidence;
255
int maxIters;
256
};
257
258
class LMeDSPointSetRegistrator : public RANSACPointSetRegistrator
259
{
260
public:
261
LMeDSPointSetRegistrator(const Ptr<PointSetRegistrator::Callback>& _cb=Ptr<PointSetRegistrator::Callback>(),
262
int _modelPoints=0, double _confidence=0.99, int _maxIters=1000)
263
: RANSACPointSetRegistrator(_cb, _modelPoints, 0, _confidence, _maxIters) {}
264
265
bool run(InputArray _m1, InputArray _m2, OutputArray _model, OutputArray _mask) const CV_OVERRIDE
266
{
267
const double outlierRatio = 0.45;
268
bool result = false;
269
Mat m1 = _m1.getMat(), m2 = _m2.getMat();
270
Mat ms1, ms2, err, errf, model, bestModel, mask, mask0;
271
272
int d1 = m1.channels() > 1 ? m1.channels() : m1.cols;
273
int d2 = m2.channels() > 1 ? m2.channels() : m2.cols;
274
int count = m1.checkVector(d1), count2 = m2.checkVector(d2);
275
double minMedian = DBL_MAX;
276
277
RNG rng((uint64)-1);
278
279
CV_Assert( cb );
280
CV_Assert( confidence > 0 && confidence < 1 );
281
282
CV_Assert( count >= 0 && count2 == count );
283
if( count < modelPoints )
284
return false;
285
286
if( _mask.needed() )
287
{
288
_mask.create(count, 1, CV_8U, -1, true);
289
mask0 = mask = _mask.getMat();
290
CV_Assert( (mask.cols == 1 || mask.rows == 1) && (int)mask.total() == count );
291
}
292
293
if( count == modelPoints )
294
{
295
if( cb->runKernel(m1, m2, bestModel) <= 0 )
296
return false;
297
bestModel.copyTo(_model);
298
mask.setTo(Scalar::all(1));
299
return true;
300
}
301
302
int iter, niters = RANSACUpdateNumIters(confidence, outlierRatio, modelPoints, maxIters);
303
niters = MAX(niters, 3);
304
305
for( iter = 0; iter < niters; iter++ )
306
{
307
int i, nmodels;
308
if( count > modelPoints )
309
{
310
bool found = getSubset( m1, m2, ms1, ms2, rng );
311
if( !found )
312
{
313
if( iter == 0 )
314
return false;
315
break;
316
}
317
}
318
319
nmodels = cb->runKernel( ms1, ms2, model );
320
if( nmodels <= 0 )
321
continue;
322
323
CV_Assert( model.rows % nmodels == 0 );
324
Size modelSize(model.cols, model.rows/nmodels);
325
326
for( i = 0; i < nmodels; i++ )
327
{
328
Mat model_i = model.rowRange( i*modelSize.height, (i+1)*modelSize.height );
329
cb->computeError( m1, m2, model_i, err );
330
if( err.depth() != CV_32F )
331
err.convertTo(errf, CV_32F);
332
else
333
errf = err;
334
CV_Assert( errf.isContinuous() && errf.type() == CV_32F && (int)errf.total() == count );
335
std::nth_element(errf.ptr<int>(), errf.ptr<int>() + count/2, errf.ptr<int>() + count);
336
double median = errf.at<float>(count/2);
337
338
if( median < minMedian )
339
{
340
minMedian = median;
341
model_i.copyTo(bestModel);
342
}
343
}
344
}
345
346
if( minMedian < DBL_MAX )
347
{
348
double sigma = 2.5*1.4826*(1 + 5./(count - modelPoints))*std::sqrt(minMedian);
349
sigma = MAX( sigma, 0.001 );
350
351
count = findInliers( m1, m2, bestModel, err, mask, sigma );
352
if( _mask.needed() && mask0.data != mask.data )
353
{
354
if( mask0.size() == mask.size() )
355
mask.copyTo(mask0);
356
else
357
transpose(mask, mask0);
358
}
359
bestModel.copyTo(_model);
360
result = count >= modelPoints;
361
}
362
else
363
_model.release();
364
365
return result;
366
}
367
368
};
369
370
Ptr<PointSetRegistrator> createRANSACPointSetRegistrator(const Ptr<PointSetRegistrator::Callback>& _cb,
371
int _modelPoints, double _threshold,
372
double _confidence, int _maxIters)
373
{
374
return Ptr<PointSetRegistrator>(
375
new RANSACPointSetRegistrator(_cb, _modelPoints, _threshold, _confidence, _maxIters));
376
}
377
378
379
Ptr<PointSetRegistrator> createLMeDSPointSetRegistrator(const Ptr<PointSetRegistrator::Callback>& _cb,
380
int _modelPoints, double _confidence, int _maxIters)
381
{
382
return Ptr<PointSetRegistrator>(
383
new LMeDSPointSetRegistrator(_cb, _modelPoints, _confidence, _maxIters));
384
}
385
386
387
/*
388
* Compute
389
* x a b c X t1
390
* y = d e f * Y + t2
391
* z g h i Z t3
392
*
393
* - every element in _m1 contains (X,Y,Z), which are called source points
394
* - every element in _m2 contains (x,y,z), which are called destination points
395
* - _model is of size 3x4, which contains
396
* a b c t1
397
* d e f t2
398
* g h i t3
399
*/
400
class Affine3DEstimatorCallback : public PointSetRegistrator::Callback
401
{
402
public:
403
int runKernel( InputArray _m1, InputArray _m2, OutputArray _model ) const CV_OVERRIDE
404
{
405
Mat m1 = _m1.getMat(), m2 = _m2.getMat();
406
const Point3f* from = m1.ptr<Point3f>();
407
const Point3f* to = m2.ptr<Point3f>();
408
409
const int N = 12;
410
double buf[N*N + N + N];
411
Mat A(N, N, CV_64F, &buf[0]);
412
Mat B(N, 1, CV_64F, &buf[0] + N*N);
413
Mat X(N, 1, CV_64F, &buf[0] + N*N + N);
414
double* Adata = A.ptr<double>();
415
double* Bdata = B.ptr<double>();
416
A = Scalar::all(0);
417
418
for( int i = 0; i < (N/3); i++ )
419
{
420
Bdata[i*3] = to[i].x;
421
Bdata[i*3+1] = to[i].y;
422
Bdata[i*3+2] = to[i].z;
423
424
double *aptr = Adata + i*3*N;
425
for(int k = 0; k < 3; ++k)
426
{
427
aptr[0] = from[i].x;
428
aptr[1] = from[i].y;
429
aptr[2] = from[i].z;
430
aptr[3] = 1.0;
431
aptr += 16;
432
}
433
}
434
435
solve(A, B, X, DECOMP_SVD);
436
X.reshape(1, 3).copyTo(_model);
437
438
return 1;
439
}
440
441
void computeError( InputArray _m1, InputArray _m2, InputArray _model, OutputArray _err ) const CV_OVERRIDE
442
{
443
Mat m1 = _m1.getMat(), m2 = _m2.getMat(), model = _model.getMat();
444
const Point3f* from = m1.ptr<Point3f>();
445
const Point3f* to = m2.ptr<Point3f>();
446
const double* F = model.ptr<double>();
447
448
int count = m1.checkVector(3);
449
CV_Assert( count > 0 );
450
451
_err.create(count, 1, CV_32F);
452
Mat err = _err.getMat();
453
float* errptr = err.ptr<float>();
454
455
for(int i = 0; i < count; i++ )
456
{
457
const Point3f& f = from[i];
458
const Point3f& t = to[i];
459
460
double a = F[0]*f.x + F[1]*f.y + F[ 2]*f.z + F[ 3] - t.x;
461
double b = F[4]*f.x + F[5]*f.y + F[ 6]*f.z + F[ 7] - t.y;
462
double c = F[8]*f.x + F[9]*f.y + F[10]*f.z + F[11] - t.z;
463
464
errptr[i] = (float)(a*a + b*b + c*c);
465
}
466
}
467
468
bool checkSubset( InputArray _ms1, InputArray _ms2, int count ) const CV_OVERRIDE
469
{
470
const float threshold = 0.996f;
471
Mat ms1 = _ms1.getMat(), ms2 = _ms2.getMat();
472
473
for( int inp = 1; inp <= 2; inp++ )
474
{
475
int j, k, i = count - 1;
476
const Mat* msi = inp == 1 ? &ms1 : &ms2;
477
const Point3f* ptr = msi->ptr<Point3f>();
478
479
CV_Assert( count <= msi->rows );
480
481
// check that the i-th selected point does not belong
482
// to a line connecting some previously selected points
483
for(j = 0; j < i; ++j)
484
{
485
Point3f d1 = ptr[j] - ptr[i];
486
float n1 = d1.x*d1.x + d1.y*d1.y;
487
488
for(k = 0; k < j; ++k)
489
{
490
Point3f d2 = ptr[k] - ptr[i];
491
float denom = (d2.x*d2.x + d2.y*d2.y)*n1;
492
float num = d1.x*d2.x + d1.y*d2.y;
493
494
if( num*num > threshold*threshold*denom )
495
return false;
496
}
497
}
498
}
499
return true;
500
}
501
};
502
503
/*
504
* Compute
505
* x a b X c
506
* = * +
507
* y d e Y f
508
*
509
* - every element in _m1 contains (X,Y), which are called source points
510
* - every element in _m2 contains (x,y), which are called destination points
511
* - _model is of size 2x3, which contains
512
* a b c
513
* d e f
514
*/
515
class Affine2DEstimatorCallback : public PointSetRegistrator::Callback
516
{
517
public:
518
int runKernel( InputArray _m1, InputArray _m2, OutputArray _model ) const CV_OVERRIDE
519
{
520
Mat m1 = _m1.getMat(), m2 = _m2.getMat();
521
const Point2f* from = m1.ptr<Point2f>();
522
const Point2f* to = m2.ptr<Point2f>();
523
_model.create(2, 3, CV_64F);
524
Mat M_mat = _model.getMat();
525
double *M = M_mat.ptr<double>();
526
527
// we need 3 points to estimate affine transform
528
double x1 = from[0].x;
529
double y1 = from[0].y;
530
double x2 = from[1].x;
531
double y2 = from[1].y;
532
double x3 = from[2].x;
533
double y3 = from[2].y;
534
535
double X1 = to[0].x;
536
double Y1 = to[0].y;
537
double X2 = to[1].x;
538
double Y2 = to[1].y;
539
double X3 = to[2].x;
540
double Y3 = to[2].y;
541
542
/*
543
We want to solve AX = B
544
545
| x1 y1 1 0 0 0 |
546
| 0 0 0 x1 y1 1 |
547
| x2 y2 1 0 0 0 |
548
A = | 0 0 0 x2 y2 1 |
549
| x3 y3 1 0 0 0 |
550
| 0 0 0 x3 y3 1 |
551
B = (X1, Y1, X2, Y2, X3, Y3).t()
552
X = (a, b, c, d, e, f).t()
553
554
As the estimate of (a, b, c) only depends on the Xi, and (d, e, f) only
555
depends on the Yi, we do the *trick* to solve each one analytically.
556
557
| X1 | | x1 y1 1 | | a |
558
| X2 | = | x2 y2 1 | * | b |
559
| X3 | | x3 y3 1 | | c |
560
561
| Y1 | | x1 y1 1 | | d |
562
| Y2 | = | x2 y2 1 | * | e |
563
| Y3 | | x3 y3 1 | | f |
564
*/
565
566
double d = 1. / ( x1*(y2-y3) + x2*(y3-y1) + x3*(y1-y2) );
567
568
M[0] = d * ( X1*(y2-y3) + X2*(y3-y1) + X3*(y1-y2) );
569
M[1] = d * ( X1*(x3-x2) + X2*(x1-x3) + X3*(x2-x1) );
570
M[2] = d * ( X1*(x2*y3 - x3*y2) + X2*(x3*y1 - x1*y3) + X3*(x1*y2 - x2*y1) );
571
572
M[3] = d * ( Y1*(y2-y3) + Y2*(y3-y1) + Y3*(y1-y2) );
573
M[4] = d * ( Y1*(x3-x2) + Y2*(x1-x3) + Y3*(x2-x1) );
574
M[5] = d * ( Y1*(x2*y3 - x3*y2) + Y2*(x3*y1 - x1*y3) + Y3*(x1*y2 - x2*y1) );
575
return 1;
576
}
577
578
void computeError( InputArray _m1, InputArray _m2, InputArray _model, OutputArray _err ) const CV_OVERRIDE
579
{
580
Mat m1 = _m1.getMat(), m2 = _m2.getMat(), model = _model.getMat();
581
const Point2f* from = m1.ptr<Point2f>();
582
const Point2f* to = m2.ptr<Point2f>();
583
const double* F = model.ptr<double>();
584
585
int count = m1.checkVector(2);
586
CV_Assert( count > 0 );
587
588
_err.create(count, 1, CV_32F);
589
Mat err = _err.getMat();
590
float* errptr = err.ptr<float>();
591
// transform matrix to floats
592
float F0 = (float)F[0], F1 = (float)F[1], F2 = (float)F[2];
593
float F3 = (float)F[3], F4 = (float)F[4], F5 = (float)F[5];
594
595
for(int i = 0; i < count; i++ )
596
{
597
const Point2f& f = from[i];
598
const Point2f& t = to[i];
599
600
float a = F0*f.x + F1*f.y + F2 - t.x;
601
float b = F3*f.x + F4*f.y + F5 - t.y;
602
603
errptr[i] = a*a + b*b;
604
}
605
}
606
607
bool checkSubset( InputArray _ms1, InputArray _ms2, int count ) const CV_OVERRIDE
608
{
609
Mat ms1 = _ms1.getMat();
610
Mat ms2 = _ms2.getMat();
611
// check collinearity and also check that points are too close
612
return !haveCollinearPoints(ms1, count) && !haveCollinearPoints(ms2, count);
613
}
614
};
615
616
/*
617
* Compute
618
* x c -s X t1
619
* = * +
620
* y s c Y t2
621
*
622
* - every element in _m1 contains (X,Y), which are called source points
623
* - every element in _m2 contains (x,y), which are called destination points
624
* - _model is of size 2x3, which contains
625
* c -s t1
626
* s c t2
627
*/
628
class AffinePartial2DEstimatorCallback : public Affine2DEstimatorCallback
629
{
630
public:
631
int runKernel( InputArray _m1, InputArray _m2, OutputArray _model ) const CV_OVERRIDE
632
{
633
Mat m1 = _m1.getMat(), m2 = _m2.getMat();
634
const Point2f* from = m1.ptr<Point2f>();
635
const Point2f* to = m2.ptr<Point2f>();
636
_model.create(2, 3, CV_64F);
637
Mat M_mat = _model.getMat();
638
double *M = M_mat.ptr<double>();
639
640
// we need only 2 points to estimate transform
641
double x1 = from[0].x;
642
double y1 = from[0].y;
643
double x2 = from[1].x;
644
double y2 = from[1].y;
645
646
double X1 = to[0].x;
647
double Y1 = to[0].y;
648
double X2 = to[1].x;
649
double Y2 = to[1].y;
650
651
/*
652
we are solving AS = B
653
| x1 -y1 1 0 |
654
| y1 x1 0 1 |
655
A = | x2 -y2 1 0 |
656
| y2 x2 0 1 |
657
B = (X1, Y1, X2, Y2).t()
658
we solve that analytically
659
*/
660
double d = 1./((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
661
662
// solution vector
663
double S0 = d * ( (X1-X2)*(x1-x2) + (Y1-Y2)*(y1-y2) );
664
double S1 = d * ( (Y1-Y2)*(x1-x2) - (X1-X2)*(y1-y2) );
665
double S2 = d * ( (Y1-Y2)*(x1*y2 - x2*y1) - (X1*y2 - X2*y1)*(y1-y2) - (X1*x2 - X2*x1)*(x1-x2) );
666
double S3 = d * (-(X1-X2)*(x1*y2 - x2*y1) - (Y1*x2 - Y2*x1)*(x1-x2) - (Y1*y2 - Y2*y1)*(y1-y2) );
667
668
// set model, rotation part is antisymmetric
669
M[0] = M[4] = S0;
670
M[1] = -S1;
671
M[2] = S2;
672
M[3] = S1;
673
M[5] = S3;
674
return 1;
675
}
676
};
677
678
class Affine2DRefineCallback : public LMSolver::Callback
679
{
680
public:
681
Affine2DRefineCallback(InputArray _src, InputArray _dst)
682
{
683
src = _src.getMat();
684
dst = _dst.getMat();
685
}
686
687
bool compute(InputArray _param, OutputArray _err, OutputArray _Jac) const CV_OVERRIDE
688
{
689
int i, count = src.checkVector(2);
690
Mat param = _param.getMat();
691
_err.create(count*2, 1, CV_64F);
692
Mat err = _err.getMat(), J;
693
if( _Jac.needed())
694
{
695
_Jac.create(count*2, param.rows, CV_64F);
696
J = _Jac.getMat();
697
CV_Assert( J.isContinuous() && J.cols == 6 );
698
}
699
700
const Point2f* M = src.ptr<Point2f>();
701
const Point2f* m = dst.ptr<Point2f>();
702
const double* h = param.ptr<double>();
703
double* errptr = err.ptr<double>();
704
double* Jptr = J.data ? J.ptr<double>() : 0;
705
706
for( i = 0; i < count; i++ )
707
{
708
double Mx = M[i].x, My = M[i].y;
709
double xi = h[0]*Mx + h[1]*My + h[2];
710
double yi = h[3]*Mx + h[4]*My + h[5];
711
errptr[i*2] = xi - m[i].x;
712
errptr[i*2+1] = yi - m[i].y;
713
714
/*
715
Jacobian should be:
716
{x, y, 1, 0, 0, 0}
717
{0, 0, 0, x, y, 1}
718
*/
719
if( Jptr )
720
{
721
Jptr[0] = Mx; Jptr[1] = My; Jptr[2] = 1.;
722
Jptr[3] = Jptr[4] = Jptr[5] = 0.;
723
Jptr[6] = Jptr[7] = Jptr[8] = 0.;
724
Jptr[9] = Mx; Jptr[10] = My; Jptr[11] = 1.;
725
726
Jptr += 6*2;
727
}
728
}
729
730
return true;
731
}
732
733
Mat src, dst;
734
};
735
736
class AffinePartial2DRefineCallback : public LMSolver::Callback
737
{
738
public:
739
AffinePartial2DRefineCallback(InputArray _src, InputArray _dst)
740
{
741
src = _src.getMat();
742
dst = _dst.getMat();
743
}
744
745
bool compute(InputArray _param, OutputArray _err, OutputArray _Jac) const CV_OVERRIDE
746
{
747
int i, count = src.checkVector(2);
748
Mat param = _param.getMat();
749
_err.create(count*2, 1, CV_64F);
750
Mat err = _err.getMat(), J;
751
if( _Jac.needed())
752
{
753
_Jac.create(count*2, param.rows, CV_64F);
754
J = _Jac.getMat();
755
CV_Assert( J.isContinuous() && J.cols == 4 );
756
}
757
758
const Point2f* M = src.ptr<Point2f>();
759
const Point2f* m = dst.ptr<Point2f>();
760
const double* h = param.ptr<double>();
761
double* errptr = err.ptr<double>();
762
double* Jptr = J.data ? J.ptr<double>() : 0;
763
764
for( i = 0; i < count; i++ )
765
{
766
double Mx = M[i].x, My = M[i].y;
767
double xi = h[0]*Mx - h[1]*My + h[2];
768
double yi = h[1]*Mx + h[0]*My + h[3];
769
errptr[i*2] = xi - m[i].x;
770
errptr[i*2+1] = yi - m[i].y;
771
772
/*
773
Jacobian should be:
774
{x, -y, 1, 0}
775
{y, x, 0, 1}
776
*/
777
if( Jptr )
778
{
779
Jptr[0] = Mx; Jptr[1] = -My; Jptr[2] = 1.; Jptr[3] = 0.;
780
Jptr[4] = My; Jptr[5] = Mx; Jptr[6] = 0.; Jptr[7] = 1.;
781
782
Jptr += 4*2;
783
}
784
}
785
786
return true;
787
}
788
789
Mat src, dst;
790
};
791
792
int estimateAffine3D(InputArray _from, InputArray _to,
793
OutputArray _out, OutputArray _inliers,
794
double ransacThreshold, double confidence)
795
{
796
CV_INSTRUMENT_REGION();
797
798
Mat from = _from.getMat(), to = _to.getMat();
799
int count = from.checkVector(3);
800
801
CV_Assert( count >= 0 && to.checkVector(3) == count );
802
803
Mat dFrom, dTo;
804
from.convertTo(dFrom, CV_32F);
805
to.convertTo(dTo, CV_32F);
806
dFrom = dFrom.reshape(3, count);
807
dTo = dTo.reshape(3, count);
808
809
const double epsilon = DBL_EPSILON;
810
ransacThreshold = ransacThreshold <= 0 ? 3 : ransacThreshold;
811
confidence = (confidence < epsilon) ? 0.99 : (confidence > 1 - epsilon) ? 0.99 : confidence;
812
813
return createRANSACPointSetRegistrator(makePtr<Affine3DEstimatorCallback>(), 4, ransacThreshold, confidence)->run(dFrom, dTo, _out, _inliers);
814
}
815
816
Mat estimateAffine2D(InputArray _from, InputArray _to, OutputArray _inliers,
817
const int method, const double ransacReprojThreshold,
818
const size_t maxIters, const double confidence,
819
const size_t refineIters)
820
{
821
Mat from = _from.getMat(), to = _to.getMat();
822
int count = from.checkVector(2);
823
bool result = false;
824
Mat H;
825
826
CV_Assert( count >= 0 && to.checkVector(2) == count );
827
828
if (from.type() != CV_32FC2 || to.type() != CV_32FC2)
829
{
830
Mat tmp1, tmp2;
831
from.convertTo(tmp1, CV_32FC2);
832
from = tmp1;
833
to.convertTo(tmp2, CV_32FC2);
834
to = tmp2;
835
}
836
// convert to N x 1 vectors
837
from = from.reshape(2, count);
838
to = to.reshape(2, count);
839
840
Mat inliers;
841
if(_inliers.needed())
842
{
843
_inliers.create(count, 1, CV_8U, -1, true);
844
inliers = _inliers.getMat();
845
}
846
847
// run robust method
848
Ptr<PointSetRegistrator::Callback> cb = makePtr<Affine2DEstimatorCallback>();
849
if( method == RANSAC )
850
result = createRANSACPointSetRegistrator(cb, 3, ransacReprojThreshold, confidence, static_cast<int>(maxIters))->run(from, to, H, inliers);
851
else if( method == LMEDS )
852
result = createLMeDSPointSetRegistrator(cb, 3, confidence, static_cast<int>(maxIters))->run(from, to, H, inliers);
853
else
854
CV_Error(Error::StsBadArg, "Unknown or unsupported robust estimation method");
855
856
if(result && count > 3 && refineIters)
857
{
858
// reorder to start with inliers
859
compressElems(from.ptr<Point2f>(), inliers.ptr<uchar>(), 1, count);
860
int inliers_count = compressElems(to.ptr<Point2f>(), inliers.ptr<uchar>(), 1, count);
861
if(inliers_count > 0)
862
{
863
Mat src = from.rowRange(0, inliers_count);
864
Mat dst = to.rowRange(0, inliers_count);
865
Mat Hvec = H.reshape(1, 6);
866
createLMSolver(makePtr<Affine2DRefineCallback>(src, dst), static_cast<int>(refineIters))->run(Hvec);
867
}
868
}
869
870
if (!result)
871
{
872
H.release();
873
if(_inliers.needed())
874
{
875
inliers = Mat::zeros(count, 1, CV_8U);
876
inliers.copyTo(_inliers);
877
}
878
}
879
880
return H;
881
}
882
883
Mat estimateAffinePartial2D(InputArray _from, InputArray _to, OutputArray _inliers,
884
const int method, const double ransacReprojThreshold,
885
const size_t maxIters, const double confidence,
886
const size_t refineIters)
887
{
888
Mat from = _from.getMat(), to = _to.getMat();
889
const int count = from.checkVector(2);
890
bool result = false;
891
Mat H;
892
893
CV_Assert( count >= 0 && to.checkVector(2) == count );
894
895
if (from.type() != CV_32FC2 || to.type() != CV_32FC2)
896
{
897
Mat tmp1, tmp2;
898
from.convertTo(tmp1, CV_32FC2);
899
from = tmp1;
900
to.convertTo(tmp2, CV_32FC2);
901
to = tmp2;
902
}
903
// convert to N x 1 vectors
904
from = from.reshape(2, count);
905
to = to.reshape(2, count);
906
907
Mat inliers;
908
if(_inliers.needed())
909
{
910
_inliers.create(count, 1, CV_8U, -1, true);
911
inliers = _inliers.getMat();
912
}
913
914
// run robust estimation
915
Ptr<PointSetRegistrator::Callback> cb = makePtr<AffinePartial2DEstimatorCallback>();
916
if( method == RANSAC )
917
result = createRANSACPointSetRegistrator(cb, 2, ransacReprojThreshold, confidence, static_cast<int>(maxIters))->run(from, to, H, inliers);
918
else if( method == LMEDS )
919
result = createLMeDSPointSetRegistrator(cb, 2, confidence, static_cast<int>(maxIters))->run(from, to, H, inliers);
920
else
921
CV_Error(Error::StsBadArg, "Unknown or unsupported robust estimation method");
922
923
if(result && count > 2 && refineIters)
924
{
925
// reorder to start with inliers
926
compressElems(from.ptr<Point2f>(), inliers.ptr<uchar>(), 1, count);
927
int inliers_count = compressElems(to.ptr<Point2f>(), inliers.ptr<uchar>(), 1, count);
928
if(inliers_count > 0)
929
{
930
Mat src = from.rowRange(0, inliers_count);
931
Mat dst = to.rowRange(0, inliers_count);
932
// H is
933
// a -b tx
934
// b a ty
935
// Hvec model for LevMarq is
936
// (a, b, tx, ty)
937
double *Hptr = H.ptr<double>();
938
double Hvec_buf[4] = {Hptr[0], Hptr[3], Hptr[2], Hptr[5]};
939
Mat Hvec (4, 1, CV_64F, Hvec_buf);
940
createLMSolver(makePtr<AffinePartial2DRefineCallback>(src, dst), static_cast<int>(refineIters))->run(Hvec);
941
// update H with refined parameters
942
Hptr[0] = Hptr[4] = Hvec_buf[0];
943
Hptr[1] = -Hvec_buf[1];
944
Hptr[2] = Hvec_buf[2];
945
Hptr[3] = Hvec_buf[1];
946
Hptr[5] = Hvec_buf[3];
947
}
948
}
949
950
if (!result)
951
{
952
H.release();
953
if(_inliers.needed())
954
{
955
inliers = Mat::zeros(count, 1, CV_8U);
956
inliers.copyTo(_inliers);
957
}
958
}
959
960
return H;
961
}
962
963
} // namespace cv
964
965