【arm汇编优化】——rgb图像分离

arm,汇编
Code

网上有大佬用arm做了一个【Arm端算法优化笔记】一,一步步优化盒子滤波算法,这里我们也体验一下arm的neon assembly加速。这里我们做一个简单的:rgb图像,r,g,b 3通道分割分别采用4种方式实现了:

  • opencv内置函数
  • 暴力for循环
  • neon intrinsic的实现
  • neon assembly汇编

以上4种方式都单独测试了耗时情况,并将处理结果保存成图片,进行结果检查

硬件

  • tx2,armv8-a,64位的处理器

0.code

0.0 opencv内置实现

直接使用opencv 中的 split函数

void opencv_rgb_split(Mat src)
{
	int size = src.rows * src.cols * 3;
	Mat b(src.rows, src.cols, CV_8UC1);
	Mat g(src.rows, src.cols, CV_8UC1);
	Mat r(src.rows, src.cols, CV_8UC1);
	
	auto start = chrono::system_clock::now();
	Mat out[] = {b, g, r};
	split(src, out);
	auto end   = chrono::system_clock::now();
	auto duration = chrono::duration_cast<chrono::microseconds>(end - start);
    cout << "opencv cost time is:" << double(duration.count()) << " us" << endl;
	
	// Mat b_color(src.rows, src.cols, CV_8UC3);
	// Mat g_color(src.rows, src.cols, CV_8UC3);
	// Mat r_color(src.rows, src.cols, CV_8UC3);
	// for(int i=0; i<size; i+=3)
	// {
	// 	b_color.data[i] = b.data[i/3];
	// 	b_color.data[i+1] = 0;
	// 	b_color.data[i+2] = 0;
		
	// 	g_color.data[i] = 0;
	// 	g_color.data[i+1] = g.data[i/3];
	// 	g_color.data[i+2] = 0;
		
	// 	r_color.data[i] = 0;
	// 	r_color.data[i+1] = 0;
	// 	r_color.data[i+2] = r.data[i/3];
	// }
	// imwrite("b.jpg", b_color);
	// imwrite("g.jpg", g_color);
	// imwrite("r.jpg", r_color);

}

0.1 暴力for

暴力循环的代码也很简单,这里就直接上代码了。

void own_split_kernel(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* rgb, int lencolor){
	for(int i=0; i<lencolor; ++i){
		b[i] = rgb[3*i];
		g[i] = rgb[3*i + 1];
		r[i] = rgb[3*i + 2];
	}
}

void own_rgb_split(Mat src){
	int size = src.rows * src.cols;
	uint8_t *rgb = src.data;
	uint8_t* r = (uint8_t*)malloc(sizeof(uint8_t) * size);
	uint8_t* g = (uint8_t*)malloc(sizeof(uint8_t) * size);
	uint8_t* b = (uint8_t*)malloc(sizeof(uint8_t) * size);

	auto start = chrono::system_clock::now();
	own_split_kernel(r, g, b, rgb, size);
	auto end   = chrono::system_clock::now();
	auto duration = chrono::duration_cast<chrono::microseconds>(end - start);
    cout << "own cost time is:" << double(duration.count()) << " us" << endl;

	// Mat b_color(src.rows, src.cols, CV_8UC3);
	// Mat g_color(src.rows, src.cols, CV_8UC3);
	// Mat r_color(src.rows, src.cols, CV_8UC3);
	// size *= 3;
	// for(int i=0; i<size; i+=3)
	// {
	// 	b_color.data[i] = b[i/3];
	// 	b_color.data[i+1] = 0;
	// 	b_color.data[i+2] = 0;
		
	// 	g_color.data[i] = 0;
	// 	g_color.data[i+1] = g[i/3];
	// 	g_color.data[i+2] = 0;
		
	// 	r_color.data[i] = 0;
	// 	r_color.data[i+1] = 0;
	// 	r_color.data[i+2] = r[i/3];
	// }
	// imwrite("b_own.jpg", b_color);
	// imwrite("g_own.jpg", g_color);
	// imwrite("r_own.jpg", r_color);
	free(r);
	free(g);
	free(b);
}

0.2 neon intrinsic

采用intrinsic的方式的时候,就像使用c中的内置函数一样,并不需要考虑寄存器的操作等。
这里使用了一些指令,解释:

  • vld3q_u8(src):ld3是隔3个读取一次,u8表示数据类型是uint8,返回的是 uint8x16x3_t 数据类型,相当于3个数组,每个数组有16个uint8的数
  • vst1q_u8:st1是将128个bit连续写入,其他的 st2 是隔2个

code

void rgb_deinterleave_neon(uint8_t *r, uint8_t *g, uint8_t *b, uint8_t *rgb, int len_color) {
    /*
     * Take the elements of "rgb" and store the individual colors "r", "g", and "b"
     */
    int num8x16 = len_color / 16;
    uint8x16x3_t intlv_rgb;
    for (int i=0; i < num8x16; i++) {
        intlv_rgb = vld3q_u8(rgb+3*16*i);
        vst1q_u8(r+16*i, intlv_rgb.val[0]);
        vst1q_u8(g+16*i, intlv_rgb.val[1]);
        vst1q_u8(b+16*i, intlv_rgb.val[2]);
    }
}


void neon_rgb_split(Mat src){
	int size = src.rows * src.cols;
	uint8_t *r, *g, *b;
	uint8_t *rgb = src.data;
	r = (uint8_t*)malloc(sizeof(uint8_t) * size);
	g = (uint8_t*)malloc(sizeof(uint8_t) * size);
	b = (uint8_t*)malloc(sizeof(uint8_t) * size);

	auto start = chrono::system_clock::now();
	rgb_deinterleave_neon(r, g, b, rgb, size);
	auto end   = chrono::system_clock::now();
	auto duration = chrono::duration_cast<chrono::microseconds>(end - start);
    cout << "neon cost time is:" << double(duration.count()) << " us" << endl;

	// Mat b_color(src.rows, src.cols, CV_8UC3);
	// Mat g_color(src.rows, src.cols, CV_8UC3);
	// Mat r_color(src.rows, src.cols, CV_8UC3);
	// size *=3;
	// for(int i=0; i<size; i+=3)
	// {
	// 	b_color.data[i] = b[i/3];
	// 	b_color.data[i+1] = 0;
	// 	b_color.data[i+2] = 0;
		
	// 	g_color.data[i] = 0;
	// 	g_color.data[i+1] = g[i/3];
	// 	g_color.data[i+2] = 0;
		
	// 	r_color.data[i] = 0;
	// 	r_color.data[i+1] = 0;
	// 	r_color.data[i+2] = r[i/3];
	// }
	// imwrite("b_neon.jpg", b_color);
	// imwrite("g_neon.jpg", g_color);
	// imwrite("r_neon.jpg", r_color);
	free(r);
	free(g);
	free(b);
}

0.3 neon assembly

汇编这里就不解释了,可以参考【arm】——汇编学习 对汇编有大概的了解。

code

void rgb_assembly_neon(uint8_t *r, uint8_t *g, uint8_t *b, uint8_t *rgb, int len_color) {

    asm volatile(
        "1:                                             \n"		// goto 标志位
		"prfm   pldl1keep, [%3, 384]    				\n"
        "ld3    {v0.16b, v1.16b, v2.16b}, [%3], #48     \n"
        "subs   %w4, %w4, #16           \n"
        "st1    {v0.16b}, [%2], #16     \n"     // b
        "st1    {v1.16b}, [%1], #16     \n"     // g
        "st1    {v2.16b}, [%0], #16     \n"     // r
        "b.gt   1b                      \n"
        : "+r"(r),
          "+r"(g),
          "+r"(b),
          "+r"(rgb),
          "+r"(len_color)
        :
        : "v0", "v1", "v2"          // use 3 个128bit寄存器
    );
}


void assembly_rgb_split(Mat src){
	int size = src.rows * src.cols;
	uint8_t *r, *g, *b;
	uint8_t *rgb = src.data;
	r = (uint8_t*)malloc(sizeof(uint8_t) * size);
	g = (uint8_t*)malloc(sizeof(uint8_t) * size);
	b = (uint8_t*)malloc(sizeof(uint8_t) * size);

	auto start = chrono::system_clock::now();
	rgb_assembly_neon(r, g, b, rgb, size);
	auto end   = chrono::system_clock::now();
	auto duration = chrono::duration_cast<chrono::microseconds>(end - start);
    cout << "assembly cost time is:" << double(duration.count()) << " us" << endl;

	// Mat b_color(src.rows, src.cols, CV_8UC3);
	// Mat g_color(src.rows, src.cols, CV_8UC3);
	// Mat r_color(src.rows, src.cols, CV_8UC3);
	// size *=3;
	// for(int i=0; i<size; i+=3)
	// {
	// 	b_color.data[i] = b[i/3];
	// 	b_color.data[i+1] = 0;
	// 	b_color.data[i+2] = 0;
		
	// 	g_color.data[i] = 0;
	// 	g_color.data[i+1] = g[i/3];
	// 	g_color.data[i+2] = 0;
		
	// 	r_color.data[i] = 0;
	// 	r_color.data[i+1] = 0;
	// 	r_color.data[i+2] = r[i/3];
	// }
	// imwrite("b_asm.jpg", b_color);
	// imwrite("g_asm.jpg", g_color);
	// imwrite("r_asm.jpg", r_color);
	free(r);
	free(g);
	free(b);
}

0.4 测试结果

methodtime/ms
neon intrinsic5.453
neon assembly2.481
opencv2.540
for loop18.284

1. other

  • opencv的耗时与自己写的汇编耗时基本差不多,可能是tx2系统中的opencv编译的时候,编译器优化比较好。
  • 有一个已知bug: 使能所有的保存可视化图片,耗时的统计好像会有问题
  • code 是项目工程,直接编译就可以运行测试了。

2. reference

1.libyuv

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值