build_intra_predictors函数作用
build_intra_predictors 函数在视频编码技术中,特别是在帧内预测过程中起着关键作用。以下是其主要作用的详细解释。
一 处理调色板模式
在某些视频编码场景下,会使用调色板模式对图像块进行编码,在这种情况系,build_intra_predictors 函数会直接根据调色板映射生成预测器,而无需进行复杂的帧内预测计算。
二 构建预测参考
当不使用调色板模式时,函数的主要任务是构建当前块进行帧内预测所需的参考像素。这包括对当前块的周边块是否存在且可用进行判断。如果周边块不可用,函数会在下一级的函数中进行padding以构建预测所需要的reference。
三 考虑多种因素进行预测
在构建预测块时,build_intra_predictors 函数会考虑多种因素,包括。
邻居像素的可用性:检测左,左下,上,右上等邻居像素是否存在,以确定预测时可以利用的参考信息。
预测模式 根据制定的预测模式(如角度预测模式等),选择合适的算法来生成预测块。
角度偏移:在角度预测模式下,角度偏移量会影响预测的方向和方式。
滤波模式:确定是否使用滤波以及使用何种滤波模式来提高预测的准确性
变换尺寸:根据当前块的变换尺寸来调整预测块的大小和形状。
四 生产预测块
结合上述考虑的因素,函数最终生成当前块的预测块。这个预测块将用于后续的编码过程,如残差计算和熵编码等,以实现高效的视频压缩。
五 参数说明
MacroBlockD *xd:指向宏块解码结构体的指针,包含当前宏块的解码信息。
top_neigh_array和left_neigh_array 分别存储上方和左侧邻居像素的数据,用于构建预测参考。
dst和dst_stride 指向预测块存储位置的指针和步长,用于输出生成的预测块。
PredictionMode mode 指定当前块的预测模式,决定预测块的生成方式。
angle_delta 角度偏移量,影响角度预测的方向和方式
FilterIntraMode filter_intra_mode 滤波模式,用于提高预测的准确性。
TxSize tx_szie 当前块的变换尺寸,影响预测块的大小和形状。
disable_edge_filter;是否禁用边缘滤波的标志,用于控制是否对预测块边缘进行滤波处理。
n_top_px, n_topright_px, n_left_px, n_bottomleft_px 分别表示上方,右上方,左侧,左下方邻居像素的数据,用于确定参考像素的有效范围。
plane 当前处理的图像通道,用于区分亮度和色度等不同的通道
六 源码分析
// 定义一个静态函数,用于构建帧内预测器
static void build_intra_predictors(const MacroBlockD *xd, // 当前宏块的解码信息uint8_t* top_neigh_array, // 上方邻居像素数组uint8_t* left_neigh_array, // 左侧邻居像素数组uint8_t *dst, int32_t dst_stride, // 预测块的存储位置和步长
PredictionMode mode, // 预测模式int32_t angle_delta, // 角度偏移量
FilterIntraMode filter_intra_mode, // 滤波模式
TxSize tx_size, // 变换尺寸int32_t disable_edge_filter, // 是否禁用边缘滤波int32_t n_top_px, // 上方邻居像素数量int32_t n_topright_px, // 右上方邻居像素数量int32_t n_left_px, // 左侧邻居像素数量int32_t n_bottomleft_px, // 左下方邻居像素数量int32_t plane) // 当前处理的图像平面
{int32_t i;//设置参考像素的步长位1int32_t ref_stride = 1;// 指向顶部和左侧邻居像素的指针const uint8_t *above_ref = top_neigh_array;//顶部const uint8_t *left_ref = left_neigh_array;//左侧// 声明并初始化用于存储左侧和上方参考像素的临时数组,对齐以优化性能DECLARE_ALIGNED(32, uint8_t, left_data[MAX_TX_SIZE * 2 + 48]);DECLARE_ALIGNED(32, uint8_t, above_data[MAX_TX_SIZE * 2 + 48]);// 将临时数组初始化为128(默认值)memset(left_data, 0x80, sizeof(left_data));memset(above_data, 0x80, sizeof(above_data));// 指向临时数组中间位置的指针,用于方便访问和操作参考像素uint8_t *const above_row = above_data + 32;uint8_t *const left_col = left_data + 32;// 获取当前变换块的宽度和高度(以像素为单位)const int32_t txwpx = tx_size_wide[tx_size];const int32_t txhpx = tx_size_high[tx_size];// 根据预测模式确定是否需要左侧、上方和左上角的像素作为参考int32_t need_left = extend_modes[mode] & NEED_LEFT;int32_t need_above = extend_modes[mode] & NEED_ABOVE;int32_t need_above_left = extend_modes[mode] & NEED_ABOVELEFT;// 初始化预测角度为0int32_t p_angle = 0;// 判断是否为方向性预测模式const int32_t is_dr_mode = av1_is_directional_mode(mode);// 判断是否启用滤波模式const int32_t use_filter_intra = filter_intra_mode != FILTER_INTRA_MODES;// 如果是方向性预测模式if (is_dr_mode) {// 计算实际预测角度,结合模式和角度偏移
p_angle = mode_to_angle_map[mode] + angle_delta * ANGLE_STEP;// 根据角度调整需要的参考像素方向if (p_angle <= 90)
need_above = 1, need_left = 0, need_above_left = 1;else if (p_angle < 180)
need_above = 1, need_left = 1, need_above_left = 1;else
need_above = 0, need_left = 1, need_above_left = 1;}// 如果启用滤波模式,强制需要所有方向的参考像素if (use_filter_intra) need_left = need_above = need_above_left = 1;// 断言确保邻居像素数量非负assert(n_top_px >= 0);assert(n_topright_px >= 0);assert(n_left_px >= 0);assert(n_bottomleft_px >= 0);// 处理特殊情况:无需参考像素if ((!need_above && n_left_px == 0) || (!need_left && n_top_px == 0)) {int32_t val;// 根据需要的参考方向选择默认值if (need_left)
val = (n_top_px > 0) ? above_ref[0] : 129;else
val = (n_left_px > 0) ? left_ref[0] : 127;// 使用默认值填充整个预测块for (i = 0; i < txhpx; ++i) {memset(dst, val, txwpx);
dst += dst_stride;}return;}// 如果需要左侧参考像素if (need_left) {// 判断是否需要底部像素(用于某些预测模式)int32_t need_bottom = !!(extend_modes[mode] & NEED_BOTTOMLEFT);if (use_filter_intra) need_bottom = 0; // 如果启用滤波,不需要底部像素if (is_dr_mode) need_bottom = p_angle > 180; // 方向性模式下根据角度判断// 计算需要的左侧像素数量const int32_t num_left_pixels_needed = txhpx + (need_bottom ? txwpx : 0);
i = 0;// 如果有左侧邻居像素if (n_left_px > 0) {// 填充左侧参考像素for (; i < n_left_px; i++) left_col[i] = left_ref[i * ref_stride];// 如果需要底部像素且有可用的底部左侧像素if (need_bottom && n_bottomleft_px > 0) {assert(i == txhpx); // 确保索引正确// 填充底部左侧像素for (; i < txhpx + n_bottomleft_px; i++)
left_col[i] = left_ref[i * ref_stride];}// 如果像素不足,用最后一个可用像素填充剩余部分if (i < num_left_pixels_needed)memset(&left_col[i], left_col[i - 1], num_left_pixels_needed - i);}else {// 如果没有左侧邻居像素,使用上方邻居像素或默认值填充if (n_top_px > 0)memset(left_col, above_ref[0], num_left_pixels_needed);elsememset(left_col, 129, num_left_pixels_needed);}}// 如果需要上方参考像素if (need_above) {// 判断是否需要右侧像素(用于某些预测模式)int32_t need_right = !!(extend_modes[mode] & NEED_ABOVERIGHT);if (use_filter_intra) need_right = 0; // 如果启用滤波,不需要右侧像素if (is_dr_mode) need_right = p_angle < 90; // 方向性模式下根据角度判断// 计算需要的上方像素数量const int32_t num_top_pixels_needed = txwpx + (need_right ? txhpx : 0);// 如果有上方邻居像素if (n_top_px > 0) {// 复制上方邻居像素到临时数组svt_memcpy(above_row, above_ref, n_top_px);
i = n_top_px;// 如果需要右侧像素且有可用的右上方像素if (need_right && n_topright_px > 0) {assert(n_top_px == txwpx); // 确保索引正确// 复制右上方像素svt_memcpy(above_row + txwpx, above_ref + txwpx, n_topright_px);
i += n_topright_px;}// 如果像素不足,用最后一个可用像素填充剩余部分if (i < num_top_pixels_needed)memset(&above_row[i], above_row[i - 1], num_top_pixels_needed - i);}else {// 如果没有上方邻居像素,使用左侧邻居像素或默认值填充if (n_left_px > 0)memset(above_row, left_ref[0], num_top_pixels_needed);elsememset(above_row, 127, num_top_pixels_needed);}}// 如果需要左上角像素if (need_above_left) {// 根据可用的邻居像素设置左上角像素if (n_top_px > 0 && n_left_px > 0)
above_row[-1] = above_ref[-1];else if (n_top_px > 0)
above_row[-1] = above_ref[0];else if (n_left_px > 0)
above_row[-1] = left_ref[0];else
above_row[-1] = 128;// 同步左上角像素到左侧列数组
left_col[-1] = above_row[-1];}// 如果启用滤波模式if (use_filter_intra) {// 调用滤波函数生成预测块svt_av1_filter_intra_predictor(dst, dst_stride, tx_size, above_row, left_col,
filter_intra_mode);return;}// 如果是方向性预测模式if (is_dr_mode) {// 初始化是否需要对上方和左侧像素进行上采样int32_t upsample_above = 0;int32_t upsample_left = 0;// 如果未禁用边缘滤波if (!disable_edge_filter) {// 根据角度判断是否需要右侧和底部像素const int32_t need_right = p_angle < 90;const int32_t need_bottom = p_angle > 180;// 获取滤波器类型const int32_t filt_type = get_filt_type(xd, plane);// 如果角度不是90或180度if (p_angle != 90 && p_angle != 180) {// 如果需要上方和左侧像素且块尺寸足够大,进行边缘和角落滤波const int32_t ab_le = need_above_left ? 1 : 0;if (need_above && need_left && (txwpx + txhpx >= 24))filter_intra_edge_corner(above_row, left_col);// 对上方像素进行滤波if (need_above && n_top_px > 0) {const int32_t strength = svt_aom_intra_edge_filter_strength(
txwpx, txhpx, p_angle - 90, filt_type);const int32_t n_px = n_top_px + ab_le + (need_right ? txhpx : 0);svt_av1_filter_intra_edge(above_row - ab_le, n_px, strength);}// 对左侧像素进行滤波if (need_left && n_left_px > 0) {const int32_t strength = svt_aom_intra_edge_filter_strength(
txhpx, txwpx, p_angle - 180, filt_type);const int32_t n_px = n_left_px + ab_le + (need_bottom ? txwpx : 0);svt_av1_filter_intra_edge(left_col - ab_le, n_px, strength);}}// 判断是否需要对上方像素进行上采样
upsample_above = svt_aom_use_intra_edge_upsample(
txwpx, txhpx, p_angle - 90, filt_type);if (need_above && upsample_above) {const int32_t n_px = txwpx + (need_right ? txhpx : 0);svt_av1_upsample_intra_edge(above_row, n_px);}// 判断是否需要对左侧像素进行上采样
upsample_left = svt_aom_use_intra_edge_upsample(
txhpx, txwpx, p_angle - 180, filt_type);if (need_left && upsample_left) {const int32_t n_px = txhpx + (need_bottom ? txwpx : 0);svt_av1_upsample_intra_edge(left_col, n_px);}}// 调用方向性预测函数生成预测块svt_aom_dr_predictor(dst, dst_stride, tx_size, above_row, left_col, upsample_above,
upsample_left, p_angle);return;}// 如果是直流预测模式if (mode == DC_PRED) {// 调用直流预测函数生成预测块
svt_aom_dc_pred[n_left_px > 0][n_top_px > 0][tx_size](dst, dst_stride, above_row,
left_col);}// 其他预测模式else {// 调用相应的预测函数生成预测块
svt_aom_eb_pred[mode][tx_size](dst, dst_stride, above_row, left_col);}
}