/work/svt-av1/Source/Lib/Codec/global_motion.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2016, Alliance for Open Media. All rights reserved |
3 | | * |
4 | | * This source code is subject to the terms of the BSD 2 Clause License and |
5 | | * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License |
6 | | * was not distributed with this source code in the LICENSE file, you can |
7 | | * obtain it at https://www.aomedia.org/license/software-license. If the Alliance for Open |
8 | | * Media Patent License 1.0 was not distributed with this source code in the |
9 | | * PATENTS file, you can obtain it at https://www.aomedia.org/license/patent-license. |
10 | | */ |
11 | | |
12 | | #include <stdio.h> |
13 | | #include <stdlib.h> |
14 | | #include <math.h> |
15 | | |
16 | | #include "global_motion.h" |
17 | | #include "utility.h" |
18 | | #include "corner_detect.h" |
19 | | #include "corner_match.h" |
20 | | #include "ransac.h" |
21 | | |
22 | | #include "enc_warped_motion.h" |
23 | | |
24 | | // Border over which to compute the global motion |
25 | 0 | #define ERRORADV_BORDER 0 |
26 | | |
27 | | static const double erroradv_tr[] = {0.65, 0.50, 0.45}; |
28 | | static const double erroradv_prod_tr[] = {20000, 15000, 14000}; |
29 | | |
30 | 0 | int svt_av1_is_enough_erroradvantage(double best_erroradvantage, int params_cost, int erroradv_type) { |
31 | 0 | assert(erroradv_type < GM_ERRORADV_TR_TYPES); |
32 | 0 | return best_erroradvantage < erroradv_tr[erroradv_type] && |
33 | 0 | best_erroradvantage * params_cost < erroradv_prod_tr[erroradv_type]; |
34 | 0 | } |
35 | | |
36 | | static void convert_to_params(const double* params, int32_t* model) { |
37 | | int i; |
38 | | model[0] = (int32_t)floor(params[0] * (1 << GM_TRANS_PREC_BITS) + 0.5); |
39 | | model[1] = (int32_t)floor(params[1] * (1 << GM_TRANS_PREC_BITS) + 0.5); |
40 | | model[0] = (int32_t)clamp(model[0], GM_TRANS_MIN, GM_TRANS_MAX) * GM_TRANS_DECODE_FACTOR; |
41 | | model[1] = (int32_t)clamp(model[1], GM_TRANS_MIN, GM_TRANS_MAX) * GM_TRANS_DECODE_FACTOR; |
42 | | |
43 | | for (i = 2; i < 6; ++i) { |
44 | | const int diag_value = ((i == 2 || i == 5) ? (1 << GM_ALPHA_PREC_BITS) : 0); |
45 | | model[i] = (int32_t)floor(params[i] * (1 << GM_ALPHA_PREC_BITS) + 0.5); |
46 | | model[i] = (int32_t)clamp(model[i] - diag_value, GM_ALPHA_MIN, GM_ALPHA_MAX); |
47 | | model[i] = (model[i] + diag_value) * GM_ALPHA_DECODE_FACTOR; |
48 | | } |
49 | | } |
50 | | |
51 | | static INLINE TransformationType get_wmtype(const WarpedMotionParams* gm) { |
52 | | if (gm->wmmat[5] == (1 << WARPEDMODEL_PREC_BITS) && !gm->wmmat[4] && gm->wmmat[2] == (1 << WARPEDMODEL_PREC_BITS) && |
53 | | !gm->wmmat[3]) { |
54 | | return ((!gm->wmmat[1] && !gm->wmmat[0]) ? IDENTITY : TRANSLATION); |
55 | | } |
56 | | if (gm->wmmat[2] == gm->wmmat[5] && gm->wmmat[3] == -gm->wmmat[4]) { |
57 | | return ROTZOOM; |
58 | | } else { |
59 | | return AFFINE; |
60 | | } |
61 | | } |
62 | | |
63 | 0 | void svt_av1_convert_model_to_params(const double* params, WarpedMotionParams* model) { |
64 | 0 | convert_to_params(params, model->wmmat); |
65 | 0 | model->wmtype = get_wmtype(model); |
66 | 0 | model->invalid = 0; |
67 | 0 | } |
68 | | |
69 | | // Adds some offset to a global motion parameter and handles |
70 | | // all of the necessary precision shifts, clamping, and |
71 | | // zero-centering. |
72 | | static int32_t add_param_offset(int param_index, int32_t param_value, int32_t offset) { |
73 | | const int scale_vals[2] = {GM_TRANS_PREC_DIFF, GM_ALPHA_PREC_DIFF}; |
74 | | const int clamp_vals[2] = {GM_TRANS_MAX, GM_ALPHA_MAX}; |
75 | | // type of param: 0 - translation, 1 - affine |
76 | | const int param_type = (param_index < 2 ? 0 : 1); |
77 | | const int is_one_centered = (param_index == 2 || param_index == 5); |
78 | | |
79 | | // Make parameter zero-centered and offset the shift that was done to make |
80 | | // it compatible with the warped model |
81 | | param_value = (param_value - (is_one_centered << WARPEDMODEL_PREC_BITS)) >> scale_vals[param_type]; |
82 | | // Add desired offset to the rescaled/zero-centered parameter |
83 | | param_value += offset; |
84 | | // Clamp the parameter so it does not overflow the number of bits allotted |
85 | | // to it in the bitstream |
86 | | param_value = (int32_t)clamp(param_value, -clamp_vals[param_type], clamp_vals[param_type]); |
87 | | // Rescale the parameter to WARPEDMODEL_PRECISION_BITS so it is compatible |
88 | | // with the warped motion library |
89 | | param_value *= (1 << scale_vals[param_type]); |
90 | | |
91 | | // Undo the zero-centering step if necessary |
92 | | return param_value + (is_one_centered << WARPEDMODEL_PREC_BITS); |
93 | | } |
94 | | |
95 | | static void force_wmtype(WarpedMotionParams* wm, TransformationType wmtype) { |
96 | | switch (wmtype) { |
97 | | case IDENTITY: |
98 | | wm->wmmat[0] = 0; |
99 | | wm->wmmat[1] = 0; |
100 | | AOM_FALLTHROUGH_INTENDED; |
101 | | case TRANSLATION: |
102 | | wm->wmmat[2] = 1 << WARPEDMODEL_PREC_BITS; |
103 | | wm->wmmat[3] = 0; |
104 | | AOM_FALLTHROUGH_INTENDED; |
105 | | case ROTZOOM: |
106 | | wm->wmmat[4] = -wm->wmmat[3]; |
107 | | wm->wmmat[5] = wm->wmmat[2]; |
108 | | AOM_FALLTHROUGH_INTENDED; |
109 | | case AFFINE: |
110 | | break; |
111 | | default: |
112 | | assert(0); |
113 | | } |
114 | | wm->wmtype = wmtype; |
115 | | } |
116 | | |
117 | | int64_t svt_av1_refine_integerized_param(GmControls* gm_ctrls, WarpedMotionParams* wm, TransformationType wmtype, |
118 | | uint8_t* ref, int r_width, int r_height, int r_stride, uint8_t* dst, |
119 | | int d_width, int d_height, int d_stride, int n_refinements, uint8_t chess_refn, |
120 | 0 | int64_t best_frame_error, uint32_t pic_sad, int params_cost) { |
121 | 0 | static const int max_trans_model_params[TRANS_TYPES] = {0, 2, 4, 6}; |
122 | 0 | const int border = ERRORADV_BORDER; |
123 | 0 | int i = 0, p; |
124 | 0 | int n_params = max_trans_model_params[wmtype]; |
125 | 0 | int32_t* param_mat = wm->wmmat; |
126 | 0 | int64_t step_error, best_error; |
127 | 0 | int32_t step; |
128 | 0 | int32_t* param; |
129 | 0 | int32_t curr_param; |
130 | 0 | int32_t best_param; |
131 | |
|
132 | 0 | force_wmtype(wm, wmtype); |
133 | 0 | best_error = svt_av1_warp_error(wm, |
134 | 0 | ref, |
135 | 0 | r_width, |
136 | 0 | r_height, |
137 | 0 | r_stride, |
138 | 0 | dst + border * d_stride + border, |
139 | 0 | border, |
140 | 0 | border, |
141 | 0 | d_width - 2 * border, |
142 | 0 | d_height - 2 * border, |
143 | 0 | d_stride, |
144 | 0 | 0, |
145 | 0 | 0, |
146 | 0 | chess_refn, |
147 | 0 | best_frame_error); |
148 | 0 | best_error = AOMMIN(best_error, best_frame_error); |
149 | 0 | if (gm_ctrls->rfn_early_exit && |
150 | 0 | !svt_av1_is_enough_erroradvantage((double)best_error / pic_sad, params_cost, GM_ERRORADV_TR_1)) { |
151 | 0 | return best_error; |
152 | 0 | } |
153 | 0 | step = 1 << (5 - 1); //initial step=16 |
154 | 0 | for (i = 0; i < n_refinements; i++, step >>= 1) { |
155 | 0 | for (p = 0; p < n_params; ++p) { |
156 | | // Skip searches for parameters that are forced to be 0 |
157 | 0 | param = param_mat + p; |
158 | 0 | curr_param = *param; |
159 | 0 | best_param = curr_param; |
160 | | // look to the left |
161 | 0 | *param = add_param_offset(p, curr_param, -step); |
162 | 0 | step_error = svt_av1_warp_error(wm, |
163 | 0 | ref, |
164 | 0 | r_width, |
165 | 0 | r_height, |
166 | 0 | r_stride, |
167 | 0 | dst + border * d_stride + border, |
168 | 0 | border, |
169 | 0 | border, |
170 | 0 | d_width - 2 * border, |
171 | 0 | d_height - 2 * border, |
172 | 0 | d_stride, |
173 | 0 | 0, |
174 | 0 | 0, |
175 | 0 | chess_refn, |
176 | 0 | best_error); |
177 | 0 | if (step_error < best_error) { |
178 | 0 | best_error = step_error; |
179 | 0 | best_param = *param; |
180 | 0 | } |
181 | | |
182 | | // look to the right |
183 | 0 | *param = add_param_offset(p, curr_param, step); |
184 | 0 | step_error = svt_av1_warp_error(wm, |
185 | 0 | ref, |
186 | 0 | r_width, |
187 | 0 | r_height, |
188 | 0 | r_stride, |
189 | 0 | dst + border * d_stride + border, |
190 | 0 | border, |
191 | 0 | border, |
192 | 0 | d_width - 2 * border, |
193 | 0 | d_height - 2 * border, |
194 | 0 | d_stride, |
195 | 0 | 0, |
196 | 0 | 0, |
197 | 0 | chess_refn, |
198 | 0 | best_error); |
199 | 0 | if (step_error < best_error) { |
200 | 0 | best_error = step_error; |
201 | 0 | best_param = *param; |
202 | 0 | } |
203 | 0 | *param = best_param; |
204 | 0 | } |
205 | 0 | } |
206 | 0 | force_wmtype(wm, wmtype); |
207 | 0 | wm->wmtype = get_wmtype(wm); |
208 | 0 | return best_error; |
209 | 0 | } |
210 | | |
211 | | // Generate the corresponding points for the current ref frame. The corners of the current frame are input. |
212 | | // The function will compute the corners of the ref frame and then generate the correspondence points. |
213 | | static void correspondence_from_corners(GmControls* gm_ctrls, uint8_t* frm_buffer, int frm_width, int frm_height, |
214 | | int frm_stride, int* frm_corners, int num_frm_corners, uint8_t* ref, |
215 | 0 | int ref_stride, Correspondence* correspondences, int* num_correspondences) { |
216 | 0 | int ref_corners[2 * MAX_CORNERS]; |
217 | |
|
218 | 0 | int num_ref_corners = svt_av1_fast_corner_detect( |
219 | 0 | (unsigned char*)ref, frm_width, frm_height, ref_stride, ref_corners, MAX_CORNERS); |
220 | |
|
221 | 0 | num_ref_corners = num_ref_corners * gm_ctrls->corners / 4; |
222 | 0 | num_frm_corners = num_frm_corners * gm_ctrls->corners / 4; |
223 | | |
224 | | // find correspondences between the two images |
225 | 0 | *num_correspondences = svt_av1_determine_correspondence(frm_buffer, |
226 | 0 | (int*)frm_corners, |
227 | 0 | num_frm_corners, |
228 | 0 | ref, |
229 | 0 | (int*)ref_corners, |
230 | 0 | num_ref_corners, |
231 | 0 | frm_width, |
232 | 0 | frm_height, |
233 | 0 | frm_stride, |
234 | 0 | ref_stride, |
235 | 0 | correspondences, |
236 | 0 | gm_ctrls->match_sz); |
237 | 0 | } |
238 | | |
239 | | static void correspondence_from_mvs(PictureParentControlSet* pcs, Correspondence* correspondences, |
240 | 0 | int* num_correspondences, uint8_t list_idx, uint8_t ref_idx) { |
241 | 0 | int count_correspondences = 0; |
242 | | // 0: 64x64, 1: 32x32, 2: 16x16, 3: 8x8 |
243 | 0 | const CorrespondenceMethod mv_search_lvl = pcs->gm_ctrls.correspondence_method; |
244 | 0 | assert(mv_search_lvl < CORNERS); |
245 | 0 | const int block_size = 64 >> mv_search_lvl; |
246 | 0 | const int blocks_per_line = 1 << mv_search_lvl; |
247 | 0 | const int num_blocks_per_sb = blocks_per_line * blocks_per_line; |
248 | 0 | const int starting_n_idx = mv_search_lvl == MV_64x64 ? 0 |
249 | 0 | : mv_search_lvl == MV_32x32 ? 1 |
250 | 0 | : mv_search_lvl == MV_16x16 ? 5 |
251 | 0 | : 21 /*MV_8x8*/; |
252 | 0 | const uint16_t pic_b64_width = (uint16_t)((pcs->aligned_width + pcs->scs->b64_size - 1) / pcs->scs->b64_size); |
253 | 0 | const uint16_t pic_b64_height = (uint16_t)((pcs->aligned_height + pcs->scs->b64_size - 1) / pcs->scs->b64_size); |
254 | 0 | assert(pcs->b64_total_count == pic_b64_width * pic_b64_height); |
255 | |
|
256 | 0 | for (uint16_t b64_y = 0; b64_y < pic_b64_height; b64_y++) { |
257 | 0 | for (uint16_t b64_x = 0; b64_x < pic_b64_width; b64_x++) { |
258 | 0 | uint16_t b64_idx = b64_y * pic_b64_width + b64_x; |
259 | 0 | for (int i = 0; i < num_blocks_per_sb; i++) { |
260 | | // If the starting x/y position is outside the frame, don't include it |
261 | 0 | if ((b64_x * pcs->scs->b64_size) + (i % blocks_per_line) * block_size >= pcs->aligned_width || |
262 | 0 | (b64_y * pcs->scs->b64_size) + (i / blocks_per_line) * block_size >= pcs->aligned_height) { |
263 | 0 | continue; |
264 | 0 | } |
265 | 0 | uint8_t n_idx = starting_n_idx + i; |
266 | |
|
267 | 0 | if (!pcs->enable_me_8x8) { |
268 | 0 | if (n_idx >= MAX_SB64_PU_COUNT_NO_8X8) { |
269 | 0 | n_idx = me_idx_85_8x8_to_16x16_conversion[n_idx - MAX_SB64_PU_COUNT_NO_8X8]; |
270 | 0 | } |
271 | 0 | if (!pcs->enable_me_16x16) { |
272 | 0 | if (n_idx >= MAX_SB64_PU_COUNT_WO_16X16) { |
273 | 0 | n_idx = me_idx_16x16_to_parent_32x32_conversion[n_idx - MAX_SB64_PU_COUNT_WO_16X16]; |
274 | 0 | } |
275 | 0 | } |
276 | 0 | } |
277 | |
|
278 | 0 | uint8_t total_me_cnt = pcs->pa_me_data->me_results[b64_idx]->total_me_candidate_index[n_idx]; |
279 | 0 | MeCandidate* me_cand_array = &( |
280 | 0 | pcs->pa_me_data->me_results[b64_idx]->me_candidate_array[n_idx * pcs->pa_me_data->max_cand]); |
281 | | |
282 | | // Find MV for the block for the appropriate reference frame |
283 | 0 | Mv mv; |
284 | 0 | bool found_mv = false; |
285 | 0 | for (uint32_t me_cand_i = 0; me_cand_i < total_me_cnt; ++me_cand_i) { |
286 | 0 | const MeCandidate* me_cand = &me_cand_array[me_cand_i]; |
287 | 0 | assert(me_cand->direction <= 2); |
288 | | |
289 | | // don't consider bipred candidates |
290 | 0 | if (me_cand->direction == 2) { |
291 | 0 | continue; |
292 | 0 | } |
293 | | |
294 | 0 | if (me_cand->direction == 0) { |
295 | 0 | if (list_idx == me_cand->ref0_list && ref_idx == me_cand->ref_idx_l0) { |
296 | 0 | mv.as_int = pcs->pa_me_data->me_results[b64_idx] |
297 | 0 | ->me_mv_array[n_idx * pcs->pa_me_data->max_refs + |
298 | 0 | (list_idx ? pcs->pa_me_data->max_l0 : 0) + ref_idx] |
299 | 0 | .as_int; |
300 | 0 | found_mv = true; |
301 | 0 | break; |
302 | 0 | } |
303 | 0 | } |
304 | 0 | if (me_cand->direction == 1) { |
305 | 0 | if (list_idx == me_cand->ref1_list && ref_idx == me_cand->ref_idx_l1) { |
306 | 0 | mv.as_int = pcs->pa_me_data->me_results[b64_idx] |
307 | 0 | ->me_mv_array[n_idx * pcs->pa_me_data->max_refs + |
308 | 0 | (list_idx ? pcs->pa_me_data->max_l0 : 0) + ref_idx] |
309 | 0 | .as_int; |
310 | 0 | found_mv = true; |
311 | 0 | break; |
312 | 0 | } |
313 | 0 | } |
314 | 0 | } |
315 | |
|
316 | 0 | if (found_mv) { |
317 | | // clang-format off |
318 | 0 | const int shift = pcs->gm_downsample_level == GM_DOWN ? 1 |
319 | 0 | : pcs->gm_downsample_level == GM_DOWN16 ? 2 |
320 | 0 | : 0; |
321 | 0 | const uint8_t b64_size = pcs->scs->b64_size; |
322 | 0 | correspondences[count_correspondences].x = |
323 | 0 | ((b64_x * b64_size) + (i % blocks_per_line) * block_size) >> shift; // x |
324 | 0 | correspondences[count_correspondences].y = |
325 | 0 | ((b64_y * b64_size) + (i / blocks_per_line) * block_size) >> shift; // y |
326 | 0 | correspondences[count_correspondences].rx = |
327 | 0 | ((b64_x * b64_size) + (i % blocks_per_line) * block_size + mv.x) >> shift; // rx |
328 | 0 | correspondences[count_correspondences].ry = |
329 | 0 | ((b64_y * b64_size) + (i / blocks_per_line) * block_size + mv.y) >> shift; // ry |
330 | 0 | count_correspondences++; |
331 | | // clang-format on |
332 | 0 | } |
333 | 0 | } |
334 | 0 | } |
335 | 0 | } |
336 | 0 | *num_correspondences = count_correspondences; |
337 | 0 | } |
338 | | |
339 | | // Generate the corresponding points for the current ref frame. The corners of the current frame are input. |
340 | | // The function will compute the corners of the ref frame and then generate the correspondence points. |
341 | | void gm_compute_correspondence(PictureParentControlSet* pcs, uint8_t* frm_buffer, int frm_width, int frm_height, |
342 | | int frm_stride, int* frm_corners, int num_frm_corners, uint8_t* ref, int ref_stride, |
343 | | Correspondence* correspondences, int* num_correspondences, uint8_t list_idx, |
344 | 0 | uint8_t ref_idx) { |
345 | 0 | if (pcs->gm_ctrls.correspondence_method == CORNERS) { |
346 | 0 | correspondence_from_corners(&pcs->gm_ctrls, |
347 | 0 | frm_buffer, |
348 | 0 | frm_width, |
349 | 0 | frm_height, |
350 | 0 | frm_stride, |
351 | 0 | frm_corners, |
352 | 0 | num_frm_corners, |
353 | 0 | ref, |
354 | 0 | ref_stride, |
355 | 0 | correspondences, |
356 | 0 | num_correspondences); |
357 | 0 | } else { |
358 | 0 | assert(pcs->gm_ctrls.correspondence_method <= MV_8x8 && pcs->gm_ctrls.correspondence_method >= MV_64x64); |
359 | 0 | correspondence_from_mvs(pcs, correspondences, num_correspondences, list_idx, ref_idx); |
360 | 0 | } |
361 | 0 | } |
362 | | |
363 | | // Take the input correspondences and determine the params for the gm type via ransac |
364 | | void determine_gm_params(TransformationType type, MotionModel* params_by_motion, int num_motions, |
365 | 0 | Correspondence* correspondences, int num_correspondences) { |
366 | | bool mem_alloc_failed; |
367 | 0 | svt_aom_ransac(correspondences, num_correspondences, type, params_by_motion, num_motions, &mem_alloc_failed); |
368 | 0 | } |