/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 | } |