AV1中的segment

被segment折磨好久了。。今天终于看懂一点点,整理一下

一、什么是segment

segmentation技术将一帧分为若干个部分,然后对这些部分提供单独的参数,也就是说同一个segment共享相同的参数。这些参数可以包含

  • 量化参数(绝对值或者delta值)
  • loop filter 强度(strength)
  • 预测参考值信息
  • 块skip信息

其在frame级的定义在标准文档的5.9.14节

二、segment中的参数

标准文档中也有介绍,下面以libaom为例,说明其中的参数含义。

enum {
  SEG_LVL_ALT_Q,       // Use alternate Quantizer ....
  SEG_LVL_ALT_LF_Y_V,  // Use alternate loop filter value on y plane vertical
  SEG_LVL_ALT_LF_Y_H,  // Use alternate loop filter value on y plane horizontal
  SEG_LVL_ALT_LF_U,    // Use alternate loop filter value on u plane
  SEG_LVL_ALT_LF_V,    // Use alternate loop filter value on v plane
  SEG_LVL_REF_FRAME,   // Optional Segment reference frame
  SEG_LVL_SKIP,        // Optional Segment (0,0) + skip mode
  SEG_LVL_GLOBALMV,
  SEG_LVL_MAX
} UENUM1BYTE(SEG_LVL_FEATURES);

struct segmentation {
  uint8_t enabled;//表示segment功能是否启用
  uint8_t update_map;//表示当前帧是否更新segmentation map
  uint8_t update_data;//表示当前帧的segment数据是否需要更新
  uint8_t temporal_update;//表示当前帧segmentation map的更新,是否参考了相邻帧的segmentation map

  int16_t feature_data[MAX_SEGMENTS][SEG_LVL_MAX];//每个segment的数据,具体数据类型如上面
  unsigned int feature_mask[MAX_SEGMENTS];//该segment启用了哪些feature
  int last_active_segid;  // The highest numbered segment id that has some
                          // enabled feature.最后一个启用feature的segment
  uint8_t segid_preskip;  // Whether the segment id will be read before the
                          // skip syntax element.段 ID 是否在 skip 语法元素之前读取
                          // 1: the segment id will be read first.
                          // 0: the skip syntax element will be read first.
};

其中主要说明一下feature_data。每个段(数组的第一维度)可以有多个特性(数组的第二维度),例如 delta QP、delta LF(Loop Filter)等。

  • MAX_SEGMENTS 是段的最大数量,SEG_LVL_MAX 是每个段可以拥有的最大特性数量。
  • SEG_LVL_ALT_Q:表示段的 delta QP,调整该段的量化参数。
  • SEG_LVL_ALT_LF_Y_V:表示垂直方向的 Y 平面的环路滤波调整。
  • SEG_LVL_ALT_LF_Y_H:表示水平方向的 Y 平面的环路滤波调整。
  • SEG_LVL_ALT_LF_U:表示 U 平面的环路滤波调整。
  • SEG_LVL_ALT_LF_V:表示 V 平面的环路滤波调整。
  • SEG_LVL_REF_FRAME:表示参考帧调整。
  • SEG_LVL_SKIP:表示跳过该段的块。
三、aom中的aq

大致思路就是先把所有block都划分到对应的segment,一共八个segment。然后再确定每个segment的deltaqp值。以VARIANCE_AQ为例。

首先在这个函数里面计算了当前block的segment_id。

static void setup_block_rdmult(const AV1_COMP *const cpi, MACROBLOCK *const x,
                               int mi_row, int mi_col, BLOCK_SIZE bsize,
                               AQ_MODE aq_mode, MB_MODE_INFO *mbmi) {
  x->rdmult = cpi->rd.RDMULT;

  if (aq_mode != NO_AQ) {
    assert(mbmi != NULL);
    if (aq_mode == VARIANCE_AQ) {
      if (cpi->vaq_refresh) {
        const int energy = bsize <= BLOCK_16X16
                               ? x->mb_energy
                               : av1_log_block_var(cpi, x, bsize);
        mbmi->segment_id = energy;
      }
      x->rdmult = set_rdmult(cpi, x, mbmi->segment_id);
    } else if (aq_mode == COMPLEXITY_AQ) {
      x->rdmult = set_rdmult(cpi, x, mbmi->segment_id);
    } else if (aq_mode == CYCLIC_REFRESH_AQ) {
      // If segment is boosted, use rdmult for that segment.
      if (cyclic_refresh_segment_id_boosted(mbmi->segment_id))
        x->rdmult = av1_cyclic_refresh_get_rdmult(cpi->cyclic_refresh);
    }
  }  
/*中间省略掉*/
  // Check to make sure that the adjustments above have not caused the
  // rd multiplier to be truncated to 0.
  x->rdmult = (x->rdmult > 0) ? x->rdmult : 1;
}

然后在这个函数里面计算各个segment的delta qindex

void av1_vaq_frame_setup(AV1_COMP *cpi) {
  AV1_COMMON *cm = &cpi->common;
  const RefreshFrameInfo *const refresh_frame = &cpi->refresh_frame;
  const int base_qindex = cm->quant_params.base_qindex;
  struct segmentation *seg = &cm->seg;
  int i;

  int resolution_change =//分辨率是否发生变化
      cm->prev_frame && (cm->width != cm->prev_frame->width ||
                         cm->height != cm->prev_frame->height);
  int avg_energy = (int)(cpi->twopass_frame.mb_av_energy - 2);
  double avg_ratio;
  if (avg_energy > 7) avg_energy = 7;
  if (avg_energy < 0) avg_energy = 0;
  avg_ratio = rate_ratio[avg_energy];

  if (resolution_change) {
    memset(cpi->enc_seg.map, 0, cm->mi_params.mi_rows * cm->mi_params.mi_cols);
    av1_clearall_segfeatures(seg);
    av1_disable_segmentation(seg);
    return;
  }
  if (frame_is_intra_only(cm) || cm->features.error_resilient_mode ||
      refresh_frame->alt_ref_frame ||
      (refresh_frame->golden_frame && !cpi->rc.is_src_frame_alt_ref)) {
    cpi->vaq_refresh = 1;

    av1_enable_segmentation(seg);
    av1_clearall_segfeatures(seg);

    for (i = 0; i < MAX_SEGMENTS; ++i) {
      // Set up avg segment id to be 1.0 and adjust the other segments around
      // it.
      int qindex_delta =
          av1_compute_qdelta_by_rate(cpi, cm->current_frame.frame_type,
                                     base_qindex, rate_ratio[i] / avg_ratio);

      // We don't allow qindex 0 in a segment if the base value is not 0.
      // Q index 0 (lossless) implies 4x4 encoding only and in AQ mode a segment
      // Q delta is sometimes applied without going back around the rd loop.
      // This could lead to an illegal combination of partition size and q.
      if ((base_qindex != 0) && ((base_qindex + qindex_delta) == 0)) {
        qindex_delta = -base_qindex + 1;
      }

      av1_set_segdata(seg, i, SEG_LVL_ALT_Q, qindex_delta);//更新该segment的delta qindex
      av1_enable_segfeature(seg, i, SEG_LVL_ALT_Q);
    }
  }
}

里面的这个函数就是用来更新segment的delta qindex的。

seg->feature_data[segment_id][feature_id] = seg_data;中feature_id传入的就是存qindex对应的位置。

void av1_set_segdata(struct segmentation *seg, int segment_id,
                     SEG_LVL_FEATURES feature_id, int seg_data) {
  if (seg_data < 0) {
    assert(seg_feature_data_signed[feature_id]);
    assert(-seg_data <= seg_feature_data_max[feature_id]);
  } else {
    assert(seg_data <= seg_feature_data_max[feature_id]);
  }

  seg->feature_data[segment_id][feature_id] = seg_data;
}

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值