xref: /aosp_15_r20/external/libaom/av1/encoder/optical_flow.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 #include <math.h>
12*77c1e3ccSAndroid Build Coastguard Worker #include <limits.h>
13*77c1e3ccSAndroid Build Coastguard Worker 
14*77c1e3ccSAndroid Build Coastguard Worker #include "config/aom_config.h"
15*77c1e3ccSAndroid Build Coastguard Worker 
16*77c1e3ccSAndroid Build Coastguard Worker #include "aom_dsp/mathutils.h"
17*77c1e3ccSAndroid Build Coastguard Worker #include "aom_mem/aom_mem.h"
18*77c1e3ccSAndroid Build Coastguard Worker 
19*77c1e3ccSAndroid Build Coastguard Worker #include "av1/common/av1_common_int.h"
20*77c1e3ccSAndroid Build Coastguard Worker #include "av1/encoder/encoder.h"
21*77c1e3ccSAndroid Build Coastguard Worker #include "av1/encoder/optical_flow.h"
22*77c1e3ccSAndroid Build Coastguard Worker #include "av1/encoder/sparse_linear_solver.h"
23*77c1e3ccSAndroid Build Coastguard Worker #include "av1/encoder/reconinter_enc.h"
24*77c1e3ccSAndroid Build Coastguard Worker 
25*77c1e3ccSAndroid Build Coastguard Worker #if CONFIG_OPTICAL_FLOW_API
26*77c1e3ccSAndroid Build Coastguard Worker 
av1_init_opfl_params(OPFL_PARAMS * opfl_params)27*77c1e3ccSAndroid Build Coastguard Worker void av1_init_opfl_params(OPFL_PARAMS *opfl_params) {
28*77c1e3ccSAndroid Build Coastguard Worker   opfl_params->pyramid_levels = OPFL_PYRAMID_LEVELS;
29*77c1e3ccSAndroid Build Coastguard Worker   opfl_params->warping_steps = OPFL_WARPING_STEPS;
30*77c1e3ccSAndroid Build Coastguard Worker   opfl_params->lk_params = NULL;
31*77c1e3ccSAndroid Build Coastguard Worker }
32*77c1e3ccSAndroid Build Coastguard Worker 
av1_init_lk_params(LK_PARAMS * lk_params)33*77c1e3ccSAndroid Build Coastguard Worker void av1_init_lk_params(LK_PARAMS *lk_params) {
34*77c1e3ccSAndroid Build Coastguard Worker   lk_params->window_size = OPFL_WINDOW_SIZE;
35*77c1e3ccSAndroid Build Coastguard Worker }
36*77c1e3ccSAndroid Build Coastguard Worker 
37*77c1e3ccSAndroid Build Coastguard Worker // Helper function to determine whether a frame is encoded with high bit-depth.
is_frame_high_bitdepth(const YV12_BUFFER_CONFIG * frame)38*77c1e3ccSAndroid Build Coastguard Worker static inline int is_frame_high_bitdepth(const YV12_BUFFER_CONFIG *frame) {
39*77c1e3ccSAndroid Build Coastguard Worker   return (frame->flags & YV12_FLAG_HIGHBITDEPTH) ? 1 : 0;
40*77c1e3ccSAndroid Build Coastguard Worker }
41*77c1e3ccSAndroid Build Coastguard Worker 
42*77c1e3ccSAndroid Build Coastguard Worker // Helper function to determine whether optical flow method is sparse.
is_sparse(const OPFL_PARAMS * opfl_params)43*77c1e3ccSAndroid Build Coastguard Worker static inline int is_sparse(const OPFL_PARAMS *opfl_params) {
44*77c1e3ccSAndroid Build Coastguard Worker   return (opfl_params->flags & OPFL_FLAG_SPARSE) ? 1 : 0;
45*77c1e3ccSAndroid Build Coastguard Worker }
46*77c1e3ccSAndroid Build Coastguard Worker 
47*77c1e3ccSAndroid Build Coastguard Worker static void gradients_over_window(const YV12_BUFFER_CONFIG *frame,
48*77c1e3ccSAndroid Build Coastguard Worker                                   const YV12_BUFFER_CONFIG *ref_frame,
49*77c1e3ccSAndroid Build Coastguard Worker                                   const double x_coord, const double y_coord,
50*77c1e3ccSAndroid Build Coastguard Worker                                   const int window_size, const int bit_depth,
51*77c1e3ccSAndroid Build Coastguard Worker                                   double *ix, double *iy, double *it,
52*77c1e3ccSAndroid Build Coastguard Worker                                   LOCALMV *mv);
53*77c1e3ccSAndroid Build Coastguard Worker 
54*77c1e3ccSAndroid Build Coastguard Worker // coefficients for bilinear interpolation on unit square
pixel_interp(const double x,const double y,const double b00,const double b01,const double b10,const double b11)55*77c1e3ccSAndroid Build Coastguard Worker static int pixel_interp(const double x, const double y, const double b00,
56*77c1e3ccSAndroid Build Coastguard Worker                         const double b01, const double b10, const double b11) {
57*77c1e3ccSAndroid Build Coastguard Worker   const int xint = (int)x;
58*77c1e3ccSAndroid Build Coastguard Worker   const int yint = (int)y;
59*77c1e3ccSAndroid Build Coastguard Worker   const double xdec = x - xint;
60*77c1e3ccSAndroid Build Coastguard Worker   const double ydec = y - yint;
61*77c1e3ccSAndroid Build Coastguard Worker   const double a = (1 - xdec) * (1 - ydec);
62*77c1e3ccSAndroid Build Coastguard Worker   const double b = xdec * (1 - ydec);
63*77c1e3ccSAndroid Build Coastguard Worker   const double c = (1 - xdec) * ydec;
64*77c1e3ccSAndroid Build Coastguard Worker   const double d = xdec * ydec;
65*77c1e3ccSAndroid Build Coastguard Worker   // if x, y are already integers, this results to b00
66*77c1e3ccSAndroid Build Coastguard Worker   int interp = (int)round(a * b00 + b * b01 + c * b10 + d * b11);
67*77c1e3ccSAndroid Build Coastguard Worker   return interp;
68*77c1e3ccSAndroid Build Coastguard Worker }
69*77c1e3ccSAndroid Build Coastguard Worker 
70*77c1e3ccSAndroid Build Coastguard Worker // Scharr filter to compute spatial gradient
spatial_gradient(const YV12_BUFFER_CONFIG * frame,const int x_coord,const int y_coord,const int direction,double * derivative)71*77c1e3ccSAndroid Build Coastguard Worker static void spatial_gradient(const YV12_BUFFER_CONFIG *frame, const int x_coord,
72*77c1e3ccSAndroid Build Coastguard Worker                              const int y_coord, const int direction,
73*77c1e3ccSAndroid Build Coastguard Worker                              double *derivative) {
74*77c1e3ccSAndroid Build Coastguard Worker   double *filter;
75*77c1e3ccSAndroid Build Coastguard Worker   // Scharr filters
76*77c1e3ccSAndroid Build Coastguard Worker   double gx[9] = { -3, 0, 3, -10, 0, 10, -3, 0, 3 };
77*77c1e3ccSAndroid Build Coastguard Worker   double gy[9] = { -3, -10, -3, 0, 0, 0, 3, 10, 3 };
78*77c1e3ccSAndroid Build Coastguard Worker   if (direction == 0) {  // x direction
79*77c1e3ccSAndroid Build Coastguard Worker     filter = gx;
80*77c1e3ccSAndroid Build Coastguard Worker   } else {  // y direction
81*77c1e3ccSAndroid Build Coastguard Worker     filter = gy;
82*77c1e3ccSAndroid Build Coastguard Worker   }
83*77c1e3ccSAndroid Build Coastguard Worker   int idx = 0;
84*77c1e3ccSAndroid Build Coastguard Worker   double d = 0;
85*77c1e3ccSAndroid Build Coastguard Worker   for (int yy = -1; yy <= 1; yy++) {
86*77c1e3ccSAndroid Build Coastguard Worker     for (int xx = -1; xx <= 1; xx++) {
87*77c1e3ccSAndroid Build Coastguard Worker       d += filter[idx] *
88*77c1e3ccSAndroid Build Coastguard Worker            frame->y_buffer[(y_coord + yy) * frame->y_stride + (x_coord + xx)];
89*77c1e3ccSAndroid Build Coastguard Worker       idx++;
90*77c1e3ccSAndroid Build Coastguard Worker     }
91*77c1e3ccSAndroid Build Coastguard Worker   }
92*77c1e3ccSAndroid Build Coastguard Worker   // normalization scaling factor for scharr
93*77c1e3ccSAndroid Build Coastguard Worker   *derivative = d / 32.0;
94*77c1e3ccSAndroid Build Coastguard Worker }
95*77c1e3ccSAndroid Build Coastguard Worker 
96*77c1e3ccSAndroid Build Coastguard Worker // Determine the spatial gradient at subpixel locations
97*77c1e3ccSAndroid Build Coastguard Worker // For example, when reducing images for pyramidal LK,
98*77c1e3ccSAndroid Build Coastguard Worker // corners found in original image may be at subpixel locations.
gradient_interp(double * fullpel_deriv,const double x_coord,const double y_coord,const int w,const int h,double * derivative)99*77c1e3ccSAndroid Build Coastguard Worker static void gradient_interp(double *fullpel_deriv, const double x_coord,
100*77c1e3ccSAndroid Build Coastguard Worker                             const double y_coord, const int w, const int h,
101*77c1e3ccSAndroid Build Coastguard Worker                             double *derivative) {
102*77c1e3ccSAndroid Build Coastguard Worker   const int xint = (int)x_coord;
103*77c1e3ccSAndroid Build Coastguard Worker   const int yint = (int)y_coord;
104*77c1e3ccSAndroid Build Coastguard Worker   double interp;
105*77c1e3ccSAndroid Build Coastguard Worker   if (xint + 1 > w - 1 || yint + 1 > h - 1) {
106*77c1e3ccSAndroid Build Coastguard Worker     interp = fullpel_deriv[yint * w + xint];
107*77c1e3ccSAndroid Build Coastguard Worker   } else {
108*77c1e3ccSAndroid Build Coastguard Worker     interp = pixel_interp(x_coord, y_coord, fullpel_deriv[yint * w + xint],
109*77c1e3ccSAndroid Build Coastguard Worker                           fullpel_deriv[yint * w + (xint + 1)],
110*77c1e3ccSAndroid Build Coastguard Worker                           fullpel_deriv[(yint + 1) * w + xint],
111*77c1e3ccSAndroid Build Coastguard Worker                           fullpel_deriv[(yint + 1) * w + (xint + 1)]);
112*77c1e3ccSAndroid Build Coastguard Worker   }
113*77c1e3ccSAndroid Build Coastguard Worker 
114*77c1e3ccSAndroid Build Coastguard Worker   *derivative = interp;
115*77c1e3ccSAndroid Build Coastguard Worker }
116*77c1e3ccSAndroid Build Coastguard Worker 
temporal_gradient(const YV12_BUFFER_CONFIG * frame,const YV12_BUFFER_CONFIG * frame2,const double x_coord,const double y_coord,const int bit_depth,double * derivative,LOCALMV * mv)117*77c1e3ccSAndroid Build Coastguard Worker static void temporal_gradient(const YV12_BUFFER_CONFIG *frame,
118*77c1e3ccSAndroid Build Coastguard Worker                               const YV12_BUFFER_CONFIG *frame2,
119*77c1e3ccSAndroid Build Coastguard Worker                               const double x_coord, const double y_coord,
120*77c1e3ccSAndroid Build Coastguard Worker                               const int bit_depth, double *derivative,
121*77c1e3ccSAndroid Build Coastguard Worker                               LOCALMV *mv) {
122*77c1e3ccSAndroid Build Coastguard Worker   const int w = 2;
123*77c1e3ccSAndroid Build Coastguard Worker   const int h = 2;
124*77c1e3ccSAndroid Build Coastguard Worker   uint8_t pred1[4];
125*77c1e3ccSAndroid Build Coastguard Worker   uint8_t pred2[4];
126*77c1e3ccSAndroid Build Coastguard Worker 
127*77c1e3ccSAndroid Build Coastguard Worker   const int y = (int)y_coord;
128*77c1e3ccSAndroid Build Coastguard Worker   const int x = (int)x_coord;
129*77c1e3ccSAndroid Build Coastguard Worker   const double ydec = y_coord - y;
130*77c1e3ccSAndroid Build Coastguard Worker   const double xdec = x_coord - x;
131*77c1e3ccSAndroid Build Coastguard Worker   const int is_intrabc = 0;  // Is intra-copied?
132*77c1e3ccSAndroid Build Coastguard Worker   const int is_high_bitdepth = is_frame_high_bitdepth(frame2);
133*77c1e3ccSAndroid Build Coastguard Worker   const int subsampling_x = 0, subsampling_y = 0;  // for y-buffer
134*77c1e3ccSAndroid Build Coastguard Worker   const int_interpfilters interp_filters =
135*77c1e3ccSAndroid Build Coastguard Worker       av1_broadcast_interp_filter(MULTITAP_SHARP);
136*77c1e3ccSAndroid Build Coastguard Worker   const int plane = 0;  // y-plane
137*77c1e3ccSAndroid Build Coastguard Worker   const struct buf_2d ref_buf2 = { NULL, frame2->y_buffer, frame2->y_crop_width,
138*77c1e3ccSAndroid Build Coastguard Worker                                    frame2->y_crop_height, frame2->y_stride };
139*77c1e3ccSAndroid Build Coastguard Worker   struct scale_factors scale;
140*77c1e3ccSAndroid Build Coastguard Worker   av1_setup_scale_factors_for_frame(&scale, frame->y_crop_width,
141*77c1e3ccSAndroid Build Coastguard Worker                                     frame->y_crop_height, frame->y_crop_width,
142*77c1e3ccSAndroid Build Coastguard Worker                                     frame->y_crop_height);
143*77c1e3ccSAndroid Build Coastguard Worker   InterPredParams inter_pred_params;
144*77c1e3ccSAndroid Build Coastguard Worker   av1_init_inter_params(&inter_pred_params, w, h, y, x, subsampling_x,
145*77c1e3ccSAndroid Build Coastguard Worker                         subsampling_y, bit_depth, is_high_bitdepth, is_intrabc,
146*77c1e3ccSAndroid Build Coastguard Worker                         &scale, &ref_buf2, interp_filters);
147*77c1e3ccSAndroid Build Coastguard Worker   inter_pred_params.interp_filter_params[0] =
148*77c1e3ccSAndroid Build Coastguard Worker       &av1_interp_filter_params_list[interp_filters.as_filters.x_filter];
149*77c1e3ccSAndroid Build Coastguard Worker   inter_pred_params.interp_filter_params[1] =
150*77c1e3ccSAndroid Build Coastguard Worker       &av1_interp_filter_params_list[interp_filters.as_filters.y_filter];
151*77c1e3ccSAndroid Build Coastguard Worker   inter_pred_params.conv_params = get_conv_params(0, plane, bit_depth);
152*77c1e3ccSAndroid Build Coastguard Worker   MV newmv = { .row = (int16_t)round((mv->row + xdec) * 8),
153*77c1e3ccSAndroid Build Coastguard Worker                .col = (int16_t)round((mv->col + ydec) * 8) };
154*77c1e3ccSAndroid Build Coastguard Worker   av1_enc_build_one_inter_predictor(pred2, w, &newmv, &inter_pred_params);
155*77c1e3ccSAndroid Build Coastguard Worker   const struct buf_2d ref_buf1 = { NULL, frame->y_buffer, frame->y_crop_width,
156*77c1e3ccSAndroid Build Coastguard Worker                                    frame->y_crop_height, frame->y_stride };
157*77c1e3ccSAndroid Build Coastguard Worker   av1_init_inter_params(&inter_pred_params, w, h, y, x, subsampling_x,
158*77c1e3ccSAndroid Build Coastguard Worker                         subsampling_y, bit_depth, is_high_bitdepth, is_intrabc,
159*77c1e3ccSAndroid Build Coastguard Worker                         &scale, &ref_buf1, interp_filters);
160*77c1e3ccSAndroid Build Coastguard Worker   inter_pred_params.interp_filter_params[0] =
161*77c1e3ccSAndroid Build Coastguard Worker       &av1_interp_filter_params_list[interp_filters.as_filters.x_filter];
162*77c1e3ccSAndroid Build Coastguard Worker   inter_pred_params.interp_filter_params[1] =
163*77c1e3ccSAndroid Build Coastguard Worker       &av1_interp_filter_params_list[interp_filters.as_filters.y_filter];
164*77c1e3ccSAndroid Build Coastguard Worker   inter_pred_params.conv_params = get_conv_params(0, plane, bit_depth);
165*77c1e3ccSAndroid Build Coastguard Worker   MV zeroMV = { .row = (int16_t)round(xdec * 8),
166*77c1e3ccSAndroid Build Coastguard Worker                 .col = (int16_t)round(ydec * 8) };
167*77c1e3ccSAndroid Build Coastguard Worker   av1_enc_build_one_inter_predictor(pred1, w, &zeroMV, &inter_pred_params);
168*77c1e3ccSAndroid Build Coastguard Worker 
169*77c1e3ccSAndroid Build Coastguard Worker   *derivative = pred2[0] - pred1[0];
170*77c1e3ccSAndroid Build Coastguard Worker }
171*77c1e3ccSAndroid Build Coastguard Worker 
172*77c1e3ccSAndroid Build Coastguard Worker // Numerical differentiate over window_size x window_size surrounding (x,y)
173*77c1e3ccSAndroid Build Coastguard Worker // location. Alters ix, iy, it to contain numerical partial derivatives
gradients_over_window(const YV12_BUFFER_CONFIG * frame,const YV12_BUFFER_CONFIG * ref_frame,const double x_coord,const double y_coord,const int window_size,const int bit_depth,double * ix,double * iy,double * it,LOCALMV * mv)174*77c1e3ccSAndroid Build Coastguard Worker static void gradients_over_window(const YV12_BUFFER_CONFIG *frame,
175*77c1e3ccSAndroid Build Coastguard Worker                                   const YV12_BUFFER_CONFIG *ref_frame,
176*77c1e3ccSAndroid Build Coastguard Worker                                   const double x_coord, const double y_coord,
177*77c1e3ccSAndroid Build Coastguard Worker                                   const int window_size, const int bit_depth,
178*77c1e3ccSAndroid Build Coastguard Worker                                   double *ix, double *iy, double *it,
179*77c1e3ccSAndroid Build Coastguard Worker                                   LOCALMV *mv) {
180*77c1e3ccSAndroid Build Coastguard Worker   const double left = x_coord - window_size / 2.0;
181*77c1e3ccSAndroid Build Coastguard Worker   const double top = y_coord - window_size / 2.0;
182*77c1e3ccSAndroid Build Coastguard Worker   // gradient operators need pixel before and after (start at 1)
183*77c1e3ccSAndroid Build Coastguard Worker   const double x_start = AOMMAX(1, left);
184*77c1e3ccSAndroid Build Coastguard Worker   const double y_start = AOMMAX(1, top);
185*77c1e3ccSAndroid Build Coastguard Worker   const int frame_height = frame->y_crop_height;
186*77c1e3ccSAndroid Build Coastguard Worker   const int frame_width = frame->y_crop_width;
187*77c1e3ccSAndroid Build Coastguard Worker   double deriv_x;
188*77c1e3ccSAndroid Build Coastguard Worker   double deriv_y;
189*77c1e3ccSAndroid Build Coastguard Worker   double deriv_t;
190*77c1e3ccSAndroid Build Coastguard Worker 
191*77c1e3ccSAndroid Build Coastguard Worker   const double x_end = AOMMIN(x_coord + window_size / 2.0, frame_width - 2);
192*77c1e3ccSAndroid Build Coastguard Worker   const double y_end = AOMMIN(y_coord + window_size / 2.0, frame_height - 2);
193*77c1e3ccSAndroid Build Coastguard Worker   const int xs = (int)AOMMAX(1, x_start - 1);
194*77c1e3ccSAndroid Build Coastguard Worker   const int ys = (int)AOMMAX(1, y_start - 1);
195*77c1e3ccSAndroid Build Coastguard Worker   const int xe = (int)AOMMIN(x_end + 2, frame_width - 2);
196*77c1e3ccSAndroid Build Coastguard Worker   const int ye = (int)AOMMIN(y_end + 2, frame_height - 2);
197*77c1e3ccSAndroid Build Coastguard Worker   // with normalization, gradients may be double values
198*77c1e3ccSAndroid Build Coastguard Worker   double *fullpel_dx = aom_malloc((ye - ys) * (xe - xs) * sizeof(deriv_x));
199*77c1e3ccSAndroid Build Coastguard Worker   double *fullpel_dy = aom_malloc((ye - ys) * (xe - xs) * sizeof(deriv_y));
200*77c1e3ccSAndroid Build Coastguard Worker   if (!fullpel_dx || !fullpel_dy) {
201*77c1e3ccSAndroid Build Coastguard Worker     aom_free(fullpel_dx);
202*77c1e3ccSAndroid Build Coastguard Worker     aom_free(fullpel_dy);
203*77c1e3ccSAndroid Build Coastguard Worker     return;
204*77c1e3ccSAndroid Build Coastguard Worker   }
205*77c1e3ccSAndroid Build Coastguard Worker 
206*77c1e3ccSAndroid Build Coastguard Worker   // TODO(any): This could be more efficient in the case that x_coord
207*77c1e3ccSAndroid Build Coastguard Worker   // and y_coord are integers.. but it may look more messy.
208*77c1e3ccSAndroid Build Coastguard Worker 
209*77c1e3ccSAndroid Build Coastguard Worker   // calculate spatial gradients at full pixel locations
210*77c1e3ccSAndroid Build Coastguard Worker   for (int j = ys; j < ye; j++) {
211*77c1e3ccSAndroid Build Coastguard Worker     for (int i = xs; i < xe; i++) {
212*77c1e3ccSAndroid Build Coastguard Worker       spatial_gradient(frame, i, j, 0, &deriv_x);
213*77c1e3ccSAndroid Build Coastguard Worker       spatial_gradient(frame, i, j, 1, &deriv_y);
214*77c1e3ccSAndroid Build Coastguard Worker       int idx = (j - ys) * (xe - xs) + (i - xs);
215*77c1e3ccSAndroid Build Coastguard Worker       fullpel_dx[idx] = deriv_x;
216*77c1e3ccSAndroid Build Coastguard Worker       fullpel_dy[idx] = deriv_y;
217*77c1e3ccSAndroid Build Coastguard Worker     }
218*77c1e3ccSAndroid Build Coastguard Worker   }
219*77c1e3ccSAndroid Build Coastguard Worker   // compute numerical differentiation for every pixel in window
220*77c1e3ccSAndroid Build Coastguard Worker   // (this potentially includes subpixels)
221*77c1e3ccSAndroid Build Coastguard Worker   for (double j = y_start; j < y_end; j++) {
222*77c1e3ccSAndroid Build Coastguard Worker     for (double i = x_start; i < x_end; i++) {
223*77c1e3ccSAndroid Build Coastguard Worker       temporal_gradient(frame, ref_frame, i, j, bit_depth, &deriv_t, mv);
224*77c1e3ccSAndroid Build Coastguard Worker       gradient_interp(fullpel_dx, i - xs, j - ys, xe - xs, ye - ys, &deriv_x);
225*77c1e3ccSAndroid Build Coastguard Worker       gradient_interp(fullpel_dy, i - xs, j - ys, xe - xs, ye - ys, &deriv_y);
226*77c1e3ccSAndroid Build Coastguard Worker       int idx = (int)(j - top) * window_size + (int)(i - left);
227*77c1e3ccSAndroid Build Coastguard Worker       ix[idx] = deriv_x;
228*77c1e3ccSAndroid Build Coastguard Worker       iy[idx] = deriv_y;
229*77c1e3ccSAndroid Build Coastguard Worker       it[idx] = deriv_t;
230*77c1e3ccSAndroid Build Coastguard Worker     }
231*77c1e3ccSAndroid Build Coastguard Worker   }
232*77c1e3ccSAndroid Build Coastguard Worker   // TODO(any): to avoid setting deriv arrays to zero for every iteration,
233*77c1e3ccSAndroid Build Coastguard Worker   // could instead pass these two values back through function call
234*77c1e3ccSAndroid Build Coastguard Worker   // int first_idx = (int)(y_start - top) * window_size + (int)(x_start - left);
235*77c1e3ccSAndroid Build Coastguard Worker   // int width = window_size - ((int)(x_start - left) + (int)(left + window_size
236*77c1e3ccSAndroid Build Coastguard Worker   // - x_end));
237*77c1e3ccSAndroid Build Coastguard Worker 
238*77c1e3ccSAndroid Build Coastguard Worker   aom_free(fullpel_dx);
239*77c1e3ccSAndroid Build Coastguard Worker   aom_free(fullpel_dy);
240*77c1e3ccSAndroid Build Coastguard Worker }
241*77c1e3ccSAndroid Build Coastguard Worker 
242*77c1e3ccSAndroid Build Coastguard Worker // To compute eigenvalues of 2x2 matrix: Solve for lambda where
243*77c1e3ccSAndroid Build Coastguard Worker // Determinant(matrix - lambda*identity) == 0
eigenvalues_2x2(const double * matrix,double * eig)244*77c1e3ccSAndroid Build Coastguard Worker static void eigenvalues_2x2(const double *matrix, double *eig) {
245*77c1e3ccSAndroid Build Coastguard Worker   const double a = 1;
246*77c1e3ccSAndroid Build Coastguard Worker   const double b = -1 * matrix[0] - matrix[3];
247*77c1e3ccSAndroid Build Coastguard Worker   const double c = -1 * matrix[1] * matrix[2] + matrix[0] * matrix[3];
248*77c1e3ccSAndroid Build Coastguard Worker   // quadratic formula
249*77c1e3ccSAndroid Build Coastguard Worker   const double discriminant = b * b - 4 * a * c;
250*77c1e3ccSAndroid Build Coastguard Worker   eig[0] = (-b - sqrt(discriminant)) / (2.0 * a);
251*77c1e3ccSAndroid Build Coastguard Worker   eig[1] = (-b + sqrt(discriminant)) / (2.0 * a);
252*77c1e3ccSAndroid Build Coastguard Worker   // double check that eigenvalues are ordered by magnitude
253*77c1e3ccSAndroid Build Coastguard Worker   if (fabs(eig[0]) > fabs(eig[1])) {
254*77c1e3ccSAndroid Build Coastguard Worker     double tmp = eig[0];
255*77c1e3ccSAndroid Build Coastguard Worker     eig[0] = eig[1];
256*77c1e3ccSAndroid Build Coastguard Worker     eig[1] = tmp;
257*77c1e3ccSAndroid Build Coastguard Worker   }
258*77c1e3ccSAndroid Build Coastguard Worker }
259*77c1e3ccSAndroid Build Coastguard Worker 
260*77c1e3ccSAndroid Build Coastguard Worker // Shi-Tomasi corner detection criteria
corner_score(const YV12_BUFFER_CONFIG * frame_to_filter,const YV12_BUFFER_CONFIG * ref_frame,const int x,const int y,double * i_x,double * i_y,double * i_t,const int n,const int bit_depth)261*77c1e3ccSAndroid Build Coastguard Worker static double corner_score(const YV12_BUFFER_CONFIG *frame_to_filter,
262*77c1e3ccSAndroid Build Coastguard Worker                            const YV12_BUFFER_CONFIG *ref_frame, const int x,
263*77c1e3ccSAndroid Build Coastguard Worker                            const int y, double *i_x, double *i_y, double *i_t,
264*77c1e3ccSAndroid Build Coastguard Worker                            const int n, const int bit_depth) {
265*77c1e3ccSAndroid Build Coastguard Worker   double eig[2];
266*77c1e3ccSAndroid Build Coastguard Worker   LOCALMV mv = { .row = 0, .col = 0 };
267*77c1e3ccSAndroid Build Coastguard Worker   // TODO(any): technically, ref_frame and i_t are not used by corner score
268*77c1e3ccSAndroid Build Coastguard Worker   // so these could be replaced by dummy variables,
269*77c1e3ccSAndroid Build Coastguard Worker   // or change this to spatial gradient function over window only
270*77c1e3ccSAndroid Build Coastguard Worker   gradients_over_window(frame_to_filter, ref_frame, x, y, n, bit_depth, i_x,
271*77c1e3ccSAndroid Build Coastguard Worker                         i_y, i_t, &mv);
272*77c1e3ccSAndroid Build Coastguard Worker   double Mres1[1] = { 0 }, Mres2[1] = { 0 }, Mres3[1] = { 0 };
273*77c1e3ccSAndroid Build Coastguard Worker   multiply_mat(i_x, i_x, Mres1, 1, n * n, 1);
274*77c1e3ccSAndroid Build Coastguard Worker   multiply_mat(i_x, i_y, Mres2, 1, n * n, 1);
275*77c1e3ccSAndroid Build Coastguard Worker   multiply_mat(i_y, i_y, Mres3, 1, n * n, 1);
276*77c1e3ccSAndroid Build Coastguard Worker   double M[4] = { Mres1[0], Mres2[0], Mres2[0], Mres3[0] };
277*77c1e3ccSAndroid Build Coastguard Worker   eigenvalues_2x2(M, eig);
278*77c1e3ccSAndroid Build Coastguard Worker   return fabs(eig[0]);
279*77c1e3ccSAndroid Build Coastguard Worker }
280*77c1e3ccSAndroid Build Coastguard Worker 
281*77c1e3ccSAndroid Build Coastguard Worker // Finds corners in frame_to_filter
282*77c1e3ccSAndroid Build Coastguard Worker // For less strict requirements (i.e. more corners), decrease threshold
detect_corners(const YV12_BUFFER_CONFIG * frame_to_filter,const YV12_BUFFER_CONFIG * ref_frame,const int maxcorners,int * ref_corners,const int bit_depth)283*77c1e3ccSAndroid Build Coastguard Worker static int detect_corners(const YV12_BUFFER_CONFIG *frame_to_filter,
284*77c1e3ccSAndroid Build Coastguard Worker                           const YV12_BUFFER_CONFIG *ref_frame,
285*77c1e3ccSAndroid Build Coastguard Worker                           const int maxcorners, int *ref_corners,
286*77c1e3ccSAndroid Build Coastguard Worker                           const int bit_depth) {
287*77c1e3ccSAndroid Build Coastguard Worker   const int frame_height = frame_to_filter->y_crop_height;
288*77c1e3ccSAndroid Build Coastguard Worker   const int frame_width = frame_to_filter->y_crop_width;
289*77c1e3ccSAndroid Build Coastguard Worker   // TODO(any): currently if maxcorners is decreased, then it only means
290*77c1e3ccSAndroid Build Coastguard Worker   // corners will be omited from bottom-right of image. if maxcorners
291*77c1e3ccSAndroid Build Coastguard Worker   // is actually used, then this algorithm would need to re-iterate
292*77c1e3ccSAndroid Build Coastguard Worker   // and choose threshold based on that
293*77c1e3ccSAndroid Build Coastguard Worker   assert(maxcorners == frame_height * frame_width);
294*77c1e3ccSAndroid Build Coastguard Worker   int countcorners = 0;
295*77c1e3ccSAndroid Build Coastguard Worker   const double threshold = 0.1;
296*77c1e3ccSAndroid Build Coastguard Worker   double score;
297*77c1e3ccSAndroid Build Coastguard Worker   const int n = 3;
298*77c1e3ccSAndroid Build Coastguard Worker   double i_x[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
299*77c1e3ccSAndroid Build Coastguard Worker   double i_y[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
300*77c1e3ccSAndroid Build Coastguard Worker   double i_t[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
301*77c1e3ccSAndroid Build Coastguard Worker   const int fromedge = n;
302*77c1e3ccSAndroid Build Coastguard Worker   double max_score = corner_score(frame_to_filter, ref_frame, fromedge,
303*77c1e3ccSAndroid Build Coastguard Worker                                   fromedge, i_x, i_y, i_t, n, bit_depth);
304*77c1e3ccSAndroid Build Coastguard Worker   // rough estimate of max corner score in image
305*77c1e3ccSAndroid Build Coastguard Worker   for (int x = fromedge; x < frame_width - fromedge; x += 1) {
306*77c1e3ccSAndroid Build Coastguard Worker     for (int y = fromedge; y < frame_height - fromedge; y += frame_height / 5) {
307*77c1e3ccSAndroid Build Coastguard Worker       for (int i = 0; i < n * n; i++) {
308*77c1e3ccSAndroid Build Coastguard Worker         i_x[i] = 0;
309*77c1e3ccSAndroid Build Coastguard Worker         i_y[i] = 0;
310*77c1e3ccSAndroid Build Coastguard Worker         i_t[i] = 0;
311*77c1e3ccSAndroid Build Coastguard Worker       }
312*77c1e3ccSAndroid Build Coastguard Worker       score = corner_score(frame_to_filter, ref_frame, x, y, i_x, i_y, i_t, n,
313*77c1e3ccSAndroid Build Coastguard Worker                            bit_depth);
314*77c1e3ccSAndroid Build Coastguard Worker       if (score > max_score) {
315*77c1e3ccSAndroid Build Coastguard Worker         max_score = score;
316*77c1e3ccSAndroid Build Coastguard Worker       }
317*77c1e3ccSAndroid Build Coastguard Worker     }
318*77c1e3ccSAndroid Build Coastguard Worker   }
319*77c1e3ccSAndroid Build Coastguard Worker   // score all the points and choose corners over threshold
320*77c1e3ccSAndroid Build Coastguard Worker   for (int x = fromedge; x < frame_width - fromedge; x += 1) {
321*77c1e3ccSAndroid Build Coastguard Worker     for (int y = fromedge;
322*77c1e3ccSAndroid Build Coastguard Worker          (y < frame_height - fromedge) && countcorners < maxcorners; y += 1) {
323*77c1e3ccSAndroid Build Coastguard Worker       for (int i = 0; i < n * n; i++) {
324*77c1e3ccSAndroid Build Coastguard Worker         i_x[i] = 0;
325*77c1e3ccSAndroid Build Coastguard Worker         i_y[i] = 0;
326*77c1e3ccSAndroid Build Coastguard Worker         i_t[i] = 0;
327*77c1e3ccSAndroid Build Coastguard Worker       }
328*77c1e3ccSAndroid Build Coastguard Worker       score = corner_score(frame_to_filter, ref_frame, x, y, i_x, i_y, i_t, n,
329*77c1e3ccSAndroid Build Coastguard Worker                            bit_depth);
330*77c1e3ccSAndroid Build Coastguard Worker       if (score > threshold * max_score) {
331*77c1e3ccSAndroid Build Coastguard Worker         ref_corners[countcorners * 2] = x;
332*77c1e3ccSAndroid Build Coastguard Worker         ref_corners[countcorners * 2 + 1] = y;
333*77c1e3ccSAndroid Build Coastguard Worker         countcorners++;
334*77c1e3ccSAndroid Build Coastguard Worker       }
335*77c1e3ccSAndroid Build Coastguard Worker     }
336*77c1e3ccSAndroid Build Coastguard Worker   }
337*77c1e3ccSAndroid Build Coastguard Worker   return countcorners;
338*77c1e3ccSAndroid Build Coastguard Worker }
339*77c1e3ccSAndroid Build Coastguard Worker 
340*77c1e3ccSAndroid Build Coastguard Worker // weights is an nxn matrix. weights is filled with a gaussian function,
341*77c1e3ccSAndroid Build Coastguard Worker // with independent variable: distance from the center point.
gaussian(const double sigma,const int n,const int normalize,double * weights)342*77c1e3ccSAndroid Build Coastguard Worker static void gaussian(const double sigma, const int n, const int normalize,
343*77c1e3ccSAndroid Build Coastguard Worker                      double *weights) {
344*77c1e3ccSAndroid Build Coastguard Worker   double total_weight = 0;
345*77c1e3ccSAndroid Build Coastguard Worker   for (int j = 0; j < n; j++) {
346*77c1e3ccSAndroid Build Coastguard Worker     for (int i = 0; i < n; i++) {
347*77c1e3ccSAndroid Build Coastguard Worker       double distance = sqrt(pow(n / 2 - i, 2) + pow(n / 2 - j, 2));
348*77c1e3ccSAndroid Build Coastguard Worker       double weight = exp(-0.5 * pow(distance / sigma, 2));
349*77c1e3ccSAndroid Build Coastguard Worker       weights[j * n + i] = weight;
350*77c1e3ccSAndroid Build Coastguard Worker       total_weight += weight;
351*77c1e3ccSAndroid Build Coastguard Worker     }
352*77c1e3ccSAndroid Build Coastguard Worker   }
353*77c1e3ccSAndroid Build Coastguard Worker   if (normalize == 1) {
354*77c1e3ccSAndroid Build Coastguard Worker     for (int j = 0; j < n; j++) {
355*77c1e3ccSAndroid Build Coastguard Worker       weights[j] = weights[j] / total_weight;
356*77c1e3ccSAndroid Build Coastguard Worker     }
357*77c1e3ccSAndroid Build Coastguard Worker   }
358*77c1e3ccSAndroid Build Coastguard Worker }
359*77c1e3ccSAndroid Build Coastguard Worker 
convolve(const double * filter,const int * img,const int size)360*77c1e3ccSAndroid Build Coastguard Worker static double convolve(const double *filter, const int *img, const int size) {
361*77c1e3ccSAndroid Build Coastguard Worker   double result = 0;
362*77c1e3ccSAndroid Build Coastguard Worker   for (int i = 0; i < size; i++) {
363*77c1e3ccSAndroid Build Coastguard Worker     result += filter[i] * img[i];
364*77c1e3ccSAndroid Build Coastguard Worker   }
365*77c1e3ccSAndroid Build Coastguard Worker   return result;
366*77c1e3ccSAndroid Build Coastguard Worker }
367*77c1e3ccSAndroid Build Coastguard Worker 
368*77c1e3ccSAndroid Build Coastguard Worker // Applies a Gaussian low-pass smoothing filter to produce
369*77c1e3ccSAndroid Build Coastguard Worker // a corresponding lower resolution image with halved dimensions
reduce(uint8_t * img,int height,int width,int stride,uint8_t * reduced_img)370*77c1e3ccSAndroid Build Coastguard Worker static void reduce(uint8_t *img, int height, int width, int stride,
371*77c1e3ccSAndroid Build Coastguard Worker                    uint8_t *reduced_img) {
372*77c1e3ccSAndroid Build Coastguard Worker   const int new_width = width / 2;
373*77c1e3ccSAndroid Build Coastguard Worker   const int window_size = 5;
374*77c1e3ccSAndroid Build Coastguard Worker   const double gaussian_filter[25] = {
375*77c1e3ccSAndroid Build Coastguard Worker     1. / 256, 1.0 / 64, 3. / 128, 1. / 64,  1. / 256, 1. / 64, 1. / 16,
376*77c1e3ccSAndroid Build Coastguard Worker     3. / 32,  1. / 16,  1. / 64,  3. / 128, 3. / 32,  9. / 64, 3. / 32,
377*77c1e3ccSAndroid Build Coastguard Worker     3. / 128, 1. / 64,  1. / 16,  3. / 32,  1. / 16,  1. / 64, 1. / 256,
378*77c1e3ccSAndroid Build Coastguard Worker     1. / 64,  3. / 128, 1. / 64,  1. / 256
379*77c1e3ccSAndroid Build Coastguard Worker   };
380*77c1e3ccSAndroid Build Coastguard Worker   // filter is 5x5 so need prev and forward 2 pixels
381*77c1e3ccSAndroid Build Coastguard Worker   int img_section[25];
382*77c1e3ccSAndroid Build Coastguard Worker   for (int y = 0; y < height - 1; y += 2) {
383*77c1e3ccSAndroid Build Coastguard Worker     for (int x = 0; x < width - 1; x += 2) {
384*77c1e3ccSAndroid Build Coastguard Worker       int i = 0;
385*77c1e3ccSAndroid Build Coastguard Worker       for (int yy = y - window_size / 2; yy <= y + window_size / 2; yy++) {
386*77c1e3ccSAndroid Build Coastguard Worker         for (int xx = x - window_size / 2; xx <= x + window_size / 2; xx++) {
387*77c1e3ccSAndroid Build Coastguard Worker           int yvalue = yy;
388*77c1e3ccSAndroid Build Coastguard Worker           int xvalue = xx;
389*77c1e3ccSAndroid Build Coastguard Worker           // copied pixels outside the boundary
390*77c1e3ccSAndroid Build Coastguard Worker           if (yvalue < 0) yvalue = 0;
391*77c1e3ccSAndroid Build Coastguard Worker           if (xvalue < 0) xvalue = 0;
392*77c1e3ccSAndroid Build Coastguard Worker           if (yvalue >= height) yvalue = height - 1;
393*77c1e3ccSAndroid Build Coastguard Worker           if (xvalue >= width) xvalue = width - 1;
394*77c1e3ccSAndroid Build Coastguard Worker           img_section[i++] = img[yvalue * stride + xvalue];
395*77c1e3ccSAndroid Build Coastguard Worker         }
396*77c1e3ccSAndroid Build Coastguard Worker       }
397*77c1e3ccSAndroid Build Coastguard Worker       reduced_img[(y / 2) * new_width + (x / 2)] = (uint8_t)convolve(
398*77c1e3ccSAndroid Build Coastguard Worker           gaussian_filter, img_section, window_size * window_size);
399*77c1e3ccSAndroid Build Coastguard Worker     }
400*77c1e3ccSAndroid Build Coastguard Worker   }
401*77c1e3ccSAndroid Build Coastguard Worker }
402*77c1e3ccSAndroid Build Coastguard Worker 
cmpfunc(const void * a,const void * b)403*77c1e3ccSAndroid Build Coastguard Worker static int cmpfunc(const void *a, const void *b) {
404*77c1e3ccSAndroid Build Coastguard Worker   return (*(int *)a - *(int *)b);
405*77c1e3ccSAndroid Build Coastguard Worker }
filter_mvs(const MV_FILTER_TYPE mv_filter,const int frame_height,const int frame_width,LOCALMV * localmvs,MV * mvs)406*77c1e3ccSAndroid Build Coastguard Worker static void filter_mvs(const MV_FILTER_TYPE mv_filter, const int frame_height,
407*77c1e3ccSAndroid Build Coastguard Worker                        const int frame_width, LOCALMV *localmvs, MV *mvs) {
408*77c1e3ccSAndroid Build Coastguard Worker   const int n = 5;  // window size
409*77c1e3ccSAndroid Build Coastguard Worker   // for smoothing filter
410*77c1e3ccSAndroid Build Coastguard Worker   const double gaussian_filter[25] = {
411*77c1e3ccSAndroid Build Coastguard Worker     1. / 256, 1. / 64,  3. / 128, 1. / 64,  1. / 256, 1. / 64, 1. / 16,
412*77c1e3ccSAndroid Build Coastguard Worker     3. / 32,  1. / 16,  1. / 64,  3. / 128, 3. / 32,  9. / 64, 3. / 32,
413*77c1e3ccSAndroid Build Coastguard Worker     3. / 128, 1. / 64,  1. / 16,  3. / 32,  1. / 16,  1. / 64, 1. / 256,
414*77c1e3ccSAndroid Build Coastguard Worker     1. / 64,  3. / 128, 1. / 64,  1. / 256
415*77c1e3ccSAndroid Build Coastguard Worker   };
416*77c1e3ccSAndroid Build Coastguard Worker   // for median filter
417*77c1e3ccSAndroid Build Coastguard Worker   int mvrows[25];
418*77c1e3ccSAndroid Build Coastguard Worker   int mvcols[25];
419*77c1e3ccSAndroid Build Coastguard Worker   if (mv_filter != MV_FILTER_NONE) {
420*77c1e3ccSAndroid Build Coastguard Worker     for (int y = 0; y < frame_height; y++) {
421*77c1e3ccSAndroid Build Coastguard Worker       for (int x = 0; x < frame_width; x++) {
422*77c1e3ccSAndroid Build Coastguard Worker         int center_idx = y * frame_width + x;
423*77c1e3ccSAndroid Build Coastguard Worker         int i = 0;
424*77c1e3ccSAndroid Build Coastguard Worker         double filtered_row = 0;
425*77c1e3ccSAndroid Build Coastguard Worker         double filtered_col = 0;
426*77c1e3ccSAndroid Build Coastguard Worker         for (int yy = y - n / 2; yy <= y + n / 2; yy++) {
427*77c1e3ccSAndroid Build Coastguard Worker           for (int xx = x - n / 2; xx <= x + n / 2; xx++) {
428*77c1e3ccSAndroid Build Coastguard Worker             int yvalue = yy;
429*77c1e3ccSAndroid Build Coastguard Worker             int xvalue = xx;
430*77c1e3ccSAndroid Build Coastguard Worker             // copied pixels outside the boundary
431*77c1e3ccSAndroid Build Coastguard Worker             if (yvalue < 0) yvalue = 0;
432*77c1e3ccSAndroid Build Coastguard Worker             if (xvalue < 0) xvalue = 0;
433*77c1e3ccSAndroid Build Coastguard Worker             if (yvalue >= frame_height) yvalue = frame_height - 1;
434*77c1e3ccSAndroid Build Coastguard Worker             if (xvalue >= frame_width) xvalue = frame_width - 1;
435*77c1e3ccSAndroid Build Coastguard Worker             int index = yvalue * frame_width + xvalue;
436*77c1e3ccSAndroid Build Coastguard Worker             if (mv_filter == MV_FILTER_SMOOTH) {
437*77c1e3ccSAndroid Build Coastguard Worker               filtered_row += mvs[index].row * gaussian_filter[i];
438*77c1e3ccSAndroid Build Coastguard Worker               filtered_col += mvs[index].col * gaussian_filter[i];
439*77c1e3ccSAndroid Build Coastguard Worker             } else if (mv_filter == MV_FILTER_MEDIAN) {
440*77c1e3ccSAndroid Build Coastguard Worker               mvrows[i] = mvs[index].row;
441*77c1e3ccSAndroid Build Coastguard Worker               mvcols[i] = mvs[index].col;
442*77c1e3ccSAndroid Build Coastguard Worker             }
443*77c1e3ccSAndroid Build Coastguard Worker             i++;
444*77c1e3ccSAndroid Build Coastguard Worker           }
445*77c1e3ccSAndroid Build Coastguard Worker         }
446*77c1e3ccSAndroid Build Coastguard Worker 
447*77c1e3ccSAndroid Build Coastguard Worker         MV mv = mvs[center_idx];
448*77c1e3ccSAndroid Build Coastguard Worker         if (mv_filter == MV_FILTER_SMOOTH) {
449*77c1e3ccSAndroid Build Coastguard Worker           mv.row = (int16_t)filtered_row;
450*77c1e3ccSAndroid Build Coastguard Worker           mv.col = (int16_t)filtered_col;
451*77c1e3ccSAndroid Build Coastguard Worker         } else if (mv_filter == MV_FILTER_MEDIAN) {
452*77c1e3ccSAndroid Build Coastguard Worker           qsort(mvrows, 25, sizeof(mv.row), cmpfunc);
453*77c1e3ccSAndroid Build Coastguard Worker           qsort(mvcols, 25, sizeof(mv.col), cmpfunc);
454*77c1e3ccSAndroid Build Coastguard Worker           mv.row = mvrows[25 / 2];
455*77c1e3ccSAndroid Build Coastguard Worker           mv.col = mvcols[25 / 2];
456*77c1e3ccSAndroid Build Coastguard Worker         }
457*77c1e3ccSAndroid Build Coastguard Worker         LOCALMV localmv = { .row = ((double)mv.row) / 8,
458*77c1e3ccSAndroid Build Coastguard Worker                             .col = ((double)mv.row) / 8 };
459*77c1e3ccSAndroid Build Coastguard Worker         localmvs[y * frame_width + x] = localmv;
460*77c1e3ccSAndroid Build Coastguard Worker         // if mvs array is immediately updated here, then the result may
461*77c1e3ccSAndroid Build Coastguard Worker         // propagate to other pixels.
462*77c1e3ccSAndroid Build Coastguard Worker       }
463*77c1e3ccSAndroid Build Coastguard Worker     }
464*77c1e3ccSAndroid Build Coastguard Worker     for (int i = 0; i < frame_height * frame_width; i++) {
465*77c1e3ccSAndroid Build Coastguard Worker       MV mv = { .row = (int16_t)round(8 * localmvs[i].row),
466*77c1e3ccSAndroid Build Coastguard Worker                 .col = (int16_t)round(8 * localmvs[i].col) };
467*77c1e3ccSAndroid Build Coastguard Worker       mvs[i] = mv;
468*77c1e3ccSAndroid Build Coastguard Worker     }
469*77c1e3ccSAndroid Build Coastguard Worker   }
470*77c1e3ccSAndroid Build Coastguard Worker }
471*77c1e3ccSAndroid Build Coastguard Worker 
472*77c1e3ccSAndroid Build Coastguard Worker // Computes optical flow at a single pyramid level,
473*77c1e3ccSAndroid Build Coastguard Worker // using Lucas-Kanade algorithm.
474*77c1e3ccSAndroid Build Coastguard Worker // Modifies mvs array.
lucas_kanade(const YV12_BUFFER_CONFIG * from_frame,const YV12_BUFFER_CONFIG * to_frame,const int level,const LK_PARAMS * lk_params,const int num_ref_corners,int * ref_corners,const int mv_stride,const int bit_depth,LOCALMV * mvs)475*77c1e3ccSAndroid Build Coastguard Worker static void lucas_kanade(const YV12_BUFFER_CONFIG *from_frame,
476*77c1e3ccSAndroid Build Coastguard Worker                          const YV12_BUFFER_CONFIG *to_frame, const int level,
477*77c1e3ccSAndroid Build Coastguard Worker                          const LK_PARAMS *lk_params, const int num_ref_corners,
478*77c1e3ccSAndroid Build Coastguard Worker                          int *ref_corners, const int mv_stride,
479*77c1e3ccSAndroid Build Coastguard Worker                          const int bit_depth, LOCALMV *mvs) {
480*77c1e3ccSAndroid Build Coastguard Worker   assert(lk_params->window_size > 0 && lk_params->window_size % 2 == 0);
481*77c1e3ccSAndroid Build Coastguard Worker   const int n = lk_params->window_size;
482*77c1e3ccSAndroid Build Coastguard Worker   // algorithm is sensitive to window size
483*77c1e3ccSAndroid Build Coastguard Worker   double *i_x = (double *)aom_malloc(n * n * sizeof(*i_x));
484*77c1e3ccSAndroid Build Coastguard Worker   double *i_y = (double *)aom_malloc(n * n * sizeof(*i_y));
485*77c1e3ccSAndroid Build Coastguard Worker   double *i_t = (double *)aom_malloc(n * n * sizeof(*i_t));
486*77c1e3ccSAndroid Build Coastguard Worker   double *weights = (double *)aom_malloc(n * n * sizeof(*weights));
487*77c1e3ccSAndroid Build Coastguard Worker   if (!i_x || !i_y || !i_t || !weights) goto free_lk_buf;
488*77c1e3ccSAndroid Build Coastguard Worker 
489*77c1e3ccSAndroid Build Coastguard Worker   const int expand_multiplier = (int)pow(2, level);
490*77c1e3ccSAndroid Build Coastguard Worker   double sigma = 0.2 * n;
491*77c1e3ccSAndroid Build Coastguard Worker   // normalizing doesn't really affect anything since it's applied
492*77c1e3ccSAndroid Build Coastguard Worker   // to every component of M and b
493*77c1e3ccSAndroid Build Coastguard Worker   gaussian(sigma, n, 0, weights);
494*77c1e3ccSAndroid Build Coastguard Worker   for (int i = 0; i < num_ref_corners; i++) {
495*77c1e3ccSAndroid Build Coastguard Worker     const double x_coord = 1.0 * ref_corners[i * 2] / expand_multiplier;
496*77c1e3ccSAndroid Build Coastguard Worker     const double y_coord = 1.0 * ref_corners[i * 2 + 1] / expand_multiplier;
497*77c1e3ccSAndroid Build Coastguard Worker     int highres_x = ref_corners[i * 2];
498*77c1e3ccSAndroid Build Coastguard Worker     int highres_y = ref_corners[i * 2 + 1];
499*77c1e3ccSAndroid Build Coastguard Worker     int mv_idx = highres_y * (mv_stride) + highres_x;
500*77c1e3ccSAndroid Build Coastguard Worker     LOCALMV mv_old = mvs[mv_idx];
501*77c1e3ccSAndroid Build Coastguard Worker     mv_old.row = mv_old.row / expand_multiplier;
502*77c1e3ccSAndroid Build Coastguard Worker     mv_old.col = mv_old.col / expand_multiplier;
503*77c1e3ccSAndroid Build Coastguard Worker     // using this instead of memset, since it's not completely
504*77c1e3ccSAndroid Build Coastguard Worker     // clear if zero memset works on double arrays
505*77c1e3ccSAndroid Build Coastguard Worker     for (int j = 0; j < n * n; j++) {
506*77c1e3ccSAndroid Build Coastguard Worker       i_x[j] = 0;
507*77c1e3ccSAndroid Build Coastguard Worker       i_y[j] = 0;
508*77c1e3ccSAndroid Build Coastguard Worker       i_t[j] = 0;
509*77c1e3ccSAndroid Build Coastguard Worker     }
510*77c1e3ccSAndroid Build Coastguard Worker     gradients_over_window(from_frame, to_frame, x_coord, y_coord, n, bit_depth,
511*77c1e3ccSAndroid Build Coastguard Worker                           i_x, i_y, i_t, &mv_old);
512*77c1e3ccSAndroid Build Coastguard Worker     double Mres1[1] = { 0 }, Mres2[1] = { 0 }, Mres3[1] = { 0 };
513*77c1e3ccSAndroid Build Coastguard Worker     double bres1[1] = { 0 }, bres2[1] = { 0 };
514*77c1e3ccSAndroid Build Coastguard Worker     for (int j = 0; j < n * n; j++) {
515*77c1e3ccSAndroid Build Coastguard Worker       Mres1[0] += weights[j] * i_x[j] * i_x[j];
516*77c1e3ccSAndroid Build Coastguard Worker       Mres2[0] += weights[j] * i_x[j] * i_y[j];
517*77c1e3ccSAndroid Build Coastguard Worker       Mres3[0] += weights[j] * i_y[j] * i_y[j];
518*77c1e3ccSAndroid Build Coastguard Worker       bres1[0] += weights[j] * i_x[j] * i_t[j];
519*77c1e3ccSAndroid Build Coastguard Worker       bres2[0] += weights[j] * i_y[j] * i_t[j];
520*77c1e3ccSAndroid Build Coastguard Worker     }
521*77c1e3ccSAndroid Build Coastguard Worker     double M[4] = { Mres1[0], Mres2[0], Mres2[0], Mres3[0] };
522*77c1e3ccSAndroid Build Coastguard Worker     double b[2] = { -1 * bres1[0], -1 * bres2[0] };
523*77c1e3ccSAndroid Build Coastguard Worker     double eig[2] = { 1, 1 };
524*77c1e3ccSAndroid Build Coastguard Worker     eigenvalues_2x2(M, eig);
525*77c1e3ccSAndroid Build Coastguard Worker     double threshold = 0.1;
526*77c1e3ccSAndroid Build Coastguard Worker     if (fabs(eig[0]) > threshold) {
527*77c1e3ccSAndroid Build Coastguard Worker       // if M is not invertible, then displacement
528*77c1e3ccSAndroid Build Coastguard Worker       // will default to zeros
529*77c1e3ccSAndroid Build Coastguard Worker       double u[2] = { 0, 0 };
530*77c1e3ccSAndroid Build Coastguard Worker       linsolve(2, M, 2, b, u);
531*77c1e3ccSAndroid Build Coastguard Worker       int mult = 1;
532*77c1e3ccSAndroid Build Coastguard Worker       if (level != 0)
533*77c1e3ccSAndroid Build Coastguard Worker         mult = expand_multiplier;  // mv doubles when resolution doubles
534*77c1e3ccSAndroid Build Coastguard Worker       LOCALMV mv = { .row = (mult * (u[0] + mv_old.row)),
535*77c1e3ccSAndroid Build Coastguard Worker                      .col = (mult * (u[1] + mv_old.col)) };
536*77c1e3ccSAndroid Build Coastguard Worker       mvs[mv_idx] = mv;
537*77c1e3ccSAndroid Build Coastguard Worker       mvs[mv_idx] = mv;
538*77c1e3ccSAndroid Build Coastguard Worker     }
539*77c1e3ccSAndroid Build Coastguard Worker   }
540*77c1e3ccSAndroid Build Coastguard Worker free_lk_buf:
541*77c1e3ccSAndroid Build Coastguard Worker   aom_free(weights);
542*77c1e3ccSAndroid Build Coastguard Worker   aom_free(i_t);
543*77c1e3ccSAndroid Build Coastguard Worker   aom_free(i_x);
544*77c1e3ccSAndroid Build Coastguard Worker   aom_free(i_y);
545*77c1e3ccSAndroid Build Coastguard Worker }
546*77c1e3ccSAndroid Build Coastguard Worker 
547*77c1e3ccSAndroid Build Coastguard Worker // Warp the src_frame to warper_frame according to mvs.
548*77c1e3ccSAndroid Build Coastguard Worker // mvs point to src_frame
warp_back_frame(YV12_BUFFER_CONFIG * warped_frame,const YV12_BUFFER_CONFIG * src_frame,const LOCALMV * mvs,int mv_stride)549*77c1e3ccSAndroid Build Coastguard Worker static void warp_back_frame(YV12_BUFFER_CONFIG *warped_frame,
550*77c1e3ccSAndroid Build Coastguard Worker                             const YV12_BUFFER_CONFIG *src_frame,
551*77c1e3ccSAndroid Build Coastguard Worker                             const LOCALMV *mvs, int mv_stride) {
552*77c1e3ccSAndroid Build Coastguard Worker   int w, h;
553*77c1e3ccSAndroid Build Coastguard Worker   const int fw = src_frame->y_crop_width;
554*77c1e3ccSAndroid Build Coastguard Worker   const int fh = src_frame->y_crop_height;
555*77c1e3ccSAndroid Build Coastguard Worker   const int src_fs = src_frame->y_stride, warped_fs = warped_frame->y_stride;
556*77c1e3ccSAndroid Build Coastguard Worker   const uint8_t *src_buf = src_frame->y_buffer;
557*77c1e3ccSAndroid Build Coastguard Worker   uint8_t *warped_buf = warped_frame->y_buffer;
558*77c1e3ccSAndroid Build Coastguard Worker   double temp;
559*77c1e3ccSAndroid Build Coastguard Worker   for (h = 0; h < fh; h++) {
560*77c1e3ccSAndroid Build Coastguard Worker     for (w = 0; w < fw; w++) {
561*77c1e3ccSAndroid Build Coastguard Worker       double cord_x = (double)w + mvs[h * mv_stride + w].col;
562*77c1e3ccSAndroid Build Coastguard Worker       double cord_y = (double)h + mvs[h * mv_stride + w].row;
563*77c1e3ccSAndroid Build Coastguard Worker       cord_x = fclamp(cord_x, 0, (double)(fw - 1));
564*77c1e3ccSAndroid Build Coastguard Worker       cord_y = fclamp(cord_y, 0, (double)(fh - 1));
565*77c1e3ccSAndroid Build Coastguard Worker       const int floorx = (int)floor(cord_x);
566*77c1e3ccSAndroid Build Coastguard Worker       const int floory = (int)floor(cord_y);
567*77c1e3ccSAndroid Build Coastguard Worker       const double fracx = cord_x - (double)floorx;
568*77c1e3ccSAndroid Build Coastguard Worker       const double fracy = cord_y - (double)floory;
569*77c1e3ccSAndroid Build Coastguard Worker 
570*77c1e3ccSAndroid Build Coastguard Worker       temp = 0;
571*77c1e3ccSAndroid Build Coastguard Worker       for (int hh = 0; hh < 2; hh++) {
572*77c1e3ccSAndroid Build Coastguard Worker         const double weighth = hh ? (fracy) : (1 - fracy);
573*77c1e3ccSAndroid Build Coastguard Worker         for (int ww = 0; ww < 2; ww++) {
574*77c1e3ccSAndroid Build Coastguard Worker           const double weightw = ww ? (fracx) : (1 - fracx);
575*77c1e3ccSAndroid Build Coastguard Worker           int y = floory + hh;
576*77c1e3ccSAndroid Build Coastguard Worker           int x = floorx + ww;
577*77c1e3ccSAndroid Build Coastguard Worker           y = clamp(y, 0, fh - 1);
578*77c1e3ccSAndroid Build Coastguard Worker           x = clamp(x, 0, fw - 1);
579*77c1e3ccSAndroid Build Coastguard Worker           temp += (double)src_buf[y * src_fs + x] * weightw * weighth;
580*77c1e3ccSAndroid Build Coastguard Worker         }
581*77c1e3ccSAndroid Build Coastguard Worker       }
582*77c1e3ccSAndroid Build Coastguard Worker       warped_buf[h * warped_fs + w] = (uint8_t)round(temp);
583*77c1e3ccSAndroid Build Coastguard Worker     }
584*77c1e3ccSAndroid Build Coastguard Worker   }
585*77c1e3ccSAndroid Build Coastguard Worker }
586*77c1e3ccSAndroid Build Coastguard Worker 
587*77c1e3ccSAndroid Build Coastguard Worker // Same as warp_back_frame, but using a better interpolation filter.
warp_back_frame_intp(YV12_BUFFER_CONFIG * warped_frame,const YV12_BUFFER_CONFIG * src_frame,const LOCALMV * mvs,int mv_stride)588*77c1e3ccSAndroid Build Coastguard Worker static void warp_back_frame_intp(YV12_BUFFER_CONFIG *warped_frame,
589*77c1e3ccSAndroid Build Coastguard Worker                                  const YV12_BUFFER_CONFIG *src_frame,
590*77c1e3ccSAndroid Build Coastguard Worker                                  const LOCALMV *mvs, int mv_stride) {
591*77c1e3ccSAndroid Build Coastguard Worker   int w, h;
592*77c1e3ccSAndroid Build Coastguard Worker   const int fw = src_frame->y_crop_width;
593*77c1e3ccSAndroid Build Coastguard Worker   const int fh = src_frame->y_crop_height;
594*77c1e3ccSAndroid Build Coastguard Worker   const int warped_fs = warped_frame->y_stride;
595*77c1e3ccSAndroid Build Coastguard Worker   uint8_t *warped_buf = warped_frame->y_buffer;
596*77c1e3ccSAndroid Build Coastguard Worker   const int blk = 2;
597*77c1e3ccSAndroid Build Coastguard Worker   uint8_t temp_blk[4];
598*77c1e3ccSAndroid Build Coastguard Worker 
599*77c1e3ccSAndroid Build Coastguard Worker   const int is_intrabc = 0;  // Is intra-copied?
600*77c1e3ccSAndroid Build Coastguard Worker   const int is_high_bitdepth = is_frame_high_bitdepth(src_frame);
601*77c1e3ccSAndroid Build Coastguard Worker   const int subsampling_x = 0, subsampling_y = 0;  // for y-buffer
602*77c1e3ccSAndroid Build Coastguard Worker   const int_interpfilters interp_filters =
603*77c1e3ccSAndroid Build Coastguard Worker       av1_broadcast_interp_filter(MULTITAP_SHARP2);
604*77c1e3ccSAndroid Build Coastguard Worker   const int plane = 0;  // y-plane
605*77c1e3ccSAndroid Build Coastguard Worker   const struct buf_2d ref_buf2 = { NULL, src_frame->y_buffer,
606*77c1e3ccSAndroid Build Coastguard Worker                                    src_frame->y_crop_width,
607*77c1e3ccSAndroid Build Coastguard Worker                                    src_frame->y_crop_height,
608*77c1e3ccSAndroid Build Coastguard Worker                                    src_frame->y_stride };
609*77c1e3ccSAndroid Build Coastguard Worker   const int bit_depth = src_frame->bit_depth;
610*77c1e3ccSAndroid Build Coastguard Worker   struct scale_factors scale;
611*77c1e3ccSAndroid Build Coastguard Worker   av1_setup_scale_factors_for_frame(
612*77c1e3ccSAndroid Build Coastguard Worker       &scale, src_frame->y_crop_width, src_frame->y_crop_height,
613*77c1e3ccSAndroid Build Coastguard Worker       src_frame->y_crop_width, src_frame->y_crop_height);
614*77c1e3ccSAndroid Build Coastguard Worker 
615*77c1e3ccSAndroid Build Coastguard Worker   for (h = 0; h < fh; h++) {
616*77c1e3ccSAndroid Build Coastguard Worker     for (w = 0; w < fw; w++) {
617*77c1e3ccSAndroid Build Coastguard Worker       InterPredParams inter_pred_params;
618*77c1e3ccSAndroid Build Coastguard Worker       av1_init_inter_params(&inter_pred_params, blk, blk, h, w, subsampling_x,
619*77c1e3ccSAndroid Build Coastguard Worker                             subsampling_y, bit_depth, is_high_bitdepth,
620*77c1e3ccSAndroid Build Coastguard Worker                             is_intrabc, &scale, &ref_buf2, interp_filters);
621*77c1e3ccSAndroid Build Coastguard Worker       inter_pred_params.interp_filter_params[0] =
622*77c1e3ccSAndroid Build Coastguard Worker           &av1_interp_filter_params_list[interp_filters.as_filters.x_filter];
623*77c1e3ccSAndroid Build Coastguard Worker       inter_pred_params.interp_filter_params[1] =
624*77c1e3ccSAndroid Build Coastguard Worker           &av1_interp_filter_params_list[interp_filters.as_filters.y_filter];
625*77c1e3ccSAndroid Build Coastguard Worker       inter_pred_params.conv_params = get_conv_params(0, plane, bit_depth);
626*77c1e3ccSAndroid Build Coastguard Worker       MV newmv = { .row = (int16_t)round((mvs[h * mv_stride + w].row) * 8),
627*77c1e3ccSAndroid Build Coastguard Worker                    .col = (int16_t)round((mvs[h * mv_stride + w].col) * 8) };
628*77c1e3ccSAndroid Build Coastguard Worker       av1_enc_build_one_inter_predictor(temp_blk, blk, &newmv,
629*77c1e3ccSAndroid Build Coastguard Worker                                         &inter_pred_params);
630*77c1e3ccSAndroid Build Coastguard Worker       warped_buf[h * warped_fs + w] = temp_blk[0];
631*77c1e3ccSAndroid Build Coastguard Worker     }
632*77c1e3ccSAndroid Build Coastguard Worker   }
633*77c1e3ccSAndroid Build Coastguard Worker }
634*77c1e3ccSAndroid Build Coastguard Worker 
635*77c1e3ccSAndroid Build Coastguard Worker #define DERIVATIVE_FILTER_LENGTH 7
636*77c1e3ccSAndroid Build Coastguard Worker double filter[DERIVATIVE_FILTER_LENGTH] = { -1.0 / 60, 9.0 / 60,  -45.0 / 60, 0,
637*77c1e3ccSAndroid Build Coastguard Worker                                             45.0 / 60, -9.0 / 60, 1.0 / 60 };
638*77c1e3ccSAndroid Build Coastguard Worker 
639*77c1e3ccSAndroid Build Coastguard Worker // Get gradient of the whole frame
get_frame_gradients(const YV12_BUFFER_CONFIG * from_frame,const YV12_BUFFER_CONFIG * to_frame,double * ix,double * iy,double * it,int grad_stride)640*77c1e3ccSAndroid Build Coastguard Worker static void get_frame_gradients(const YV12_BUFFER_CONFIG *from_frame,
641*77c1e3ccSAndroid Build Coastguard Worker                                 const YV12_BUFFER_CONFIG *to_frame, double *ix,
642*77c1e3ccSAndroid Build Coastguard Worker                                 double *iy, double *it, int grad_stride) {
643*77c1e3ccSAndroid Build Coastguard Worker   int w, h, k, idx;
644*77c1e3ccSAndroid Build Coastguard Worker   const int fw = from_frame->y_crop_width;
645*77c1e3ccSAndroid Build Coastguard Worker   const int fh = from_frame->y_crop_height;
646*77c1e3ccSAndroid Build Coastguard Worker   const int from_fs = from_frame->y_stride, to_fs = to_frame->y_stride;
647*77c1e3ccSAndroid Build Coastguard Worker   const uint8_t *from_buf = from_frame->y_buffer;
648*77c1e3ccSAndroid Build Coastguard Worker   const uint8_t *to_buf = to_frame->y_buffer;
649*77c1e3ccSAndroid Build Coastguard Worker 
650*77c1e3ccSAndroid Build Coastguard Worker   const int lh = DERIVATIVE_FILTER_LENGTH;
651*77c1e3ccSAndroid Build Coastguard Worker   const int hleft = (lh - 1) / 2;
652*77c1e3ccSAndroid Build Coastguard Worker 
653*77c1e3ccSAndroid Build Coastguard Worker   for (h = 0; h < fh; h++) {
654*77c1e3ccSAndroid Build Coastguard Worker     for (w = 0; w < fw; w++) {
655*77c1e3ccSAndroid Build Coastguard Worker       // x
656*77c1e3ccSAndroid Build Coastguard Worker       ix[h * grad_stride + w] = 0;
657*77c1e3ccSAndroid Build Coastguard Worker       for (k = 0; k < lh; k++) {
658*77c1e3ccSAndroid Build Coastguard Worker         // if we want to make this block dependent, need to extend the
659*77c1e3ccSAndroid Build Coastguard Worker         // boundaries using other initializations.
660*77c1e3ccSAndroid Build Coastguard Worker         idx = w + k - hleft;
661*77c1e3ccSAndroid Build Coastguard Worker         idx = clamp(idx, 0, fw - 1);
662*77c1e3ccSAndroid Build Coastguard Worker         ix[h * grad_stride + w] += filter[k] * 0.5 *
663*77c1e3ccSAndroid Build Coastguard Worker                                    ((double)from_buf[h * from_fs + idx] +
664*77c1e3ccSAndroid Build Coastguard Worker                                     (double)to_buf[h * to_fs + idx]);
665*77c1e3ccSAndroid Build Coastguard Worker       }
666*77c1e3ccSAndroid Build Coastguard Worker       // y
667*77c1e3ccSAndroid Build Coastguard Worker       iy[h * grad_stride + w] = 0;
668*77c1e3ccSAndroid Build Coastguard Worker       for (k = 0; k < lh; k++) {
669*77c1e3ccSAndroid Build Coastguard Worker         // if we want to make this block dependent, need to extend the
670*77c1e3ccSAndroid Build Coastguard Worker         // boundaries using other initializations.
671*77c1e3ccSAndroid Build Coastguard Worker         idx = h + k - hleft;
672*77c1e3ccSAndroid Build Coastguard Worker         idx = clamp(idx, 0, fh - 1);
673*77c1e3ccSAndroid Build Coastguard Worker         iy[h * grad_stride + w] += filter[k] * 0.5 *
674*77c1e3ccSAndroid Build Coastguard Worker                                    ((double)from_buf[idx * from_fs + w] +
675*77c1e3ccSAndroid Build Coastguard Worker                                     (double)to_buf[idx * to_fs + w]);
676*77c1e3ccSAndroid Build Coastguard Worker       }
677*77c1e3ccSAndroid Build Coastguard Worker       // t
678*77c1e3ccSAndroid Build Coastguard Worker       it[h * grad_stride + w] =
679*77c1e3ccSAndroid Build Coastguard Worker           (double)to_buf[h * to_fs + w] - (double)from_buf[h * from_fs + w];
680*77c1e3ccSAndroid Build Coastguard Worker     }
681*77c1e3ccSAndroid Build Coastguard Worker   }
682*77c1e3ccSAndroid Build Coastguard Worker }
683*77c1e3ccSAndroid Build Coastguard Worker 
684*77c1e3ccSAndroid Build Coastguard Worker // Solve for linear equations given by the H-S method
solve_horn_schunck(const double * ix,const double * iy,const double * it,int grad_stride,int width,int height,const LOCALMV * init_mvs,int init_mv_stride,LOCALMV * mvs,int mv_stride)685*77c1e3ccSAndroid Build Coastguard Worker static void solve_horn_schunck(const double *ix, const double *iy,
686*77c1e3ccSAndroid Build Coastguard Worker                                const double *it, int grad_stride, int width,
687*77c1e3ccSAndroid Build Coastguard Worker                                int height, const LOCALMV *init_mvs,
688*77c1e3ccSAndroid Build Coastguard Worker                                int init_mv_stride, LOCALMV *mvs,
689*77c1e3ccSAndroid Build Coastguard Worker                                int mv_stride) {
690*77c1e3ccSAndroid Build Coastguard Worker   // TODO(bohanli): May just need to allocate the buffers once per optical flow
691*77c1e3ccSAndroid Build Coastguard Worker   // calculation
692*77c1e3ccSAndroid Build Coastguard Worker   int *row_pos = aom_calloc(width * height * 28, sizeof(*row_pos));
693*77c1e3ccSAndroid Build Coastguard Worker   int *col_pos = aom_calloc(width * height * 28, sizeof(*col_pos));
694*77c1e3ccSAndroid Build Coastguard Worker   double *values = aom_calloc(width * height * 28, sizeof(*values));
695*77c1e3ccSAndroid Build Coastguard Worker   double *mv_vec = aom_calloc(width * height * 2, sizeof(*mv_vec));
696*77c1e3ccSAndroid Build Coastguard Worker   double *mv_init_vec = aom_calloc(width * height * 2, sizeof(*mv_init_vec));
697*77c1e3ccSAndroid Build Coastguard Worker   double *temp_b = aom_calloc(width * height * 2, sizeof(*temp_b));
698*77c1e3ccSAndroid Build Coastguard Worker   double *b = aom_calloc(width * height * 2, sizeof(*b));
699*77c1e3ccSAndroid Build Coastguard Worker   if (!row_pos || !col_pos || !values || !mv_vec || !mv_init_vec || !temp_b ||
700*77c1e3ccSAndroid Build Coastguard Worker       !b) {
701*77c1e3ccSAndroid Build Coastguard Worker     goto free_hs_solver_buf;
702*77c1e3ccSAndroid Build Coastguard Worker   }
703*77c1e3ccSAndroid Build Coastguard Worker 
704*77c1e3ccSAndroid Build Coastguard Worker   // the location idx for neighboring pixels, k < 4 are the 4 direct neighbors
705*77c1e3ccSAndroid Build Coastguard Worker   const int check_locs_y[12] = { 0, 0, -1, 1, -1, -1, 1, 1, 0, 0, -2, 2 };
706*77c1e3ccSAndroid Build Coastguard Worker   const int check_locs_x[12] = { -1, 1, 0, 0, -1, 1, -1, 1, -2, 2, 0, 0 };
707*77c1e3ccSAndroid Build Coastguard Worker 
708*77c1e3ccSAndroid Build Coastguard Worker   int h, w, checkh, checkw, k, ret;
709*77c1e3ccSAndroid Build Coastguard Worker   const int offset = height * width;
710*77c1e3ccSAndroid Build Coastguard Worker   SPARSE_MTX A;
711*77c1e3ccSAndroid Build Coastguard Worker   int c = 0;
712*77c1e3ccSAndroid Build Coastguard Worker   const double lambda = 100;
713*77c1e3ccSAndroid Build Coastguard Worker 
714*77c1e3ccSAndroid Build Coastguard Worker   for (w = 0; w < width; w++) {
715*77c1e3ccSAndroid Build Coastguard Worker     for (h = 0; h < height; h++) {
716*77c1e3ccSAndroid Build Coastguard Worker       mv_init_vec[w * height + h] = init_mvs[h * init_mv_stride + w].col;
717*77c1e3ccSAndroid Build Coastguard Worker       mv_init_vec[w * height + h + offset] =
718*77c1e3ccSAndroid Build Coastguard Worker           init_mvs[h * init_mv_stride + w].row;
719*77c1e3ccSAndroid Build Coastguard Worker     }
720*77c1e3ccSAndroid Build Coastguard Worker   }
721*77c1e3ccSAndroid Build Coastguard Worker 
722*77c1e3ccSAndroid Build Coastguard Worker   // get matrix A
723*77c1e3ccSAndroid Build Coastguard Worker   for (w = 0; w < width; w++) {
724*77c1e3ccSAndroid Build Coastguard Worker     for (h = 0; h < height; h++) {
725*77c1e3ccSAndroid Build Coastguard Worker       int center_num_direct = 4;
726*77c1e3ccSAndroid Build Coastguard Worker       const int center_idx = w * height + h;
727*77c1e3ccSAndroid Build Coastguard Worker       if (w == 0 || w == width - 1) center_num_direct--;
728*77c1e3ccSAndroid Build Coastguard Worker       if (h == 0 || h == height - 1) center_num_direct--;
729*77c1e3ccSAndroid Build Coastguard Worker       // diagonal entry for this row from the center pixel
730*77c1e3ccSAndroid Build Coastguard Worker       double cor_w = center_num_direct * center_num_direct + center_num_direct;
731*77c1e3ccSAndroid Build Coastguard Worker       row_pos[c] = center_idx;
732*77c1e3ccSAndroid Build Coastguard Worker       col_pos[c] = center_idx;
733*77c1e3ccSAndroid Build Coastguard Worker       values[c] = lambda * cor_w;
734*77c1e3ccSAndroid Build Coastguard Worker       c++;
735*77c1e3ccSAndroid Build Coastguard Worker       row_pos[c] = center_idx + offset;
736*77c1e3ccSAndroid Build Coastguard Worker       col_pos[c] = center_idx + offset;
737*77c1e3ccSAndroid Build Coastguard Worker       values[c] = lambda * cor_w;
738*77c1e3ccSAndroid Build Coastguard Worker       c++;
739*77c1e3ccSAndroid Build Coastguard Worker       // other entries from direct neighbors
740*77c1e3ccSAndroid Build Coastguard Worker       for (k = 0; k < 4; k++) {
741*77c1e3ccSAndroid Build Coastguard Worker         checkh = h + check_locs_y[k];
742*77c1e3ccSAndroid Build Coastguard Worker         checkw = w + check_locs_x[k];
743*77c1e3ccSAndroid Build Coastguard Worker         if (checkh < 0 || checkh >= height || checkw < 0 || checkw >= width) {
744*77c1e3ccSAndroid Build Coastguard Worker           continue;
745*77c1e3ccSAndroid Build Coastguard Worker         }
746*77c1e3ccSAndroid Build Coastguard Worker         int this_idx = checkw * height + checkh;
747*77c1e3ccSAndroid Build Coastguard Worker         int this_num_direct = 4;
748*77c1e3ccSAndroid Build Coastguard Worker         if (checkw == 0 || checkw == width - 1) this_num_direct--;
749*77c1e3ccSAndroid Build Coastguard Worker         if (checkh == 0 || checkh == height - 1) this_num_direct--;
750*77c1e3ccSAndroid Build Coastguard Worker         cor_w = -center_num_direct - this_num_direct;
751*77c1e3ccSAndroid Build Coastguard Worker         row_pos[c] = center_idx;
752*77c1e3ccSAndroid Build Coastguard Worker         col_pos[c] = this_idx;
753*77c1e3ccSAndroid Build Coastguard Worker         values[c] = lambda * cor_w;
754*77c1e3ccSAndroid Build Coastguard Worker         c++;
755*77c1e3ccSAndroid Build Coastguard Worker         row_pos[c] = center_idx + offset;
756*77c1e3ccSAndroid Build Coastguard Worker         col_pos[c] = this_idx + offset;
757*77c1e3ccSAndroid Build Coastguard Worker         values[c] = lambda * cor_w;
758*77c1e3ccSAndroid Build Coastguard Worker         c++;
759*77c1e3ccSAndroid Build Coastguard Worker       }
760*77c1e3ccSAndroid Build Coastguard Worker       // entries from neighbors on the diagonal corners
761*77c1e3ccSAndroid Build Coastguard Worker       for (k = 4; k < 8; k++) {
762*77c1e3ccSAndroid Build Coastguard Worker         checkh = h + check_locs_y[k];
763*77c1e3ccSAndroid Build Coastguard Worker         checkw = w + check_locs_x[k];
764*77c1e3ccSAndroid Build Coastguard Worker         if (checkh < 0 || checkh >= height || checkw < 0 || checkw >= width) {
765*77c1e3ccSAndroid Build Coastguard Worker           continue;
766*77c1e3ccSAndroid Build Coastguard Worker         }
767*77c1e3ccSAndroid Build Coastguard Worker         int this_idx = checkw * height + checkh;
768*77c1e3ccSAndroid Build Coastguard Worker         cor_w = 2;
769*77c1e3ccSAndroid Build Coastguard Worker         row_pos[c] = center_idx;
770*77c1e3ccSAndroid Build Coastguard Worker         col_pos[c] = this_idx;
771*77c1e3ccSAndroid Build Coastguard Worker         values[c] = lambda * cor_w;
772*77c1e3ccSAndroid Build Coastguard Worker         c++;
773*77c1e3ccSAndroid Build Coastguard Worker         row_pos[c] = center_idx + offset;
774*77c1e3ccSAndroid Build Coastguard Worker         col_pos[c] = this_idx + offset;
775*77c1e3ccSAndroid Build Coastguard Worker         values[c] = lambda * cor_w;
776*77c1e3ccSAndroid Build Coastguard Worker         c++;
777*77c1e3ccSAndroid Build Coastguard Worker       }
778*77c1e3ccSAndroid Build Coastguard Worker       // entries from neighbors with dist of 2
779*77c1e3ccSAndroid Build Coastguard Worker       for (k = 8; k < 12; k++) {
780*77c1e3ccSAndroid Build Coastguard Worker         checkh = h + check_locs_y[k];
781*77c1e3ccSAndroid Build Coastguard Worker         checkw = w + check_locs_x[k];
782*77c1e3ccSAndroid Build Coastguard Worker         if (checkh < 0 || checkh >= height || checkw < 0 || checkw >= width) {
783*77c1e3ccSAndroid Build Coastguard Worker           continue;
784*77c1e3ccSAndroid Build Coastguard Worker         }
785*77c1e3ccSAndroid Build Coastguard Worker         int this_idx = checkw * height + checkh;
786*77c1e3ccSAndroid Build Coastguard Worker         cor_w = 1;
787*77c1e3ccSAndroid Build Coastguard Worker         row_pos[c] = center_idx;
788*77c1e3ccSAndroid Build Coastguard Worker         col_pos[c] = this_idx;
789*77c1e3ccSAndroid Build Coastguard Worker         values[c] = lambda * cor_w;
790*77c1e3ccSAndroid Build Coastguard Worker         c++;
791*77c1e3ccSAndroid Build Coastguard Worker         row_pos[c] = center_idx + offset;
792*77c1e3ccSAndroid Build Coastguard Worker         col_pos[c] = this_idx + offset;
793*77c1e3ccSAndroid Build Coastguard Worker         values[c] = lambda * cor_w;
794*77c1e3ccSAndroid Build Coastguard Worker         c++;
795*77c1e3ccSAndroid Build Coastguard Worker       }
796*77c1e3ccSAndroid Build Coastguard Worker     }
797*77c1e3ccSAndroid Build Coastguard Worker   }
798*77c1e3ccSAndroid Build Coastguard Worker   ret = av1_init_sparse_mtx(row_pos, col_pos, values, c, 2 * width * height,
799*77c1e3ccSAndroid Build Coastguard Worker                             2 * width * height, &A);
800*77c1e3ccSAndroid Build Coastguard Worker   if (ret < 0) goto free_hs_solver_buf;
801*77c1e3ccSAndroid Build Coastguard Worker   // subtract init mv part from b
802*77c1e3ccSAndroid Build Coastguard Worker   av1_mtx_vect_multi_left(&A, mv_init_vec, temp_b, 2 * width * height);
803*77c1e3ccSAndroid Build Coastguard Worker   for (int i = 0; i < 2 * width * height; i++) {
804*77c1e3ccSAndroid Build Coastguard Worker     b[i] = -temp_b[i];
805*77c1e3ccSAndroid Build Coastguard Worker   }
806*77c1e3ccSAndroid Build Coastguard Worker   av1_free_sparse_mtx_elems(&A);
807*77c1e3ccSAndroid Build Coastguard Worker 
808*77c1e3ccSAndroid Build Coastguard Worker   // add cross terms to A and modify b with ExEt / EyEt
809*77c1e3ccSAndroid Build Coastguard Worker   for (w = 0; w < width; w++) {
810*77c1e3ccSAndroid Build Coastguard Worker     for (h = 0; h < height; h++) {
811*77c1e3ccSAndroid Build Coastguard Worker       int curidx = w * height + h;
812*77c1e3ccSAndroid Build Coastguard Worker       // modify b
813*77c1e3ccSAndroid Build Coastguard Worker       b[curidx] += -ix[h * grad_stride + w] * it[h * grad_stride + w];
814*77c1e3ccSAndroid Build Coastguard Worker       b[curidx + offset] += -iy[h * grad_stride + w] * it[h * grad_stride + w];
815*77c1e3ccSAndroid Build Coastguard Worker       // add cross terms to A
816*77c1e3ccSAndroid Build Coastguard Worker       row_pos[c] = curidx;
817*77c1e3ccSAndroid Build Coastguard Worker       col_pos[c] = curidx + offset;
818*77c1e3ccSAndroid Build Coastguard Worker       values[c] = ix[h * grad_stride + w] * iy[h * grad_stride + w];
819*77c1e3ccSAndroid Build Coastguard Worker       c++;
820*77c1e3ccSAndroid Build Coastguard Worker       row_pos[c] = curidx + offset;
821*77c1e3ccSAndroid Build Coastguard Worker       col_pos[c] = curidx;
822*77c1e3ccSAndroid Build Coastguard Worker       values[c] = ix[h * grad_stride + w] * iy[h * grad_stride + w];
823*77c1e3ccSAndroid Build Coastguard Worker       c++;
824*77c1e3ccSAndroid Build Coastguard Worker     }
825*77c1e3ccSAndroid Build Coastguard Worker   }
826*77c1e3ccSAndroid Build Coastguard Worker   // Add diagonal terms to A
827*77c1e3ccSAndroid Build Coastguard Worker   for (int i = 0; i < c; i++) {
828*77c1e3ccSAndroid Build Coastguard Worker     if (row_pos[i] == col_pos[i]) {
829*77c1e3ccSAndroid Build Coastguard Worker       if (row_pos[i] < offset) {
830*77c1e3ccSAndroid Build Coastguard Worker         w = row_pos[i] / height;
831*77c1e3ccSAndroid Build Coastguard Worker         h = row_pos[i] % height;
832*77c1e3ccSAndroid Build Coastguard Worker         values[i] += pow(ix[h * grad_stride + w], 2);
833*77c1e3ccSAndroid Build Coastguard Worker       } else {
834*77c1e3ccSAndroid Build Coastguard Worker         w = (row_pos[i] - offset) / height;
835*77c1e3ccSAndroid Build Coastguard Worker         h = (row_pos[i] - offset) % height;
836*77c1e3ccSAndroid Build Coastguard Worker         values[i] += pow(iy[h * grad_stride + w], 2);
837*77c1e3ccSAndroid Build Coastguard Worker       }
838*77c1e3ccSAndroid Build Coastguard Worker     }
839*77c1e3ccSAndroid Build Coastguard Worker   }
840*77c1e3ccSAndroid Build Coastguard Worker 
841*77c1e3ccSAndroid Build Coastguard Worker   ret = av1_init_sparse_mtx(row_pos, col_pos, values, c, 2 * width * height,
842*77c1e3ccSAndroid Build Coastguard Worker                             2 * width * height, &A);
843*77c1e3ccSAndroid Build Coastguard Worker   if (ret < 0) goto free_hs_solver_buf;
844*77c1e3ccSAndroid Build Coastguard Worker 
845*77c1e3ccSAndroid Build Coastguard Worker   // solve for the mvs
846*77c1e3ccSAndroid Build Coastguard Worker   ret = av1_conjugate_gradient_sparse(&A, b, 2 * width * height, mv_vec);
847*77c1e3ccSAndroid Build Coastguard Worker   if (ret < 0) goto free_hs_solver_buf;
848*77c1e3ccSAndroid Build Coastguard Worker 
849*77c1e3ccSAndroid Build Coastguard Worker   // copy mvs
850*77c1e3ccSAndroid Build Coastguard Worker   for (w = 0; w < width; w++) {
851*77c1e3ccSAndroid Build Coastguard Worker     for (h = 0; h < height; h++) {
852*77c1e3ccSAndroid Build Coastguard Worker       mvs[h * mv_stride + w].col = mv_vec[w * height + h];
853*77c1e3ccSAndroid Build Coastguard Worker       mvs[h * mv_stride + w].row = mv_vec[w * height + h + offset];
854*77c1e3ccSAndroid Build Coastguard Worker     }
855*77c1e3ccSAndroid Build Coastguard Worker   }
856*77c1e3ccSAndroid Build Coastguard Worker free_hs_solver_buf:
857*77c1e3ccSAndroid Build Coastguard Worker   aom_free(row_pos);
858*77c1e3ccSAndroid Build Coastguard Worker   aom_free(col_pos);
859*77c1e3ccSAndroid Build Coastguard Worker   aom_free(values);
860*77c1e3ccSAndroid Build Coastguard Worker   aom_free(mv_vec);
861*77c1e3ccSAndroid Build Coastguard Worker   aom_free(mv_init_vec);
862*77c1e3ccSAndroid Build Coastguard Worker   aom_free(b);
863*77c1e3ccSAndroid Build Coastguard Worker   aom_free(temp_b);
864*77c1e3ccSAndroid Build Coastguard Worker   av1_free_sparse_mtx_elems(&A);
865*77c1e3ccSAndroid Build Coastguard Worker }
866*77c1e3ccSAndroid Build Coastguard Worker 
867*77c1e3ccSAndroid Build Coastguard Worker // Calculate optical flow from from_frame to to_frame using the H-S method.
horn_schunck(const YV12_BUFFER_CONFIG * from_frame,const YV12_BUFFER_CONFIG * to_frame,const int level,const int mv_stride,const int mv_height,const int mv_width,const OPFL_PARAMS * opfl_params,LOCALMV * mvs)868*77c1e3ccSAndroid Build Coastguard Worker static void horn_schunck(const YV12_BUFFER_CONFIG *from_frame,
869*77c1e3ccSAndroid Build Coastguard Worker                          const YV12_BUFFER_CONFIG *to_frame, const int level,
870*77c1e3ccSAndroid Build Coastguard Worker                          const int mv_stride, const int mv_height,
871*77c1e3ccSAndroid Build Coastguard Worker                          const int mv_width, const OPFL_PARAMS *opfl_params,
872*77c1e3ccSAndroid Build Coastguard Worker                          LOCALMV *mvs) {
873*77c1e3ccSAndroid Build Coastguard Worker   // mvs are always on level 0, here we define two new mv arrays that is of size
874*77c1e3ccSAndroid Build Coastguard Worker   // of this level.
875*77c1e3ccSAndroid Build Coastguard Worker   const int fw = from_frame->y_crop_width;
876*77c1e3ccSAndroid Build Coastguard Worker   const int fh = from_frame->y_crop_height;
877*77c1e3ccSAndroid Build Coastguard Worker   const int factor = (int)pow(2, level);
878*77c1e3ccSAndroid Build Coastguard Worker   int w, h, k, init_mv_stride;
879*77c1e3ccSAndroid Build Coastguard Worker   LOCALMV *init_mvs = NULL, *refine_mvs = NULL;
880*77c1e3ccSAndroid Build Coastguard Worker   double *ix = NULL, *iy = NULL, *it = NULL;
881*77c1e3ccSAndroid Build Coastguard Worker   YV12_BUFFER_CONFIG temp_frame;
882*77c1e3ccSAndroid Build Coastguard Worker   temp_frame.y_buffer = NULL;
883*77c1e3ccSAndroid Build Coastguard Worker   if (level == 0) {
884*77c1e3ccSAndroid Build Coastguard Worker     init_mvs = mvs;
885*77c1e3ccSAndroid Build Coastguard Worker     init_mv_stride = mv_stride;
886*77c1e3ccSAndroid Build Coastguard Worker   } else {
887*77c1e3ccSAndroid Build Coastguard Worker     init_mvs = aom_calloc(fw * fh, sizeof(*mvs));
888*77c1e3ccSAndroid Build Coastguard Worker     if (!init_mvs) goto free_hs_buf;
889*77c1e3ccSAndroid Build Coastguard Worker     init_mv_stride = fw;
890*77c1e3ccSAndroid Build Coastguard Worker     for (h = 0; h < fh; h++) {
891*77c1e3ccSAndroid Build Coastguard Worker       for (w = 0; w < fw; w++) {
892*77c1e3ccSAndroid Build Coastguard Worker         init_mvs[h * init_mv_stride + w].row =
893*77c1e3ccSAndroid Build Coastguard Worker             mvs[h * factor * mv_stride + w * factor].row / (double)factor;
894*77c1e3ccSAndroid Build Coastguard Worker         init_mvs[h * init_mv_stride + w].col =
895*77c1e3ccSAndroid Build Coastguard Worker             mvs[h * factor * mv_stride + w * factor].col / (double)factor;
896*77c1e3ccSAndroid Build Coastguard Worker       }
897*77c1e3ccSAndroid Build Coastguard Worker     }
898*77c1e3ccSAndroid Build Coastguard Worker   }
899*77c1e3ccSAndroid Build Coastguard Worker   refine_mvs = aom_calloc(fw * fh, sizeof(*mvs));
900*77c1e3ccSAndroid Build Coastguard Worker   if (!refine_mvs) goto free_hs_buf;
901*77c1e3ccSAndroid Build Coastguard Worker   // temp frame for warping
902*77c1e3ccSAndroid Build Coastguard Worker   temp_frame.y_buffer =
903*77c1e3ccSAndroid Build Coastguard Worker       (uint8_t *)aom_calloc(fh * fw, sizeof(*temp_frame.y_buffer));
904*77c1e3ccSAndroid Build Coastguard Worker   if (!temp_frame.y_buffer) goto free_hs_buf;
905*77c1e3ccSAndroid Build Coastguard Worker   temp_frame.y_crop_height = fh;
906*77c1e3ccSAndroid Build Coastguard Worker   temp_frame.y_crop_width = fw;
907*77c1e3ccSAndroid Build Coastguard Worker   temp_frame.y_stride = fw;
908*77c1e3ccSAndroid Build Coastguard Worker   // gradient buffers
909*77c1e3ccSAndroid Build Coastguard Worker   ix = aom_calloc(fw * fh, sizeof(*ix));
910*77c1e3ccSAndroid Build Coastguard Worker   iy = aom_calloc(fw * fh, sizeof(*iy));
911*77c1e3ccSAndroid Build Coastguard Worker   it = aom_calloc(fw * fh, sizeof(*it));
912*77c1e3ccSAndroid Build Coastguard Worker   if (!ix || !iy || !it) goto free_hs_buf;
913*77c1e3ccSAndroid Build Coastguard Worker   // For each warping step
914*77c1e3ccSAndroid Build Coastguard Worker   for (k = 0; k < opfl_params->warping_steps; k++) {
915*77c1e3ccSAndroid Build Coastguard Worker     // warp from_frame with init_mv
916*77c1e3ccSAndroid Build Coastguard Worker     if (level == 0) {
917*77c1e3ccSAndroid Build Coastguard Worker       warp_back_frame_intp(&temp_frame, to_frame, init_mvs, init_mv_stride);
918*77c1e3ccSAndroid Build Coastguard Worker     } else {
919*77c1e3ccSAndroid Build Coastguard Worker       warp_back_frame(&temp_frame, to_frame, init_mvs, init_mv_stride);
920*77c1e3ccSAndroid Build Coastguard Worker     }
921*77c1e3ccSAndroid Build Coastguard Worker     // calculate frame gradients
922*77c1e3ccSAndroid Build Coastguard Worker     get_frame_gradients(from_frame, &temp_frame, ix, iy, it, fw);
923*77c1e3ccSAndroid Build Coastguard Worker     // form linear equations and solve mvs
924*77c1e3ccSAndroid Build Coastguard Worker     solve_horn_schunck(ix, iy, it, fw, fw, fh, init_mvs, init_mv_stride,
925*77c1e3ccSAndroid Build Coastguard Worker                        refine_mvs, fw);
926*77c1e3ccSAndroid Build Coastguard Worker     // update init_mvs
927*77c1e3ccSAndroid Build Coastguard Worker     for (h = 0; h < fh; h++) {
928*77c1e3ccSAndroid Build Coastguard Worker       for (w = 0; w < fw; w++) {
929*77c1e3ccSAndroid Build Coastguard Worker         init_mvs[h * init_mv_stride + w].col += refine_mvs[h * fw + w].col;
930*77c1e3ccSAndroid Build Coastguard Worker         init_mvs[h * init_mv_stride + w].row += refine_mvs[h * fw + w].row;
931*77c1e3ccSAndroid Build Coastguard Worker       }
932*77c1e3ccSAndroid Build Coastguard Worker     }
933*77c1e3ccSAndroid Build Coastguard Worker   }
934*77c1e3ccSAndroid Build Coastguard Worker   // copy back the mvs if needed
935*77c1e3ccSAndroid Build Coastguard Worker   if (level != 0) {
936*77c1e3ccSAndroid Build Coastguard Worker     for (h = 0; h < mv_height; h++) {
937*77c1e3ccSAndroid Build Coastguard Worker       for (w = 0; w < mv_width; w++) {
938*77c1e3ccSAndroid Build Coastguard Worker         mvs[h * mv_stride + w].row =
939*77c1e3ccSAndroid Build Coastguard Worker             init_mvs[h / factor * init_mv_stride + w / factor].row *
940*77c1e3ccSAndroid Build Coastguard Worker             (double)factor;
941*77c1e3ccSAndroid Build Coastguard Worker         mvs[h * mv_stride + w].col =
942*77c1e3ccSAndroid Build Coastguard Worker             init_mvs[h / factor * init_mv_stride + w / factor].col *
943*77c1e3ccSAndroid Build Coastguard Worker             (double)factor;
944*77c1e3ccSAndroid Build Coastguard Worker       }
945*77c1e3ccSAndroid Build Coastguard Worker     }
946*77c1e3ccSAndroid Build Coastguard Worker   }
947*77c1e3ccSAndroid Build Coastguard Worker free_hs_buf:
948*77c1e3ccSAndroid Build Coastguard Worker   if (level != 0) aom_free(init_mvs);
949*77c1e3ccSAndroid Build Coastguard Worker   aom_free(refine_mvs);
950*77c1e3ccSAndroid Build Coastguard Worker   aom_free(temp_frame.y_buffer);
951*77c1e3ccSAndroid Build Coastguard Worker   aom_free(ix);
952*77c1e3ccSAndroid Build Coastguard Worker   aom_free(iy);
953*77c1e3ccSAndroid Build Coastguard Worker   aom_free(it);
954*77c1e3ccSAndroid Build Coastguard Worker }
955*77c1e3ccSAndroid Build Coastguard Worker 
956*77c1e3ccSAndroid Build Coastguard Worker // Apply optical flow iteratively at each pyramid level
pyramid_optical_flow(const YV12_BUFFER_CONFIG * from_frame,const YV12_BUFFER_CONFIG * to_frame,const int bit_depth,const OPFL_PARAMS * opfl_params,const OPTFLOW_METHOD method,LOCALMV * mvs)957*77c1e3ccSAndroid Build Coastguard Worker static void pyramid_optical_flow(const YV12_BUFFER_CONFIG *from_frame,
958*77c1e3ccSAndroid Build Coastguard Worker                                  const YV12_BUFFER_CONFIG *to_frame,
959*77c1e3ccSAndroid Build Coastguard Worker                                  const int bit_depth,
960*77c1e3ccSAndroid Build Coastguard Worker                                  const OPFL_PARAMS *opfl_params,
961*77c1e3ccSAndroid Build Coastguard Worker                                  const OPTFLOW_METHOD method, LOCALMV *mvs) {
962*77c1e3ccSAndroid Build Coastguard Worker   assert(opfl_params->pyramid_levels > 0 &&
963*77c1e3ccSAndroid Build Coastguard Worker          opfl_params->pyramid_levels <= MAX_PYRAMID_LEVELS);
964*77c1e3ccSAndroid Build Coastguard Worker   int levels = opfl_params->pyramid_levels;
965*77c1e3ccSAndroid Build Coastguard Worker   const int frame_height = from_frame->y_crop_height;
966*77c1e3ccSAndroid Build Coastguard Worker   const int frame_width = from_frame->y_crop_width;
967*77c1e3ccSAndroid Build Coastguard Worker   if ((frame_height / pow(2.0, levels - 1) < 50 ||
968*77c1e3ccSAndroid Build Coastguard Worker        frame_height / pow(2.0, levels - 1) < 50) &&
969*77c1e3ccSAndroid Build Coastguard Worker       levels > 1)
970*77c1e3ccSAndroid Build Coastguard Worker     levels = levels - 1;
971*77c1e3ccSAndroid Build Coastguard Worker   uint8_t *images1[MAX_PYRAMID_LEVELS] = { NULL };
972*77c1e3ccSAndroid Build Coastguard Worker   uint8_t *images2[MAX_PYRAMID_LEVELS] = { NULL };
973*77c1e3ccSAndroid Build Coastguard Worker   int *ref_corners = NULL;
974*77c1e3ccSAndroid Build Coastguard Worker 
975*77c1e3ccSAndroid Build Coastguard Worker   images1[0] = from_frame->y_buffer;
976*77c1e3ccSAndroid Build Coastguard Worker   images2[0] = to_frame->y_buffer;
977*77c1e3ccSAndroid Build Coastguard Worker   YV12_BUFFER_CONFIG *buffers1 = aom_malloc(levels * sizeof(*buffers1));
978*77c1e3ccSAndroid Build Coastguard Worker   YV12_BUFFER_CONFIG *buffers2 = aom_malloc(levels * sizeof(*buffers2));
979*77c1e3ccSAndroid Build Coastguard Worker   if (!buffers1 || !buffers2) goto free_pyramid_buf;
980*77c1e3ccSAndroid Build Coastguard Worker   buffers1[0] = *from_frame;
981*77c1e3ccSAndroid Build Coastguard Worker   buffers2[0] = *to_frame;
982*77c1e3ccSAndroid Build Coastguard Worker   int fw = frame_width;
983*77c1e3ccSAndroid Build Coastguard Worker   int fh = frame_height;
984*77c1e3ccSAndroid Build Coastguard Worker   for (int i = 1; i < levels; i++) {
985*77c1e3ccSAndroid Build Coastguard Worker     // TODO(bohanli): may need to extend buffers for better interpolation SIMD
986*77c1e3ccSAndroid Build Coastguard Worker     images1[i] = (uint8_t *)aom_calloc(fh / 2 * fw / 2, sizeof(*images1[i]));
987*77c1e3ccSAndroid Build Coastguard Worker     images2[i] = (uint8_t *)aom_calloc(fh / 2 * fw / 2, sizeof(*images2[i]));
988*77c1e3ccSAndroid Build Coastguard Worker     if (!images1[i] || !images2[i]) goto free_pyramid_buf;
989*77c1e3ccSAndroid Build Coastguard Worker     int stride;
990*77c1e3ccSAndroid Build Coastguard Worker     if (i == 1)
991*77c1e3ccSAndroid Build Coastguard Worker       stride = from_frame->y_stride;
992*77c1e3ccSAndroid Build Coastguard Worker     else
993*77c1e3ccSAndroid Build Coastguard Worker       stride = fw;
994*77c1e3ccSAndroid Build Coastguard Worker     reduce(images1[i - 1], fh, fw, stride, images1[i]);
995*77c1e3ccSAndroid Build Coastguard Worker     reduce(images2[i - 1], fh, fw, stride, images2[i]);
996*77c1e3ccSAndroid Build Coastguard Worker     fh /= 2;
997*77c1e3ccSAndroid Build Coastguard Worker     fw /= 2;
998*77c1e3ccSAndroid Build Coastguard Worker     YV12_BUFFER_CONFIG a = { .y_buffer = images1[i],
999*77c1e3ccSAndroid Build Coastguard Worker                              .y_crop_width = fw,
1000*77c1e3ccSAndroid Build Coastguard Worker                              .y_crop_height = fh,
1001*77c1e3ccSAndroid Build Coastguard Worker                              .y_stride = fw };
1002*77c1e3ccSAndroid Build Coastguard Worker     YV12_BUFFER_CONFIG b = { .y_buffer = images2[i],
1003*77c1e3ccSAndroid Build Coastguard Worker                              .y_crop_width = fw,
1004*77c1e3ccSAndroid Build Coastguard Worker                              .y_crop_height = fh,
1005*77c1e3ccSAndroid Build Coastguard Worker                              .y_stride = fw };
1006*77c1e3ccSAndroid Build Coastguard Worker     buffers1[i] = a;
1007*77c1e3ccSAndroid Build Coastguard Worker     buffers2[i] = b;
1008*77c1e3ccSAndroid Build Coastguard Worker   }
1009*77c1e3ccSAndroid Build Coastguard Worker   // Compute corners for specific frame
1010*77c1e3ccSAndroid Build Coastguard Worker   int num_ref_corners = 0;
1011*77c1e3ccSAndroid Build Coastguard Worker   if (is_sparse(opfl_params)) {
1012*77c1e3ccSAndroid Build Coastguard Worker     int maxcorners = from_frame->y_crop_width * from_frame->y_crop_height;
1013*77c1e3ccSAndroid Build Coastguard Worker     ref_corners = aom_malloc(maxcorners * 2 * sizeof(*ref_corners));
1014*77c1e3ccSAndroid Build Coastguard Worker     if (!ref_corners) goto free_pyramid_buf;
1015*77c1e3ccSAndroid Build Coastguard Worker     num_ref_corners = detect_corners(from_frame, to_frame, maxcorners,
1016*77c1e3ccSAndroid Build Coastguard Worker                                      ref_corners, bit_depth);
1017*77c1e3ccSAndroid Build Coastguard Worker   }
1018*77c1e3ccSAndroid Build Coastguard Worker   const int stop_level = 0;
1019*77c1e3ccSAndroid Build Coastguard Worker   for (int i = levels - 1; i >= stop_level; i--) {
1020*77c1e3ccSAndroid Build Coastguard Worker     if (method == LUCAS_KANADE) {
1021*77c1e3ccSAndroid Build Coastguard Worker       assert(is_sparse(opfl_params));
1022*77c1e3ccSAndroid Build Coastguard Worker       lucas_kanade(&buffers1[i], &buffers2[i], i, opfl_params->lk_params,
1023*77c1e3ccSAndroid Build Coastguard Worker                    num_ref_corners, ref_corners, buffers1[0].y_crop_width,
1024*77c1e3ccSAndroid Build Coastguard Worker                    bit_depth, mvs);
1025*77c1e3ccSAndroid Build Coastguard Worker     } else if (method == HORN_SCHUNCK) {
1026*77c1e3ccSAndroid Build Coastguard Worker       assert(!is_sparse(opfl_params));
1027*77c1e3ccSAndroid Build Coastguard Worker       horn_schunck(&buffers1[i], &buffers2[i], i, buffers1[0].y_crop_width,
1028*77c1e3ccSAndroid Build Coastguard Worker                    buffers1[0].y_crop_height, buffers1[0].y_crop_width,
1029*77c1e3ccSAndroid Build Coastguard Worker                    opfl_params, mvs);
1030*77c1e3ccSAndroid Build Coastguard Worker     }
1031*77c1e3ccSAndroid Build Coastguard Worker   }
1032*77c1e3ccSAndroid Build Coastguard Worker free_pyramid_buf:
1033*77c1e3ccSAndroid Build Coastguard Worker   for (int i = 1; i < levels; i++) {
1034*77c1e3ccSAndroid Build Coastguard Worker     aom_free(images1[i]);
1035*77c1e3ccSAndroid Build Coastguard Worker     aom_free(images2[i]);
1036*77c1e3ccSAndroid Build Coastguard Worker   }
1037*77c1e3ccSAndroid Build Coastguard Worker   aom_free(ref_corners);
1038*77c1e3ccSAndroid Build Coastguard Worker   aom_free(buffers1);
1039*77c1e3ccSAndroid Build Coastguard Worker   aom_free(buffers2);
1040*77c1e3ccSAndroid Build Coastguard Worker }
1041*77c1e3ccSAndroid Build Coastguard Worker // Computes optical flow by applying algorithm at
1042*77c1e3ccSAndroid Build Coastguard Worker // multiple pyramid levels of images (lower-resolution, smoothed images)
1043*77c1e3ccSAndroid Build Coastguard Worker // This accounts for larger motions.
1044*77c1e3ccSAndroid Build Coastguard Worker // Inputs:
1045*77c1e3ccSAndroid Build Coastguard Worker //   from_frame Frame buffer.
1046*77c1e3ccSAndroid Build Coastguard Worker //   to_frame: Frame buffer. MVs point from_frame -> to_frame.
1047*77c1e3ccSAndroid Build Coastguard Worker //   from_frame_idx: Index of from_frame.
1048*77c1e3ccSAndroid Build Coastguard Worker //   to_frame_idx: Index of to_frame. Return all zero MVs when idx are equal.
1049*77c1e3ccSAndroid Build Coastguard Worker //   bit_depth:
1050*77c1e3ccSAndroid Build Coastguard Worker //   opfl_params: contains algorithm-specific parameters.
1051*77c1e3ccSAndroid Build Coastguard Worker //   mv_filter: MV_FILTER_NONE, MV_FILTER_SMOOTH, or MV_FILTER_MEDIAN.
1052*77c1e3ccSAndroid Build Coastguard Worker //   method: LUCAS_KANADE, HORN_SCHUNCK
1053*77c1e3ccSAndroid Build Coastguard Worker //   mvs: pointer to MVs. Contains initialization, and modified
1054*77c1e3ccSAndroid Build Coastguard Worker //   based on optical flow. Must have
1055*77c1e3ccSAndroid Build Coastguard Worker //   dimensions = from_frame->y_crop_width * from_frame->y_crop_height
av1_optical_flow(const YV12_BUFFER_CONFIG * from_frame,const YV12_BUFFER_CONFIG * to_frame,const int from_frame_idx,const int to_frame_idx,const int bit_depth,const OPFL_PARAMS * opfl_params,const MV_FILTER_TYPE mv_filter,const OPTFLOW_METHOD method,MV * mvs)1056*77c1e3ccSAndroid Build Coastguard Worker void av1_optical_flow(const YV12_BUFFER_CONFIG *from_frame,
1057*77c1e3ccSAndroid Build Coastguard Worker                       const YV12_BUFFER_CONFIG *to_frame,
1058*77c1e3ccSAndroid Build Coastguard Worker                       const int from_frame_idx, const int to_frame_idx,
1059*77c1e3ccSAndroid Build Coastguard Worker                       const int bit_depth, const OPFL_PARAMS *opfl_params,
1060*77c1e3ccSAndroid Build Coastguard Worker                       const MV_FILTER_TYPE mv_filter,
1061*77c1e3ccSAndroid Build Coastguard Worker                       const OPTFLOW_METHOD method, MV *mvs) {
1062*77c1e3ccSAndroid Build Coastguard Worker   const int frame_height = from_frame->y_crop_height;
1063*77c1e3ccSAndroid Build Coastguard Worker   const int frame_width = from_frame->y_crop_width;
1064*77c1e3ccSAndroid Build Coastguard Worker   // TODO(any): deal with the case where frames are not of the same dimensions
1065*77c1e3ccSAndroid Build Coastguard Worker   assert(frame_height == to_frame->y_crop_height &&
1066*77c1e3ccSAndroid Build Coastguard Worker          frame_width == to_frame->y_crop_width);
1067*77c1e3ccSAndroid Build Coastguard Worker   if (from_frame_idx == to_frame_idx) {
1068*77c1e3ccSAndroid Build Coastguard Worker     // immediately return all zero mvs when frame indices are equal
1069*77c1e3ccSAndroid Build Coastguard Worker     for (int yy = 0; yy < frame_height; yy++) {
1070*77c1e3ccSAndroid Build Coastguard Worker       for (int xx = 0; xx < frame_width; xx++) {
1071*77c1e3ccSAndroid Build Coastguard Worker         MV mv = { .row = 0, .col = 0 };
1072*77c1e3ccSAndroid Build Coastguard Worker         mvs[yy * frame_width + xx] = mv;
1073*77c1e3ccSAndroid Build Coastguard Worker       }
1074*77c1e3ccSAndroid Build Coastguard Worker     }
1075*77c1e3ccSAndroid Build Coastguard Worker     return;
1076*77c1e3ccSAndroid Build Coastguard Worker   }
1077*77c1e3ccSAndroid Build Coastguard Worker 
1078*77c1e3ccSAndroid Build Coastguard Worker   // Initialize double mvs based on input parameter mvs array
1079*77c1e3ccSAndroid Build Coastguard Worker   LOCALMV *localmvs =
1080*77c1e3ccSAndroid Build Coastguard Worker       aom_malloc(frame_height * frame_width * sizeof(*localmvs));
1081*77c1e3ccSAndroid Build Coastguard Worker   if (!localmvs) return;
1082*77c1e3ccSAndroid Build Coastguard Worker 
1083*77c1e3ccSAndroid Build Coastguard Worker   filter_mvs(MV_FILTER_SMOOTH, frame_height, frame_width, localmvs, mvs);
1084*77c1e3ccSAndroid Build Coastguard Worker 
1085*77c1e3ccSAndroid Build Coastguard Worker   for (int i = 0; i < frame_width * frame_height; i++) {
1086*77c1e3ccSAndroid Build Coastguard Worker     MV mv = mvs[i];
1087*77c1e3ccSAndroid Build Coastguard Worker     LOCALMV localmv = { .row = ((double)mv.row) / 8,
1088*77c1e3ccSAndroid Build Coastguard Worker                         .col = ((double)mv.col) / 8 };
1089*77c1e3ccSAndroid Build Coastguard Worker     localmvs[i] = localmv;
1090*77c1e3ccSAndroid Build Coastguard Worker   }
1091*77c1e3ccSAndroid Build Coastguard Worker   // Apply optical flow algorithm
1092*77c1e3ccSAndroid Build Coastguard Worker   pyramid_optical_flow(from_frame, to_frame, bit_depth, opfl_params, method,
1093*77c1e3ccSAndroid Build Coastguard Worker                        localmvs);
1094*77c1e3ccSAndroid Build Coastguard Worker 
1095*77c1e3ccSAndroid Build Coastguard Worker   // Update original mvs array
1096*77c1e3ccSAndroid Build Coastguard Worker   for (int j = 0; j < frame_height; j++) {
1097*77c1e3ccSAndroid Build Coastguard Worker     for (int i = 0; i < frame_width; i++) {
1098*77c1e3ccSAndroid Build Coastguard Worker       int idx = j * frame_width + i;
1099*77c1e3ccSAndroid Build Coastguard Worker       if (j + localmvs[idx].row < 0 || j + localmvs[idx].row >= frame_height ||
1100*77c1e3ccSAndroid Build Coastguard Worker           i + localmvs[idx].col < 0 || i + localmvs[idx].col >= frame_width) {
1101*77c1e3ccSAndroid Build Coastguard Worker         continue;
1102*77c1e3ccSAndroid Build Coastguard Worker       }
1103*77c1e3ccSAndroid Build Coastguard Worker       MV mv = { .row = (int16_t)round(8 * localmvs[idx].row),
1104*77c1e3ccSAndroid Build Coastguard Worker                 .col = (int16_t)round(8 * localmvs[idx].col) };
1105*77c1e3ccSAndroid Build Coastguard Worker       mvs[idx] = mv;
1106*77c1e3ccSAndroid Build Coastguard Worker     }
1107*77c1e3ccSAndroid Build Coastguard Worker   }
1108*77c1e3ccSAndroid Build Coastguard Worker 
1109*77c1e3ccSAndroid Build Coastguard Worker   filter_mvs(mv_filter, frame_height, frame_width, localmvs, mvs);
1110*77c1e3ccSAndroid Build Coastguard Worker 
1111*77c1e3ccSAndroid Build Coastguard Worker   aom_free(localmvs);
1112*77c1e3ccSAndroid Build Coastguard Worker }
1113*77c1e3ccSAndroid Build Coastguard Worker #endif
1114