首页 > 编程语言 >libaom 源码分析:帧内递归滤波模式

libaom 源码分析:帧内递归滤波模式

时间:2024-11-04 20:52:03浏览次数:3  
标签:filter libaom 滤波 av1 rd 源码 mode const intra

帧内递归滤波模式原理

  1. 在AV1编码标准中,滤波帧内预测模式是一组用于帧内预测的工具,它们通过应用不同的滤波器来预测当前编码块(coding block)内的像素值,基于周围的已知像素(通常是顶部和左侧的像素)。
  2. 5 种滤波帧内模式被定义(FILTER_DC_PRED、FILTER_V_PRED、FILTER_H_PRED、FILTER_D157_PRED、FILTER_PAETH_PRED),每一种模式都特定一组八个 7-tap 滤波器;
  3. 给定选定的滤波模式索引(0到4),当前块被划分为4x2的子块;
  4. 对于一个4x2的子块,每个样本通过使用7个顶部和左侧的相邻样本作为输入,通过7-tap插值来预测;
  5. 对于每个4x2的子块,使用相应的7-tap滤波器根据相邻的样本来预测当前子块内的样本;
  6. 预测过程可以以4x2子块为单位递归进行,这意味着为一个4x2预测块生成的预测样本可以用来预测另一个4x2子块;
  7. 递归内预测模式特别适合于预测具有明显方向性纹理的区域,例如边缘或斜线。通过递归地利用已预测的样本来预测新的样本,这种方法可以有效地捕捉和延续图像中的方向性特征。
    在这里插入图片描述

libaom相关源码分析

  1. 在 enums.h 中定义 5 种递归滤波模式,即FILTER_DC_PRED、FILTER_V_PRED、FILTER_H_PRED、FILTER_D157_PRED、FILTER_PAETH_PRED。
    在这里插入图片描述
  2. 函数关系:
    在这里插入图片描述
  3. handle_filter_intra_mode 函数
  • 这个函数通过遍历所有 filter_intra 模式,并计算每种模式的 RD 值,来找到最佳的 filter_intra 模式。可以看到该函数的调用只在帧间模式选择中,因此滤波帧内模式属于帧间预测模块中,不在帧内预测帧中应用;
  • 遍历所有 filter_intra 模式:其中核心函数av1_pick_uniform_tx_size_type_yrd这个函数用于选择统一的变换大小和类型,并计算 Y 通道的
    RD 统计;intra_mode_info_cost_y这个函数用来计算当前模式信息的成本;最终选择成本最小的滤波帧内模式。
/*!\brief Search for the best filter_intra mode when coding inter frame.
 *
 * \ingroup intra_mode_search
 * \callergraph
 * This function loops through all filter_intra modes to find the best one.
 *
 * \remark Returns nothing, but updates the mbmi and rd_stats.
 */
static INLINE void handle_filter_intra_mode(const AV1_COMP *cpi, MACROBLOCK *x,
                                            BLOCK_SIZE bsize,
                                            const PICK_MODE_CONTEXT *ctx,
                                            RD_STATS *rd_stats_y, int mode_cost,
                                            int64_t best_rd,
                                            int64_t best_rd_so_far) {
  MACROBLOCKD *const xd = &x->e_mbd;
  MB_MODE_INFO *const mbmi = xd->mi[0];
  assert(mbmi->mode == DC_PRED &&
         av1_filter_intra_allowed_bsize(&cpi->common, bsize));

  RD_STATS rd_stats_y_fi;
  int filter_intra_selected_flag = 0;
  TX_SIZE best_tx_size = mbmi->tx_size;
  FILTER_INTRA_MODE best_fi_mode = FILTER_DC_PRED;
  uint8_t best_blk_skip[MAX_MIB_SIZE * MAX_MIB_SIZE];
  memcpy(best_blk_skip, x->txfm_search_info.blk_skip,
         sizeof(best_blk_skip[0]) * ctx->num_4x4_blk);
  uint8_t best_tx_type_map[MAX_MIB_SIZE * MAX_MIB_SIZE];
  av1_copy_array(best_tx_type_map, xd->tx_type_map, ctx->num_4x4_blk);
  mbmi->filter_intra_mode_info.use_filter_intra = 1;
  for (FILTER_INTRA_MODE fi_mode = FILTER_DC_PRED; fi_mode < FILTER_INTRA_MODES;
       ++fi_mode) {
    mbmi->filter_intra_mode_info.filter_intra_mode = fi_mode;
    av1_pick_uniform_tx_size_type_yrd(cpi, x, &rd_stats_y_fi, bsize, best_rd);
    if (rd_stats_y_fi.rate == INT_MAX) continue;
    const int this_rate_tmp =
        rd_stats_y_fi.rate +
        intra_mode_info_cost_y(cpi, x, mbmi, bsize, mode_cost, 0);
    const int64_t this_rd_tmp =
        RDCOST(x->rdmult, this_rate_tmp, rd_stats_y_fi.dist);

    if (this_rd_tmp != INT64_MAX && this_rd_tmp / 2 > best_rd) {
      break;
    }
    if (this_rd_tmp < best_rd_so_far) {
      best_tx_size = mbmi->tx_size;
      av1_copy_array(best_tx_type_map, xd->tx_type_map, ctx->num_4x4_blk);
      memcpy(best_blk_skip, x->txfm_search_info.blk_skip,
             sizeof(best_blk_skip[0]) * ctx->num_4x4_blk);
      best_fi_mode = fi_mode;
      *rd_stats_y = rd_stats_y_fi;
      filter_intra_selected_flag = 1;
      best_rd_so_far = this_rd_tmp;
    }
  }
  1. av1_pick_uniform_tx_size_type_yrd 函数
  • 该函数通过不同的搜索方法选择最佳的变换大小和类型,并计算 Y 通道的 RD 统计。
  • 哈希计算:get_block_residue_hash(x, bs) 用于计算当前块的残留哈希值。
  • RD 信息查找:find_mb_rd_info(mb_rd_record, ref_best_rd, hash) 用于查找匹配的 RD 信息。
  • RD 统计更新:fetch_mb_rd_info(num_blks, mb_rd_info, rd_stats, x) 用于获取保存的 RD 信息。
  • 跳过变换的设置:set_skip_txfm(x, rd_stats, bs, dist) 用于更新 RD 统计以反映跳过变换的决策。
  • 变换大小选择:choose_smallest_tx_size、choose_largest_tx_size 和 choose_tx_size_type_from_rd 是用于选择变换大小的具体实现。
void av1_pick_uniform_tx_size_type_yrd(const AV1_COMP *const cpi, MACROBLOCK *x,
                                       RD_STATS *rd_stats, BLOCK_SIZE bs,
                                       int64_t ref_best_rd) {
  MACROBLOCKD *const xd = &x->e_mbd;
  MB_MODE_INFO *const mbmi = xd->mi[0];
  const TxfmSearchParams *tx_params = &x->txfm_search_params;
  assert(bs == mbmi->bsize);
  const int is_inter = is_inter_block(mbmi);
  const int mi_row = xd->mi_row;
  const int mi_col = xd->mi_col;

  av1_init_rd_stats(rd_stats);

  // Hashing based speed feature for inter blocks. If the hash of the residue
  // block is found in the table, use previously saved search results and
  // terminate early.
  uint32_t hash = 0;
  MB_RD_RECORD *mb_rd_record = NULL;
  const int num_blks = bsize_to_num_blk(bs);
  if (is_inter && cpi->sf.rd_sf.use_mb_rd_hash) {
    const int within_border =
        mi_row >= xd->tile.mi_row_start &&
        (mi_row + mi_size_high[bs] < xd->tile.mi_row_end) &&
        mi_col >= xd->tile.mi_col_start &&
        (mi_col + mi_size_wide[bs] < xd->tile.mi_col_end);
    if (within_border) {
      hash = get_block_residue_hash(x, bs);
      mb_rd_record = x->txfm_search_info.mb_rd_record;
      const int match_index = find_mb_rd_info(mb_rd_record, ref_best_rd, hash);
      if (match_index != -1) {
        MB_RD_INFO *mb_rd_info = &mb_rd_record->mb_rd_info[match_index];
        fetch_mb_rd_info(num_blks, mb_rd_info, rd_stats, x);
        return;
      }
    }
  }

  // If we predict that skip is the optimal RD decision - set the respective
  // context and terminate early.
  int64_t dist;
  if (tx_params->skip_txfm_level && is_inter &&
      !xd->lossless[mbmi->segment_id] &&
      predict_skip_txfm(x, bs, &dist,
                        cpi->common.features.reduced_tx_set_used)) {
    // Populate rdstats as per skip decision
    set_skip_txfm(x, rd_stats, bs, dist);
    // Save the RD search results into mb_rd_record.
    if (mb_rd_record) {
      save_mb_rd_info(num_blks, hash, x, rd_stats, mb_rd_record);
    }
    return;
  }

  if (xd->lossless[mbmi->segment_id]) {
    // Lossless mode can only pick the smallest (4x4) transform size.
    choose_smallest_tx_size(cpi, x, rd_stats, ref_best_rd, bs);
  } else if (tx_params->tx_size_search_method == USE_LARGESTALL) {
    choose_largest_tx_size(cpi, x, rd_stats, ref_best_rd, bs);
  } else {
    choose_tx_size_type_from_rd(cpi, x, rd_stats, ref_best_rd, bs);
  }

  // Save the RD search results into mb_rd_record for possible reuse in future.
  if (mb_rd_record) {
    save_mb_rd_info(num_blks, hash, x, rd_stats, mb_rd_record);
  }
}
  1. intra_mode_info_cost_y 函数
  • 该函数目的是计算 Y 通道(亮度通道)的帧内预测模式信息的率成本。这个函数考虑了多种帧内预测模式,包括调色板(palette)模式、filter_intra
    模式、方向模式以及 intraBC 模式,并计算每种模式的编码成本。
  • av1_allow_palette:检查是否允许调色板模式。
  • av1_get_palette_bsize_ctx 和 av1_get_palette_mode_ctx:获取调色板模式的上下文。
  • write_uniform_cost:计算写入统一值的成本。
  • av1_get_palette_cache 和 av1_palette_color_cost_y:计算调色板颜色的成本。
  • av1_cost_color_map:计算颜色映射的成本。
  • av1_filter_intra_allowed:检查是否允许 filter_intra 模式。
  • av1_is_directional_mode 和 av1_use_angle_delta:检查是否使用方向模式和角度增量。
  • av1_allow_intrabc:检查是否允许 intraBC 模式。
/*!\endcond */

/*!\brief Returns the rate cost for luma prediction mode info of intra blocks.
 *
 * \callergraph
 */
static AOM_INLINE int intra_mode_info_cost_y(const AV1_COMP *cpi,
                                             const MACROBLOCK *x,
                                             const MB_MODE_INFO *mbmi,
                                             BLOCK_SIZE bsize, int mode_cost,
                                             int discount_color_cost) {
  int total_rate = mode_cost;
  const ModeCosts *mode_costs = &x->mode_costs;
  const int use_palette = mbmi->palette_mode_info.palette_size[0] > 0;
  const int use_filter_intra = mbmi->filter_intra_mode_info.use_filter_intra;
  const int use_intrabc = mbmi->use_intrabc;
  // Can only activate one mode.
  assert(((mbmi->mode != DC_PRED) + use_palette + use_intrabc +
          use_filter_intra) <= 1);
  const int try_palette = av1_allow_palette(
      cpi->common.features.allow_screen_content_tools, mbmi->bsize);
  if (try_palette && mbmi->mode == DC_PRED) {
    const MACROBLOCKD *xd = &x->e_mbd;
    const int bsize_ctx = av1_get_palette_bsize_ctx(bsize);
    const int mode_ctx = av1_get_palette_mode_ctx(xd);
    total_rate +=
        mode_costs->palette_y_mode_cost[bsize_ctx][mode_ctx][use_palette];
    if (use_palette) {
      const uint8_t *const color_map = xd->plane[0].color_index_map;
      int block_width, block_height, rows, cols;
      av1_get_block_dimensions(bsize, 0, xd, &block_width, &block_height, &rows,
                               &cols);
      const int plt_size = mbmi->palette_mode_info.palette_size[0];
      int palette_mode_cost =
          mode_costs
              ->palette_y_size_cost[bsize_ctx][plt_size - PALETTE_MIN_SIZE] +
          write_uniform_cost(plt_size, color_map[0]);
      uint16_t color_cache[2 * PALETTE_MAX_SIZE];
      const int n_cache = av1_get_palette_cache(xd, 0, color_cache);
      palette_mode_cost +=
          av1_palette_color_cost_y(&mbmi->palette_mode_info, color_cache,
                                   n_cache, cpi->common.seq_params->bit_depth);
      if (!discount_color_cost)
        palette_mode_cost +=
            av1_cost_color_map(x, 0, bsize, mbmi->tx_size, PALETTE_MAP);

      total_rate += palette_mode_cost;
    }
  }
  if (av1_filter_intra_allowed(&cpi->common, mbmi)) {
    total_rate += mode_costs->filter_intra_cost[mbmi->bsize][use_filter_intra];
    if (use_filter_intra) {
      total_rate +=
          mode_costs->filter_intra_mode_cost[mbmi->filter_intra_mode_info
                                                 .filter_intra_mode];
    }
  }
  if (av1_is_directional_mode(mbmi->mode)) {
    if (av1_use_angle_delta(bsize)) {
      total_rate +=
          mode_costs->angle_delta_cost[mbmi->mode - V_PRED]
                                      [MAX_ANGLE_DELTA +
                                       mbmi->angle_delta[PLANE_TYPE_Y]];
    }
  }
  if (av1_allow_intrabc(&cpi->common))
    total_rate += mode_costs->intrabc_cost[use_intrabc];
  return total_rate;
}
  1. av1_filter_intra_predictor_c 函数
  • 该函数用于生成滤波帧内模式的预测值的核心函数;
  • 初始化和边界检查:
    • 初始化一个本地缓冲区 buffer,用于存储预测过程中的中间结果。
    • 获取变换大小 tx_size 对应的宽度 bw 和高度 bh,并确保它们不超过缓冲区的大小(32x32)。
  • 填充缓冲区:
    • 将左侧像素值复制到 buffer 的第一列。
    • 将上方像素值复制到 buffer 的第一行。
  • 应用 filter_intra 滤波器:
  • 对于 bh 的每一行,以 2 为步长进行迭代。
  • 对于 bw 的每一列,以 4 为步长进行迭代。
  • 对于每个 2x4 的子块,应用 filter_intra 滤波器,计算预测值。
  • 使用 av1_filter_intra_taps 数组中的滤波器系数,对周围的像素值进行加权求和。
  • 使用 ROUND_POWER_OF_TWO 函数对加权求和的结果进行缩放,然后使用 clip_pixel 函数将结果裁剪到有效的像素值范围内。
  • 存储预测结果:
    • 将计算出的预测值从 buffer 复制到目标块 dst 中。
void av1_filter_intra_predictor_c(uint8_t *dst, ptrdiff_t stride,
                                  TX_SIZE tx_size, const uint8_t *above,
                                  const uint8_t *left, int mode) {
  int r, c;
  uint8_t buffer[33][33];
  const int bw = tx_size_wide[tx_size];
  const int bh = tx_size_high[tx_size];

  assert(bw <= 32 && bh <= 32);

  for (r = 0; r < bh; ++r) buffer[r + 1][0] = left[r];
  memcpy(buffer[0], &above[-1], (bw + 1) * sizeof(uint8_t));

  for (r = 1; r < bh + 1; r += 2)
    for (c = 1; c < bw + 1; c += 4) {
      const uint8_t p0 = buffer[r - 1][c - 1];
      const uint8_t p1 = buffer[r - 1][c];
      const uint8_t p2 = buffer[r - 1][c + 1];
      const uint8_t p3 = buffer[r - 1][c + 2];
      const uint8_t p4 = buffer[r - 1][c + 3];
      const uint8_t p5 = buffer[r][c - 1];
      const uint8_t p6 = buffer[r + 1][c - 1];
      for (int k = 0; k < 8; ++k) {
        int r_offset = k >> 2;
        int c_offset = k & 0x03;
        int pr = av1_filter_intra_taps[mode][k][0] * p0 +
                 av1_filter_intra_taps[mode][k][1] * p1 +
                 av1_filter_intra_taps[mode][k][2] * p2 +
                 av1_filter_intra_taps[mode][k][3] * p3 +
                 av1_filter_intra_taps[mode][k][4] * p4 +
                 av1_filter_intra_taps[mode][k][5] * p5 +
                 av1_filter_intra_taps[mode][k][6] * p6;
        // Section 7.11.2.3 specifies the right-hand side of the assignment as
        //   Clip1( Round2Signed( pr, INTRA_FILTER_SCALE_BITS ) ).
        // Since Clip1() clips a negative value to 0, it is safe to replace
        // Round2Signed() with Round2().
        buffer[r + r_offset][c + c_offset] =
            clip_pixel(ROUND_POWER_OF_TWO(pr, FILTER_INTRA_SCALE_BITS));
      }
    }

  for (r = 0; r < bh; ++r) {
    memcpy(dst, &buffer[r + 1][1], bw * sizeof(uint8_t));
    dst += stride;
  }
}

标签:filter,libaom,滤波,av1,rd,源码,mode,const,intra
From: https://blog.csdn.net/yanceyxin/article/details/143492890

相关文章