• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2016, Alliance for Open Media. All rights reserved
3  *
4  * This source code is subject to the terms of the BSD 2 Clause License and
5  * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
6  * was not distributed with this source code in the LICENSE file, you can
7  * obtain it at www.aomedia.org/license/software. If the Alliance for Open
8  * Media Patent License 1.0 was not distributed with this source code in the
9  * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
10  */
11 
12 #include <stdlib.h>
13 #include <memory.h>
14 #include <math.h>
15 
16 #include "config/aom_dsp_rtcd.h"
17 
18 #include "aom_dsp/flow_estimation/corner_detect.h"
19 #include "aom_dsp/flow_estimation/corner_match.h"
20 #include "aom_dsp/flow_estimation/flow_estimation.h"
21 #include "aom_dsp/flow_estimation/ransac.h"
22 #include "aom_scale/yv12config.h"
23 
24 #define SEARCH_SZ 9
25 #define SEARCH_SZ_BY2 ((SEARCH_SZ - 1) / 2)
26 
27 #define THRESHOLD_NCC 0.75
28 
29 /* Compute var(im) * MATCH_SZ_SQ over a MATCH_SZ by MATCH_SZ window of im,
30    centered at (x, y).
31 */
compute_variance(unsigned char * im,int stride,int x,int y)32 static double compute_variance(unsigned char *im, int stride, int x, int y) {
33   int sum = 0;
34   int sumsq = 0;
35   int var;
36   int i, j;
37   for (i = 0; i < MATCH_SZ; ++i)
38     for (j = 0; j < MATCH_SZ; ++j) {
39       sum += im[(i + y - MATCH_SZ_BY2) * stride + (j + x - MATCH_SZ_BY2)];
40       sumsq += im[(i + y - MATCH_SZ_BY2) * stride + (j + x - MATCH_SZ_BY2)] *
41                im[(i + y - MATCH_SZ_BY2) * stride + (j + x - MATCH_SZ_BY2)];
42     }
43   var = sumsq * MATCH_SZ_SQ - sum * sum;
44   return (double)var;
45 }
46 
47 /* Compute corr(im1, im2) * MATCH_SZ * stddev(im1), where the
48    correlation/standard deviation are taken over MATCH_SZ by MATCH_SZ windows
49    of each image, centered at (x1, y1) and (x2, y2) respectively.
50 */
av1_compute_cross_correlation_c(unsigned char * im1,int stride1,int x1,int y1,unsigned char * im2,int stride2,int x2,int y2)51 double av1_compute_cross_correlation_c(unsigned char *im1, int stride1, int x1,
52                                        int y1, unsigned char *im2, int stride2,
53                                        int x2, int y2) {
54   int v1, v2;
55   int sum1 = 0;
56   int sum2 = 0;
57   int sumsq2 = 0;
58   int cross = 0;
59   int var2, cov;
60   int i, j;
61   for (i = 0; i < MATCH_SZ; ++i)
62     for (j = 0; j < MATCH_SZ; ++j) {
63       v1 = im1[(i + y1 - MATCH_SZ_BY2) * stride1 + (j + x1 - MATCH_SZ_BY2)];
64       v2 = im2[(i + y2 - MATCH_SZ_BY2) * stride2 + (j + x2 - MATCH_SZ_BY2)];
65       sum1 += v1;
66       sum2 += v2;
67       sumsq2 += v2 * v2;
68       cross += v1 * v2;
69     }
70   var2 = sumsq2 * MATCH_SZ_SQ - sum2 * sum2;
71   cov = cross * MATCH_SZ_SQ - sum1 * sum2;
72   return cov / sqrt((double)var2);
73 }
74 
is_eligible_point(int pointx,int pointy,int width,int height)75 static int is_eligible_point(int pointx, int pointy, int width, int height) {
76   return (pointx >= MATCH_SZ_BY2 && pointy >= MATCH_SZ_BY2 &&
77           pointx + MATCH_SZ_BY2 < width && pointy + MATCH_SZ_BY2 < height);
78 }
79 
is_eligible_distance(int point1x,int point1y,int point2x,int point2y,int width,int height)80 static int is_eligible_distance(int point1x, int point1y, int point2x,
81                                 int point2y, int width, int height) {
82   const int thresh = (width < height ? height : width) >> 4;
83   return ((point1x - point2x) * (point1x - point2x) +
84           (point1y - point2y) * (point1y - point2y)) <= thresh * thresh;
85 }
86 
improve_correspondence(unsigned char * frm,unsigned char * ref,int width,int height,int frm_stride,int ref_stride,Correspondence * correspondences,int num_correspondences)87 static void improve_correspondence(unsigned char *frm, unsigned char *ref,
88                                    int width, int height, int frm_stride,
89                                    int ref_stride,
90                                    Correspondence *correspondences,
91                                    int num_correspondences) {
92   int i;
93   for (i = 0; i < num_correspondences; ++i) {
94     int x, y, best_x = 0, best_y = 0;
95     double best_match_ncc = 0.0;
96     for (y = -SEARCH_SZ_BY2; y <= SEARCH_SZ_BY2; ++y) {
97       for (x = -SEARCH_SZ_BY2; x <= SEARCH_SZ_BY2; ++x) {
98         double match_ncc;
99         if (!is_eligible_point(correspondences[i].rx + x,
100                                correspondences[i].ry + y, width, height))
101           continue;
102         if (!is_eligible_distance(correspondences[i].x, correspondences[i].y,
103                                   correspondences[i].rx + x,
104                                   correspondences[i].ry + y, width, height))
105           continue;
106         match_ncc = av1_compute_cross_correlation(
107             frm, frm_stride, correspondences[i].x, correspondences[i].y, ref,
108             ref_stride, correspondences[i].rx + x, correspondences[i].ry + y);
109         if (match_ncc > best_match_ncc) {
110           best_match_ncc = match_ncc;
111           best_y = y;
112           best_x = x;
113         }
114       }
115     }
116     correspondences[i].rx += best_x;
117     correspondences[i].ry += best_y;
118   }
119   for (i = 0; i < num_correspondences; ++i) {
120     int x, y, best_x = 0, best_y = 0;
121     double best_match_ncc = 0.0;
122     for (y = -SEARCH_SZ_BY2; y <= SEARCH_SZ_BY2; ++y)
123       for (x = -SEARCH_SZ_BY2; x <= SEARCH_SZ_BY2; ++x) {
124         double match_ncc;
125         if (!is_eligible_point(correspondences[i].x + x,
126                                correspondences[i].y + y, width, height))
127           continue;
128         if (!is_eligible_distance(
129                 correspondences[i].x + x, correspondences[i].y + y,
130                 correspondences[i].rx, correspondences[i].ry, width, height))
131           continue;
132         match_ncc = av1_compute_cross_correlation(
133             ref, ref_stride, correspondences[i].rx, correspondences[i].ry, frm,
134             frm_stride, correspondences[i].x + x, correspondences[i].y + y);
135         if (match_ncc > best_match_ncc) {
136           best_match_ncc = match_ncc;
137           best_y = y;
138           best_x = x;
139         }
140       }
141     correspondences[i].x += best_x;
142     correspondences[i].y += best_y;
143   }
144 }
145 
aom_determine_correspondence(unsigned char * src,int * src_corners,int num_src_corners,unsigned char * ref,int * ref_corners,int num_ref_corners,int width,int height,int src_stride,int ref_stride,int * correspondence_pts)146 int aom_determine_correspondence(unsigned char *src, int *src_corners,
147                                  int num_src_corners, unsigned char *ref,
148                                  int *ref_corners, int num_ref_corners,
149                                  int width, int height, int src_stride,
150                                  int ref_stride, int *correspondence_pts) {
151   // TODO(sarahparker) Improve this to include 2-way match
152   int i, j;
153   Correspondence *correspondences = (Correspondence *)correspondence_pts;
154   int num_correspondences = 0;
155   for (i = 0; i < num_src_corners; ++i) {
156     double best_match_ncc = 0.0;
157     double template_norm;
158     int best_match_j = -1;
159     if (!is_eligible_point(src_corners[2 * i], src_corners[2 * i + 1], width,
160                            height))
161       continue;
162     for (j = 0; j < num_ref_corners; ++j) {
163       double match_ncc;
164       if (!is_eligible_point(ref_corners[2 * j], ref_corners[2 * j + 1], width,
165                              height))
166         continue;
167       if (!is_eligible_distance(src_corners[2 * i], src_corners[2 * i + 1],
168                                 ref_corners[2 * j], ref_corners[2 * j + 1],
169                                 width, height))
170         continue;
171       match_ncc = av1_compute_cross_correlation(
172           src, src_stride, src_corners[2 * i], src_corners[2 * i + 1], ref,
173           ref_stride, ref_corners[2 * j], ref_corners[2 * j + 1]);
174       if (match_ncc > best_match_ncc) {
175         best_match_ncc = match_ncc;
176         best_match_j = j;
177       }
178     }
179     // Note: We want to test if the best correlation is >= THRESHOLD_NCC,
180     // but need to account for the normalization in
181     // av1_compute_cross_correlation.
182     template_norm = compute_variance(src, src_stride, src_corners[2 * i],
183                                      src_corners[2 * i + 1]);
184     if (best_match_ncc > THRESHOLD_NCC * sqrt(template_norm)) {
185       correspondences[num_correspondences].x = src_corners[2 * i];
186       correspondences[num_correspondences].y = src_corners[2 * i + 1];
187       correspondences[num_correspondences].rx = ref_corners[2 * best_match_j];
188       correspondences[num_correspondences].ry =
189           ref_corners[2 * best_match_j + 1];
190       num_correspondences++;
191     }
192   }
193   improve_correspondence(src, ref, width, height, src_stride, ref_stride,
194                          correspondences, num_correspondences);
195   return num_correspondences;
196 }
197 
get_inliers_from_indices(MotionModel * params,int * correspondences)198 static bool get_inliers_from_indices(MotionModel *params,
199                                      int *correspondences) {
200   int *inliers_tmp = (int *)aom_calloc(2 * MAX_CORNERS, sizeof(*inliers_tmp));
201   if (!inliers_tmp) return false;
202 
203   for (int i = 0; i < params->num_inliers; i++) {
204     int index = params->inliers[i];
205     inliers_tmp[2 * i] = correspondences[4 * index];
206     inliers_tmp[2 * i + 1] = correspondences[4 * index + 1];
207   }
208   memcpy(params->inliers, inliers_tmp, sizeof(*inliers_tmp) * 2 * MAX_CORNERS);
209   aom_free(inliers_tmp);
210   return true;
211 }
212 
av1_compute_global_motion_feature_based(TransformationType type,unsigned char * src_buffer,int src_width,int src_height,int src_stride,int * src_corners,int num_src_corners,YV12_BUFFER_CONFIG * ref,int bit_depth,int * num_inliers_by_motion,MotionModel * params_by_motion,int num_motions)213 int av1_compute_global_motion_feature_based(
214     TransformationType type, unsigned char *src_buffer, int src_width,
215     int src_height, int src_stride, int *src_corners, int num_src_corners,
216     YV12_BUFFER_CONFIG *ref, int bit_depth, int *num_inliers_by_motion,
217     MotionModel *params_by_motion, int num_motions) {
218   int i;
219   int num_ref_corners;
220   int num_correspondences;
221   int *correspondences;
222   int ref_corners[2 * MAX_CORNERS];
223   unsigned char *ref_buffer = ref->y_buffer;
224   RansacFunc ransac = av1_get_ransac_type(type);
225 
226   if (ref->flags & YV12_FLAG_HIGHBITDEPTH) {
227     ref_buffer = av1_downconvert_frame(ref, bit_depth);
228   }
229 
230   num_ref_corners =
231       av1_fast_corner_detect(ref_buffer, ref->y_width, ref->y_height,
232                              ref->y_stride, ref_corners, MAX_CORNERS);
233 
234   // find correspondences between the two images
235   correspondences =
236       (int *)aom_malloc(num_src_corners * 4 * sizeof(*correspondences));
237   if (!correspondences) return 0;
238   num_correspondences = aom_determine_correspondence(
239       src_buffer, (int *)src_corners, num_src_corners, ref_buffer,
240       (int *)ref_corners, num_ref_corners, src_width, src_height, src_stride,
241       ref->y_stride, correspondences);
242 
243   ransac(correspondences, num_correspondences, num_inliers_by_motion,
244          params_by_motion, num_motions);
245 
246   // Set num_inliers = 0 for motions with too few inliers so they are ignored.
247   for (i = 0; i < num_motions; ++i) {
248     if (num_inliers_by_motion[i] < MIN_INLIER_PROB * num_correspondences ||
249         num_correspondences == 0) {
250       num_inliers_by_motion[i] = 0;
251     } else if (!get_inliers_from_indices(&params_by_motion[i],
252                                          correspondences)) {
253       aom_free(correspondences);
254       return 0;
255     }
256   }
257 
258   aom_free(correspondences);
259 
260   // Return true if any one of the motions has inliers.
261   for (i = 0; i < num_motions; ++i) {
262     if (num_inliers_by_motion[i] > 0) return 1;
263   }
264   return 0;
265 }
266