SVT-AV1源码分析build_intra_predictors函数

build_intra_predictors函数作用

build_intra_predictors 函数视频编码技术中特别是帧内预测过程起着关键作用以下主要作用详细解释

一 处理调色板模式

在某些视频编码场景使用调色模式图像进行编码在这种情况系build_intra_predictors 函数直接根据调色映射预测器无需进行复杂帧内预测计算

二 构建预测参考

使用调色板模式时函数的主要任务构建当前进行帧内预测所需参考像素这包括当前周边是否存在可用进行判断如果周边不可用函数下一级函数进行padding构建预测所需要reference

三 考虑多种因素进行预测

在构建预测build_intra_predictors 函数考虑多种因素包括

邻居像素可用性检测左下邻居像素是否存在确定预测可以利用参考信息

预测模式 根据制定预测模式(如角度预测模式等)选择合适算法生成预测

角度偏移角度预测模式角度偏移量影响预测方向方式

滤波模式确定是否使用滤波以及使用何种滤波模式提高预测准确性

变换尺寸根据当前变换尺寸调整预测大小形状

四 生产预测块

结合上述考虑因素函数最终生成当前预测块这个预测用于后续编码过程残差计算熵编码实现高效视频压缩

五 参数说明

MacroBlockD *xd:指向宏块解码结构体的指针,包含当前宏块的解码信息。

top_neigh_arrayleft_neigh_array 分别存储上方左侧邻居像素数据用于构建预测参考

dstdst_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);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值