1、前言
继上一个例子,本次继续来学习NEON,本次学习NEON中向量拼接的操作,主要应用在图像的padding中。
https://blog.csdn.net/weixin_42108183/article/details/136440707
2、案例
2.1 案例1
在某些情况下,需要取在每个向量中去一部分值出来组成一个新的向量,这些值在逻辑上是连续的,如下图所示:
#include <stdio.h>
#include <stdlib.h>
#include <arm_neon.h>
#include <iostream>
#include <vector>
using namespace std;
#include <arm_neon.h>
int main(){
// a拼接b 再取偏移量
float _a[] = {1,2,3,4}, _b[] = {5,6,7,8} ;
float32x4_t a = vld1q_f32(_a);
float32x4_t b = vld1q_f32(_b);
float32x4_t r1 = vextq_f32(a,b,1); //r1={2,3,4,5}
vector<float> show_data(4);
vst1q_f32 (show_data.data(),r1);
for(auto n : show_data){
cout << static_cast<int>(n) << endl;
}
cout << "----------" << endl;
float32x4_t r2 = vextq_f32(a,b,2); //r2={3,4,5,6}
vst1q_f32 (show_data.data(),r2);
for(auto n : show_data){
cout << static_cast<int>(n) << endl;
}
cout << "----------" << endl;
float32x4_t r3 = vextq_f32(a,b,3); //r3={4,5,6,7}
vst1q_f32 (show_data.data(),r3);
for(auto n : show_data){
cout << static_cast<int>(n) << endl;
}
// float32x4_t r4 = vextq_f32(a,b,4); // 错误 对vextq_f32偏移量的范围有限制,最多偏移一个q寄存器范围的值
return 0;
}
2.2、案例2
在padding时,如果使用传统的方法,直接在原数据的周围加一圈数据,再将新数据储存起来,再进行其他操作。如果在一行一行padding的同时进行其他操作,就可以不用申请额外的空间了。
-
常量填充
常量填充是指,实现定义一个固定的padding向量,每一行的数据,都利用这个padding向量来计算边界值,如下图时针对左边界的填充
#include <stdio.h>
#include <stdlib.h>
#include <arm_neon.h>
#include <iostream>
#include <vector>
using namespace std;
#include <arm_neon.h>
int main(){
uint8_t pad_val = 0;
//dup指令生成padding向量
uint8x16_t v8_const_pad = vdupq_n_u8(pad_val);
// 第0行数据
vector<uint8_t> row0;
for(int i=0;i<16;i++){
row0.push_back(i);
}
auto v8_pre_row_data = v8_const_pad; // 填充数据
//读取第0行数据
uint8x16_t v8_tmp_data = vld1q_u8(row0.data());
//第0行带有左padding的数据
uint8x16_t v8_row_cur_data = vextq_u8(v8_const_pad, v8_tmp_data, 15); // 拼接起来从第15个偏移量的位置取数值
// //读取第1行数据,假设第一行数据与第二行数据相等
vector<uint8_t> row1 = row0
v8_tmp_data = vld1q_u8(row1.data());
// //第1行带有左padding的数据
uint8x16_t v8_next_row_data = vextq_u8(v8_const_pad, v8_tmp_data, 15);
return 0;
}
-
复制填充
复制填充就是将填充值修改为图像最边界的值,基本上和常量填充一致。
-
反射填充
反射填充的解释就在复制填充的情况下,将paddding向量逆序,例如有一个向量为 abcdefgh ,对于普通反射,直接取值然后逆序即可abcdefgh -> cdba|abcdefgh| hgfe,而对于101反射,则需要再变化一点 ,abcdefgh -> ecdb|abcdefgh| gfed。普通反射和101反射的区别是对称中心两个a的中间还是a本身。代码如下:
#include <stdio.h> #include <stdlib.h> #include <arm_neon.h> #include <iostream> #include <vector> using namespace std; #include <arm_neon.h> int main(){ vector<uint8_t> row0; for(int i=0;i<16;i++){ row0.push_back(i); } // 取第1~7 uint8x8_t v8_ref_pad0 = vld1_u8(row0.data()+1); // 1,2,3,4,5,6,7,0 // 逆序 uint8x8_t v8_ref_pad1 = vrev64_u8(v8_ref_pad0); // 0,7,6,5,4,3,2,1 // 第0行原始数据 uint8x16_t v8_row0_data = vld1q_u8(row0.data()); // 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 // 拼接,必须与第0行原始数据一样的长度才能调用vextq_u8这个函数 uint8x16_t vcombine_0_1 = vcombine_u8(v8_ref_pad0, v8_ref_pad1); // 1,2,3,4,5,6,7,0,0,7,6,5,4,3,2,1 // 得到拼接后的向量 uint8x16_t v8_cur_row_data = vextq_u8(vcombine_0_1, v8_row0_data, 15); // 1,2,3,4,5,6,7,0,0,7,6,5,4,3,2,|1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14|,15 return 0; }
3、总结
本文主要学习了NEON中向量拼接并取值的操作,并在图像padding的应用中,有所体现。