1*77c1e3ccSAndroid Build Coastguard Worker /*
2*77c1e3ccSAndroid Build Coastguard Worker * Copyright (c) 2016, Alliance for Open Media. All rights reserved.
3*77c1e3ccSAndroid Build Coastguard Worker *
4*77c1e3ccSAndroid Build Coastguard Worker * This source code is subject to the terms of the BSD 2 Clause License and
5*77c1e3ccSAndroid Build Coastguard Worker * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
6*77c1e3ccSAndroid Build Coastguard Worker * was not distributed with this source code in the LICENSE file, you can
7*77c1e3ccSAndroid Build Coastguard Worker * obtain it at www.aomedia.org/license/software. If the Alliance for Open
8*77c1e3ccSAndroid Build Coastguard Worker * Media Patent License 1.0 was not distributed with this source code in the
9*77c1e3ccSAndroid Build Coastguard Worker * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
10*77c1e3ccSAndroid Build Coastguard Worker */
11*77c1e3ccSAndroid Build Coastguard Worker
12*77c1e3ccSAndroid Build Coastguard Worker #include <memory.h>
13*77c1e3ccSAndroid Build Coastguard Worker #include <math.h>
14*77c1e3ccSAndroid Build Coastguard Worker #include <time.h>
15*77c1e3ccSAndroid Build Coastguard Worker #include <stdio.h>
16*77c1e3ccSAndroid Build Coastguard Worker #include <stdbool.h>
17*77c1e3ccSAndroid Build Coastguard Worker #include <string.h>
18*77c1e3ccSAndroid Build Coastguard Worker #include <assert.h>
19*77c1e3ccSAndroid Build Coastguard Worker
20*77c1e3ccSAndroid Build Coastguard Worker #include "aom_dsp/flow_estimation/ransac.h"
21*77c1e3ccSAndroid Build Coastguard Worker #include "aom_dsp/mathutils.h"
22*77c1e3ccSAndroid Build Coastguard Worker #include "aom_mem/aom_mem.h"
23*77c1e3ccSAndroid Build Coastguard Worker
24*77c1e3ccSAndroid Build Coastguard Worker // TODO(rachelbarker): Remove dependence on code in av1/encoder/
25*77c1e3ccSAndroid Build Coastguard Worker #include "av1/encoder/random.h"
26*77c1e3ccSAndroid Build Coastguard Worker
27*77c1e3ccSAndroid Build Coastguard Worker #define MAX_MINPTS 4
28*77c1e3ccSAndroid Build Coastguard Worker #define MINPTS_MULTIPLIER 5
29*77c1e3ccSAndroid Build Coastguard Worker
30*77c1e3ccSAndroid Build Coastguard Worker #define INLIER_THRESHOLD 1.25
31*77c1e3ccSAndroid Build Coastguard Worker #define INLIER_THRESHOLD_SQUARED (INLIER_THRESHOLD * INLIER_THRESHOLD)
32*77c1e3ccSAndroid Build Coastguard Worker
33*77c1e3ccSAndroid Build Coastguard Worker // Number of initial models to generate
34*77c1e3ccSAndroid Build Coastguard Worker #define NUM_TRIALS 20
35*77c1e3ccSAndroid Build Coastguard Worker
36*77c1e3ccSAndroid Build Coastguard Worker // Number of times to refine the best model found
37*77c1e3ccSAndroid Build Coastguard Worker #define NUM_REFINES 5
38*77c1e3ccSAndroid Build Coastguard Worker
39*77c1e3ccSAndroid Build Coastguard Worker // Flag to enable functions for finding TRANSLATION type models.
40*77c1e3ccSAndroid Build Coastguard Worker //
41*77c1e3ccSAndroid Build Coastguard Worker // These modes are not considered currently due to a spec bug (see comments
42*77c1e3ccSAndroid Build Coastguard Worker // in gm_get_motion_vector() in av1/common/mv.h). Thus we don't need to compile
43*77c1e3ccSAndroid Build Coastguard Worker // the corresponding search functions, but it is nice to keep the source around
44*77c1e3ccSAndroid Build Coastguard Worker // but disabled, for completeness.
45*77c1e3ccSAndroid Build Coastguard Worker #define ALLOW_TRANSLATION_MODELS 0
46*77c1e3ccSAndroid Build Coastguard Worker
47*77c1e3ccSAndroid Build Coastguard Worker typedef struct {
48*77c1e3ccSAndroid Build Coastguard Worker int num_inliers;
49*77c1e3ccSAndroid Build Coastguard Worker double sse; // Sum of squared errors of inliers
50*77c1e3ccSAndroid Build Coastguard Worker int *inlier_indices;
51*77c1e3ccSAndroid Build Coastguard Worker } RANSAC_MOTION;
52*77c1e3ccSAndroid Build Coastguard Worker
53*77c1e3ccSAndroid Build Coastguard Worker ////////////////////////////////////////////////////////////////////////////////
54*77c1e3ccSAndroid Build Coastguard Worker // ransac
55*77c1e3ccSAndroid Build Coastguard Worker typedef bool (*FindTransformationFunc)(const Correspondence *points,
56*77c1e3ccSAndroid Build Coastguard Worker const int *indices, int num_indices,
57*77c1e3ccSAndroid Build Coastguard Worker double *params);
58*77c1e3ccSAndroid Build Coastguard Worker typedef void (*ScoreModelFunc)(const double *mat, const Correspondence *points,
59*77c1e3ccSAndroid Build Coastguard Worker int num_points, RANSAC_MOTION *model);
60*77c1e3ccSAndroid Build Coastguard Worker
61*77c1e3ccSAndroid Build Coastguard Worker // vtable-like structure which stores all of the information needed by RANSAC
62*77c1e3ccSAndroid Build Coastguard Worker // for a particular model type
63*77c1e3ccSAndroid Build Coastguard Worker typedef struct {
64*77c1e3ccSAndroid Build Coastguard Worker FindTransformationFunc find_transformation;
65*77c1e3ccSAndroid Build Coastguard Worker ScoreModelFunc score_model;
66*77c1e3ccSAndroid Build Coastguard Worker
67*77c1e3ccSAndroid Build Coastguard Worker // The minimum number of points which can be passed to find_transformation
68*77c1e3ccSAndroid Build Coastguard Worker // to generate a model.
69*77c1e3ccSAndroid Build Coastguard Worker //
70*77c1e3ccSAndroid Build Coastguard Worker // This should be set as small as possible. This is due to an observation
71*77c1e3ccSAndroid Build Coastguard Worker // from section 4 of "Optimal Ransac" by A. Hast, J. Nysjö and
72*77c1e3ccSAndroid Build Coastguard Worker // A. Marchetti (https://dspace5.zcu.cz/bitstream/11025/6869/1/Hast.pdf):
73*77c1e3ccSAndroid Build Coastguard Worker // using the minimum possible number of points in the initial model maximizes
74*77c1e3ccSAndroid Build Coastguard Worker // the chance that all of the selected points are inliers.
75*77c1e3ccSAndroid Build Coastguard Worker //
76*77c1e3ccSAndroid Build Coastguard Worker // That paper proposes a method which can deal with models which are
77*77c1e3ccSAndroid Build Coastguard Worker // contaminated by outliers, which helps in cases where the inlier fraction
78*77c1e3ccSAndroid Build Coastguard Worker // is low. However, for our purposes, global motion only gives significant
79*77c1e3ccSAndroid Build Coastguard Worker // gains when the inlier fraction is high.
80*77c1e3ccSAndroid Build Coastguard Worker //
81*77c1e3ccSAndroid Build Coastguard Worker // So we do not use the method from this paper, but we do find that
82*77c1e3ccSAndroid Build Coastguard Worker // minimizing the number of points used for initial model fitting helps
83*77c1e3ccSAndroid Build Coastguard Worker // make the best use of the limited number of models we consider.
84*77c1e3ccSAndroid Build Coastguard Worker int minpts;
85*77c1e3ccSAndroid Build Coastguard Worker } RansacModelInfo;
86*77c1e3ccSAndroid Build Coastguard Worker
87*77c1e3ccSAndroid Build Coastguard Worker #if ALLOW_TRANSLATION_MODELS
score_translation(const double * mat,const Correspondence * points,int num_points,RANSAC_MOTION * model)88*77c1e3ccSAndroid Build Coastguard Worker static void score_translation(const double *mat, const Correspondence *points,
89*77c1e3ccSAndroid Build Coastguard Worker int num_points, RANSAC_MOTION *model) {
90*77c1e3ccSAndroid Build Coastguard Worker model->num_inliers = 0;
91*77c1e3ccSAndroid Build Coastguard Worker model->sse = 0.0;
92*77c1e3ccSAndroid Build Coastguard Worker
93*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < num_points; ++i) {
94*77c1e3ccSAndroid Build Coastguard Worker const double x1 = points[i].x;
95*77c1e3ccSAndroid Build Coastguard Worker const double y1 = points[i].y;
96*77c1e3ccSAndroid Build Coastguard Worker const double x2 = points[i].rx;
97*77c1e3ccSAndroid Build Coastguard Worker const double y2 = points[i].ry;
98*77c1e3ccSAndroid Build Coastguard Worker
99*77c1e3ccSAndroid Build Coastguard Worker const double proj_x = x1 + mat[0];
100*77c1e3ccSAndroid Build Coastguard Worker const double proj_y = y1 + mat[1];
101*77c1e3ccSAndroid Build Coastguard Worker
102*77c1e3ccSAndroid Build Coastguard Worker const double dx = proj_x - x2;
103*77c1e3ccSAndroid Build Coastguard Worker const double dy = proj_y - y2;
104*77c1e3ccSAndroid Build Coastguard Worker const double sse = dx * dx + dy * dy;
105*77c1e3ccSAndroid Build Coastguard Worker
106*77c1e3ccSAndroid Build Coastguard Worker if (sse < INLIER_THRESHOLD_SQUARED) {
107*77c1e3ccSAndroid Build Coastguard Worker model->inlier_indices[model->num_inliers++] = i;
108*77c1e3ccSAndroid Build Coastguard Worker model->sse += sse;
109*77c1e3ccSAndroid Build Coastguard Worker }
110*77c1e3ccSAndroid Build Coastguard Worker }
111*77c1e3ccSAndroid Build Coastguard Worker }
112*77c1e3ccSAndroid Build Coastguard Worker #endif // ALLOW_TRANSLATION_MODELS
113*77c1e3ccSAndroid Build Coastguard Worker
score_affine(const double * mat,const Correspondence * points,int num_points,RANSAC_MOTION * model)114*77c1e3ccSAndroid Build Coastguard Worker static void score_affine(const double *mat, const Correspondence *points,
115*77c1e3ccSAndroid Build Coastguard Worker int num_points, RANSAC_MOTION *model) {
116*77c1e3ccSAndroid Build Coastguard Worker model->num_inliers = 0;
117*77c1e3ccSAndroid Build Coastguard Worker model->sse = 0.0;
118*77c1e3ccSAndroid Build Coastguard Worker
119*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < num_points; ++i) {
120*77c1e3ccSAndroid Build Coastguard Worker const double x1 = points[i].x;
121*77c1e3ccSAndroid Build Coastguard Worker const double y1 = points[i].y;
122*77c1e3ccSAndroid Build Coastguard Worker const double x2 = points[i].rx;
123*77c1e3ccSAndroid Build Coastguard Worker const double y2 = points[i].ry;
124*77c1e3ccSAndroid Build Coastguard Worker
125*77c1e3ccSAndroid Build Coastguard Worker const double proj_x = mat[2] * x1 + mat[3] * y1 + mat[0];
126*77c1e3ccSAndroid Build Coastguard Worker const double proj_y = mat[4] * x1 + mat[5] * y1 + mat[1];
127*77c1e3ccSAndroid Build Coastguard Worker
128*77c1e3ccSAndroid Build Coastguard Worker const double dx = proj_x - x2;
129*77c1e3ccSAndroid Build Coastguard Worker const double dy = proj_y - y2;
130*77c1e3ccSAndroid Build Coastguard Worker const double sse = dx * dx + dy * dy;
131*77c1e3ccSAndroid Build Coastguard Worker
132*77c1e3ccSAndroid Build Coastguard Worker if (sse < INLIER_THRESHOLD_SQUARED) {
133*77c1e3ccSAndroid Build Coastguard Worker model->inlier_indices[model->num_inliers++] = i;
134*77c1e3ccSAndroid Build Coastguard Worker model->sse += sse;
135*77c1e3ccSAndroid Build Coastguard Worker }
136*77c1e3ccSAndroid Build Coastguard Worker }
137*77c1e3ccSAndroid Build Coastguard Worker }
138*77c1e3ccSAndroid Build Coastguard Worker
139*77c1e3ccSAndroid Build Coastguard Worker #if ALLOW_TRANSLATION_MODELS
find_translation(const Correspondence * points,const int * indices,int num_indices,double * params)140*77c1e3ccSAndroid Build Coastguard Worker static bool find_translation(const Correspondence *points, const int *indices,
141*77c1e3ccSAndroid Build Coastguard Worker int num_indices, double *params) {
142*77c1e3ccSAndroid Build Coastguard Worker double sumx = 0;
143*77c1e3ccSAndroid Build Coastguard Worker double sumy = 0;
144*77c1e3ccSAndroid Build Coastguard Worker
145*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < num_indices; ++i) {
146*77c1e3ccSAndroid Build Coastguard Worker int index = indices[i];
147*77c1e3ccSAndroid Build Coastguard Worker const double sx = points[index].x;
148*77c1e3ccSAndroid Build Coastguard Worker const double sy = points[index].y;
149*77c1e3ccSAndroid Build Coastguard Worker const double dx = points[index].rx;
150*77c1e3ccSAndroid Build Coastguard Worker const double dy = points[index].ry;
151*77c1e3ccSAndroid Build Coastguard Worker
152*77c1e3ccSAndroid Build Coastguard Worker sumx += dx - sx;
153*77c1e3ccSAndroid Build Coastguard Worker sumy += dy - sy;
154*77c1e3ccSAndroid Build Coastguard Worker }
155*77c1e3ccSAndroid Build Coastguard Worker
156*77c1e3ccSAndroid Build Coastguard Worker params[0] = sumx / np;
157*77c1e3ccSAndroid Build Coastguard Worker params[1] = sumy / np;
158*77c1e3ccSAndroid Build Coastguard Worker params[2] = 1;
159*77c1e3ccSAndroid Build Coastguard Worker params[3] = 0;
160*77c1e3ccSAndroid Build Coastguard Worker params[4] = 0;
161*77c1e3ccSAndroid Build Coastguard Worker params[5] = 1;
162*77c1e3ccSAndroid Build Coastguard Worker return true;
163*77c1e3ccSAndroid Build Coastguard Worker }
164*77c1e3ccSAndroid Build Coastguard Worker #endif // ALLOW_TRANSLATION_MODELS
165*77c1e3ccSAndroid Build Coastguard Worker
find_rotzoom(const Correspondence * points,const int * indices,int num_indices,double * params)166*77c1e3ccSAndroid Build Coastguard Worker static bool find_rotzoom(const Correspondence *points, const int *indices,
167*77c1e3ccSAndroid Build Coastguard Worker int num_indices, double *params) {
168*77c1e3ccSAndroid Build Coastguard Worker const int n = 4; // Size of least-squares problem
169*77c1e3ccSAndroid Build Coastguard Worker double mat[4 * 4]; // Accumulator for A'A
170*77c1e3ccSAndroid Build Coastguard Worker double y[4]; // Accumulator for A'b
171*77c1e3ccSAndroid Build Coastguard Worker double a[4]; // Single row of A
172*77c1e3ccSAndroid Build Coastguard Worker double b; // Single element of b
173*77c1e3ccSAndroid Build Coastguard Worker
174*77c1e3ccSAndroid Build Coastguard Worker least_squares_init(mat, y, n);
175*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < num_indices; ++i) {
176*77c1e3ccSAndroid Build Coastguard Worker int index = indices[i];
177*77c1e3ccSAndroid Build Coastguard Worker const double sx = points[index].x;
178*77c1e3ccSAndroid Build Coastguard Worker const double sy = points[index].y;
179*77c1e3ccSAndroid Build Coastguard Worker const double dx = points[index].rx;
180*77c1e3ccSAndroid Build Coastguard Worker const double dy = points[index].ry;
181*77c1e3ccSAndroid Build Coastguard Worker
182*77c1e3ccSAndroid Build Coastguard Worker a[0] = 1;
183*77c1e3ccSAndroid Build Coastguard Worker a[1] = 0;
184*77c1e3ccSAndroid Build Coastguard Worker a[2] = sx;
185*77c1e3ccSAndroid Build Coastguard Worker a[3] = sy;
186*77c1e3ccSAndroid Build Coastguard Worker b = dx;
187*77c1e3ccSAndroid Build Coastguard Worker least_squares_accumulate(mat, y, a, b, n);
188*77c1e3ccSAndroid Build Coastguard Worker
189*77c1e3ccSAndroid Build Coastguard Worker a[0] = 0;
190*77c1e3ccSAndroid Build Coastguard Worker a[1] = 1;
191*77c1e3ccSAndroid Build Coastguard Worker a[2] = sy;
192*77c1e3ccSAndroid Build Coastguard Worker a[3] = -sx;
193*77c1e3ccSAndroid Build Coastguard Worker b = dy;
194*77c1e3ccSAndroid Build Coastguard Worker least_squares_accumulate(mat, y, a, b, n);
195*77c1e3ccSAndroid Build Coastguard Worker }
196*77c1e3ccSAndroid Build Coastguard Worker
197*77c1e3ccSAndroid Build Coastguard Worker // Fill in params[0] .. params[3] with output model
198*77c1e3ccSAndroid Build Coastguard Worker if (!least_squares_solve(mat, y, params, n)) {
199*77c1e3ccSAndroid Build Coastguard Worker return false;
200*77c1e3ccSAndroid Build Coastguard Worker }
201*77c1e3ccSAndroid Build Coastguard Worker
202*77c1e3ccSAndroid Build Coastguard Worker // Fill in remaining parameters
203*77c1e3ccSAndroid Build Coastguard Worker params[4] = -params[3];
204*77c1e3ccSAndroid Build Coastguard Worker params[5] = params[2];
205*77c1e3ccSAndroid Build Coastguard Worker
206*77c1e3ccSAndroid Build Coastguard Worker return true;
207*77c1e3ccSAndroid Build Coastguard Worker }
208*77c1e3ccSAndroid Build Coastguard Worker
find_affine(const Correspondence * points,const int * indices,int num_indices,double * params)209*77c1e3ccSAndroid Build Coastguard Worker static bool find_affine(const Correspondence *points, const int *indices,
210*77c1e3ccSAndroid Build Coastguard Worker int num_indices, double *params) {
211*77c1e3ccSAndroid Build Coastguard Worker // Note: The least squares problem for affine models is 6-dimensional,
212*77c1e3ccSAndroid Build Coastguard Worker // but it splits into two independent 3-dimensional subproblems.
213*77c1e3ccSAndroid Build Coastguard Worker // Solving these two subproblems separately and recombining at the end
214*77c1e3ccSAndroid Build Coastguard Worker // results in less total computation than solving the 6-dimensional
215*77c1e3ccSAndroid Build Coastguard Worker // problem directly.
216*77c1e3ccSAndroid Build Coastguard Worker //
217*77c1e3ccSAndroid Build Coastguard Worker // The two subproblems correspond to all the parameters which contribute
218*77c1e3ccSAndroid Build Coastguard Worker // to the x output of the model, and all the parameters which contribute
219*77c1e3ccSAndroid Build Coastguard Worker // to the y output, respectively.
220*77c1e3ccSAndroid Build Coastguard Worker
221*77c1e3ccSAndroid Build Coastguard Worker const int n = 3; // Size of each least-squares problem
222*77c1e3ccSAndroid Build Coastguard Worker double mat[2][3 * 3]; // Accumulator for A'A
223*77c1e3ccSAndroid Build Coastguard Worker double y[2][3]; // Accumulator for A'b
224*77c1e3ccSAndroid Build Coastguard Worker double x[2][3]; // Output vector
225*77c1e3ccSAndroid Build Coastguard Worker double a[2][3]; // Single row of A
226*77c1e3ccSAndroid Build Coastguard Worker double b[2]; // Single element of b
227*77c1e3ccSAndroid Build Coastguard Worker
228*77c1e3ccSAndroid Build Coastguard Worker least_squares_init(mat[0], y[0], n);
229*77c1e3ccSAndroid Build Coastguard Worker least_squares_init(mat[1], y[1], n);
230*77c1e3ccSAndroid Build Coastguard Worker for (int i = 0; i < num_indices; ++i) {
231*77c1e3ccSAndroid Build Coastguard Worker int index = indices[i];
232*77c1e3ccSAndroid Build Coastguard Worker const double sx = points[index].x;
233*77c1e3ccSAndroid Build Coastguard Worker const double sy = points[index].y;
234*77c1e3ccSAndroid Build Coastguard Worker const double dx = points[index].rx;
235*77c1e3ccSAndroid Build Coastguard Worker const double dy = points[index].ry;
236*77c1e3ccSAndroid Build Coastguard Worker
237*77c1e3ccSAndroid Build Coastguard Worker a[0][0] = 1;
238*77c1e3ccSAndroid Build Coastguard Worker a[0][1] = sx;
239*77c1e3ccSAndroid Build Coastguard Worker a[0][2] = sy;
240*77c1e3ccSAndroid Build Coastguard Worker b[0] = dx;
241*77c1e3ccSAndroid Build Coastguard Worker least_squares_accumulate(mat[0], y[0], a[0], b[0], n);
242*77c1e3ccSAndroid Build Coastguard Worker
243*77c1e3ccSAndroid Build Coastguard Worker a[1][0] = 1;
244*77c1e3ccSAndroid Build Coastguard Worker a[1][1] = sx;
245*77c1e3ccSAndroid Build Coastguard Worker a[1][2] = sy;
246*77c1e3ccSAndroid Build Coastguard Worker b[1] = dy;
247*77c1e3ccSAndroid Build Coastguard Worker least_squares_accumulate(mat[1], y[1], a[1], b[1], n);
248*77c1e3ccSAndroid Build Coastguard Worker }
249*77c1e3ccSAndroid Build Coastguard Worker
250*77c1e3ccSAndroid Build Coastguard Worker if (!least_squares_solve(mat[0], y[0], x[0], n)) {
251*77c1e3ccSAndroid Build Coastguard Worker return false;
252*77c1e3ccSAndroid Build Coastguard Worker }
253*77c1e3ccSAndroid Build Coastguard Worker if (!least_squares_solve(mat[1], y[1], x[1], n)) {
254*77c1e3ccSAndroid Build Coastguard Worker return false;
255*77c1e3ccSAndroid Build Coastguard Worker }
256*77c1e3ccSAndroid Build Coastguard Worker
257*77c1e3ccSAndroid Build Coastguard Worker // Rearrange least squares result to form output model
258*77c1e3ccSAndroid Build Coastguard Worker params[0] = x[0][0];
259*77c1e3ccSAndroid Build Coastguard Worker params[1] = x[1][0];
260*77c1e3ccSAndroid Build Coastguard Worker params[2] = x[0][1];
261*77c1e3ccSAndroid Build Coastguard Worker params[3] = x[0][2];
262*77c1e3ccSAndroid Build Coastguard Worker params[4] = x[1][1];
263*77c1e3ccSAndroid Build Coastguard Worker params[5] = x[1][2];
264*77c1e3ccSAndroid Build Coastguard Worker
265*77c1e3ccSAndroid Build Coastguard Worker return true;
266*77c1e3ccSAndroid Build Coastguard Worker }
267*77c1e3ccSAndroid Build Coastguard Worker
268*77c1e3ccSAndroid Build Coastguard Worker // Return -1 if 'a' is a better motion, 1 if 'b' is better, 0 otherwise.
compare_motions(const void * arg_a,const void * arg_b)269*77c1e3ccSAndroid Build Coastguard Worker static int compare_motions(const void *arg_a, const void *arg_b) {
270*77c1e3ccSAndroid Build Coastguard Worker const RANSAC_MOTION *motion_a = (RANSAC_MOTION *)arg_a;
271*77c1e3ccSAndroid Build Coastguard Worker const RANSAC_MOTION *motion_b = (RANSAC_MOTION *)arg_b;
272*77c1e3ccSAndroid Build Coastguard Worker
273*77c1e3ccSAndroid Build Coastguard Worker if (motion_a->num_inliers > motion_b->num_inliers) return -1;
274*77c1e3ccSAndroid Build Coastguard Worker if (motion_a->num_inliers < motion_b->num_inliers) return 1;
275*77c1e3ccSAndroid Build Coastguard Worker if (motion_a->sse < motion_b->sse) return -1;
276*77c1e3ccSAndroid Build Coastguard Worker if (motion_a->sse > motion_b->sse) return 1;
277*77c1e3ccSAndroid Build Coastguard Worker return 0;
278*77c1e3ccSAndroid Build Coastguard Worker }
279*77c1e3ccSAndroid Build Coastguard Worker
is_better_motion(const RANSAC_MOTION * motion_a,const RANSAC_MOTION * motion_b)280*77c1e3ccSAndroid Build Coastguard Worker static bool is_better_motion(const RANSAC_MOTION *motion_a,
281*77c1e3ccSAndroid Build Coastguard Worker const RANSAC_MOTION *motion_b) {
282*77c1e3ccSAndroid Build Coastguard Worker return compare_motions(motion_a, motion_b) < 0;
283*77c1e3ccSAndroid Build Coastguard Worker }
284*77c1e3ccSAndroid Build Coastguard Worker
285*77c1e3ccSAndroid Build Coastguard Worker // Returns true on success, false on error
ransac_internal(const Correspondence * matched_points,int npoints,MotionModel * motion_models,int num_desired_motions,const RansacModelInfo * model_info,bool * mem_alloc_failed)286*77c1e3ccSAndroid Build Coastguard Worker static bool ransac_internal(const Correspondence *matched_points, int npoints,
287*77c1e3ccSAndroid Build Coastguard Worker MotionModel *motion_models, int num_desired_motions,
288*77c1e3ccSAndroid Build Coastguard Worker const RansacModelInfo *model_info,
289*77c1e3ccSAndroid Build Coastguard Worker bool *mem_alloc_failed) {
290*77c1e3ccSAndroid Build Coastguard Worker assert(npoints >= 0);
291*77c1e3ccSAndroid Build Coastguard Worker int i = 0;
292*77c1e3ccSAndroid Build Coastguard Worker int minpts = model_info->minpts;
293*77c1e3ccSAndroid Build Coastguard Worker bool ret_val = true;
294*77c1e3ccSAndroid Build Coastguard Worker
295*77c1e3ccSAndroid Build Coastguard Worker unsigned int seed = (unsigned int)npoints;
296*77c1e3ccSAndroid Build Coastguard Worker
297*77c1e3ccSAndroid Build Coastguard Worker int indices[MAX_MINPTS] = { 0 };
298*77c1e3ccSAndroid Build Coastguard Worker
299*77c1e3ccSAndroid Build Coastguard Worker // Store information for the num_desired_motions best transformations found
300*77c1e3ccSAndroid Build Coastguard Worker // and the worst motion among them, as well as the motion currently under
301*77c1e3ccSAndroid Build Coastguard Worker // consideration.
302*77c1e3ccSAndroid Build Coastguard Worker RANSAC_MOTION *motions, *worst_kept_motion = NULL;
303*77c1e3ccSAndroid Build Coastguard Worker RANSAC_MOTION current_motion;
304*77c1e3ccSAndroid Build Coastguard Worker
305*77c1e3ccSAndroid Build Coastguard Worker // Store the parameters and the indices of the inlier points for the motion
306*77c1e3ccSAndroid Build Coastguard Worker // currently under consideration.
307*77c1e3ccSAndroid Build Coastguard Worker double params_this_motion[MAX_PARAMDIM];
308*77c1e3ccSAndroid Build Coastguard Worker
309*77c1e3ccSAndroid Build Coastguard Worker // Initialize output models, as a fallback in case we can't find a model
310*77c1e3ccSAndroid Build Coastguard Worker for (i = 0; i < num_desired_motions; i++) {
311*77c1e3ccSAndroid Build Coastguard Worker memcpy(motion_models[i].params, kIdentityParams,
312*77c1e3ccSAndroid Build Coastguard Worker MAX_PARAMDIM * sizeof(*(motion_models[i].params)));
313*77c1e3ccSAndroid Build Coastguard Worker motion_models[i].num_inliers = 0;
314*77c1e3ccSAndroid Build Coastguard Worker }
315*77c1e3ccSAndroid Build Coastguard Worker
316*77c1e3ccSAndroid Build Coastguard Worker if (npoints < minpts * MINPTS_MULTIPLIER || npoints == 0) {
317*77c1e3ccSAndroid Build Coastguard Worker return false;
318*77c1e3ccSAndroid Build Coastguard Worker }
319*77c1e3ccSAndroid Build Coastguard Worker
320*77c1e3ccSAndroid Build Coastguard Worker int min_inliers = AOMMAX((int)(MIN_INLIER_PROB * npoints), minpts);
321*77c1e3ccSAndroid Build Coastguard Worker
322*77c1e3ccSAndroid Build Coastguard Worker motions =
323*77c1e3ccSAndroid Build Coastguard Worker (RANSAC_MOTION *)aom_calloc(num_desired_motions, sizeof(RANSAC_MOTION));
324*77c1e3ccSAndroid Build Coastguard Worker
325*77c1e3ccSAndroid Build Coastguard Worker // Allocate one large buffer which will be carved up to store the inlier
326*77c1e3ccSAndroid Build Coastguard Worker // indices for the current motion plus the num_desired_motions many
327*77c1e3ccSAndroid Build Coastguard Worker // output models
328*77c1e3ccSAndroid Build Coastguard Worker // This allows us to keep the allocation/deallocation logic simple, without
329*77c1e3ccSAndroid Build Coastguard Worker // having to (for example) check that `motions` is non-null before allocating
330*77c1e3ccSAndroid Build Coastguard Worker // the inlier arrays
331*77c1e3ccSAndroid Build Coastguard Worker int *inlier_buffer = (int *)aom_malloc(sizeof(*inlier_buffer) * npoints *
332*77c1e3ccSAndroid Build Coastguard Worker (num_desired_motions + 1));
333*77c1e3ccSAndroid Build Coastguard Worker
334*77c1e3ccSAndroid Build Coastguard Worker if (!(motions && inlier_buffer)) {
335*77c1e3ccSAndroid Build Coastguard Worker ret_val = false;
336*77c1e3ccSAndroid Build Coastguard Worker *mem_alloc_failed = true;
337*77c1e3ccSAndroid Build Coastguard Worker goto finish_ransac;
338*77c1e3ccSAndroid Build Coastguard Worker }
339*77c1e3ccSAndroid Build Coastguard Worker
340*77c1e3ccSAndroid Build Coastguard Worker // Once all our allocations are known-good, we can fill in our structures
341*77c1e3ccSAndroid Build Coastguard Worker worst_kept_motion = motions;
342*77c1e3ccSAndroid Build Coastguard Worker
343*77c1e3ccSAndroid Build Coastguard Worker for (i = 0; i < num_desired_motions; ++i) {
344*77c1e3ccSAndroid Build Coastguard Worker motions[i].inlier_indices = inlier_buffer + i * npoints;
345*77c1e3ccSAndroid Build Coastguard Worker }
346*77c1e3ccSAndroid Build Coastguard Worker memset(¤t_motion, 0, sizeof(current_motion));
347*77c1e3ccSAndroid Build Coastguard Worker current_motion.inlier_indices = inlier_buffer + num_desired_motions * npoints;
348*77c1e3ccSAndroid Build Coastguard Worker
349*77c1e3ccSAndroid Build Coastguard Worker for (int trial_count = 0; trial_count < NUM_TRIALS; trial_count++) {
350*77c1e3ccSAndroid Build Coastguard Worker lcg_pick(npoints, minpts, indices, &seed);
351*77c1e3ccSAndroid Build Coastguard Worker
352*77c1e3ccSAndroid Build Coastguard Worker if (!model_info->find_transformation(matched_points, indices, minpts,
353*77c1e3ccSAndroid Build Coastguard Worker params_this_motion)) {
354*77c1e3ccSAndroid Build Coastguard Worker continue;
355*77c1e3ccSAndroid Build Coastguard Worker }
356*77c1e3ccSAndroid Build Coastguard Worker
357*77c1e3ccSAndroid Build Coastguard Worker model_info->score_model(params_this_motion, matched_points, npoints,
358*77c1e3ccSAndroid Build Coastguard Worker ¤t_motion);
359*77c1e3ccSAndroid Build Coastguard Worker
360*77c1e3ccSAndroid Build Coastguard Worker if (current_motion.num_inliers < min_inliers) {
361*77c1e3ccSAndroid Build Coastguard Worker // Reject models with too few inliers
362*77c1e3ccSAndroid Build Coastguard Worker continue;
363*77c1e3ccSAndroid Build Coastguard Worker }
364*77c1e3ccSAndroid Build Coastguard Worker
365*77c1e3ccSAndroid Build Coastguard Worker if (is_better_motion(¤t_motion, worst_kept_motion)) {
366*77c1e3ccSAndroid Build Coastguard Worker // This motion is better than the worst currently kept motion. Remember
367*77c1e3ccSAndroid Build Coastguard Worker // the inlier points and sse. The parameters for each kept motion
368*77c1e3ccSAndroid Build Coastguard Worker // will be recomputed later using only the inliers.
369*77c1e3ccSAndroid Build Coastguard Worker worst_kept_motion->num_inliers = current_motion.num_inliers;
370*77c1e3ccSAndroid Build Coastguard Worker worst_kept_motion->sse = current_motion.sse;
371*77c1e3ccSAndroid Build Coastguard Worker
372*77c1e3ccSAndroid Build Coastguard Worker // Rather than copying the (potentially many) inlier indices from
373*77c1e3ccSAndroid Build Coastguard Worker // current_motion.inlier_indices to worst_kept_motion->inlier_indices,
374*77c1e3ccSAndroid Build Coastguard Worker // we can swap the underlying pointers.
375*77c1e3ccSAndroid Build Coastguard Worker //
376*77c1e3ccSAndroid Build Coastguard Worker // This is okay because the next time current_motion.inlier_indices
377*77c1e3ccSAndroid Build Coastguard Worker // is used will be in the next trial, where we ignore its previous
378*77c1e3ccSAndroid Build Coastguard Worker // contents anyway. And both arrays will be deallocated together at the
379*77c1e3ccSAndroid Build Coastguard Worker // end of this function, so there are no lifetime issues.
380*77c1e3ccSAndroid Build Coastguard Worker int *tmp = worst_kept_motion->inlier_indices;
381*77c1e3ccSAndroid Build Coastguard Worker worst_kept_motion->inlier_indices = current_motion.inlier_indices;
382*77c1e3ccSAndroid Build Coastguard Worker current_motion.inlier_indices = tmp;
383*77c1e3ccSAndroid Build Coastguard Worker
384*77c1e3ccSAndroid Build Coastguard Worker // Determine the new worst kept motion and its num_inliers and sse.
385*77c1e3ccSAndroid Build Coastguard Worker for (i = 0; i < num_desired_motions; ++i) {
386*77c1e3ccSAndroid Build Coastguard Worker if (is_better_motion(worst_kept_motion, &motions[i])) {
387*77c1e3ccSAndroid Build Coastguard Worker worst_kept_motion = &motions[i];
388*77c1e3ccSAndroid Build Coastguard Worker }
389*77c1e3ccSAndroid Build Coastguard Worker }
390*77c1e3ccSAndroid Build Coastguard Worker }
391*77c1e3ccSAndroid Build Coastguard Worker }
392*77c1e3ccSAndroid Build Coastguard Worker
393*77c1e3ccSAndroid Build Coastguard Worker // Sort the motions, best first.
394*77c1e3ccSAndroid Build Coastguard Worker qsort(motions, num_desired_motions, sizeof(RANSAC_MOTION), compare_motions);
395*77c1e3ccSAndroid Build Coastguard Worker
396*77c1e3ccSAndroid Build Coastguard Worker // Refine each of the best N models using iterative estimation.
397*77c1e3ccSAndroid Build Coastguard Worker //
398*77c1e3ccSAndroid Build Coastguard Worker // The idea here is loosely based on the iterative method from
399*77c1e3ccSAndroid Build Coastguard Worker // "Locally Optimized RANSAC" by O. Chum, J. Matas and Josef Kittler:
400*77c1e3ccSAndroid Build Coastguard Worker // https://cmp.felk.cvut.cz/ftp/articles/matas/chum-dagm03.pdf
401*77c1e3ccSAndroid Build Coastguard Worker //
402*77c1e3ccSAndroid Build Coastguard Worker // However, we implement a simpler version than their proposal, and simply
403*77c1e3ccSAndroid Build Coastguard Worker // refit the model repeatedly until the number of inliers stops increasing,
404*77c1e3ccSAndroid Build Coastguard Worker // with a cap on the number of iterations to defend against edge cases which
405*77c1e3ccSAndroid Build Coastguard Worker // only improve very slowly.
406*77c1e3ccSAndroid Build Coastguard Worker for (i = 0; i < num_desired_motions; ++i) {
407*77c1e3ccSAndroid Build Coastguard Worker if (motions[i].num_inliers <= 0) {
408*77c1e3ccSAndroid Build Coastguard Worker // Output model has already been initialized to the identity model,
409*77c1e3ccSAndroid Build Coastguard Worker // so just skip setup
410*77c1e3ccSAndroid Build Coastguard Worker continue;
411*77c1e3ccSAndroid Build Coastguard Worker }
412*77c1e3ccSAndroid Build Coastguard Worker
413*77c1e3ccSAndroid Build Coastguard Worker bool bad_model = false;
414*77c1e3ccSAndroid Build Coastguard Worker for (int refine_count = 0; refine_count < NUM_REFINES; refine_count++) {
415*77c1e3ccSAndroid Build Coastguard Worker int num_inliers = motions[i].num_inliers;
416*77c1e3ccSAndroid Build Coastguard Worker assert(num_inliers >= min_inliers);
417*77c1e3ccSAndroid Build Coastguard Worker
418*77c1e3ccSAndroid Build Coastguard Worker if (!model_info->find_transformation(matched_points,
419*77c1e3ccSAndroid Build Coastguard Worker motions[i].inlier_indices,
420*77c1e3ccSAndroid Build Coastguard Worker num_inliers, params_this_motion)) {
421*77c1e3ccSAndroid Build Coastguard Worker // In the unlikely event that this model fitting fails, we don't have a
422*77c1e3ccSAndroid Build Coastguard Worker // good fallback. So leave this model set to the identity model
423*77c1e3ccSAndroid Build Coastguard Worker bad_model = true;
424*77c1e3ccSAndroid Build Coastguard Worker break;
425*77c1e3ccSAndroid Build Coastguard Worker }
426*77c1e3ccSAndroid Build Coastguard Worker
427*77c1e3ccSAndroid Build Coastguard Worker // Score the newly generated model
428*77c1e3ccSAndroid Build Coastguard Worker model_info->score_model(params_this_motion, matched_points, npoints,
429*77c1e3ccSAndroid Build Coastguard Worker ¤t_motion);
430*77c1e3ccSAndroid Build Coastguard Worker
431*77c1e3ccSAndroid Build Coastguard Worker // At this point, there are three possibilities:
432*77c1e3ccSAndroid Build Coastguard Worker // 1) If we found more inliers, keep refining.
433*77c1e3ccSAndroid Build Coastguard Worker // 2) If we found the same number of inliers but a lower SSE, we want to
434*77c1e3ccSAndroid Build Coastguard Worker // keep the new model, but further refinement is unlikely to gain much.
435*77c1e3ccSAndroid Build Coastguard Worker // So commit to this new model
436*77c1e3ccSAndroid Build Coastguard Worker // 3) It is possible, but very unlikely, that the new model will have
437*77c1e3ccSAndroid Build Coastguard Worker // fewer inliers. If it does happen, we probably just lost a few
438*77c1e3ccSAndroid Build Coastguard Worker // borderline inliers. So treat the same as case (2).
439*77c1e3ccSAndroid Build Coastguard Worker if (current_motion.num_inliers > motions[i].num_inliers) {
440*77c1e3ccSAndroid Build Coastguard Worker motions[i].num_inliers = current_motion.num_inliers;
441*77c1e3ccSAndroid Build Coastguard Worker motions[i].sse = current_motion.sse;
442*77c1e3ccSAndroid Build Coastguard Worker int *tmp = motions[i].inlier_indices;
443*77c1e3ccSAndroid Build Coastguard Worker motions[i].inlier_indices = current_motion.inlier_indices;
444*77c1e3ccSAndroid Build Coastguard Worker current_motion.inlier_indices = tmp;
445*77c1e3ccSAndroid Build Coastguard Worker } else {
446*77c1e3ccSAndroid Build Coastguard Worker // Refined model is no better, so stop
447*77c1e3ccSAndroid Build Coastguard Worker // This shouldn't be significantly worse than the previous model,
448*77c1e3ccSAndroid Build Coastguard Worker // so it's fine to use the parameters in params_this_motion.
449*77c1e3ccSAndroid Build Coastguard Worker // This saves us from having to cache the previous iteration's params.
450*77c1e3ccSAndroid Build Coastguard Worker break;
451*77c1e3ccSAndroid Build Coastguard Worker }
452*77c1e3ccSAndroid Build Coastguard Worker }
453*77c1e3ccSAndroid Build Coastguard Worker
454*77c1e3ccSAndroid Build Coastguard Worker if (bad_model) continue;
455*77c1e3ccSAndroid Build Coastguard Worker
456*77c1e3ccSAndroid Build Coastguard Worker // Fill in output struct
457*77c1e3ccSAndroid Build Coastguard Worker memcpy(motion_models[i].params, params_this_motion,
458*77c1e3ccSAndroid Build Coastguard Worker MAX_PARAMDIM * sizeof(*motion_models[i].params));
459*77c1e3ccSAndroid Build Coastguard Worker for (int j = 0; j < motions[i].num_inliers; j++) {
460*77c1e3ccSAndroid Build Coastguard Worker int index = motions[i].inlier_indices[j];
461*77c1e3ccSAndroid Build Coastguard Worker const Correspondence *corr = &matched_points[index];
462*77c1e3ccSAndroid Build Coastguard Worker motion_models[i].inliers[2 * j + 0] = (int)rint(corr->x);
463*77c1e3ccSAndroid Build Coastguard Worker motion_models[i].inliers[2 * j + 1] = (int)rint(corr->y);
464*77c1e3ccSAndroid Build Coastguard Worker }
465*77c1e3ccSAndroid Build Coastguard Worker motion_models[i].num_inliers = motions[i].num_inliers;
466*77c1e3ccSAndroid Build Coastguard Worker }
467*77c1e3ccSAndroid Build Coastguard Worker
468*77c1e3ccSAndroid Build Coastguard Worker finish_ransac:
469*77c1e3ccSAndroid Build Coastguard Worker aom_free(inlier_buffer);
470*77c1e3ccSAndroid Build Coastguard Worker aom_free(motions);
471*77c1e3ccSAndroid Build Coastguard Worker
472*77c1e3ccSAndroid Build Coastguard Worker return ret_val;
473*77c1e3ccSAndroid Build Coastguard Worker }
474*77c1e3ccSAndroid Build Coastguard Worker
475*77c1e3ccSAndroid Build Coastguard Worker static const RansacModelInfo ransac_model_info[TRANS_TYPES] = {
476*77c1e3ccSAndroid Build Coastguard Worker // IDENTITY
477*77c1e3ccSAndroid Build Coastguard Worker { NULL, NULL, 0 },
478*77c1e3ccSAndroid Build Coastguard Worker // TRANSLATION
479*77c1e3ccSAndroid Build Coastguard Worker #if ALLOW_TRANSLATION_MODELS
480*77c1e3ccSAndroid Build Coastguard Worker { find_translation, score_translation, 1 },
481*77c1e3ccSAndroid Build Coastguard Worker #else
482*77c1e3ccSAndroid Build Coastguard Worker { NULL, NULL, 0 },
483*77c1e3ccSAndroid Build Coastguard Worker #endif
484*77c1e3ccSAndroid Build Coastguard Worker // ROTZOOM
485*77c1e3ccSAndroid Build Coastguard Worker { find_rotzoom, score_affine, 2 },
486*77c1e3ccSAndroid Build Coastguard Worker // AFFINE
487*77c1e3ccSAndroid Build Coastguard Worker { find_affine, score_affine, 3 },
488*77c1e3ccSAndroid Build Coastguard Worker };
489*77c1e3ccSAndroid Build Coastguard Worker
490*77c1e3ccSAndroid Build Coastguard Worker // Returns true on success, false on error
ransac(const Correspondence * matched_points,int npoints,TransformationType type,MotionModel * motion_models,int num_desired_motions,bool * mem_alloc_failed)491*77c1e3ccSAndroid Build Coastguard Worker bool ransac(const Correspondence *matched_points, int npoints,
492*77c1e3ccSAndroid Build Coastguard Worker TransformationType type, MotionModel *motion_models,
493*77c1e3ccSAndroid Build Coastguard Worker int num_desired_motions, bool *mem_alloc_failed) {
494*77c1e3ccSAndroid Build Coastguard Worker #if ALLOW_TRANSLATION_MODELS
495*77c1e3ccSAndroid Build Coastguard Worker assert(type > IDENTITY && type < TRANS_TYPES);
496*77c1e3ccSAndroid Build Coastguard Worker #else
497*77c1e3ccSAndroid Build Coastguard Worker assert(type > TRANSLATION && type < TRANS_TYPES);
498*77c1e3ccSAndroid Build Coastguard Worker #endif // ALLOW_TRANSLATION_MODELS
499*77c1e3ccSAndroid Build Coastguard Worker
500*77c1e3ccSAndroid Build Coastguard Worker return ransac_internal(matched_points, npoints, motion_models,
501*77c1e3ccSAndroid Build Coastguard Worker num_desired_motions, &ransac_model_info[type],
502*77c1e3ccSAndroid Build Coastguard Worker mem_alloc_failed);
503*77c1e3ccSAndroid Build Coastguard Worker }
504