分类网站上怎么做锚文本,做擦边球视频网站,怎么制作图片和文字一起,郑州高端定制建设网站1、前言
在上个博客中对NEON有了基础的了解#xff0c;本文将针对一个图像下采样的例子对NEON进行学习。
学习链接:CPU优化技术 - NEON 开发进阶
上文链接:https://blog.csdn.net/weixin_42108183/article/details/136412104
2、第一个例子
现在有一张图片#xff0c;需…1、前言
在上个博客中对NEON有了基础的了解本文将针对一个图像下采样的例子对NEON进行学习。
学习链接:CPU优化技术 - NEON 开发进阶
上文链接:https://blog.csdn.net/weixin_42108183/article/details/136412104
2、第一个例子
现在有一张图片需要对UV通道的数据进行下采样对于同种类型的数据相邻的4个元素求和并求均值。示意图如下图所示: 假定图像数据的宽为16的整数倍如果使用c代码可以写出下面的代码:
void DownscaleUv(uint8_t *src, uint8_t *dst, int32_t src_stride, int32_t dst_width, int32_t dst_height, int32_t dst_stride)
{//遍历每一行的数据for (int32_t j 0; j dst_height; j){ // 偶数行起始位置uint8_t *src_ptr0 src src_stride * j * 2;// 奇数行起始位置uint8_t *src_ptr1 src_ptr0 src_stride;// 存储起始位置uint8_t *dst_ptr dst dst_stride * j;// 没一次循环计算没for (int32_t i 0; i dst_width; i 2){// U通道 (u1 u2 u3 u4) / 4dst_ptr[i] (src_ptr0[i * 2] src_ptr0[i * 2 2] src_ptr1[i * 2] src_ptr1[i * 2 2]) / 4;// V通道 (v1 v2 v3 v4) / 4dst_ptr[i 1] (src_ptr0[i * 2 1] src_ptr0[i * 2 3] src_ptr1[i * 2 1] src_ptr1[i * 2 3]) / 4;}}
}通过学习向量化编程我们可知数据的计算可以利用单指令多数据的方式进行加速例如上面的例子中的内层循环下面就使用NEON来试试吧。
3、第2个例子
为了进行向量化加速首先需要将UV数据分离将UV数据分离的操作在NEON中很容易进行 使用vld2交织加载或者储存即可。对于每一行的数据交织加载的示意图如下。
交织加载的基本原理是按照间隔挑选数据。交织加载的例子如下所示:
void DownscaleUvNeon()
{vectoruint8_t data; // UVUVUVUVUV...for(int i0;i32;i){data.push_back(i);}// uint8_t *src_ptr0 (uint8_t *)data.data(); // load 第一行的数据uint8x16x2_t src;src vld2q_u8(src_ptr0); // 交织读取 16 * 2 的数据,需要两个q寄存器。auto a src_odd.val[0]; // 一行的U数据vectoruint8_t show_data(16);vst1q_u8 (show_data.data(),a); // 将U数据顺序储存到内存中// 打印for(auto n : show_data){cout static_castint(n) endl; // 0,2,4,6,...}
}4、第3个例子
对于下UV数据采样来说在偶数行进行上面的交织加载再在奇数行上进行同样的操作。奇数行和偶数行相应的数据进行相加再求平均即可得到最后的结果。代码实现如下
#include arm_neon.h
void DownscaleUvNeon(uint8_t *src, uint8_t *dst, int32_t src_width, int32_t src_stride, int32_t dst_width, int32_t dst_height, int32_t dst_stride)
{//用于加载偶数行的源数据2组每组16个u8类型数据16 * 8) * 2 128 * 128, 因此需要两个q寄存器。 uint8x16x2_t v8_src0;//用于加载奇数行的源数据uint8x16x2_t v8_src1;//目的数据变量需要一个Q寄存器uint8x8x2_t v8_dst;//目前只处理16整数倍部分的结果int32_t dst_width_align dst_width (-16); // dst_width (-16)最大能够整除16的数。//向量化剩余的部分需要单独处理int32_t remain dst_width 15;int32_t i 0;//外层高度循环逐行处理for (int32_t j 0; j dst_height; j){//偶数行源数据地址uint8_t *src_ptr0 src src_stride * j * 2;//奇数行源数据地址uint8_t *src_ptr1 src_ptr0 src_stride;//目的数据指针uint8_t *dst_ptr dst dst_stride * j;//内层循环一次16个u8结果输出for (i 0; i dst_width_align; i 16){//提取数据进行UV分离v8_src0 vld2q_u8(src_ptr0); src_ptr0 32; // 偶数行进入下一个stridev8_src1 vld2q_u8(src_ptr1);src_ptr1 32; // 奇数行行进入下一个stride//水平两个数据相加uint16x8_t v16_u_sum0 vpaddlq_u8(v8_src0.val[0]);uint16x8_t v16_v_sum0 vpaddlq_u8(v8_src0.val[1]);uint16x8_t v16_u_sum1 vpaddlq_u8(v8_src1.val[0]);uint16x8_t v16_v_sum1 vpaddlq_u8(v8_src1.val[1]);//上下两个数据相加之后求均值v8_dst.val[0] vshrn_n_u16(vaddq_u16(v16_u_sum0, v16_u_sum1), 2);v8_dst.val[1] vshrn_n_u16(vaddq_u16(v16_v_sum0, v16_v_sum1), 2);//UV通道结果交织存储vst2_u8(dst_ptr, v8_dst);dst_ptr 16;}//process leftovers......}
}5、第4个例子
当图像的宽度不是16的整数倍需要考虑结尾数据处理按照链接里面的例子可以分为以下几种。
1、 padding
也就是将数据补齐到想要的长度如下图所示比如我这里需要操作 uint8x8_t的数据但是我的数据长度只有5可以将数据的长度填充至8。
2、Overlap
也就是重复利用其中的某些数据在不填充其他数据的情况下进行如下图所示当需要利用uint8x4_t来对下面的数据进行计算时可以先将04加载到寄存器上再将36加载到寄存器上操作。
常用第二种方法对结尾数据进行处理那么图像下采样的数据代码可以写成:
#include arm_neon.hvoid DownscaleUvNeon(uint8_t *src, uint8_t *dst, int32_t src_width, int32_t src_stride, int32_t dst_width, int32_t dst_height, int32_t dst_stride)
{uint8x16x2_t v8_src0;uint8x16x2_t v8_src1;uint8x8x2_t v8_dst;int32_t dst_width_align dst_width (-16); // 最大能够整除16的数。int32_t remain dst_width 15; // 需要剩余处理的数据长度int32_t i 0;for (int32_t j 0; j dst_height; j){uint8_t *src_ptr0 src src_stride * j * 2;uint8_t *src_ptr1 src_ptr0 src_stride;uint8_t *dst_ptr dst dst_stride * j;// 处理完宽度为16的整数倍数据了for (i 0; i dst_width_align; i 16){v8_src0 vld2q_u8(src_ptr0);src_ptr0 32;v8_src1 vld2q_u8(src_ptr1);src_ptr1 32;uint16x8_t v16_u_sum0 vpaddlq_u8(v8_src0.val[0]);uint16x8_t v16_v_sum0 vpaddlq_u8(v8_src0.val[1]);uint16x8_t v16_u_sum1 vpaddlq_u8(v8_src1.val[0]);uint16x8_t v16_v_sum1 vpaddlq_u8(v8_src1.val[1]);v8_dst.val[0] vshrn_n_u16(vaddq_u16(v16_u_sum0, v16_u_sum1), 2);v8_dst.val[1] vshrn_n_u16(vaddq_u16(v16_v_sum0, v16_v_sum1), 2);vst2_u8(dst_ptr, v8_dst);dst_ptr 16;}// process leftover// remain 剩余需要处理的数据长度if (remain 0){// 从后往前回退一次向量计算需要的数据长度// 有部分数据是之前处理过的这部分的数据在这里重复计算一次src_ptr0 src src_stride * (j * 2) src_width - 32; src_ptr1 src_ptr0 src_stride;dst_ptr dst dst_stride * j dst_width - 16;v8_src0 vld2q_u8(src_ptr0);v8_src1 vld2q_u8(src_ptr1);uint16x8_t v16_u_sum0 vpaddlq_u8(v8_src0.val[0]);uint16x8_t v16_v_sum0 vpaddlq_u8(v8_src0.val[1]);uint16x8_t v16_u_sum1 vpaddlq_u8(v8_src1.val[0]);uint16x8_t v16_v_sum1 vpaddlq_u8(v8_src1.val[1]);v8_dst.val[0] vshrn_n_u16(vaddq_u16(v16_u_sum0, v16_u_sum1), 2);v8_dst.val[1] vshrn_n_u16(vaddq_u16(v16_v_sum0, v16_v_sum1), 2);vst2_u8(dst_ptr, v8_dst);}}
}3、 single
将剩余的元素单独处理就是将剩余的元素利用NEON的只加载一个元素的功能不推荐使用因为这里又可能for循环多次。
4、将剩余的元素当作标量处理
也就是将剩下的元素直接使用c语言编程的方式进行计算。
void DownscaleUvNeonScalar(uint8_t *src, uint8_t *dst, int32_t src_width, int32_t src_stride, int32_t dst_width, int32_t dst_height, int32_t dst_stride)
{uint8x16x2_t v8_src0;uint8x16x2_t v8_src1;uint8x8x2_t v8_dst;int32_t dst_width_align dst_width (-16);int32_t remain dst_width 15;int32_t i 0;for (int32_t j 0; j dst_height; j){uint8_t *src_ptr0 src src_stride * j * 2;uint8_t *src_ptr1 src_ptr0 src_stride;uint8_t *dst_ptr dst dst_stride * j;for (i 0; i dst_width_align; i 16) // 16 items output at one time{v8_src0 vld2q_u8(src_ptr0);src_ptr0 32;v8_src1 vld2q_u8(src_ptr1);src_ptr1 32;uint16x8_t v16_u_sum0 vpaddlq_u8(v8_src0.val[0]);uint16x8_t v16_v_sum0 vpaddlq_u8(v8_src0.val[1]);uint16x8_t v16_u_sum1 vpaddlq_u8(v8_src1.val[0]);uint16x8_t v16_v_sum1 vpaddlq_u8(v8_src1.val[1]);v8_dst.val[0] vshrn_n_u16(vaddq_u16(v16_u_sum0, v16_u_sum1), 2);v8_dst.val[1] vshrn_n_u16(vaddq_u16(v16_v_sum0, v16_v_sum1), 2);vst2_u8(dst_ptr, v8_dst);dst_ptr 16;}//process leftoversrc_ptr0 src src_stride * j * 2;src_ptr1 src_ptr0 src_stride;dst_ptr dst dst_stride * j;for (int32_t i dst_width_align; i dst_width; i 2){dst_ptr[i] (src_ptr0[i * 2] src_ptr0[i * 2 2] src_ptr1[i * 2] src_ptr1[i * 2 2]) / 4;dst_ptr[i 1] (src_ptr0[i * 2 1] src_ptr0[i * 2 3] src_ptr1[i * 2 1] src_ptr1[i * 2 3]) / 4;}}
}6、总结
本次学习中通过一个下采样的例子学习的NEON编程过程中的优势以及将要面临的问题主要是剩余数据处理的方式后面将继续深入学习。 / 4; dst_ptr[i 1] (src_ptr0[i * 2 1] src_ptr0[i * 2 3] src_ptr1[i * 2 1] src_ptr1[i * 2 3]) / 4;}
}} #### 6、总结本次学习中通过一个下采样的例子学习的NEON编程过程中的优势以及将要面临的问题主要是剩余数据处理的方式后面将继续深入学习。