xref: /aosp_15_r20/external/libaom/aom_dsp/flow_estimation/ransac.c (revision 77c1e3ccc04c968bd2bc212e87364f250e820521)
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(&current_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                             &current_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(&current_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                               &current_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