被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;
}