ARM intrinsics 实战(1):全局标定-1

前言

学习ARM intrinsics主要是为了完成关于相机标定的一些加速工作,所以这个系列的实战也主要以相机标定为例,计划主要会有以下几个部分:
(1)全局标定
(2)逐像素标定
(3)距离转深度
(4)深度转点云

全局标定介绍

全局标定的数学逻辑很简单,就是给距离矩阵减去一个固定偏差。固定偏差有相应的计算方法。类似于
A=B-offset;

本项目的数据类型采用的是float32位,处理器是128位。

总体完成目标:
(1)实测三种寻址方式的快慢。寻址方式有三种,一种是按行读取,一种是按列读取,一种是按小矩阵读取。
(2)完成全局标定的ARM intrinsics优化
本文目标:
跑通ARM intrinsics,并且结果无误。

实现

汇编逻辑

定义所需的变量---->将数据从内存加载到寄存器---->执行减法操作---->将数据从寄存器加载到内存
注意事项:
(1)本项目可以同时处理4个数据,因此访问矩阵时,循环边界也要改变
(2)数据的三种读取顺序

c++实现

void distance_offset_correction(cv::Mat m1, cv::Mat m2)
{
	for (int i = 0; i < m1.rows; i++) //矩阵行数循环
	{
		for (int j = 0; j < m1.cols; j++) //矩阵列数循环
		{
			m1.at<float>(i, j) = m1.at<float>(i, j)+100.0;
			//m2.at<float>(i, j) = m2.at<float>(i, j) + 10.0;
		}
	}
}

neon intrinsics 实现

void distance_offset_correction_neon(cv::Mat m1, float scalar, cv::Mat m2, int n) {
   float* ptr_m1 = (float*)m1.data; //转换为指针
   float* ptr_m2 = (float*)m2.data;
   float32x4_t scalar_vec = vdupq_n_f32(scalar); //将单个的浮点数,转化为向量
   for (int i = 0; i < n; i += 4) {  //每次读取4个数据
       float32x4_t mat_vec = vld1q_f32(ptr_m1 + i); //从内存加载数据到向量
       float32x4_t result_vec = vaddq_f32(mat_vec, scalar_vec); //向量相加
       vst1q_f32(ptr_m2 + i, result_vec); //将数据加载到内存
   }
}

遇到的问题

(1)vld1q_f32这个函数,用来将数据从内存加载到寄存器,由于我的寄存器是128位,数据类型是32位,所以一个寄存器一次能存储4个数据,****因此,如果你的数据大于四个,读取要放在循环里,并且读取完之后,指针要加4,下一次循环读取新的数据。另外,数据长度最好是4的倍数。

(2)vst1q_f32这个函数,用来将数据从寄存器加载到内存,这里踩了很多坑
第一个,要注意这个函数的参数类型,
在这里插入图片描述两个参数,一个是保存数据的指针,一个是要保存的向量

第二个,这个函数和vld1q_f32一样,每次调用只能将寄存器中的数据加载到内存,也就是说一次只能保存4个数据。加载完之后,指针要加4。

结果

首先贴下完整代码

#include <arm_neon.h>
#include <iomanip>
#include "opencv2/opencv.hpp"
#include <stdio.h>

void distance_offset_correction(cv::Mat m1, cv::Mat m2)
{
	for (int i = 0; i < m1.rows; i++) // 矩阵行数循环
	{
		for (int j = 0; j < m1.cols; j++) // 矩阵列数循环
		{
			m2.at<float>(i, j) = m1.at<float>(i, j) + 100.0;
			// m2.at<float>(i, j) = m2.at<float>(i, j) + 10.0;
		}
	}
}
void matrix_addition_neon(float *matA, float *matB, float *result, int n)
{
	for (int i = 0; i < n; i += 4)
	{
		float32x4_t matA_vec = vld1q_f32(matA + i);
		float32x4_t matB_vec = vld1q_f32(matB + i);
		float32x4_t result_vec = vaddq_f32(matA_vec, matB_vec);
		vst1q_f32(result + i, result_vec);
	}
}
void matrix_scalar_addition_neon(float *mat, float scalar, float *result, int n)
{
	float32x4_t scalar_vec = vdupq_n_f32(scalar);
	for (int i = 0; i < n; i += 4)
	{
		float32x4_t mat_vec = vld1q_f32(mat + i);
		float32x4_t result_vec = vaddq_f32(mat_vec, scalar_vec);
		vst1q_f32(result + i, result_vec);
	}
}
void distance_offset_correction_neon(cv::Mat m1, float scalar, cv::Mat m2, int n)
{
	float *ptr_m1 = (float *)m1.data; // 转换为指针
	float *ptr_m2 = (float *)m2.data;
	// double t2_neon1 = (double)cv::getTickCount(); //测时间
	float32x4_t scalar_vec = vdupq_n_f32(scalar); // 将单个的浮点数,转化为向量
	// t2_neon1 = (double)cv::getTickCount() - t2_neon1;
	// double time2_neon1 = (t2_neon1 * 1000.) / ((double)cv::getTickFrequency());
	// std::cout << "--neon-----------vdupq_n_f32---------------------------time=" << time2_neon1 << " ms. " << std::endl;
	for (int i = 0; i < n; i += 4)
	{ // 每次读取4个数据
		// double t2_neon2 = (double)cv::getTickCount(); //测时间
		float32x4_t mat_vec = vld1q_f32(ptr_m1 + i); // 从内存加载数据到向量
		// t2_neon2 = (double)cv::getTickCount() - t2_neon2;
		// double time2_neon2 = (t2_neon2 * 1000.) / ((double)cv::getTickFrequency());
		// std::cout << "--neon---------vld1q_f32-----------------------------time=" << time2_neon2 << " ms. " << std::endl;
		// double t2_neon3 = (double)cv::getTickCount(); //测时间
		float32x4_t result_vec = vaddq_f32(mat_vec, scalar_vec); // 向量相加
		// t2_neon3 = (double)cv::getTickCount() - t2_neon3;
		// double time2_neon3 = (t2_neon3 * 1000.) / ((double)cv::getTickFrequency());
		// std::cout << "--neon---------vaddq_f32-----------------------------time=" << time2_neon3 << " ms. " << std::endl;
		// double t2_neon4 = (double)cv::getTickCount(); //测时间
		vst1q_f32(ptr_m2 + i, result_vec); // 将数据加载到内存
		// t2_neon4 = (double)cv::getTickCount() - t2_neon4;
		// double time2_neon4 = (t2_neon4 * 1000.) / ((double)cv::getTickFrequency());
		// std::cout << "--neon---------vst1q_f32-----------------------------time=" << time2_neon4 << " ms. " << std::endl;
	}
}
int main()
{
	// 读入数据
	static int count = 0;
	cv::Mat m1(640, 480, CV_32FC1);
	cv::Mat m2(640, 480, CV_32FC1);
	cv::Mat m3(640, 480, CV_32FC1);
	float a = 1.0;
	for (int i = 0; i < m1.rows; i++) // 矩阵行数循环
	{
		for (int j = 0; j < m1.cols; j++) // 矩阵列数循环
		{

			m1.at<float>(i, j) = a;
			m2.at<float>(i, j) = 65.0 - a;
			m3.at<float>(i, j) = 1.0;
			a = a + 1.0;
		}
	}
	// std::cout << "M1= " << std::endl << " " << m1 << std::endl;
	// std::cout << "M2= " << std::endl << " " << m2 << std::endl;
	// std::cout << "M3= " << std::endl << " " << m3 << std::endl;

	float *ptr_m1 = (float *)m1.data;
	float *ptr_m2 = (float *)m2.data;
	float *ptr_m3 = (float *)m3.data;
	float scalar = 100.0;

	// 全局标定
	bool test_c = true;
	if (test_c == true)
	{
		for (int i = 0; i < 50; i++)
		{
			//--c
			double t2_c = (double)cv::getTickCount(); // 测时间
			distance_offset_correction(m1, m2);
			t2_c = (double)cv::getTickCount() - t2_c;
			double time2_c = (t2_c * 1000.) / ((double)cv::getTickFrequency());
			std::cout << "--cccccc--------------------------------------time=" << time2_c << " ms. " << std::endl;
		}
	}
	else
	{
		for (int i = 0; i < 50; i++)
		{
			double t2_neon = (double)cv::getTickCount(); // 测时间
			// distance_offset_correction_neon(m1,scalar,m2,64);
			matrix_scalar_addition_neon(ptr_m1, scalar, ptr_m3, 307200);
			t2_neon = (double)cv::getTickCount() - t2_neon;
			double time2_neon = (t2_neon * 1000.) / ((double)cv::getTickFrequency());
			std::cout << "--neon--------------------------------------time=" << time2_neon << " ms. " << std::endl;
			// std::cout << "---------------M2= " << std::endl << " " << m2 << std::endl;
			std::cout << "---------------ptr_M3= " << std::endl<< " " << *ptr_m3 << std::endl;
		}
	}
	return 0;
}

本文用到了opencv,使用时需要提前配置好opencv,后面有时间了在写下,opencv的aarch64-liunx编译以及配置。
运行结果如下:
c++的代码运行大概是9-10ms,neon的代码运行大概是2ms多一点,基本上刚好是4倍速率。
在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值