Coverage Report

Created: 2026-05-16 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/work/svt-av1/Source/Lib/Codec/segmentation.c
Line
Count
Source
1
/*
2
* Copyright(c) 2019 Intel Corporation
3
* Copyright (c) 2016, Alliance for Open Media. All rights reserved
4
*
5
* This source code is subject to the terms of the BSD 2 Clause License and
6
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
7
* was not distributed with this source code in the LICENSE file, you can
8
* obtain it at https://www.aomedia.org/license/software-license. If the Alliance for Open
9
* Media Patent License 1.0 was not distributed with this source code in the
10
* PATENTS file, you can obtain it at https://www.aomedia.org/license/patent-license.
11
*/
12
13
#include "segmentation.h"
14
#include "segmentation_params.h"
15
#include "me_context.h"
16
#include "common_dsp_rtcd.h"
17
#if DEBUG_SEGMENT_QP || DEBUG_ROI
18
#include "svt_log.h"
19
#include <inttypes.h>
20
#endif
21
#include "deblocking_filter.h"
22
23
0
static uint16_t get_variance_for_cu(const BlockSize bsize, const int org_x, const int org_y, uint16_t* variance_ptr) {
24
0
    int index0, index1;
25
    //Assumes max CU size is 64
26
0
    switch (bsize) {
27
0
    case BLOCK_4X4:
28
0
    case BLOCK_4X8:
29
0
    case BLOCK_8X4:
30
0
    case BLOCK_8X8:
31
0
        index0 = index1 = ME_TIER_ZERO_PU_8x8_0 + ((org_x >> 3) + org_y);
32
0
        break;
33
34
0
    case BLOCK_8X16:
35
0
        index0 = ME_TIER_ZERO_PU_8x8_0 + ((org_x >> 3) + org_y);
36
0
        index1 = index0 + 1;
37
0
        break;
38
39
0
    case BLOCK_16X8:
40
0
        index0 = ME_TIER_ZERO_PU_8x8_0 + ((org_x >> 3) + org_y);
41
0
        index1 = index0 + org_y;
42
0
        break;
43
44
0
    case BLOCK_4X16:
45
0
    case BLOCK_16X4:
46
0
    case BLOCK_16X16:
47
0
        index0 = index1 = ME_TIER_ZERO_PU_16x16_0 + ((org_x >> 4) + (org_y >> 2));
48
0
        break;
49
50
0
    case BLOCK_16X32:
51
0
        index0 = ME_TIER_ZERO_PU_16x16_0 + ((org_x >> 4) + (org_y >> 2));
52
0
        index1 = index0 + 1;
53
0
        break;
54
55
0
    case BLOCK_32X16:
56
0
        index0 = ME_TIER_ZERO_PU_16x16_0 + ((org_x >> 4) + (org_y >> 2));
57
0
        index1 = index0 + (org_y >> 2);
58
0
        break;
59
60
0
    case BLOCK_8X32:
61
0
    case BLOCK_32X8:
62
0
    case BLOCK_32X32:
63
0
        index0 = index1 = ME_TIER_ZERO_PU_32x32_0 + ((org_x >> 5) + (org_y >> 4));
64
0
        break;
65
66
0
    case BLOCK_32X64:
67
0
        index0 = ME_TIER_ZERO_PU_32x32_0 + ((org_x >> 5) + (org_y >> 4));
68
0
        index1 = index0 + 1;
69
0
        break;
70
71
0
    case BLOCK_64X32:
72
0
        index0 = ME_TIER_ZERO_PU_32x32_0 + ((org_x >> 5) + (org_y >> 4));
73
0
        index1 = index0 + (org_y >> 4);
74
0
        break;
75
76
0
    case BLOCK_64X64:
77
0
    case BLOCK_16X64:
78
0
    case BLOCK_64X16:
79
0
    default:
80
0
        index0 = index1 = 0;
81
0
        break;
82
0
    }
83
0
    return (variance_ptr[index0] + variance_ptr[index1]) >> 1;
84
0
}
85
86
// org_x/y are the block location with respect to current SB origin
87
static void roi_map_apply_segmentation_based_quantization(PictureControlSet* pcs, SuperBlock* sb_ptr,
88
                                                          BlkStruct* blk_ptr, const BlockSize bsize, const int org_x,
89
0
                                                          const int org_y) {
90
0
    SequenceControlSet*    scs                 = pcs->ppcs->scs;
91
0
    const SvtAv1RoiMapEvt* roi_map             = pcs->ppcs->roi_map_evt;
92
0
    SegmentationParams*    segmentation_params = &pcs->ppcs->frm_hdr.segmentation_params;
93
0
    const int              stride_b64          = (scs->max_input_luma_width + 63) / 64;
94
0
    uint8_t                segment_id          = MAX_SEGMENTS;
95
0
    if (scs->seq_header.sb_size == BLOCK_64X64) {
96
0
        const int column_b64 = sb_ptr->org_x >> 6;
97
0
        const int row_b64    = sb_ptr->org_y >> 6;
98
0
        segment_id           = roi_map->b64_seg_map[row_b64 * stride_b64 + column_b64];
99
0
    } else { // sb128
100
        // 4 b64 blocks to check intersection
101
0
        const int b64_seg_columns[4] = {sb_ptr->org_x, sb_ptr->org_x + 64, sb_ptr->org_x, sb_ptr->org_x + 64};
102
0
        const int b64_seg_rows[4]    = {sb_ptr->org_y, sb_ptr->org_y, sb_ptr->org_y + 64, sb_ptr->org_y + 64};
103
0
        const int blk_org_x          = sb_ptr->org_x + org_x;
104
0
        const int blk_org_y          = sb_ptr->org_y + org_y;
105
0
        const int bwidth             = block_size_wide[bsize];
106
0
        const int bheight            = block_size_high[bsize];
107
0
        for (int i = 0; i < 4; ++i) {
108
0
            if (blk_org_x < b64_seg_columns[i] + 64 && blk_org_x + bwidth > b64_seg_columns[i] &&
109
0
                blk_org_y < b64_seg_rows[i] + 64 && blk_org_y + bheight > b64_seg_rows[i]) {
110
0
                const int column_b64 = b64_seg_columns[i] >> 6;
111
0
                const int row_b64    = b64_seg_rows[i] >> 6;
112
0
                segment_id           = MIN(segment_id, roi_map->b64_seg_map[row_b64 * stride_b64 + column_b64]);
113
0
            }
114
0
        }
115
0
    }
116
0
    assert(segment_id != MAX_SEGMENTS);
117
0
    if (segment_id == MAX_SEGMENTS) {
118
        // No intersection with any segment, assign to segment 0
119
0
        segment_id = 0;
120
0
    }
121
122
0
    for (int i = segment_id; i >= 0; i--) {
123
0
        int32_t q_index = pcs->ppcs->frm_hdr.quantization_params.base_q_idx +
124
0
            segmentation_params->feature_data[i][SEG_LVL_ALT_Q];
125
        // Avoid lossless since SVT-AV1 doesn't support it.
126
0
        if (q_index > 0) {
127
0
            blk_ptr->segment_id = i;
128
0
            break;
129
0
        }
130
0
    }
131
0
    assert(pcs->ppcs->frm_hdr.quantization_params.base_q_idx +
132
0
               segmentation_params->feature_data[blk_ptr->segment_id][SEG_LVL_ALT_Q] >
133
0
           0);
134
0
}
135
136
void svt_aom_apply_segmentation_based_quantization(PictureControlSet* pcs, SuperBlock* sb_ptr, BlkStruct* blk_ptr,
137
0
                                                   const BlockSize bsize, const int org_x, const int org_y) {
138
0
    if (pcs->ppcs->roi_map_evt != NULL) {
139
0
        roi_map_apply_segmentation_based_quantization(pcs, sb_ptr, blk_ptr, bsize, org_x, org_y);
140
0
        return;
141
0
    }
142
0
    uint16_t*           variance_ptr        = pcs->ppcs->variance[sb_ptr->index];
143
0
    SegmentationParams* segmentation_params = &pcs->ppcs->frm_hdr.segmentation_params;
144
0
    uint16_t            variance            = get_variance_for_cu(bsize, org_x, org_y, variance_ptr);
145
0
    blk_ptr->segment_id                     = 0;
146
0
    for (int i = MAX_SEGMENTS - 1; i >= 0; i--) {
147
0
        if (variance <= segmentation_params->variance_bin_edge[i]) {
148
0
            int32_t q_index = pcs->ppcs->frm_hdr.quantization_params.base_q_idx +
149
0
                segmentation_params->feature_data[i][SEG_LVL_ALT_Q];
150
            // Avoid lossless since SVT-AV1 doesn't support it.
151
            // Spec: Uncompressed header syntax and get_qindex(1, segmentID). And spec 5.11.34. Residual syntax. force TX_4X4 when lossless.
152
0
            if (q_index > 0) {
153
0
                blk_ptr->segment_id = i;
154
0
                break;
155
0
            }
156
0
        }
157
0
    }
158
0
}
159
160
0
static void roi_map_setup_segmentation(PictureControlSet* pcs, SequenceControlSet* scs) {
161
0
    UNUSED(scs);
162
0
    SvtAv1RoiMapEvt*    roi_map                       = pcs->ppcs->roi_map_evt;
163
0
    SegmentationParams* segmentation_params           = &pcs->ppcs->frm_hdr.segmentation_params;
164
0
    segmentation_params->segmentation_enabled         = true;
165
0
    segmentation_params->segmentation_update_data     = true;
166
0
    segmentation_params->segmentation_update_map      = true;
167
0
    segmentation_params->segmentation_temporal_update = false;
168
169
0
    for (int i = 0; i <= roi_map->max_seg_id; i++) {
170
0
        segmentation_params->feature_enabled[i][SEG_LVL_ALT_Q]      = 1;
171
0
        segmentation_params->feature_data[i][SEG_LVL_ALT_Q]         = roi_map->seg_qp[i];
172
0
        segmentation_params->feature_enabled[i][SEG_LVL_ALT_LF_Y_V] = 1;
173
0
        segmentation_params->feature_enabled[i][SEG_LVL_ALT_LF_Y_H] = 1;
174
0
        segmentation_params->feature_enabled[i][SEG_LVL_ALT_LF_U]   = 1;
175
0
        segmentation_params->feature_enabled[i][SEG_LVL_ALT_LF_V]   = 1;
176
0
    }
177
178
    // setup loop filter data
179
0
    int32_t filter_level[4];
180
0
    uint8_t qindex = pcs->ppcs->frm_hdr.quantization_params.base_q_idx;
181
0
    svt_av1_pick_filter_level_by_q(pcs, qindex, filter_level);
182
0
    for (int i = 0; i <= roi_map->max_seg_id; i++) {
183
0
        uint8_t qindex_seg = CLIP3(0, 255, qindex + segmentation_params->feature_data[i][SEG_LVL_ALT_Q]);
184
0
        int32_t filter_level_seg[4];
185
0
        svt_av1_pick_filter_level_by_q(pcs, qindex_seg, filter_level_seg);
186
0
        if (segmentation_params->feature_enabled[i][SEG_LVL_ALT_LF_Y_V]) {
187
0
            segmentation_params->feature_data[i][SEG_LVL_ALT_LF_Y_V] = filter_level_seg[0] - filter_level[0];
188
0
        }
189
0
        if (segmentation_params->feature_enabled[i][SEG_LVL_ALT_LF_Y_H]) {
190
0
            segmentation_params->feature_data[i][SEG_LVL_ALT_LF_Y_H] = filter_level_seg[1] - filter_level[1];
191
0
        }
192
0
        if (segmentation_params->feature_enabled[i][SEG_LVL_ALT_LF_U]) {
193
0
            segmentation_params->feature_data[i][SEG_LVL_ALT_LF_U] = filter_level_seg[2] - filter_level[2];
194
0
        }
195
0
        if (segmentation_params->feature_enabled[i][SEG_LVL_ALT_LF_V]) {
196
0
            segmentation_params->feature_data[i][SEG_LVL_ALT_LF_V] = filter_level_seg[3] - filter_level[3];
197
0
        }
198
0
    }
199
#if DEBUG_ROI
200
    if (segmentation_params->feature_enabled[0][SEG_LVL_ALT_LF_Y_V]) {
201
        SVT_LOG("frame %" PRIu64 ", lf_y_v: %d %d %d %d %d %d %d %d\n",
202
                pcs->picture_number,
203
                segmentation_params->feature_data[0][SEG_LVL_ALT_LF_Y_V],
204
                segmentation_params->feature_data[1][SEG_LVL_ALT_LF_Y_V],
205
                segmentation_params->feature_data[2][SEG_LVL_ALT_LF_Y_V],
206
                segmentation_params->feature_data[3][SEG_LVL_ALT_LF_Y_V],
207
                segmentation_params->feature_data[4][SEG_LVL_ALT_LF_Y_V],
208
                segmentation_params->feature_data[5][SEG_LVL_ALT_LF_Y_V],
209
                segmentation_params->feature_data[6][SEG_LVL_ALT_LF_Y_V],
210
                segmentation_params->feature_data[7][SEG_LVL_ALT_LF_Y_V]);
211
    }
212
    if (segmentation_params->feature_enabled[0][SEG_LVL_ALT_LF_Y_H]) {
213
        SVT_LOG("frame %" PRIu64 ", lf_y_h: %d %d %d %d %d %d %d %d\n",
214
                pcs->picture_number,
215
                segmentation_params->feature_data[0][SEG_LVL_ALT_LF_Y_H],
216
                segmentation_params->feature_data[1][SEG_LVL_ALT_LF_Y_H],
217
                segmentation_params->feature_data[2][SEG_LVL_ALT_LF_Y_H],
218
                segmentation_params->feature_data[3][SEG_LVL_ALT_LF_Y_H],
219
                segmentation_params->feature_data[4][SEG_LVL_ALT_LF_Y_H],
220
                segmentation_params->feature_data[5][SEG_LVL_ALT_LF_Y_H],
221
                segmentation_params->feature_data[6][SEG_LVL_ALT_LF_Y_H],
222
                segmentation_params->feature_data[7][SEG_LVL_ALT_LF_Y_H]);
223
    }
224
#endif
225
0
    calculate_segmentation_data(segmentation_params);
226
0
}
227
228
474
void svt_aom_setup_segmentation(PictureControlSet* pcs, SequenceControlSet* scs) {
229
474
    if (pcs->ppcs->roi_map_evt != NULL) {
230
0
        roi_map_setup_segmentation(pcs, scs);
231
0
        return;
232
0
    }
233
474
    SegmentationParams* segmentation_params   = &pcs->ppcs->frm_hdr.segmentation_params;
234
474
    segmentation_params->segmentation_enabled = scs->static_config.aq_mode == 1;
235
474
    if (segmentation_params->segmentation_enabled) {
236
0
        segmentation_params->segmentation_update_data =
237
0
            1; //always updating for now. Need to set this based on actual deltas
238
0
        segmentation_params->segmentation_update_map      = 1;
239
0
        segmentation_params->segmentation_temporal_update = false; //!frame_is_intra_only(pcs->ppcs);
240
0
        find_segment_qps(segmentation_params, pcs);
241
0
        for (int i = 0; i < MAX_SEGMENTS; i++) {
242
0
            segmentation_params->feature_enabled[i][SEG_LVL_ALT_Q] = 1;
243
0
        }
244
245
0
        calculate_segmentation_data(segmentation_params);
246
0
    }
247
474
}
248
249
0
void calculate_segmentation_data(SegmentationParams* segmentation_params) {
250
0
    for (int i = 0; i < MAX_SEGMENTS; i++) {
251
0
        for (int j = 0; j < SEG_LVL_MAX; j++) {
252
0
            if (segmentation_params->feature_enabled[i][j]) {
253
0
                segmentation_params->last_active_seg_id = i;
254
0
                if (j >= SEG_LVL_REF_FRAME) {
255
0
                    segmentation_params->seg_id_pre_skip = 1;
256
0
                }
257
0
            }
258
0
        }
259
0
    }
260
0
}
261
262
void find_segment_qps(SegmentationParams* segmentation_params,
263
0
                      PictureControlSet*  pcs) { //QP needs to be specified as qpindex, not qp.
264
0
    uint16_t    min_var = UINT16_MAX, max_var = MIN_UNSIGNED_VALUE, avg_var = 0;
265
0
    const float strength = 2; //to tune
266
267
    // get range of variance
268
0
    for (uint32_t sb_idx = 0; sb_idx < pcs->b64_total_count; ++sb_idx) {
269
0
        uint16_t* variance_ptr = pcs->ppcs->variance[sb_idx];
270
0
        uint32_t  var_index, local_avg = 0;
271
        // Loop over all 8x8s in a 64x64
272
0
        for (var_index = ME_TIER_ZERO_PU_8x8_0; var_index <= ME_TIER_ZERO_PU_8x8_63; var_index++) {
273
0
            max_var = MAX(max_var, variance_ptr[var_index]);
274
0
            min_var = MIN(min_var, variance_ptr[var_index]);
275
0
            local_avg += variance_ptr[var_index];
276
0
        }
277
0
        avg_var += (local_avg >> 6);
278
0
    }
279
0
    avg_var /= pcs->b64_total_count;
280
0
    avg_var = svt_log2f(avg_var);
281
282
    //get variance bin edges & QPs
283
0
    uint16_t min_var_log = svt_log2f(MAX(1, min_var));
284
0
    uint16_t max_var_log = svt_log2f(MAX(1, max_var));
285
0
    uint16_t step_size   = (uint16_t)(max_var_log - min_var_log) <= MAX_SEGMENTS
286
0
          ? 1
287
0
          : ROUND(((max_var_log - min_var_log)) / MAX_SEGMENTS);
288
0
    uint16_t bin_edge    = min_var_log + step_size;
289
0
    uint16_t bin_center  = bin_edge >> 1;
290
291
0
    for (int i = MAX_SEGMENTS - 1; i >= 0; i--) {
292
0
        segmentation_params->variance_bin_edge[i]           = POW2(bin_edge);
293
0
        segmentation_params->feature_data[i][SEG_LVL_ALT_Q] = ROUND((uint16_t)strength *
294
0
                                                                    (MAX(1, bin_center) - avg_var));
295
0
        bin_edge += step_size;
296
0
        bin_center += step_size;
297
0
    }
298
0
    if (segmentation_params->feature_data[0][SEG_LVL_ALT_Q] < 0) {
299
        // avoid lossless block
300
0
        segmentation_params->feature_data[0][SEG_LVL_ALT_Q] = 0;
301
0
    }
302
#if DEBUG_SEGMENT_QP
303
    SVT_LOG("frame %d, base_qindex %d, seg qp offset: %d %d %d %d %d %d %d %d\n",
304
            (int)pcs->picture_number,
305
            pcs->ppcs->frm_hdr.quantization_params.base_q_idx,
306
            segmentation_params->feature_data[0][SEG_LVL_ALT_Q],
307
            segmentation_params->feature_data[1][SEG_LVL_ALT_Q],
308
            segmentation_params->feature_data[2][SEG_LVL_ALT_Q],
309
            segmentation_params->feature_data[3][SEG_LVL_ALT_Q],
310
            segmentation_params->feature_data[4][SEG_LVL_ALT_Q],
311
            segmentation_params->feature_data[5][SEG_LVL_ALT_Q],
312
            segmentation_params->feature_data[6][SEG_LVL_ALT_Q],
313
            segmentation_params->feature_data[7][SEG_LVL_ALT_Q]);
314
#endif
315
0
}