Coverage Report

Created: 2026-05-30 06:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}