Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/stitching/src/motion_estimators.cpp
16337 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
// 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 "opencv2/calib3d/calib3d_c.h"
45
#include "opencv2/core/cvdef.h"
46
47
using namespace cv;
48
using namespace cv::detail;
49
50
namespace {
51
52
struct IncDistance
53
{
54
IncDistance(std::vector<int> &vdists) : dists(&vdists[0]) {}
55
void operator ()(const GraphEdge &edge) { dists[edge.to] = dists[edge.from] + 1; }
56
int* dists;
57
};
58
59
60
struct CalcRotation
61
{
62
CalcRotation(int _num_images, const std::vector<MatchesInfo> &_pairwise_matches, std::vector<CameraParams> &_cameras)
63
: num_images(_num_images), pairwise_matches(&_pairwise_matches[0]), cameras(&_cameras[0]) {}
64
65
void operator ()(const GraphEdge &edge)
66
{
67
int pair_idx = edge.from * num_images + edge.to;
68
69
Mat_<double> K_from = Mat::eye(3, 3, CV_64F);
70
K_from(0,0) = cameras[edge.from].focal;
71
K_from(1,1) = cameras[edge.from].focal * cameras[edge.from].aspect;
72
K_from(0,2) = cameras[edge.from].ppx;
73
K_from(1,2) = cameras[edge.from].ppy;
74
75
Mat_<double> K_to = Mat::eye(3, 3, CV_64F);
76
K_to(0,0) = cameras[edge.to].focal;
77
K_to(1,1) = cameras[edge.to].focal * cameras[edge.to].aspect;
78
K_to(0,2) = cameras[edge.to].ppx;
79
K_to(1,2) = cameras[edge.to].ppy;
80
81
Mat R = K_from.inv() * pairwise_matches[pair_idx].H.inv() * K_to;
82
cameras[edge.to].R = cameras[edge.from].R * R;
83
}
84
85
int num_images;
86
const MatchesInfo* pairwise_matches;
87
CameraParams* cameras;
88
};
89
90
91
/**
92
* @brief Functor calculating final transformation by chaining linear transformations
93
*/
94
struct CalcAffineTransform
95
{
96
CalcAffineTransform(int _num_images,
97
const std::vector<MatchesInfo> &_pairwise_matches,
98
std::vector<CameraParams> &_cameras)
99
: num_images(_num_images), pairwise_matches(&_pairwise_matches[0]), cameras(&_cameras[0]) {}
100
101
void operator()(const GraphEdge &edge)
102
{
103
int pair_idx = edge.from * num_images + edge.to;
104
cameras[edge.to].R = cameras[edge.from].R * pairwise_matches[pair_idx].H;
105
}
106
107
int num_images;
108
const MatchesInfo *pairwise_matches;
109
CameraParams *cameras;
110
};
111
112
113
//////////////////////////////////////////////////////////////////////////////
114
115
void calcDeriv(const Mat &err1, const Mat &err2, double h, Mat res)
116
{
117
for (int i = 0; i < err1.rows; ++i)
118
res.at<double>(i, 0) = (err2.at<double>(i, 0) - err1.at<double>(i, 0)) / h;
119
}
120
121
} // namespace
122
123
124
namespace cv {
125
namespace detail {
126
127
bool HomographyBasedEstimator::estimate(
128
const std::vector<ImageFeatures> &features,
129
const std::vector<MatchesInfo> &pairwise_matches,
130
std::vector<CameraParams> &cameras)
131
{
132
LOGLN("Estimating rotations...");
133
#if ENABLE_LOG
134
int64 t = getTickCount();
135
#endif
136
137
const int num_images = static_cast<int>(features.size());
138
139
#if 0
140
// Robustly estimate focal length from rotating cameras
141
std::vector<Mat> Hs;
142
for (int iter = 0; iter < 100; ++iter)
143
{
144
int len = 2 + rand()%(pairwise_matches.size() - 1);
145
std::vector<int> subset;
146
selectRandomSubset(len, pairwise_matches.size(), subset);
147
Hs.clear();
148
for (size_t i = 0; i < subset.size(); ++i)
149
if (!pairwise_matches[subset[i]].H.empty())
150
Hs.push_back(pairwise_matches[subset[i]].H);
151
Mat_<double> K;
152
if (Hs.size() >= 2)
153
{
154
if (calibrateRotatingCamera(Hs, K))
155
cin.get();
156
}
157
}
158
#endif
159
160
if (!is_focals_estimated_)
161
{
162
// Estimate focal length and set it for all cameras
163
std::vector<double> focals;
164
estimateFocal(features, pairwise_matches, focals);
165
cameras.assign(num_images, CameraParams());
166
for (int i = 0; i < num_images; ++i)
167
cameras[i].focal = focals[i];
168
}
169
else
170
{
171
for (int i = 0; i < num_images; ++i)
172
{
173
cameras[i].ppx -= 0.5 * features[i].img_size.width;
174
cameras[i].ppy -= 0.5 * features[i].img_size.height;
175
}
176
}
177
178
// Restore global motion
179
Graph span_tree;
180
std::vector<int> span_tree_centers;
181
findMaxSpanningTree(num_images, pairwise_matches, span_tree, span_tree_centers);
182
span_tree.walkBreadthFirst(span_tree_centers[0], CalcRotation(num_images, pairwise_matches, cameras));
183
184
// As calculations were performed under assumption that p.p. is in image center
185
for (int i = 0; i < num_images; ++i)
186
{
187
cameras[i].ppx += 0.5 * features[i].img_size.width;
188
cameras[i].ppy += 0.5 * features[i].img_size.height;
189
}
190
191
LOGLN("Estimating rotations, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");
192
return true;
193
}
194
195
196
//////////////////////////////////////////////////////////////////////////////
197
198
bool AffineBasedEstimator::estimate(const std::vector<ImageFeatures> &features,
199
const std::vector<MatchesInfo> &pairwise_matches,
200
std::vector<CameraParams> &cameras)
201
{
202
cameras.assign(features.size(), CameraParams());
203
const int num_images = static_cast<int>(features.size());
204
205
// find maximum spaning tree on pairwise matches
206
cv::detail::Graph span_tree;
207
std::vector<int> span_tree_centers;
208
// uses number of inliers as weights
209
findMaxSpanningTree(num_images, pairwise_matches, span_tree,
210
span_tree_centers);
211
212
// compute final transform by chaining H together
213
span_tree.walkBreadthFirst(
214
span_tree_centers[0],
215
CalcAffineTransform(num_images, pairwise_matches, cameras));
216
// this estimator never fails
217
return true;
218
}
219
220
221
//////////////////////////////////////////////////////////////////////////////
222
223
bool BundleAdjusterBase::estimate(const std::vector<ImageFeatures> &features,
224
const std::vector<MatchesInfo> &pairwise_matches,
225
std::vector<CameraParams> &cameras)
226
{
227
LOG_CHAT("Bundle adjustment");
228
#if ENABLE_LOG
229
int64 t = getTickCount();
230
#endif
231
232
num_images_ = static_cast<int>(features.size());
233
features_ = &features[0];
234
pairwise_matches_ = &pairwise_matches[0];
235
236
setUpInitialCameraParams(cameras);
237
238
// Leave only consistent image pairs
239
edges_.clear();
240
for (int i = 0; i < num_images_ - 1; ++i)
241
{
242
for (int j = i + 1; j < num_images_; ++j)
243
{
244
const MatchesInfo& matches_info = pairwise_matches_[i * num_images_ + j];
245
if (matches_info.confidence > conf_thresh_)
246
edges_.push_back(std::make_pair(i, j));
247
}
248
}
249
250
// Compute number of correspondences
251
total_num_matches_ = 0;
252
for (size_t i = 0; i < edges_.size(); ++i)
253
total_num_matches_ += static_cast<int>(pairwise_matches[edges_[i].first * num_images_ +
254
edges_[i].second].num_inliers);
255
256
CvLevMarq solver(num_images_ * num_params_per_cam_,
257
total_num_matches_ * num_errs_per_measurement_,
258
cvTermCriteria(term_criteria_));
259
260
Mat err, jac;
261
CvMat matParams = cvMat(cam_params_);
262
cvCopy(&matParams, solver.param);
263
264
int iter = 0;
265
for(;;)
266
{
267
const CvMat* _param = 0;
268
CvMat* _jac = 0;
269
CvMat* _err = 0;
270
271
bool proceed = solver.update(_param, _jac, _err);
272
273
cvCopy(_param, &matParams);
274
275
if (!proceed || !_err)
276
break;
277
278
if (_jac)
279
{
280
calcJacobian(jac);
281
CvMat tmp = cvMat(jac);
282
cvCopy(&tmp, _jac);
283
}
284
285
if (_err)
286
{
287
calcError(err);
288
LOG_CHAT(".");
289
iter++;
290
CvMat tmp = cvMat(err);
291
cvCopy(&tmp, _err);
292
}
293
}
294
295
LOGLN_CHAT("");
296
LOGLN_CHAT("Bundle adjustment, final RMS error: " << std::sqrt(err.dot(err) / total_num_matches_));
297
LOGLN_CHAT("Bundle adjustment, iterations done: " << iter);
298
299
// Check if all camera parameters are valid
300
bool ok = true;
301
for (int i = 0; i < cam_params_.rows; ++i)
302
{
303
if (cvIsNaN(cam_params_.at<double>(i,0)))
304
{
305
ok = false;
306
break;
307
}
308
}
309
if (!ok)
310
return false;
311
312
obtainRefinedCameraParams(cameras);
313
314
// Normalize motion to center image
315
Graph span_tree;
316
std::vector<int> span_tree_centers;
317
findMaxSpanningTree(num_images_, pairwise_matches, span_tree, span_tree_centers);
318
Mat R_inv = cameras[span_tree_centers[0]].R.inv();
319
for (int i = 0; i < num_images_; ++i)
320
cameras[i].R = R_inv * cameras[i].R;
321
322
LOGLN_CHAT("Bundle adjustment, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");
323
return true;
324
}
325
326
327
//////////////////////////////////////////////////////////////////////////////
328
329
void BundleAdjusterReproj::setUpInitialCameraParams(const std::vector<CameraParams> &cameras)
330
{
331
cam_params_.create(num_images_ * 7, 1, CV_64F);
332
SVD svd;
333
for (int i = 0; i < num_images_; ++i)
334
{
335
cam_params_.at<double>(i * 7, 0) = cameras[i].focal;
336
cam_params_.at<double>(i * 7 + 1, 0) = cameras[i].ppx;
337
cam_params_.at<double>(i * 7 + 2, 0) = cameras[i].ppy;
338
cam_params_.at<double>(i * 7 + 3, 0) = cameras[i].aspect;
339
340
svd(cameras[i].R, SVD::FULL_UV);
341
Mat R = svd.u * svd.vt;
342
if (determinant(R) < 0)
343
R *= -1;
344
345
Mat rvec;
346
Rodrigues(R, rvec);
347
CV_Assert(rvec.type() == CV_32F);
348
cam_params_.at<double>(i * 7 + 4, 0) = rvec.at<float>(0, 0);
349
cam_params_.at<double>(i * 7 + 5, 0) = rvec.at<float>(1, 0);
350
cam_params_.at<double>(i * 7 + 6, 0) = rvec.at<float>(2, 0);
351
}
352
}
353
354
355
void BundleAdjusterReproj::obtainRefinedCameraParams(std::vector<CameraParams> &cameras) const
356
{
357
for (int i = 0; i < num_images_; ++i)
358
{
359
cameras[i].focal = cam_params_.at<double>(i * 7, 0);
360
cameras[i].ppx = cam_params_.at<double>(i * 7 + 1, 0);
361
cameras[i].ppy = cam_params_.at<double>(i * 7 + 2, 0);
362
cameras[i].aspect = cam_params_.at<double>(i * 7 + 3, 0);
363
364
Mat rvec(3, 1, CV_64F);
365
rvec.at<double>(0, 0) = cam_params_.at<double>(i * 7 + 4, 0);
366
rvec.at<double>(1, 0) = cam_params_.at<double>(i * 7 + 5, 0);
367
rvec.at<double>(2, 0) = cam_params_.at<double>(i * 7 + 6, 0);
368
Rodrigues(rvec, cameras[i].R);
369
370
Mat tmp;
371
cameras[i].R.convertTo(tmp, CV_32F);
372
cameras[i].R = tmp;
373
}
374
}
375
376
377
void BundleAdjusterReproj::calcError(Mat &err)
378
{
379
err.create(total_num_matches_ * 2, 1, CV_64F);
380
381
int match_idx = 0;
382
for (size_t edge_idx = 0; edge_idx < edges_.size(); ++edge_idx)
383
{
384
int i = edges_[edge_idx].first;
385
int j = edges_[edge_idx].second;
386
double f1 = cam_params_.at<double>(i * 7, 0);
387
double f2 = cam_params_.at<double>(j * 7, 0);
388
double ppx1 = cam_params_.at<double>(i * 7 + 1, 0);
389
double ppx2 = cam_params_.at<double>(j * 7 + 1, 0);
390
double ppy1 = cam_params_.at<double>(i * 7 + 2, 0);
391
double ppy2 = cam_params_.at<double>(j * 7 + 2, 0);
392
double a1 = cam_params_.at<double>(i * 7 + 3, 0);
393
double a2 = cam_params_.at<double>(j * 7 + 3, 0);
394
395
double R1[9];
396
Mat R1_(3, 3, CV_64F, R1);
397
Mat rvec(3, 1, CV_64F);
398
rvec.at<double>(0, 0) = cam_params_.at<double>(i * 7 + 4, 0);
399
rvec.at<double>(1, 0) = cam_params_.at<double>(i * 7 + 5, 0);
400
rvec.at<double>(2, 0) = cam_params_.at<double>(i * 7 + 6, 0);
401
Rodrigues(rvec, R1_);
402
403
double R2[9];
404
Mat R2_(3, 3, CV_64F, R2);
405
rvec.at<double>(0, 0) = cam_params_.at<double>(j * 7 + 4, 0);
406
rvec.at<double>(1, 0) = cam_params_.at<double>(j * 7 + 5, 0);
407
rvec.at<double>(2, 0) = cam_params_.at<double>(j * 7 + 6, 0);
408
Rodrigues(rvec, R2_);
409
410
const ImageFeatures& features1 = features_[i];
411
const ImageFeatures& features2 = features_[j];
412
const MatchesInfo& matches_info = pairwise_matches_[i * num_images_ + j];
413
414
Mat_<double> K1 = Mat::eye(3, 3, CV_64F);
415
K1(0,0) = f1; K1(0,2) = ppx1;
416
K1(1,1) = f1*a1; K1(1,2) = ppy1;
417
418
Mat_<double> K2 = Mat::eye(3, 3, CV_64F);
419
K2(0,0) = f2; K2(0,2) = ppx2;
420
K2(1,1) = f2*a2; K2(1,2) = ppy2;
421
422
Mat_<double> H = K2 * R2_.inv() * R1_ * K1.inv();
423
424
for (size_t k = 0; k < matches_info.matches.size(); ++k)
425
{
426
if (!matches_info.inliers_mask[k])
427
continue;
428
429
const DMatch& m = matches_info.matches[k];
430
Point2f p1 = features1.keypoints[m.queryIdx].pt;
431
Point2f p2 = features2.keypoints[m.trainIdx].pt;
432
double x = H(0,0)*p1.x + H(0,1)*p1.y + H(0,2);
433
double y = H(1,0)*p1.x + H(1,1)*p1.y + H(1,2);
434
double z = H(2,0)*p1.x + H(2,1)*p1.y + H(2,2);
435
436
err.at<double>(2 * match_idx, 0) = p2.x - x/z;
437
err.at<double>(2 * match_idx + 1, 0) = p2.y - y/z;
438
match_idx++;
439
}
440
}
441
}
442
443
444
void BundleAdjusterReproj::calcJacobian(Mat &jac)
445
{
446
jac.create(total_num_matches_ * 2, num_images_ * 7, CV_64F);
447
jac.setTo(0);
448
449
double val;
450
const double step = 1e-4;
451
452
for (int i = 0; i < num_images_; ++i)
453
{
454
if (refinement_mask_.at<uchar>(0, 0))
455
{
456
val = cam_params_.at<double>(i * 7, 0);
457
cam_params_.at<double>(i * 7, 0) = val - step;
458
calcError(err1_);
459
cam_params_.at<double>(i * 7, 0) = val + step;
460
calcError(err2_);
461
calcDeriv(err1_, err2_, 2 * step, jac.col(i * 7));
462
cam_params_.at<double>(i * 7, 0) = val;
463
}
464
if (refinement_mask_.at<uchar>(0, 2))
465
{
466
val = cam_params_.at<double>(i * 7 + 1, 0);
467
cam_params_.at<double>(i * 7 + 1, 0) = val - step;
468
calcError(err1_);
469
cam_params_.at<double>(i * 7 + 1, 0) = val + step;
470
calcError(err2_);
471
calcDeriv(err1_, err2_, 2 * step, jac.col(i * 7 + 1));
472
cam_params_.at<double>(i * 7 + 1, 0) = val;
473
}
474
if (refinement_mask_.at<uchar>(1, 2))
475
{
476
val = cam_params_.at<double>(i * 7 + 2, 0);
477
cam_params_.at<double>(i * 7 + 2, 0) = val - step;
478
calcError(err1_);
479
cam_params_.at<double>(i * 7 + 2, 0) = val + step;
480
calcError(err2_);
481
calcDeriv(err1_, err2_, 2 * step, jac.col(i * 7 + 2));
482
cam_params_.at<double>(i * 7 + 2, 0) = val;
483
}
484
if (refinement_mask_.at<uchar>(1, 1))
485
{
486
val = cam_params_.at<double>(i * 7 + 3, 0);
487
cam_params_.at<double>(i * 7 + 3, 0) = val - step;
488
calcError(err1_);
489
cam_params_.at<double>(i * 7 + 3, 0) = val + step;
490
calcError(err2_);
491
calcDeriv(err1_, err2_, 2 * step, jac.col(i * 7 + 3));
492
cam_params_.at<double>(i * 7 + 3, 0) = val;
493
}
494
for (int j = 4; j < 7; ++j)
495
{
496
val = cam_params_.at<double>(i * 7 + j, 0);
497
cam_params_.at<double>(i * 7 + j, 0) = val - step;
498
calcError(err1_);
499
cam_params_.at<double>(i * 7 + j, 0) = val + step;
500
calcError(err2_);
501
calcDeriv(err1_, err2_, 2 * step, jac.col(i * 7 + j));
502
cam_params_.at<double>(i * 7 + j, 0) = val;
503
}
504
}
505
}
506
507
508
//////////////////////////////////////////////////////////////////////////////
509
510
void BundleAdjusterRay::setUpInitialCameraParams(const std::vector<CameraParams> &cameras)
511
{
512
cam_params_.create(num_images_ * 4, 1, CV_64F);
513
SVD svd;
514
for (int i = 0; i < num_images_; ++i)
515
{
516
cam_params_.at<double>(i * 4, 0) = cameras[i].focal;
517
518
svd(cameras[i].R, SVD::FULL_UV);
519
Mat R = svd.u * svd.vt;
520
if (determinant(R) < 0)
521
R *= -1;
522
523
Mat rvec;
524
Rodrigues(R, rvec);
525
CV_Assert(rvec.type() == CV_32F);
526
cam_params_.at<double>(i * 4 + 1, 0) = rvec.at<float>(0, 0);
527
cam_params_.at<double>(i * 4 + 2, 0) = rvec.at<float>(1, 0);
528
cam_params_.at<double>(i * 4 + 3, 0) = rvec.at<float>(2, 0);
529
}
530
}
531
532
533
void BundleAdjusterRay::obtainRefinedCameraParams(std::vector<CameraParams> &cameras) const
534
{
535
for (int i = 0; i < num_images_; ++i)
536
{
537
cameras[i].focal = cam_params_.at<double>(i * 4, 0);
538
539
Mat rvec(3, 1, CV_64F);
540
rvec.at<double>(0, 0) = cam_params_.at<double>(i * 4 + 1, 0);
541
rvec.at<double>(1, 0) = cam_params_.at<double>(i * 4 + 2, 0);
542
rvec.at<double>(2, 0) = cam_params_.at<double>(i * 4 + 3, 0);
543
Rodrigues(rvec, cameras[i].R);
544
545
Mat tmp;
546
cameras[i].R.convertTo(tmp, CV_32F);
547
cameras[i].R = tmp;
548
}
549
}
550
551
552
void BundleAdjusterRay::calcError(Mat &err)
553
{
554
err.create(total_num_matches_ * 3, 1, CV_64F);
555
556
int match_idx = 0;
557
for (size_t edge_idx = 0; edge_idx < edges_.size(); ++edge_idx)
558
{
559
int i = edges_[edge_idx].first;
560
int j = edges_[edge_idx].second;
561
double f1 = cam_params_.at<double>(i * 4, 0);
562
double f2 = cam_params_.at<double>(j * 4, 0);
563
564
double R1[9];
565
Mat R1_(3, 3, CV_64F, R1);
566
Mat rvec(3, 1, CV_64F);
567
rvec.at<double>(0, 0) = cam_params_.at<double>(i * 4 + 1, 0);
568
rvec.at<double>(1, 0) = cam_params_.at<double>(i * 4 + 2, 0);
569
rvec.at<double>(2, 0) = cam_params_.at<double>(i * 4 + 3, 0);
570
Rodrigues(rvec, R1_);
571
572
double R2[9];
573
Mat R2_(3, 3, CV_64F, R2);
574
rvec.at<double>(0, 0) = cam_params_.at<double>(j * 4 + 1, 0);
575
rvec.at<double>(1, 0) = cam_params_.at<double>(j * 4 + 2, 0);
576
rvec.at<double>(2, 0) = cam_params_.at<double>(j * 4 + 3, 0);
577
Rodrigues(rvec, R2_);
578
579
const ImageFeatures& features1 = features_[i];
580
const ImageFeatures& features2 = features_[j];
581
const MatchesInfo& matches_info = pairwise_matches_[i * num_images_ + j];
582
583
Mat_<double> K1 = Mat::eye(3, 3, CV_64F);
584
K1(0,0) = f1; K1(0,2) = features1.img_size.width * 0.5;
585
K1(1,1) = f1; K1(1,2) = features1.img_size.height * 0.5;
586
587
Mat_<double> K2 = Mat::eye(3, 3, CV_64F);
588
K2(0,0) = f2; K2(0,2) = features2.img_size.width * 0.5;
589
K2(1,1) = f2; K2(1,2) = features2.img_size.height * 0.5;
590
591
Mat_<double> H1 = R1_ * K1.inv();
592
Mat_<double> H2 = R2_ * K2.inv();
593
594
for (size_t k = 0; k < matches_info.matches.size(); ++k)
595
{
596
if (!matches_info.inliers_mask[k])
597
continue;
598
599
const DMatch& m = matches_info.matches[k];
600
601
Point2f p1 = features1.keypoints[m.queryIdx].pt;
602
double x1 = H1(0,0)*p1.x + H1(0,1)*p1.y + H1(0,2);
603
double y1 = H1(1,0)*p1.x + H1(1,1)*p1.y + H1(1,2);
604
double z1 = H1(2,0)*p1.x + H1(2,1)*p1.y + H1(2,2);
605
double len = std::sqrt(x1*x1 + y1*y1 + z1*z1);
606
x1 /= len; y1 /= len; z1 /= len;
607
608
Point2f p2 = features2.keypoints[m.trainIdx].pt;
609
double x2 = H2(0,0)*p2.x + H2(0,1)*p2.y + H2(0,2);
610
double y2 = H2(1,0)*p2.x + H2(1,1)*p2.y + H2(1,2);
611
double z2 = H2(2,0)*p2.x + H2(2,1)*p2.y + H2(2,2);
612
len = std::sqrt(x2*x2 + y2*y2 + z2*z2);
613
x2 /= len; y2 /= len; z2 /= len;
614
615
double mult = std::sqrt(f1 * f2);
616
err.at<double>(3 * match_idx, 0) = mult * (x1 - x2);
617
err.at<double>(3 * match_idx + 1, 0) = mult * (y1 - y2);
618
err.at<double>(3 * match_idx + 2, 0) = mult * (z1 - z2);
619
620
match_idx++;
621
}
622
}
623
}
624
625
626
void BundleAdjusterRay::calcJacobian(Mat &jac)
627
{
628
jac.create(total_num_matches_ * 3, num_images_ * 4, CV_64F);
629
630
double val;
631
const double step = 1e-3;
632
633
for (int i = 0; i < num_images_; ++i)
634
{
635
for (int j = 0; j < 4; ++j)
636
{
637
val = cam_params_.at<double>(i * 4 + j, 0);
638
cam_params_.at<double>(i * 4 + j, 0) = val - step;
639
calcError(err1_);
640
cam_params_.at<double>(i * 4 + j, 0) = val + step;
641
calcError(err2_);
642
calcDeriv(err1_, err2_, 2 * step, jac.col(i * 4 + j));
643
cam_params_.at<double>(i * 4 + j, 0) = val;
644
}
645
}
646
}
647
648
//////////////////////////////////////////////////////////////////////////////
649
650
void BundleAdjusterAffine::setUpInitialCameraParams(const std::vector<CameraParams> &cameras)
651
{
652
cam_params_.create(num_images_ * 6, 1, CV_64F);
653
for (size_t i = 0; i < static_cast<size_t>(num_images_); ++i)
654
{
655
CV_Assert(cameras[i].R.type() == CV_32F);
656
// cameras[i].R is
657
// a b tx
658
// c d ty
659
// 0 0 1. (optional)
660
// cam_params_ model for LevMarq is
661
// (a, b, tx, c, d, ty)
662
Mat params (2, 3, CV_64F, cam_params_.ptr<double>() + i * 6);
663
cameras[i].R.rowRange(0, 2).convertTo(params, CV_64F);
664
}
665
}
666
667
668
void BundleAdjusterAffine::obtainRefinedCameraParams(std::vector<CameraParams> &cameras) const
669
{
670
for (int i = 0; i < num_images_; ++i)
671
{
672
// cameras[i].R will be
673
// a b tx
674
// c d ty
675
// 0 0 1
676
cameras[i].R = Mat::eye(3, 3, CV_32F);
677
Mat params = cam_params_.rowRange(i * 6, i * 6 + 6).reshape(1, 2);
678
params.convertTo(cameras[i].R.rowRange(0, 2), CV_32F);
679
}
680
}
681
682
683
void BundleAdjusterAffine::calcError(Mat &err)
684
{
685
err.create(total_num_matches_ * 2, 1, CV_64F);
686
687
int match_idx = 0;
688
for (size_t edge_idx = 0; edge_idx < edges_.size(); ++edge_idx)
689
{
690
size_t i = edges_[edge_idx].first;
691
size_t j = edges_[edge_idx].second;
692
693
const ImageFeatures& features1 = features_[i];
694
const ImageFeatures& features2 = features_[j];
695
const MatchesInfo& matches_info = pairwise_matches_[i * num_images_ + j];
696
697
Mat H1 (2, 3, CV_64F, cam_params_.ptr<double>() + i * 6);
698
Mat H2 (2, 3, CV_64F, cam_params_.ptr<double>() + j * 6);
699
700
// invert H1
701
Mat H1_inv;
702
invertAffineTransform(H1, H1_inv);
703
704
// convert to representation in homogeneous coordinates
705
Mat last_row = Mat::zeros(1, 3, CV_64F);
706
last_row.at<double>(2) = 1.;
707
H1_inv.push_back(last_row);
708
H2.push_back(last_row);
709
710
Mat_<double> H = H1_inv * H2;
711
712
for (size_t k = 0; k < matches_info.matches.size(); ++k)
713
{
714
if (!matches_info.inliers_mask[k])
715
continue;
716
717
const DMatch& m = matches_info.matches[k];
718
const Point2f& p1 = features1.keypoints[m.queryIdx].pt;
719
const Point2f& p2 = features2.keypoints[m.trainIdx].pt;
720
721
double x = H(0,0)*p1.x + H(0,1)*p1.y + H(0,2);
722
double y = H(1,0)*p1.x + H(1,1)*p1.y + H(1,2);
723
724
err.at<double>(2 * match_idx + 0, 0) = p2.x - x;
725
err.at<double>(2 * match_idx + 1, 0) = p2.y - y;
726
727
++match_idx;
728
}
729
}
730
}
731
732
733
void BundleAdjusterAffine::calcJacobian(Mat &jac)
734
{
735
jac.create(total_num_matches_ * 2, num_images_ * 6, CV_64F);
736
737
double val;
738
const double step = 1e-4;
739
740
for (int i = 0; i < num_images_; ++i)
741
{
742
for (int j = 0; j < 6; ++j)
743
{
744
val = cam_params_.at<double>(i * 6 + j, 0);
745
cam_params_.at<double>(i * 6 + j, 0) = val - step;
746
calcError(err1_);
747
cam_params_.at<double>(i * 6 + j, 0) = val + step;
748
calcError(err2_);
749
calcDeriv(err1_, err2_, 2 * step, jac.col(i * 6 + j));
750
cam_params_.at<double>(i * 6 + j, 0) = val;
751
}
752
}
753
}
754
755
756
//////////////////////////////////////////////////////////////////////////////
757
758
void BundleAdjusterAffinePartial::setUpInitialCameraParams(const std::vector<CameraParams> &cameras)
759
{
760
cam_params_.create(num_images_ * 4, 1, CV_64F);
761
for (size_t i = 0; i < static_cast<size_t>(num_images_); ++i)
762
{
763
CV_Assert(cameras[i].R.type() == CV_32F);
764
// cameras[i].R is
765
// a -b tx
766
// b a ty
767
// 0 0 1. (optional)
768
// cam_params_ model for LevMarq is
769
// (a, b, tx, ty)
770
double *params = cam_params_.ptr<double>() + i * 4;
771
params[0] = cameras[i].R.at<float>(0, 0);
772
params[1] = cameras[i].R.at<float>(1, 0);
773
params[2] = cameras[i].R.at<float>(0, 2);
774
params[3] = cameras[i].R.at<float>(1, 2);
775
}
776
}
777
778
779
void BundleAdjusterAffinePartial::obtainRefinedCameraParams(std::vector<CameraParams> &cameras) const
780
{
781
for (size_t i = 0; i < static_cast<size_t>(num_images_); ++i)
782
{
783
// cameras[i].R will be
784
// a -b tx
785
// b a ty
786
// 0 0 1
787
// cam_params_ model for LevMarq is
788
// (a, b, tx, ty)
789
const double *params = cam_params_.ptr<double>() + i * 4;
790
double transform_buf[9] =
791
{
792
params[0], -params[1], params[2],
793
params[1], params[0], params[3],
794
0., 0., 1.
795
};
796
Mat transform(3, 3, CV_64F, transform_buf);
797
transform.convertTo(cameras[i].R, CV_32F);
798
}
799
}
800
801
802
void BundleAdjusterAffinePartial::calcError(Mat &err)
803
{
804
err.create(total_num_matches_ * 2, 1, CV_64F);
805
806
int match_idx = 0;
807
for (size_t edge_idx = 0; edge_idx < edges_.size(); ++edge_idx)
808
{
809
size_t i = edges_[edge_idx].first;
810
size_t j = edges_[edge_idx].second;
811
812
const ImageFeatures& features1 = features_[i];
813
const ImageFeatures& features2 = features_[j];
814
const MatchesInfo& matches_info = pairwise_matches_[i * num_images_ + j];
815
816
const double *H1_ptr = cam_params_.ptr<double>() + i * 4;
817
double H1_buf[9] =
818
{
819
H1_ptr[0], -H1_ptr[1], H1_ptr[2],
820
H1_ptr[1], H1_ptr[0], H1_ptr[3],
821
0., 0., 1.
822
};
823
Mat H1 (3, 3, CV_64F, H1_buf);
824
const double *H2_ptr = cam_params_.ptr<double>() + j * 4;
825
double H2_buf[9] =
826
{
827
H2_ptr[0], -H2_ptr[1], H2_ptr[2],
828
H2_ptr[1], H2_ptr[0], H2_ptr[3],
829
0., 0., 1.
830
};
831
Mat H2 (3, 3, CV_64F, H2_buf);
832
833
// invert H1
834
Mat H1_aff (H1, Range(0, 2));
835
double H1_inv_buf[6];
836
Mat H1_inv (2, 3, CV_64F, H1_inv_buf);
837
invertAffineTransform(H1_aff, H1_inv);
838
H1_inv.copyTo(H1_aff);
839
840
Mat_<double> H = H1 * H2;
841
842
for (size_t k = 0; k < matches_info.matches.size(); ++k)
843
{
844
if (!matches_info.inliers_mask[k])
845
continue;
846
847
const DMatch& m = matches_info.matches[k];
848
const Point2f& p1 = features1.keypoints[m.queryIdx].pt;
849
const Point2f& p2 = features2.keypoints[m.trainIdx].pt;
850
851
double x = H(0,0)*p1.x + H(0,1)*p1.y + H(0,2);
852
double y = H(1,0)*p1.x + H(1,1)*p1.y + H(1,2);
853
854
err.at<double>(2 * match_idx + 0, 0) = p2.x - x;
855
err.at<double>(2 * match_idx + 1, 0) = p2.y - y;
856
857
++match_idx;
858
}
859
}
860
}
861
862
863
void BundleAdjusterAffinePartial::calcJacobian(Mat &jac)
864
{
865
jac.create(total_num_matches_ * 2, num_images_ * 4, CV_64F);
866
867
double val;
868
const double step = 1e-4;
869
870
for (int i = 0; i < num_images_; ++i)
871
{
872
for (int j = 0; j < 4; ++j)
873
{
874
val = cam_params_.at<double>(i * 4 + j, 0);
875
cam_params_.at<double>(i * 4 + j, 0) = val - step;
876
calcError(err1_);
877
cam_params_.at<double>(i * 4 + j, 0) = val + step;
878
calcError(err2_);
879
calcDeriv(err1_, err2_, 2 * step, jac.col(i * 4 + j));
880
cam_params_.at<double>(i * 4 + j, 0) = val;
881
}
882
}
883
}
884
885
886
//////////////////////////////////////////////////////////////////////////////
887
888
void waveCorrect(std::vector<Mat> &rmats, WaveCorrectKind kind)
889
{
890
LOGLN("Wave correcting...");
891
#if ENABLE_LOG
892
int64 t = getTickCount();
893
#endif
894
if (rmats.size() <= 1)
895
{
896
LOGLN("Wave correcting, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");
897
return;
898
}
899
900
Mat moment = Mat::zeros(3, 3, CV_32F);
901
for (size_t i = 0; i < rmats.size(); ++i)
902
{
903
Mat col = rmats[i].col(0);
904
moment += col * col.t();
905
}
906
Mat eigen_vals, eigen_vecs;
907
eigen(moment, eigen_vals, eigen_vecs);
908
909
Mat rg1;
910
if (kind == WAVE_CORRECT_HORIZ)
911
rg1 = eigen_vecs.row(2).t();
912
else if (kind == WAVE_CORRECT_VERT)
913
rg1 = eigen_vecs.row(0).t();
914
else
915
CV_Error(CV_StsBadArg, "unsupported kind of wave correction");
916
917
Mat img_k = Mat::zeros(3, 1, CV_32F);
918
for (size_t i = 0; i < rmats.size(); ++i)
919
img_k += rmats[i].col(2);
920
Mat rg0 = rg1.cross(img_k);
921
double rg0_norm = norm(rg0);
922
923
if( rg0_norm <= DBL_MIN )
924
{
925
return;
926
}
927
928
rg0 /= rg0_norm;
929
930
Mat rg2 = rg0.cross(rg1);
931
932
double conf = 0;
933
if (kind == WAVE_CORRECT_HORIZ)
934
{
935
for (size_t i = 0; i < rmats.size(); ++i)
936
conf += rg0.dot(rmats[i].col(0));
937
if (conf < 0)
938
{
939
rg0 *= -1;
940
rg1 *= -1;
941
}
942
}
943
else if (kind == WAVE_CORRECT_VERT)
944
{
945
for (size_t i = 0; i < rmats.size(); ++i)
946
conf -= rg1.dot(rmats[i].col(0));
947
if (conf < 0)
948
{
949
rg0 *= -1;
950
rg1 *= -1;
951
}
952
}
953
954
Mat R = Mat::zeros(3, 3, CV_32F);
955
Mat tmp = R.row(0);
956
Mat(rg0.t()).copyTo(tmp);
957
tmp = R.row(1);
958
Mat(rg1.t()).copyTo(tmp);
959
tmp = R.row(2);
960
Mat(rg2.t()).copyTo(tmp);
961
962
for (size_t i = 0; i < rmats.size(); ++i)
963
rmats[i] = R * rmats[i];
964
965
LOGLN("Wave correcting, time: " << ((getTickCount() - t) / getTickFrequency()) << " sec");
966
}
967
968
969
//////////////////////////////////////////////////////////////////////////////
970
971
String matchesGraphAsString(std::vector<String> &pathes, std::vector<MatchesInfo> &pairwise_matches,
972
float conf_threshold)
973
{
974
std::stringstream str;
975
str << "graph matches_graph{\n";
976
977
const int num_images = static_cast<int>(pathes.size());
978
std::set<std::pair<int,int> > span_tree_edges;
979
DisjointSets comps(num_images);
980
981
for (int i = 0; i < num_images; ++i)
982
{
983
for (int j = 0; j < num_images; ++j)
984
{
985
if (pairwise_matches[i*num_images + j].confidence < conf_threshold)
986
continue;
987
int comp1 = comps.findSetByElem(i);
988
int comp2 = comps.findSetByElem(j);
989
if (comp1 != comp2)
990
{
991
comps.mergeSets(comp1, comp2);
992
span_tree_edges.insert(std::make_pair(i, j));
993
}
994
}
995
}
996
997
for (std::set<std::pair<int,int> >::const_iterator itr = span_tree_edges.begin();
998
itr != span_tree_edges.end(); ++itr)
999
{
1000
std::pair<int,int> edge = *itr;
1001
if (span_tree_edges.find(edge) != span_tree_edges.end())
1002
{
1003
String name_src = pathes[edge.first];
1004
size_t prefix_len = name_src.find_last_of("/\\");
1005
if (prefix_len != String::npos) prefix_len++; else prefix_len = 0;
1006
name_src = name_src.substr(prefix_len, name_src.size() - prefix_len);
1007
1008
String name_dst = pathes[edge.second];
1009
prefix_len = name_dst.find_last_of("/\\");
1010
if (prefix_len != String::npos) prefix_len++; else prefix_len = 0;
1011
name_dst = name_dst.substr(prefix_len, name_dst.size() - prefix_len);
1012
1013
int pos = edge.first*num_images + edge.second;
1014
str << "\"" << name_src.c_str() << "\" -- \"" << name_dst.c_str() << "\""
1015
<< "[label=\"Nm=" << pairwise_matches[pos].matches.size()
1016
<< ", Ni=" << pairwise_matches[pos].num_inliers
1017
<< ", C=" << pairwise_matches[pos].confidence << "\"];\n";
1018
}
1019
}
1020
1021
for (size_t i = 0; i < comps.size.size(); ++i)
1022
{
1023
if (comps.size[comps.findSetByElem((int)i)] == 1)
1024
{
1025
String name = pathes[i];
1026
size_t prefix_len = name.find_last_of("/\\");
1027
if (prefix_len != String::npos) prefix_len++; else prefix_len = 0;
1028
name = name.substr(prefix_len, name.size() - prefix_len);
1029
str << "\"" << name.c_str() << "\";\n";
1030
}
1031
}
1032
1033
str << "}";
1034
return str.str().c_str();
1035
}
1036
1037
std::vector<int> leaveBiggestComponent(std::vector<ImageFeatures> &features, std::vector<MatchesInfo> &pairwise_matches,
1038
float conf_threshold)
1039
{
1040
const int num_images = static_cast<int>(features.size());
1041
1042
DisjointSets comps(num_images);
1043
for (int i = 0; i < num_images; ++i)
1044
{
1045
for (int j = 0; j < num_images; ++j)
1046
{
1047
if (pairwise_matches[i*num_images + j].confidence < conf_threshold)
1048
continue;
1049
int comp1 = comps.findSetByElem(i);
1050
int comp2 = comps.findSetByElem(j);
1051
if (comp1 != comp2)
1052
comps.mergeSets(comp1, comp2);
1053
}
1054
}
1055
1056
int max_comp = static_cast<int>(std::max_element(comps.size.begin(), comps.size.end()) - comps.size.begin());
1057
1058
std::vector<int> indices;
1059
std::vector<int> indices_removed;
1060
for (int i = 0; i < num_images; ++i)
1061
if (comps.findSetByElem(i) == max_comp)
1062
indices.push_back(i);
1063
else
1064
indices_removed.push_back(i);
1065
1066
std::vector<ImageFeatures> features_subset;
1067
std::vector<MatchesInfo> pairwise_matches_subset;
1068
for (size_t i = 0; i < indices.size(); ++i)
1069
{
1070
features_subset.push_back(features[indices[i]]);
1071
for (size_t j = 0; j < indices.size(); ++j)
1072
{
1073
pairwise_matches_subset.push_back(pairwise_matches[indices[i]*num_images + indices[j]]);
1074
pairwise_matches_subset.back().src_img_idx = static_cast<int>(i);
1075
pairwise_matches_subset.back().dst_img_idx = static_cast<int>(j);
1076
}
1077
}
1078
1079
if (static_cast<int>(features_subset.size()) == num_images)
1080
return indices;
1081
1082
LOG("Removed some images, because can't match them or there are too similar images: (");
1083
LOG(indices_removed[0] + 1);
1084
for (size_t i = 1; i < indices_removed.size(); ++i)
1085
LOG(", " << indices_removed[i]+1);
1086
LOGLN(").");
1087
LOGLN("Try to decrease the match confidence threshold and/or check if you're stitching duplicates.");
1088
1089
features = features_subset;
1090
pairwise_matches = pairwise_matches_subset;
1091
1092
return indices;
1093
}
1094
1095
1096
void findMaxSpanningTree(int num_images, const std::vector<MatchesInfo> &pairwise_matches,
1097
Graph &span_tree, std::vector<int> &centers)
1098
{
1099
Graph graph(num_images);
1100
std::vector<GraphEdge> edges;
1101
1102
// Construct images graph and remember its edges
1103
for (int i = 0; i < num_images; ++i)
1104
{
1105
for (int j = 0; j < num_images; ++j)
1106
{
1107
if (pairwise_matches[i * num_images + j].H.empty())
1108
continue;
1109
float conf = static_cast<float>(pairwise_matches[i * num_images + j].num_inliers);
1110
graph.addEdge(i, j, conf);
1111
edges.push_back(GraphEdge(i, j, conf));
1112
}
1113
}
1114
1115
DisjointSets comps(num_images);
1116
span_tree.create(num_images);
1117
std::vector<int> span_tree_powers(num_images, 0);
1118
1119
// Find maximum spanning tree
1120
sort(edges.begin(), edges.end(), std::greater<GraphEdge>());
1121
for (size_t i = 0; i < edges.size(); ++i)
1122
{
1123
int comp1 = comps.findSetByElem(edges[i].from);
1124
int comp2 = comps.findSetByElem(edges[i].to);
1125
if (comp1 != comp2)
1126
{
1127
comps.mergeSets(comp1, comp2);
1128
span_tree.addEdge(edges[i].from, edges[i].to, edges[i].weight);
1129
span_tree.addEdge(edges[i].to, edges[i].from, edges[i].weight);
1130
span_tree_powers[edges[i].from]++;
1131
span_tree_powers[edges[i].to]++;
1132
}
1133
}
1134
1135
// Find spanning tree leafs
1136
std::vector<int> span_tree_leafs;
1137
for (int i = 0; i < num_images; ++i)
1138
if (span_tree_powers[i] == 1)
1139
span_tree_leafs.push_back(i);
1140
1141
// Find maximum distance from each spanning tree vertex
1142
std::vector<int> max_dists(num_images, 0);
1143
std::vector<int> cur_dists;
1144
for (size_t i = 0; i < span_tree_leafs.size(); ++i)
1145
{
1146
cur_dists.assign(num_images, 0);
1147
span_tree.walkBreadthFirst(span_tree_leafs[i], IncDistance(cur_dists));
1148
for (int j = 0; j < num_images; ++j)
1149
max_dists[j] = std::max(max_dists[j], cur_dists[j]);
1150
}
1151
1152
// Find min-max distance
1153
int min_max_dist = max_dists[0];
1154
for (int i = 1; i < num_images; ++i)
1155
if (min_max_dist > max_dists[i])
1156
min_max_dist = max_dists[i];
1157
1158
// Find spanning tree centers
1159
centers.clear();
1160
for (int i = 0; i < num_images; ++i)
1161
if (max_dists[i] == min_max_dist)
1162
centers.push_back(i);
1163
CV_Assert(centers.size() > 0 && centers.size() <= 2);
1164
}
1165
1166
} // namespace detail
1167
} // namespace cv
1168
1169