1 | |
2 | //
|
3 | // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
4 | //
|
5 | // By downloading, copying, installing or using the software you agree to this license.
|
6 | // If you do not agree to this license, do not download, install,
|
7 | // copy or use the software.
|
8 | //
|
9 | //
|
10 | // License Agreement
|
11 | // For Open Source Computer Vision Library
|
12 | //
|
13 | // Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
14 | // Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved.
|
15 | // Third party copyrights are property of their respective owners.
|
16 | //
|
17 | // Redistribution and use in source and binary forms, with or without modification,
|
18 | // are permitted provided that the following conditions are met:
|
19 | //
|
20 | // * Redistribution's of source code must retain the above copyright notice,
|
21 | // this list of conditions and the following disclaimer.
|
22 | //
|
23 | // * Redistribution's in binary form must reproduce the above copyright notice,
|
24 | // this list of conditions and the following disclaimer in the documentation
|
25 | // and/or other materials provided with the distribution.
|
26 | //
|
27 | // * The name of the copyright holders may not be used to endorse or promote products
|
28 | // derived from this software without specific prior written permission.
|
29 | //
|
30 | // This software is provided by the copyright holders and contributors "as is" and
|
31 | // any express or implied warranties, including, but not limited to, the implied
|
32 | // warranties of merchantability and fitness for a particular purpose are disclaimed.
|
33 | // In no event shall the Intel Corporation or contributors be liable for any direct,
|
34 | // indirect, incidental, special, exemplary, or consequential damages
|
35 | // (including, but not limited to, procurement of substitute goods or services;
|
36 | // loss of use, data, or profits; or business interruption) however caused
|
37 | // and on any theory of liability, whether in contract, strict liability,
|
38 | // or tort (including negligence or otherwise) arising in any way out of
|
39 | // the use of this software, even if advised of the possibility of such damage.
|
40 | //
|
41 | //M*/
|
42 |
|
43 | #include "precomp.hpp"
|
44 | #include "opencv2/highgui/highgui.hpp"
|
45 | #include "opencv2/videostab/global_motion.hpp"
|
46 |
|
47 | using namespace std;
|
48 |
|
49 | namespace cv
|
50 | {
|
51 | namespace videostab
|
52 | {
|
53 |
|
54 | static Mat estimateGlobMotionLeastSquaresTranslation(
|
55 | int npoints, const Point2f *points0, const Point2f *points1, float *rmse)
|
56 | {
|
57 | Mat_<float> M = Mat::eye(3, 3, CV_32F);
|
58 | for (int i = 0; i < npoints; ++i)
|
59 | {
|
60 | M(0,2) += points1[i].x - points0[i].x;
|
61 | M(1,2) += points1[i].y - points0[i].y;
|
62 | }
|
63 | M(0,2) /= npoints;
|
64 | M(1,2) /= npoints;
|
65 | if (rmse)
|
66 | {
|
67 | *rmse = 0;
|
68 | for (int i = 0; i < npoints; ++i)
|
69 | *rmse += sqr(points1[i].x - points0[i].x - M(0,2)) +
|
70 | sqr(points1[i].y - points0[i].y - M(1,2));
|
71 | *rmse = sqrt(*rmse / npoints);
|
72 | }
|
73 | return M;
|
74 | }
|
75 |
|
76 |
|
77 | static Mat estimateGlobMotionLeastSquaresTranslationAndScale(
|
78 | int npoints, const Point2f *points0, const Point2f *points1, float *rmse)
|
79 | {
|
80 | Mat_<float> A(2*npoints, 3), b(2*npoints, 1);
|
81 | float *a0, *a1;
|
82 | Point2f p0, p1;
|
83 |
|
84 | for (int i = 0; i < npoints; ++i)
|
85 | {
|
86 | a0 = A[2*i];
|
87 | a1 = A[2*i+1];
|
88 | p0 = points0[i];
|
89 | p1 = points1[i];
|
90 | a0[0] = p0.x; a0[1] = 1; a0[2] = 0;
|
91 | a1[0] = p0.y; a1[1] = 0; a1[2] = 1;
|
92 | b(2*i,0) = p1.x;
|
93 | b(2*i+1,0) = p1.y;
|
94 | }
|
95 |
|
96 | Mat_<float> sol;
|
97 | solve(A, b, sol, DECOMP_SVD);
|
98 |
|
99 | if (rmse)
|
100 | *rmse = static_cast<float>(norm(A*sol, b, NORM_L2) / sqrt(static_cast<double>(npoints)));
|
101 |
|
102 | Mat_<float> M = Mat::eye(3, 3, CV_32F);
|
103 | M(0,0) = M(1,1) = sol(0,0);
|
104 | M(0,2) = sol(1,0);
|
105 | M(1,2) = sol(2,0);
|
106 | return M;
|
107 | }
|
108 |
|
109 |
|
110 | static Mat estimateGlobMotionLeastSquaresLinearSimilarity(
|
111 | int npoints, const Point2f *points0, const Point2f *points1, float *rmse)
|
112 | {
|
113 | Mat_<float> A(2*npoints, 4), b(2*npoints, 1);
|
114 | float *a0, *a1;
|
115 | Point2f p0, p1;
|
116 |
|
117 | for (int i = 0; i < npoints; ++i)
|
118 | {
|
119 | a0 = A[2*i];
|
120 | a1 = A[2*i+1];
|
121 | p0 = points0[i];
|
122 | p1 = points1[i];
|
123 | a0[0] = p0.x; a0[1] = p0.y; a0[2] = 1; a0[3] = 0;
|
124 | a1[0] = p0.y; a1[1] = -p0.x; a1[2] = 0; a1[3] = 1;
|
125 | b(2*i,0) = p1.x;
|
126 | b(2*i+1,0) = p1.y;
|
127 | }
|
128 |
|
129 | Mat_<float> sol;
|
130 | solve(A, b, sol, DECOMP_SVD);
|
131 |
|
132 | if (rmse)
|
133 | *rmse = static_cast<float>(norm(A*sol, b, NORM_L2) / sqrt(static_cast<double>(npoints)));
|
134 |
|
135 | Mat_<float> M = Mat::eye(3, 3, CV_32F);
|
136 | M(0,0) = M(1,1) = sol(0,0);
|
137 | M(0,1) = sol(1,0);
|
138 | M(1,0) = -sol(1,0);
|
139 | M(0,2) = sol(2,0);
|
140 | M(1,2) = sol(3,0);
|
141 | return M;
|
142 | }
|
143 |
|
144 |
|
145 | static Mat estimateGlobMotionLeastSquaresAffine(
|
146 | int npoints, const Point2f *points0, const Point2f *points1, float *rmse)
|
147 | {
|
148 | Mat_<float> A(2*npoints, 6), b(2*npoints, 1);
|
149 | float *a0, *a1;
|
150 | Point2f p0, p1;
|
151 |
|
152 | for (int i = 0; i < npoints; ++i)
|
153 | {
|
154 | a0 = A[2*i];
|
155 | a1 = A[2*i+1];
|
156 | p0 = points0[i];
|
157 | p1 = points1[i];
|
158 | a0[0] = p0.x; a0[1] = p0.y; a0[2] = 1; a0[3] = a0[4] = a0[5] = 0;
|
159 | a1[0] = a1[1] = a1[2] = 0; a1[3] = p0.x; a1[4] = p0.y; a1[5] = 1;
|
160 | b(2*i,0) = p1.x;
|
161 | b(2*i+1,0) = p1.y;
|
162 | }
|
163 |
|
164 | Mat_<float> sol;
|
165 | solve(A, b, sol, DECOMP_SVD);
|
166 |
|
167 | if (rmse)
|
168 | *rmse = static_cast<float>(norm(A*sol, b, NORM_L2) / sqrt(static_cast<double>(npoints)));
|
169 |
|
170 | Mat_<float> M = Mat::eye(3, 3, CV_32F);
|
171 | for (int i = 0, k = 0; i < 2; ++i)
|
172 | for (int j = 0; j < 3; ++j, ++k)
|
173 | M(i,j) = sol(k,0);
|
174 |
|
175 | return M;
|
176 | }
|
177 |
|
178 |
|
179 | Mat estimateGlobalMotionLeastSquares(
|
180 | const vector<Point2f> &points0, const vector<Point2f> &points1, int model, float *rmse)
|
181 | {
|
182 | CV_Assert(points0.size() == points1.size());
|
183 |
|
184 | typedef Mat (*Impl)(int, const Point2f*, const Point2f*, float*);
|
185 | static Impl impls[] = { estimateGlobMotionLeastSquaresTranslation,
|
186 | estimateGlobMotionLeastSquaresTranslationAndScale,
|
187 | estimateGlobMotionLeastSquaresLinearSimilarity,
|
188 | estimateGlobMotionLeastSquaresAffine };
|
189 |
|
190 | int npoints = static_cast<int>(points0.size());
|
191 | return impls[model](npoints, &points0[0], &points1[0], rmse);
|
192 | }
|
193 |
|
194 |
|
195 | Mat estimateGlobalMotionRobust(
|
196 | const vector<Point2f> &points0, const vector<Point2f> &points1, int model,
|
197 | const RansacParams ¶ms, float *rmse, int *ninliers)
|
198 | {
|
199 | CV_Assert(points0.size() == points1.size());
|
200 |
|
201 | typedef Mat (*Impl)(int, const Point2f*, const Point2f*, float*);
|
202 | static Impl impls[] = { estimateGlobMotionLeastSquaresTranslation,
|
203 | estimateGlobMotionLeastSquaresTranslationAndScale,
|
204 | estimateGlobMotionLeastSquaresLinearSimilarity,
|
205 | estimateGlobMotionLeastSquaresAffine };
|
206 |
|
207 | const int npoints = static_cast<int>(points0.size());
|
208 | const int niters = static_cast<int>(ceil(log(1 - params.prob) /
|
209 | log(1 - pow(1 - params.eps, params.size))));
|
210 | if (npoints==0)
|
211 | {
|
212 | Mat_<float> result = Mat_<float>::zeros(2,3);
|
213 | result(0,0) = 1.0f;
|
214 | result(1,1) = 1.0f;
|
215 | return result;
|
216 | }
|
217 |
|
218 | RNG rng(0);
|
219 | vector<int> indices(params.size);
|
220 | vector<Point2f> subset0(params.size), subset1(params.size);
|
221 | vector<Point2f> subset0best(params.size), subset1best(params.size);
|
222 | Mat_<float> bestM;
|
223 | int ninliersMax = -1;
|
224 | Point2f p0, p1;
|
225 | float x, y;
|
226 |
|
227 | for (int iter = 0; iter < niters; ++iter)
|
228 | {
|
229 | for (int i = 0; i < params.size; ++i)
|
230 | {
|
231 | bool ok = false;
|
232 | while (!ok)
|
233 | {
|
234 | ok = true;
|
235 | indices[i] = static_cast<unsigned>(rng) % npoints;
|
236 | for (int j = 0; j < i; ++j)
|
237 | if (indices[i] == indices[j])
|
238 | { ok = false; break; }
|
239 | }
|
240 | }
|
241 | for (int i = 0; i < params.size; ++i)
|
242 | {
|
243 | subset0[i] = points0[indices[i]];
|
244 | subset1[i] = points1[indices[i]];
|
245 | }
|
246 |
|
247 | Mat_<float> M = impls[model](params.size, &subset0[0], &subset1[0], 0);
|
248 |
|
249 | int _ninliers = 0;
|
250 | for (int i = 0; i < npoints; ++i)
|
251 | {
|
252 | p0 = points0[i]; p1 = points1[i];
|
253 | x = M(0,0)*p0.x + M(0,1)*p0.y + M(0,2);
|
254 | y = M(1,0)*p0.x + M(1,1)*p0.y + M(1,2);
|
255 | if (sqr(x - p1.x) + sqr(y - p1.y) < params.thresh * params.thresh)
|
256 | _ninliers++;
|
257 | }
|
258 | if (_ninliers >= ninliersMax)
|
259 | {
|
260 | bestM = M;
|
261 | ninliersMax = _ninliers;
|
262 | subset0best.swap(subset0);
|
263 | subset1best.swap(subset1);
|
264 | }
|
265 | }
|
266 |
|
267 | if (ninliersMax < params.size)
|
268 |
|
269 | bestM = impls[model](params.size, &subset0best[0], &subset1best[0], rmse);
|
270 | else
|
271 | {
|
272 | subset0.resize(ninliersMax);
|
273 | subset1.resize(ninliersMax);
|
274 | for (int i = 0, j = 0; i < npoints; ++i)
|
275 | {
|
276 | p0 = points0[i]; p1 = points1[i];
|
277 | x = bestM(0,0)*p0.x + bestM(0,1)*p0.y + bestM(0,2);
|
278 | y = bestM(1,0)*p0.x + bestM(1,1)*p0.y + bestM(1,2);
|
279 | if (sqr(x - p1.x) + sqr(y - p1.y) < params.thresh * params.thresh)
|
280 | {
|
281 | subset0[j] = p0;
|
282 | subset1[j] = p1;
|
283 | j++;
|
284 | }
|
285 | }
|
286 | bestM = impls[model](ninliersMax, &subset0[0], &subset1[0], rmse);
|
287 | }
|
288 |
|
289 | if (ninliers)
|
290 | *ninliers = ninliersMax;
|
291 |
|
292 | return bestM;
|
293 | }
|
294 |
|
295 |
|
296 | PyrLkRobustMotionEstimator::PyrLkRobustMotionEstimator()
|
297 | : ransacParams_(RansacParams::affine2dMotionStd())
|
298 | {
|
299 | setDetector(new GoodFeaturesToTrackDetector());
|
300 | setOptFlowEstimator(new SparsePyrLkOptFlowEstimator());
|
301 | setMotionModel(AFFINE);
|
302 | setMaxRmse(0.5f);
|
303 | setMinInlierRatio(0.1f);
|
304 | }
|
305 |
|
306 |
|
307 | Mat PyrLkRobustMotionEstimator::estimate(const Mat &frame0, const Mat &frame1)
|
308 | {
|
309 | detector_->detect(frame0, keypointsPrev_);
|
310 |
|
311 | pointsPrev_.resize(keypointsPrev_.size());
|
312 | for (size_t i = 0; i < keypointsPrev_.size(); ++i)
|
313 | pointsPrev_[i] = keypointsPrev_[i].pt;
|
314 |
|
315 | optFlowEstimator_->run(frame0, frame1, pointsPrev_, points_, status_, noArray());
|
316 |
|
317 | size_t npoints = points_.size();
|
318 | pointsPrevGood_.clear();
|
319 | pointsPrevGood_.reserve(npoints);
|
320 | pointsGood_.clear();
|
321 | pointsGood_.reserve(npoints);
|
322 | for (size_t i = 0; i < npoints; ++i)
|
323 | {
|
324 | if (status_[i])
|
325 | {
|
326 | pointsPrevGood_.push_back(pointsPrev_[i]);
|
327 | pointsGood_.push_back(points_[i]);
|
328 | }
|
329 | }
|
330 |
|
331 | float rmse;
|
332 | int ninliers;
|
333 | Mat M = estimateGlobalMotionRobust(
|
334 | pointsPrevGood_, pointsGood_, motionModel_, ransacParams_, &rmse, &ninliers);
|
335 |
|
336 | if (rmse > maxRmse_ || static_cast<float>(ninliers) / pointsGood_.size() < minInlierRatio_)
|
337 | M = Mat::eye(3, 3, CV_32F);
|
338 |
|
339 | return M;
|
340 | }
|
341 |
|
342 |
|
343 | Mat getMotion(int from, int to, const Mat *motions, int size)
|
344 | {
|
345 | Mat M = Mat::eye(3, 3, CV_32F);
|
346 | if (to > from)
|
347 | {
|
348 | for (int i = from; i < to; ++i)
|
349 | M = at(i, motions, size) * M;
|
350 | }
|
351 | else if (from > to)
|
352 | {
|
353 | for (int i = to; i < from; ++i)
|
354 | M = at(i, motions, size) * M;
|
355 | M = M.inv();
|
356 | }
|
357 | return M;
|
358 | }
|
359 |
|
360 |
|
361 | Mat getMotion(int from, int to, const vector<Mat> &motions)
|
362 | {
|
363 | return getMotion(from, to, &motions[0], (int)motions.size());
|
364 | }
|
365 |
|
366 | }
|
367 | }
|