SVTAV1热点函数-svt_ext_all_sad_calculation_8x8_16x16_avx2

一 svt_ext_all_sad_calculation_8x8_16x16_avx2函数的作用

是SVT-AV1编码其中计算运动估计绝对值差值和SAD计算,这个函数对8x8和16x16大小的块进行SAD计算,并且利用了AVX2指令集来加速计算。

函数参数解释:

src 指向原图像数据的指针

src_stride 源图像的步长,即美航像素之间的字节偏移量。

ref: 指向参考图像数据的指针

ref_stride: 参考图像的步长

mv: 运动矢量,表示当前块相对于参考块的位移

p_best_sad_8x8:指向存储最佳8x8块的SAD值的指针

p_best_sad_16x16:指向存储最佳16x16块的SAD值的指针

p_best_mv8x8 指向存储最佳8x8块对应运动矢量的指针

p_best_mv16x16 一个二维数组,用于存储多个16x16块的SAD值

p_eight_sad16x16 一个二维数组,用于存储多个16x16块的SAD值

p_eight_sad8x8 一个二维数组,用于存储多个8x8块的SAD值

sub_sad 一个布尔值,用于表示是否进行子块SAD计算。

函数的作用,在运动估计过程中,计算当前块与参考块之间的SAD值,找到最佳匹配块。对于16x16的块,会计算多个子块(8x8)的SAD值,并选择具有最小SAD值的块作为最佳匹配块。同时,它还会记录对应的最佳运动矢量。通过这种方式,编码器可以有效的确定如何在参考帧中找到当前块的最佳匹配,从而提高编码效率和压缩性能。

二 函数逐行注释

void svt_ext_all_sad_calculation_8x8_16x16_avx2(uint8_t *src, uint32_t src_stride, uint8_t *ref, uint32_t ref_stride,

uint32_t mv, uint32_t *p_best_sad_8x8, uint32_t *p_best_sad_16x16,

uint32_t *p_best_mv8x8, uint32_t *p_best_mv16x16,

uint32_t p_eight_sad16x16[16][8], uint32_t p_eight_sad8x8[64][8],

Bool sub_sad)

{

// 定义一个静态数组,用于存储16x16块的偏移量,这些偏移量用于确定16x16块在更大图像中的位置

static const char offsets[16] = {0, 1, 4, 5, 2, 3, 6, 7, 8, 9, 12, 13, 10, 11, 14, 15};

// 外层循环,遍历16x16块的y轴方向,共4行

for (int y = 0; y < 4; y++) {

// 内层循环,遍历16x16块的x轴方向,共4列

for (int x = 0; x < 4; x++) {

// 根据当前的y和x,计算16x16块的起始位置索引

const uint32_t start_16x16_pos = offsets[4 * y + x];

// 计算对应8x8块的起始位置索引,因为一个16x16块包含4个8x8块

const uint32_t start_8x8_pos = 4 * start_16x16_pos;

// 计算当前16x16块在源图像中的起始地址

const uint8_t *s = src + 16 * y * src_stride + 16 * x;

// 计算当前16x16块在参考图像中的起始地址

const uint8_t *r = ref + 16 * y * ref_stride + 16 * x;

// 初始化两个256位的AVX寄存器,用于存储SAD计算的中间结果

__m256i sad02 = _mm256_setzero_si256();

__m256i sad13 = _mm256_setzero_si256();

// 如果sub_sad为真,表示需要进行子块的SAD计算

if (sub_sad) {

// 循环4次,处理16x16块中的每个4x4子块

for (int i = 0; i < 4; i++) {

// 从源图像中加载两个128位的16像素数据(两个8x8块的行数据)

const __m128i src01 = _mm_loadu_si128((__m128i *)(s + 0 * src_stride));

const __m128i src23 = _mm_loadu_si128((__m128i *)(s + 8 * src_stride));

// 从参考图像中加载四个128位的16像素数据,分别对应四个8x8块的行数据

const __m128i ref0 = _mm_loadu_si128((__m128i *)(r + 0 * ref_stride + 0));

const __m128i ref1 = _mm_loadu_si128((__m128i *)(r + 0 * ref_stride + 8));

const __m128i ref2 = _mm_loadu_si128((__m128i *)(r + 8 * ref_stride + 0));

const __m128i ref3 = _mm_loadu_si128((__m128i *)(r + 8 * ref_stride + 8));

// 将两个128位的源数据合并成一个256位的源数据

const __m256i src0123 = _mm256_insertf128_si256(_mm256_castsi128_si256(src01), src23, 1);

// 将两个128位的参考数据合并成一个256位的参考数据(ref0和ref2)

const __m256i ref02 = _mm256_insertf128_si256(_mm256_castsi128_si256(ref0), ref2, 1);

// 将两个128位的参考数据合并成一个256位的参考数据(ref1和ref3)

const __m256i ref13 = _mm256_insertf128_si256(_mm256_castsi128_si256(ref1), ref3, 1);

// 使用AVX指令计算ref02和src0123之间的SAD值,并累加到sad02中,模式为000 000

sad02 = _mm256_adds_epu16(sad02, _mm256_mpsadbw_epu8(ref02, src0123, 0));

// 再次计算ref02和src0123之间的SAD值,模式为101 101,并累加到sad02中

sad02 = _mm256_adds_epu16(sad02, _mm256_mpsadbw_epu8(ref02, src0123, 45));

// 计算ref13和src0123之间的SAD值,模式为010 010,并累加到sad13中

sad13 = _mm256_adds_epu16(sad13, _mm256_mpsadbw_epu8(ref13, src0123, 18));

// 再次计算ref13和src0123之间的SAD值,模式为111 111,并累加到sad13中

sad13 = _mm256_adds_epu16(sad13, _mm256_mpsadbw_epu8(ref13, src0123, 63));

// 更新源和参考图像的指针,移动到下一行的对应位置

s += 2 * src_stride;

r += 2 * ref_stride;

}

// 将sad02中的值左移1位,相当于乘以2,进行数据调整

sad02 = _mm256_slli_epi16(sad02, 1);

// 将sad13中的值左移1位,相当于乘以2,进行数据调整

sad13 = _mm256_slli_epi16(sad13, 1);

} else {

// 循环8次,处理16x16块中的每个8x8块

for (int i = 0; i < 8; i++) {

// 从源图像中加载两个128位的16像素数据(两个8x8块的行数据)

const __m128i src01 = _mm_loadu_si128((__m128i *)(s + 0 * src_stride));

const __m128i src23 = _mm_loadu_si128((__m128i *)(s + 8 * src_stride));

// 从参考图像中加载四个128位的16像素数据,分别对应四个8x8块的行数据

const __m128i ref0 = _mm_loadu_si128((__m128i *)(r + 0 * ref_stride + 0));

const __m128i ref1 = _mm_loadu_si128((__m128i *)(r + 0 * ref_stride + 8));

const __m128i ref2 = _mm_loadu_si128((__m128i *)(r + 8 * ref_stride + 0));

const __m128i ref3 = _mm_loadu_si128((__m128i *)(r + 8 * ref_stride + 8));

// 将两个128位的源数据合并成一个256位的源数据

const __m256i src0123 = _mm256_insertf128_si256(_mm256_castsi128_si256(src01), src23, 1);

// 将两个128位的参考数据合并成一个256位的参考数据(ref0和ref2)

const __m256i ref02 = _mm256_insertf128_si256(_mm256_castsi128_si256(ref0), ref2, 1);

// 将两个128位的参考数据合并成一个256位的参考数据(ref1和ref3)

const __m256i ref13 = _mm256_insertf128_si256(_mm256_castsi128_si256(ref1), ref3, 1);

// 使用AVX指令计算ref02和src0123之间的SAD值,并累加到sad02中,模式为000 000

sad02 = _mm256_adds_epu16(sad02, _mm256_mpsadbw_epu8(ref02, src0123, 0));

// 再次计算ref02和src0123之间的SAD值,模式为101 101,并累加到sad02中

sad02 = _mm256_adds_epu16(sad02, _mm256_mpsadbw_epu8(ref02, src0123, 45));

// 计算ref13和src0123之间的SAD值,模式为010 010,并累加到sad13中

sad13 = _mm256_adds_epu16(sad13, _mm256_mpsadbw_epu8(ref13, src0123, 18));

// 再次计算ref13和src0123之间的SAD值,模式为111 111,并累加到sad13中

sad13 = _mm256_adds_epu16(sad13, _mm256_mpsadbw_epu8(ref13, src0123, 63));

// 更新源和参考图像的指针,移动到下一行的对应位置

s += src_stride;

r += ref_stride;

}

}

// 忽略p_eight_sad8x8参数,可能用于其他功能

(void)p_eight_sad8x8;

// 将sad02的低128位部分提取出来,存储到sad0中

const __m128i sad0 = _mm256_castsi256_si128(sad02);

// 将sad13的低128位部分提取出来,存储到sad1中

const __m128i sad1 = _mm256_castsi256_si128(sad13);

// 将sad02的高128位部分提取出来,存储到sad2中

const __m128i sad2 = _mm256_extracti128_si256(sad02, 1);

// 将sad13的高128位部分提取出来,存储到sad3中

const __m128i sad3 = _mm256_extracti128_si256(sad13, 1);

// 对sad0中的每个16位元素,找到最小值及其位置索引,存储到minpos0中

const __m128i minpos0 = _mm_minpos_epu16(sad0);

// 对sad1中的每个16位元素,找到最小值及其位置索引,存储到minpos1中

const __m128i minpos1 = _mm_minpos_epu16(sad1);

// 对sad2中的每个16位元素,找到最小值及其位置索引,存储到minpos2中

const __m128i minpos2 = _mm_minpos_epu16(sad2);

// 对sad3中的每个16位元素,找到最小值及其位置索引,存储到minpos3中

const __m128i minpos3 = _mm_minpos_epu16(sad3);

// 将minpos0和minpos1的低16位元素组合成一个新的128位数据

const __m128i minpos01 = _mm_unpacklo_epi16(minpos0, minpos1);

// 将minpos2和minpos3的低16位元素组合成一个新的128位数据

const __m128i minpos23 = _mm_unpacklo_epi16(minpos2, minpos3);

// 将minpos01和minpos23的低32位元素组合成一个新的128位数据

const __m128i minpos0123 = _mm_unpacklo_epi32(minpos01, minpos23);

// 提取minpos0123中的SAD值部分,存储到sad8x8中

const __m128i sad8x8 = _mm_unpacklo_epi16(minpos0123, _mm_setzero_si128());

// 提取minpos0123中的位置索引部分,存储到pos8x8中

const __m128i pos8x8 = _mm_unpackhi_epi16(minpos0123, _mm_setzero_si128());

// 加载当前8x8块的最佳SAD值

__m128i best_sad8x8 = _mm_loadu_si128((__m128i *)(p_best_sad_8x8 + start_8x8_pos));

// 比较当前计算的sad8x8和已有的最佳SAD值,生成掩码

const __m128i mask = _mm_cmplt_epi32(sad8x8, best_sad8x8);

// 更新最佳SAD值,取当前计算值和已有值中的较小者

best_sad8x8 = _mm_min_epi32(best_sad8x8, sad8x8);

// 将更新后的最佳SAD值存储回p_best_sad_8x8数组中

_mm_storeu_si128((__m128i *)(p_best_sad_8x8 + start_8x8_pos), best_sad8x8);

// 设置运动矢量mv的128位数据

const __m128i mvs = _mm_set1_epi32(mv);

// 加载当前8x8块的最佳运动矢量

__m128i best_mv8x8 = _mm_loadu_si128((__m128i *)(p_best_mv8x8 + start_8x8_pos));

// 根据位置索引计算当前块的运动矢量

const __m128i mv8x8 = _mm_add_epi16(mvs, pos8x8);

// 根据掩码更新最佳运动矢量,当当前SAD更小时使用新的运动矢量

best_mv8x8 = _mm_blendv_epi8(best_mv8x8, mv8x8, mask);

// 将更新后的最佳运动矢量存储回p_best_mv8x8数组中

_mm_storeu_si128((__m128i *)(p_best_mv8x8 + start_8x8_pos), best_mv8x8);

// 计算sad0和sad1的和,得到sum01

const __m128i sum01 = _mm_add_epi16(sad0, sad1);

// 计算sad2和sad3的和,得到sum23

const __m128i sum23 = _mm_add_epi16(sad2, sad3);

// 计算sum01和sum23的和,得到16x16块的SAD值

const __m128i sad16x16_16 = _mm_add_epi16(sum01, sum23);

// 将16位的SAD值扩展为32位,便于后续处理

const __m256i sad16x16_32 = _mm256_cvtepu16_epi32(sad16x16_16);

// 将计算得到的16x16块的SAD值存储到p_eight_sad16x16数组中

_mm256_storeu_si256((__m256i *)(p_eight_sad16x16[start_16x16_pos]), sad16x16_32);

// 对sad16x16_16中的每个16位元素,找到最小值及其位置索引,存储到minpos16x16中

const __m128i minpos16x16 = _mm_minpos_epu16(sad16x16_16);

// 提取最小值

const uint32_t min16x16 = _mm_extract_epi16(minpos16x16, 0);

// 如果当前计算的最小SAD值小于已有的最佳SAD值,则更新

if (min16x16 < p_best_sad_16x16[start_16x16_pos]) {

// 更新最佳SAD值

p_best_sad_16x16[start_16x16_pos] = min16x16;

// 提取位置索引部分

const __m128i pos16x16 = _mm_srli_si128(minpos16x16, 2);

// 计算对应的运动矢量

const __m128i mv16x16 = _mm_add_epi16(mvs, pos16x16);

// 更新最佳运动矢量

p_best_mv16x16[start_16x16_pos] = _mm_extract_epi32(mv16x16, 0);

}

}

}

}

相关推荐
枉费红笺3 分钟前
目标检测竞赛训练策略解析与拓展
人工智能·目标检测·计算机视觉
小白的高手之路17 分钟前
常用的卷积神经网络及Pytorch示例实现
人工智能·pytorch·python·深度学习·神经网络·cnn
IT古董24 分钟前
【漫话机器学习系列】173.模型可识别度(Model Identifiability)
人工智能·机器学习
Julian.zhou27 分钟前
MCP服务:五分钟实现微服务治理革命,无缝整合Nacos/Zookeeper/OpenResty
人工智能·微服务·zookeeper·交互·openresty
weixin_4094110228 分钟前
支持 MCP 协议的开源 AI Agent 项目
人工智能·microsoft
jndingxin30 分钟前
OpenCV 图形API(6)将一个矩阵(或图像)与一个标量值相加的函数addC()
人工智能·opencv
神经星星38 分钟前
在线教程丨YOLO系列重要创新!清华团队发布YOLOE,直击开放场景物体实时检测与分割
人工智能·深度学习·机器学习
李帅149698241916739 分钟前
🌟《从一到二:基于Trae的多智能体合作Prompt自动生成迭代指南》
人工智能
全栈开发圈1 小时前
新书速览|深入理解DSP:基于TMS320F28379D的开发与实践
人工智能·信号处理·dsp开发
在下_诸葛1 小时前
DeepSeek的API调用 | 结合DeepSeek API文档 | Python环境 | 对话补全(二)
人工智能·python·gpt·prompt·aigc