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;
//设置参考像素的步长位1
int32_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;
// 初始化预测角度为0
int32_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);
else
memset(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);
else
memset(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);
}
}