libaom 源码分析:帧内方向预测模式
帧内方向预测模式原理
- 方向帧内预测模式被应用到帧内预测,通过给定的方向模式来模拟局部纹理信息;方向帧内模式由名义模式和角度增量表示,名义模式类似 VP9,有 8 种角度(45°、67°、90°、113°、135°、157°、180°、203°),角度增量的索引值范围-3 ~ +3,零增量表示名义模式,预测角度是由名义模式加上一个角度增量表示;
- 一共有 56 种方向预测模式;
- 名义模式索引和角度增量索引是分开信号传输,且名义模式的索引再相关角度增量索引之前被信号传输;
- 值得注意的是,对于小尺寸(4x4、4x8、8x4),扩展帧内预测角度带来的编码增益很小,因此名义模式被使用而角度增量索引没有被信号传输;
libaom相关源码分析
- 方向帧内模式由名义模式和角度增量表示, 8 种名义角度模式即V_PRED、H_PRED、D45_PRED、D135_PRED、D113_PRED、D157_PRED、D203_PRED、D67_PRED;在 enums.h 文件中定义。
- 函数逻辑关系:
- av1_rd_pick_intra_sby_mode 函数
- 该函数通过评估不同帧内模式的 RD 成本来选择最佳模式。这个过程涉及到多种跳过条件,以优化搜索效率,并减少不必要的计算。通过这种方式,AV1 编码器能够为每个块选择最合适的模式,从而提高编码效率和图像质量。
- 模式评估:
- 遍历所有帧内模式,对于每种模式,计算其 RD 成本,并与当前最佳 RD 值进行比较。
- 方向模式处理:
- 如果是方向模式,根据角度调整需要的邻近像素。
- 调色板模式处理:
- 如果允许调色板模式,调用 av1_rd_pick_palette_intra_sby 函数进行调色板模式的 RD 评估。
- filter_intra 模式处理:
- 如果 beat_best_rd 为真且当前块大小允许使用 filter_intra 模式,调用 rd_pick_filter_intra_sby 函数进行 filter_intra 模式的 RD 评估。
- 维纳模式处理:
- 如果启用了维纳模式处理,对几个最佳模式进行额外的变换搜索。
- 更新最佳模式信息:
- 更新最佳模式的 RD 值、编码率、失真度等信息。
// Finds the best non-intrabc mode on an intra frame.
int64_t av1_rd_pick_intra_sby_mode(const AV1_COMP *const cpi, MACROBLOCK *x,
int *rate, int *rate_tokenonly,
int64_t *distortion, uint8_t *skippable,
BLOCK_SIZE bsize, int64_t best_rd,
PICK_MODE_CONTEXT *ctx) {
MACROBLOCKD *const xd = &x->e_mbd;
MB_MODE_INFO *const mbmi = xd->mi[0];
assert(!is_inter_block(mbmi));
int64_t best_model_rd = INT64_MAX;
int is_directional_mode;
uint8_t directional_mode_skip_mask[INTRA_MODES] = { 0 };
// Flag to check rd of any intra mode is better than best_rd passed to this
// function
int beat_best_rd = 0;
const int *bmode_costs;
const IntraModeCfg *const intra_mode_cfg = &cpi->oxcf.intra_mode_cfg;
PALETTE_MODE_INFO *const pmi = &mbmi->palette_mode_info;
const int try_palette =
cpi->oxcf.tool_cfg.enable_palette &&
av1_allow_palette(cpi->common.features.allow_screen_content_tools,
mbmi->bsize);
uint8_t *best_palette_color_map =
try_palette ? x->palette_buffer->best_palette_color_map : NULL;
const MB_MODE_INFO *above_mi = xd->above_mbmi;
const MB_MODE_INFO *left_mi = xd->left_mbmi;
const PREDICTION_MODE A = av1_above_block_mode(above_mi);
const PREDICTION_MODE L = av1_left_block_mode(left_mi);
const int above_ctx = intra_mode_context[A];
const int left_ctx = intra_mode_context[L];
bmode_costs = x->mode_costs.y_mode_costs[above_ctx][left_ctx];
mbmi->angle_delta[PLANE_TYPE_Y] = 0;
const INTRA_MODE_SPEED_FEATURES *const intra_sf = &cpi->sf.intra_sf;
if (intra_sf->intra_pruning_with_hog) {
// Less aggressive thresholds are used here than those used in inter frame
// encoding in av1_handle_intra_y_mode() because we want key frames/intra
// frames to have higher quality.
const float thresh[4] = { -1.2f, -1.2f, -0.6f, 0.4f };
const int is_chroma = 0;
prune_intra_mode_with_hog(x, bsize, cpi->common.seq_params->sb_size,
thresh[intra_sf->intra_pruning_with_hog - 1],
directional_mode_skip_mask, is_chroma);
}
mbmi->filter_intra_mode_info.use_filter_intra = 0;
pmi->palette_size[0] = 0;
// Set params for mode evaluation
set_mode_eval_params(cpi, x, MODE_EVAL);
MB_MODE_INFO best_mbmi = *mbmi;
const int max_winner_mode_count =
winner_mode_count_allowed[cpi->sf.winner_mode_sf.multi_winner_mode_type];
zero_winner_mode_stats(bsize, max_winner_mode_count, x->winner_mode_stats);
x->winner_mode_count = 0;
// Searches the intra-modes except for intrabc, palette, and filter_intra.
int64_t top_intra_model_rd[TOP_INTRA_MODEL_COUNT];
for (int i = 0; i < TOP_INTRA_MODEL_COUNT; i++) {
top_intra_model_rd[i] = INT64_MAX;
}
// Initialize the rdcost corresponding to all the directional and
// non-directional intra modes.
// 1. For directional modes, it stores the rdcost values for delta angles -4,
// -3, ..., 3, 4.
// 2. The rdcost value for luma_delta_angle is stored at index
// luma_delta_angle + MAX_ANGLE_DELTA + 1.
// 3. The rdcost values for fictitious/nonexistent luma_delta_angle -4 and 4
// (array indices 0 and 8) are always set to INT64_MAX (the initial value).
int64_t intra_modes_rd_cost[INTRA_MODE_END]
[SIZE_OF_ANGLE_DELTA_RD_COST_ARRAY];
for (int i = 0; i < INTRA_MODE_END; i++) {
for (int j = 0; j < SIZE_OF_ANGLE_DELTA_RD_COST_ARRAY; j++) {
intra_modes_rd_cost[i][j] = INT64_MAX;
}
}
for (int mode_idx = INTRA_MODE_START; mode_idx < LUMA_MODE_COUNT;
++mode_idx) {
set_y_mode_and_delta_angle(mode_idx, mbmi,
intra_sf->prune_luma_odd_delta_angles_in_intra);
RD_STATS this_rd_stats;
int this_rate, this_rate_tokenonly, s;
int is_diagonal_mode;
int64_t this_distortion, this_rd;
const int luma_delta_angle = mbmi->angle_delta[PLANE_TYPE_Y];
is_diagonal_mode = av1_is_diagonal_mode(mbmi->mode);
if (is_diagonal_mode && !intra_mode_cfg->enable_diagonal_intra) continue;
if (av1_is_directional_mode(mbmi->mode) &&
!intra_mode_cfg->enable_directional_intra)
continue;
// The smooth prediction mode appears to be more frequently picked
// than horizontal / vertical smooth prediction modes. Hence treat
// them differently in speed features.
if ((!intra_mode_cfg->enable_smooth_intra ||
intra_sf->disable_smooth_intra) &&
(mbmi->mode == SMOOTH_H_PRED || mbmi->mode == SMOOTH_V_PRED))
continue;
if (!intra_mode_cfg->enable_smooth_intra && mbmi->mode == SMOOTH_PRED)
continue;
// The functionality of filter intra modes and smooth prediction
// overlap. Hence smooth prediction is pruned only if all the
// filter intra modes are enabled.
if (intra_sf->disable_smooth_intra &&
intra_sf->prune_filter_intra_level == 0 && mbmi->mode == SMOOTH_PRED)
continue;
if (!intra_mode_cfg->enable_paeth_intra && mbmi->mode == PAETH_PRED)
continue;
// Skip the evaluation of modes that do not match with the winner mode in
// x->mb_mode_cache.
if (x->use_mb_mode_cache && mbmi->mode != x->mb_mode_cache->mode) continue;
is_directional_mode = av1_is_directional_mode(mbmi->mode);
if (is_directional_mode && directional_mode_skip_mask[mbmi->mode]) continue;
if (is_directional_mode &&
!(av1_use_angle_delta(bsize) && intra_mode_cfg->enable_angle_delta) &&
luma_delta_angle != 0)
continue;
// Use intra_y_mode_mask speed feature to skip intra mode evaluation.
if (!(intra_sf->intra_y_mode_mask[max_txsize_lookup[bsize]] &
(1 << mbmi->mode)))
continue;
if (prune_luma_odd_delta_angles_using_rd_cost(
mbmi, intra_modes_rd_cost[mbmi->mode], best_rd,
intra_sf->prune_luma_odd_delta_angles_in_intra))
continue;
const TX_SIZE tx_size = AOMMIN(TX_32X32, max_txsize_lookup[bsize]);
const int64_t this_model_rd =
intra_model_rd(&cpi->common, x, 0, bsize, tx_size, /*use_hadamard=*/1);
const int model_rd_index_for_pruning =
get_model_rd_index_for_pruning(x, intra_sf);
if (prune_intra_y_mode(this_model_rd, &best_model_rd, top_intra_model_rd,
intra_sf->top_intra_model_count_allowed,
model_rd_index_for_pruning))
continue;
// Builds the actual prediction. The prediction from
// model_intra_yrd_and_prune was just an estimation that did not take into
// account the effect of txfm pipeline, so we need to redo it for real
// here.
av1_pick_uniform_tx_size_type_yrd(cpi, x, &this_rd_stats, bsize, best_rd);
this_rate_tokenonly = this_rd_stats.rate;
this_distortion = this_rd_stats.dist;
s = this_rd_stats.skip_txfm;
if (this_rate_tokenonly == INT_MAX) continue;
if (!xd->lossless[mbmi->segment_id] && block_signals_txsize(mbmi->bsize)) {
// av1_pick_uniform_tx_size_type_yrd above includes the cost of the
// tx_size in the tokenonly rate, but for intra blocks, tx_size is always
// coded (prediction granularity), so we account for it in the full rate,
// not the tokenonly rate.
this_rate_tokenonly -= tx_size_cost(x, bsize, mbmi->tx_size);
}
this_rate =
this_rd_stats.rate +
intra_mode_info_cost_y(cpi, x, mbmi, bsize, bmode_costs[mbmi->mode], 0);
this_rd = RDCOST(x->rdmult, this_rate, this_distortion);
// Visual quality adjustment based on recon vs source variance.
if ((cpi->oxcf.mode == ALLINTRA) && (this_rd != INT64_MAX)) {
this_rd = (int64_t)(this_rd * intra_rd_variance_factor(cpi, x, bsize));
}
intra_modes_rd_cost[mbmi->mode][luma_delta_angle + MAX_ANGLE_DELTA + 1] =
this_rd;
// Collect mode stats for multiwinner mode processing
const int txfm_search_done = 1;
store_winner_mode_stats(
&cpi->common, x, mbmi, NULL, NULL, NULL, 0, NULL, bsize, this_rd,
cpi->sf.winner_mode_sf.multi_winner_mode_type, txfm_search_done);
if (this_rd < best_rd) {
best_mbmi = *mbmi;
best_rd = this_rd;
// Setting beat_best_rd flag because current mode rd is better than
// best_rd passed to this function
beat_best_rd = 1;
*rate = this_rate;
*rate_tokenonly = this_rate_tokenonly;
*distortion = this_distortion;
*skippable = s;
memcpy(ctx->blk_skip, x->txfm_search_info.blk_skip,
sizeof(x->txfm_search_info.blk_skip[0]) * ctx->num_4x4_blk);
av1_copy_array(ctx->tx_type_map, xd->tx_type_map, ctx->num_4x4_blk);
}
}
// Searches palette
if (try_palette) {
av1_rd_pick_palette_intra_sby(
cpi, x, bsize, bmode_costs[DC_PRED], &best_mbmi, best_palette_color_map,
&best_rd, rate, rate_tokenonly, distortion, skippable, &beat_best_rd,
ctx, ctx->blk_skip, ctx->tx_type_map);
}
// Searches filter_intra
if (beat_best_rd && av1_filter_intra_allowed_bsize(&cpi->common, bsize)) {
if (rd_pick_filter_intra_sby(cpi, x, rate, rate_tokenonly, distortion,
skippable, bsize, bmode_costs[DC_PRED],
best_mbmi.mode, &best_rd, &best_model_rd,
ctx)) {
best_mbmi = *mbmi;
}
}
// No mode is identified with less rd value than best_rd passed to this
// function. In such cases winner mode processing is not necessary and return
// best_rd as INT64_MAX to indicate best mode is not identified
if (!beat_best_rd) return INT64_MAX;
// In multi-winner mode processing, perform tx search for few best modes
// identified during mode evaluation. Winner mode processing uses best tx
// configuration for tx search.
if (cpi->sf.winner_mode_sf.multi_winner_mode_type) {
int best_mode_idx = 0;
int block_width, block_height;
uint8_t *color_map_dst = xd->plane[PLANE_TYPE_Y].color_index_map;
av1_get_block_dimensions(bsize, AOM_PLANE_Y, xd, &block_width,
&block_height, NULL, NULL);
for (int mode_idx = 0; mode_idx < x->winner_mode_count; mode_idx++) {
*mbmi = x->winner_mode_stats[mode_idx].mbmi;
if (is_winner_mode_processing_enabled(cpi, x, mbmi, 0)) {
// Restore color_map of palette mode before winner mode processing
if (mbmi->palette_mode_info.palette_size[0] > 0) {
uint8_t *color_map_src =
x->winner_mode_stats[mode_idx].color_index_map;
memcpy(color_map_dst, color_map_src,
block_width * block_height * sizeof(*color_map_src));
}
// Set params for winner mode evaluation
set_mode_eval_params(cpi, x, WINNER_MODE_EVAL);
// Winner mode processing
// If previous searches use only the default tx type/no R-D optimization
// of quantized coeffs, do an extra search for the best tx type/better
// R-D optimization of quantized coeffs
if (intra_block_yrd(cpi, x, bsize, bmode_costs, &best_rd, rate,
rate_tokenonly, distortion, skippable, &best_mbmi,
ctx))
best_mode_idx = mode_idx;
}
}
// Copy color_map of palette mode for final winner mode
if (best_mbmi.palette_mode_info.palette_size[0] > 0) {
uint8_t *color_map_src =
x->winner_mode_stats[best_mode_idx].color_index_map;
memcpy(color_map_dst, color_map_src,
block_width * block_height * sizeof(*color_map_src));
}
} else {
// If previous searches use only the default tx type/no R-D optimization of
// quantized coeffs, do an extra search for the best tx type/better R-D
// optimization of quantized coeffs
if (is_winner_mode_processing_enabled(cpi, x, mbmi, 0)) {
// Set params for winner mode evaluation
set_mode_eval_params(cpi, x, WINNER_MODE_EVAL);
*mbmi = best_mbmi;
intra_block_yrd(cpi, x, bsize, bmode_costs, &best_rd, rate,
rate_tokenonly, distortion, skippable, &best_mbmi, ctx);
}
}
*mbmi = best_mbmi;
av1_copy_array(xd->tx_type_map, ctx->tx_type_map, ctx->num_4x4_blk);
return best_rd;
}
- set_y_mode_and_delta_angle 函数
- 该函数根据模式索引设置宏块的预测模式和角度增量。
- 检查模式索引范围:
- 如果 mode_idx 小于 INTRA_MODE_END,表示这是一个普通的帧内预测模式(如 DC_PRED、V_PRED 等),而不是方向模式。
- 在这种情况下,设置 mbmi->mode 为 intra_rd_search_mode_order[mode_idx],这是一个预定义的模式顺序数组,并且将
mbmi->angle_delta[PLANE_TYPE_Y] 设置为 0,因为普通模式没有角度增量。- 处理方向模式:
- 如果 mode_idx 大于或等于 INTRA_MODE_END,表示这是一个方向模式。
- 在这种情况下,计算 mbmi->mode 为 V_PRED 加上 mode_idx 减去 INTRA_MODE_END 再除以 (MAX_ANGLE_DELTA * 2) 的结果,以确定方向模式的基预测模式。
- 计算 delta_angle_eval_idx 为 mode_idx 减去 INTRA_MODE_END 再对 (MAX_ANGLE_DELTA * 2) 取模的结果,以确定角度增量的索引。
- 确定角度增量:
- 如果 reorder_delta_angle_eval 为真,使用luma_delta_angles_order[delta_angle_eval_idx] 作为 mbmi->angle_delta[PLANE_TYPE_Y] 的值,这是一个预定义的角度增量顺序数组。
- 如果 reorder_delta_angle_eval 为假,根据 delta_angle_eval_idx 的值计算角度增量,如果 delta_angle_eval_idx 小于 3,则角度增量为 delta_angle_eval_idx - 3,否则为delta_angle_eval_idx - 2。
void set_y_mode_and_delta_angle(const int mode_idx, MB_MODE_INFO *const mbmi,
int reorder_delta_angle_eval) {
if (mode_idx < INTRA_MODE_END) {
mbmi->mode = intra_rd_search_mode_order[mode_idx];
mbmi->angle_delta[PLANE_TYPE_Y] = 0;
} else {
mbmi->mode = (mode_idx - INTRA_MODE_END) / (MAX_ANGLE_DELTA * 2) + V_PRED;
int delta_angle_eval_idx =
(mode_idx - INTRA_MODE_END) % (MAX_ANGLE_DELTA * 2);
if (reorder_delta_angle_eval) {
mbmi->angle_delta[PLANE_TYPE_Y] =
luma_delta_angles_order[delta_angle_eval_idx];
} else {
mbmi->angle_delta[PLANE_TYPE_Y] =
(delta_angle_eval_idx < 3 ? (delta_angle_eval_idx - 3)
: (delta_angle_eval_idx - 2));
}
}
}
- av1_is_diagonal_mode 函数
- 该函数是用于判断给定的预测模式 mode 是否是斜向(diagonal)模式。
- 函数检查传入的 mode 是否在 D45_PRED 和 D67_PRED 之间(包括这两个值)。
static INLINE int av1_is_diagonal_mode(PREDICTION_MODE mode) {
return mode >= D45_PRED && mode <= D67_PRED;
}
- av1_is_directional_mode 函数
- 用于判断给定的预测模式 mode 是否是方向性模式。在 AV1 编码器中,方向性模式包括从垂直(V_PRED)到对角线(D67_PRED)的一系列预测模式。
static INLINE int av1_is_directional_mode(PREDICTION_MODE mode) {
return mode >= V_PRED && mode <= D67_PRED;
}
- av1_use_angle_delta 函数
- 该函数用于确定在给定的块大小 bsize 下是否使用角度增量(angle delta)特性;
- 如果 bsize 大于或等于 BLOCK_8X8,则返回 1(真),表示可以使用角度增量;如果小于 BLOCK_8X8,则返回 0(假),表示不使用角度增量。
static INLINE int av1_use_angle_delta(BLOCK_SIZE bsize) {
return bsize >= BLOCK_8X8;
}
- intra_model_rd 函数
- 该函数通过快速估计帧内预测的 RD 成本来帮助编码器在模式决策阶段选择最佳预测模式,而不需要完整地执行变换(transform)、量化(quantize)和逆变换(itxfm)过程。
- av1_predict_intra_block_facade:进行帧内预测。
- av1_subtract_block:计算预测残差。
- av1_quick_txfm:快速变换,用于估计变换系数。
- aom_satd:计算 SATD 成本。
/*!\cond */
// Makes a quick intra prediction and estimate the rdcost with a model without
// going through the whole txfm/quantize/itxfm process.
static int64_t intra_model_rd(const AV1_COMMON *cm, MACROBLOCK *const x,
int plane, BLOCK_SIZE plane_bsize,
TX_SIZE tx_size, int use_hadamard) {
MACROBLOCKD *const xd = &x->e_mbd;
const BitDepthInfo bd_info = get_bit_depth_info(xd);
int row, col;
assert(!is_inter_block(xd->mi[0]));
const int stepr = tx_size_high_unit[tx_size];
const int stepc = tx_size_wide_unit[tx_size];
const int txbw = tx_size_wide[tx_size];
const int txbh = tx_size_high[tx_size];
const int max_blocks_wide = max_block_wide(xd, plane_bsize, plane);
const int max_blocks_high = max_block_high(xd, plane_bsize, plane);
int64_t satd_cost = 0;
struct macroblock_plane *p = &x->plane[plane];
struct macroblockd_plane *pd = &xd->plane[plane];
// Prediction.
for (row = 0; row < max_blocks_high; row += stepr) {
for (col = 0; col < max_blocks_wide; col += stepc) {
av1_predict_intra_block_facade(cm, xd, plane, col, row, tx_size);
// Here we use p->src_diff and p->coeff as temporary buffers for
// prediction residue and transform coefficients. The buffers are only
// used in this for loop, therefore we don't need to properly add offset
// to the buffers.
av1_subtract_block(
bd_info, txbh, txbw, p->src_diff, block_size_wide[plane_bsize],
p->src.buf + (((row * p->src.stride) + col) << 2), p->src.stride,
pd->dst.buf + (((row * pd->dst.stride) + col) << 2), pd->dst.stride);
av1_quick_txfm(use_hadamard, tx_size, bd_info, p->src_diff,
block_size_wide[plane_bsize], p->coeff);
satd_cost += aom_satd(p->coeff, tx_size_2d[tx_size]);
}
}
return satd_cost;
}
原文地址:https://blog.csdn.net/yanceyxin/article/details/143504623
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!