RK3568-Linux应用学习记录

1.交叉编译器

  1. 正点原子打包在: A 盘-基础资料/05、开发工具/0x、交叉编译工具/ atk-dlxxxxxx-toolchain-arm-buildroot-linux-gnueabihf-x86_64-日期-版本号.run,如果你不想用正点原子打包的,自行编译 Linux SDK Buildroot。一般构建成功后会生成SDK 目录/buildroot/output/开发板名字/host。编译器就在 host 目录里。也可以使用 SDK目录/buildroot/output/开发板名字/host 下的编译器去开发。
  2. chmod添加执行权限,./atk*运行安装程序,回车,默认安装在/opt/atk-dlrk356x-toolchain/usr/bin/或者自行修改路径。
  3. 每次打开新终端时source /opt/atk-dlrk3588-toolchain/environment-setup或者export PATH=$PATH:/opt/atk-dlrk356x-toolchain/usr/bin/或者之间加在~/.zshrc文件中
  4. 编译器命令
aarch64-buildroot-linux-gnu-gcc // 像 rk3588/3568等ARM 64位(aarch64)架构的 CPU 。执行此指令。
arm-linux-gnueabihf-gcc // 像 rv1126 这样的 32 位的 CPU 请执行此指令。

2.qt应用编译

  1. 进入pro文件所在文件夹
  2. /opt/atk-dlrk356x-toolchain/bin/qmake生成make所需文件,makefile中的编译器路径才是rk3568芯片对应的。
  3. 检查Makefile文件中的cc路径
  4. make编译生成可执行程序
  5. stm32mp1对的程序拿过来用的话:检查有没有ifndef arm,这个开发板不会定义arm所以不会运行,删掉这个语句

3.使用onnxruntime进行模型推理

3.1 新建源文件

在这里插入图片描述
其中内容如下:

  1. main.cpp
/*
这里使用一个图像分割作为测试,读取图片分类分割后保存
*/
#include<iostream>
#include<memory>
#include <chrono>
//#include<format>
using namespace std;

#include<opencv2/opencv.hpp>
using namespace cv;
using namespace cv::dnn;

#include<onnxruntime_cxx_api.h>
using namespace Ort;

struct Obj {
	int id = 0;
	float accu = 0.0;
	Rect bound;
	cv::Mat mask;
};
int seg_ch = 32;
int seg_w = 160, seg_h = 160;
int net_w = 640, net_h = 640;
float accu_thresh = 0.25, mask_thresh = 0.5;

struct ImageInfo {
	Size raw_size;
	Vec4d trans;
};
vector<string> class_names = { "bean","corn","impurity","rice","ricebrown","ricemilled","wheat","xian" };

void get_mask(const Mat& mask_info, const Mat& mask_data, const ImageInfo& para, Rect bound, Mat& mast_out)
{
	Vec4f trans = para.trans;
	int r_x = floor((bound.x * trans[0] + trans[2]) / net_w * seg_w);
	int r_y = floor((bound.y * trans[1] + trans[3]) / net_h * seg_h);
	int r_w = ceil(((bound.x + bound.width) * trans[0] + trans[2]) / net_w * seg_w) - r_x;
	int r_h = ceil(((bound.y + bound.height) * trans[1] + trans[3]) / net_h * seg_h) - r_y;
	r_w = MAX(r_w, 1);
	r_h = MAX(r_h, 1);
	if (r_x + r_w > seg_w) //crop
	{
		seg_w - r_x > 0 ? r_w = seg_w - r_x : r_x -= 1;
	}
	if (r_y + r_h > seg_h)
	{
		seg_h - r_y > 0 ? r_h = seg_h - r_y : r_y -= 1;
	}
	vector<Range> roi_rangs = { Range(0, 1) ,Range::all() , Range(r_y, r_h + r_y) ,Range(r_x, r_w + r_x) };
	Mat temp_mask = mask_data(roi_rangs).clone();
	Mat protos = temp_mask.reshape(0, { seg_ch,r_w * r_h });
	Mat matmul_res = (mask_info * protos).t();
	Mat masks_feature = matmul_res.reshape(1, { r_h,r_w });
	Mat dest;
	exp(-masks_feature, dest);//sigmoid
	dest = 1.0 / (1.0 + dest);
	int left = floor((net_w / seg_w * r_x - trans[2]) / trans[0]);
	int top = floor((net_h / seg_h * r_y - trans[3]) / trans[1]);
	int width = ceil(net_w / seg_w * r_w / trans[0]);
	int height = ceil(net_h / seg_h * r_h / trans[1]);
	Mat mask;
	resize(dest, mask, Size(width, height));
	mast_out = mask(bound - Point(left, top)) > mask_thresh;
}

void draw_result(Mat img, vector<Obj>& result, vector<Scalar> color)
{
	Mat mask = img.clone();
	for (int i = 0; i < result.size(); i++)
	{
		int left, top;
		left = result[i].bound.x;
		top = result[i].bound.y;
		int color_num = i;
		rectangle(img, result[i].bound, color[result[i].id], 8);
		if (result[i].mask.rows && result[i].mask.cols > 0)
		{
			mask(result[i].bound).setTo(color[result[i].id], result[i].mask);
		}
		//string label = std::format("{}:{:.2f}", class_names[result[i].id], result[i].accu);
		string label = "label";
		putText(img, label, Point(left, top), FONT_HERSHEY_SIMPLEX, 2, color[result[i].id], 4);
	}
	addWeighted(img, 0.6, mask, 0.4, 0, img); //add mask to src
	resize(img, img, Size(640, 640));
	//imshow("img", img);
	imwrite("img.jpg",img);
	//waitKey();
}
void decode_output(Mat& output0, Mat& output1, ImageInfo para, vector<Obj>& output)
{
	output.clear();
	vector<int> class_ids;
	vector<float> accus;
	vector<Rect> boxes;
	vector<vector<float>> masks;
	int data_width = class_names.size() + 4 + 32;
	int rows = output0.rows;
	float* pdata = (float*)output0.data;
	for (int r = 0; r < rows; ++r)
	{
		Mat scores(1, class_names.size(), CV_32FC1, pdata + 4);
		Point class_id;
		double max_socre;
		minMaxLoc(scores, 0, &max_socre, 0, &class_id);
		if (max_socre >= accu_thresh)
		{
			masks.push_back(vector<float>(pdata + 4 + class_names.size(), pdata + data_width));
			float w = pdata[2] / para.trans[0];
			float h = pdata[3] / para.trans[1];
			int left = MAX(int((pdata[0] - para.trans[2]) / para.trans[0] - 0.5 * w + 0.5), 0);
			int top = MAX(int((pdata[1] - para.trans[3]) / para.trans[1] - 0.5 * h + 0.5), 0);
			class_ids.push_back(class_id.x);
			accus.push_back(max_socre);
			boxes.push_back(Rect(left, top, int(w + 0.5), int(h + 0.5)));
		}
		pdata += data_width;//next line
	}
	vector<int> nms_result;
	NMSBoxes(boxes, accus, accu_thresh, mask_thresh, nms_result);//opencv 内置NMSBoxes
	for (int i = 0; i < nms_result.size(); ++i)
	{
		int idx = nms_result[i];
		boxes[idx] = boxes[idx] & Rect(0, 0, para.raw_size.width, para.raw_size.height);
		Obj result = { class_ids[idx] ,accus[idx] ,boxes[idx] };
		get_mask(Mat(masks[idx]).t(), output1, para, boxes[idx], result.mask);
		output.push_back(result);
	}
}
bool detect_seg_ort(string& mpath, Mat& img, vector<Obj>& objs)
{
	Mat image;
	resize(img, image, Size(640, 640));
	Env env(OrtLoggingLevel::ORT_LOGGING_LEVEL_ERROR, "yolov8");
	//Session* session = new Session(env,wstring(mpath.begin(), mpath.end()).c_str(), SessionOptions());
	Session* session = new Session(env,"./bestm.onnx", SessionOptions());
	vector<const char*> input_names = { "images" };
	vector<const char*> output_names = { "output0","output1" };
	vector<int64_t> input_shape = { 1, 3, 640, 640 };
	Mat blob = blobFromImage(image, 1 / 255.0, Size(640, 640), Scalar(0, 0, 0), true, false);
	Value input_tensor = Value::CreateTensor<float>(MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault),
		(float*)blob.data, 3 * 640 * 640, input_shape.data(), input_shape.size());
	auto start = chrono::high_resolution_clock::now();
	auto outputs = session->Run(RunOptions{ nullptr },////使用netron查看网络结构,输入为1*3*640*640,输出为output0:1*44*8400,output1:1*32*160*160
		input_names.data(), &input_tensor, 1, output_names.data(), output_names.size());
	auto end = chrono::high_resolution_clock::now();
	auto duration = chrono::duration_cast<chrono::milliseconds>(end - start).count();
	cout << "ort time: " << duration << " millis.";
	float* all_data = outputs[0].GetTensorMutableData<float>();
	auto data_shape = outputs[0].GetTensorTypeAndShapeInfo().GetShape();//1*44*8400//检测框
	Mat output0 = Mat(Size((int)data_shape[2], (int)data_shape[1]), CV_32F, all_data).t();
	auto mask_shape = outputs[1].GetTensorTypeAndShapeInfo().GetShape();//1*32*160*160,mask
	vector<int> mask_sz = { 1,(int)mask_shape[1],(int)mask_shape[2],(int)mask_shape[3] };//1*32*160*160
	Mat output1 = Mat(mask_sz, CV_32F, outputs[1].GetTensorMutableData<float>());
	ImageInfo img_info = { img.size(), { 640.0 / img.cols ,640.0 / img.rows,0,0 } };
	decode_output(output0, output1, img_info, objs);
	return objs.size() > 0;
}
int main()
{
	srand(time(0));
	vector<Scalar> color;
	for (int i = 0; i < class_names.size(); i++)
	{
		color.push_back(Scalar(rand() % 128 + 128, rand() % 128 + 128, rand() % 128 + 128));
	}
	string model_path = "./bestm.onnx";
	Mat img1 = imread("./1.jpg");
	Mat img2 = img1.clone();
	vector<Obj> result;
	/*
	if (detect_seg_dnn(model_path, img1, result))
	{
		draw_result(img1, result, color);
	}*///如果需要dnn推理则取消注释,dnn推理时间2900ms,ort为530ms
	if (detect_seg_ort(model_path, img2, result))
	{
		draw_result(img2, result, color);
	}
	return 0;
}

  1. CMakeLists.txt
# 设置项目名称  
cmake_minimum_required(VERSION 3.10) # 确保CMake版本足够新  
project(MyProject)  
  

# 设置编译选项  
set(CMAKE_CXX_STANDARD 14) # 假设您使用C++ (如果是C,请使用CMAKE_C_STANDARD)  
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -Wall")  
  

set(LIBS pthread m)  
  
# 定义目录变量 这里存放的是main.cpp源文件,或者h头文件
set(DIR_Examples "${CMAKE_SOURCE_DIR}/src")  

# 添加include目录  
include_directories(  
    ${DIR_Examples}  
)  
  
# 添加源文件并设置目标  
file(GLOB_RECURSE SRC_LIST      
    "${DIR_Examples}/*.cpp"  
)  

#for opencv
# 设置ONNX库和头文件的路径(这里是已经通过arm交叉编译器编译后可以用在开发板上的库文件)  
set(ONNX_INCLUDE_DIR "/home/tao/linux/rk3568/onnxruntime/include")  
set(ONNX_LIBRARY "/home/tao/linux/rk3568/onnxruntime/lib/libonnxruntime.so")  # 根据实际情况修改库文件名  

set(Opencv_INCLUDE_DIR "/home/tao/opencv/opencv-rk3568/install/include")  
set(Opencv_LIB_DIR "/home/tao/opencv/opencv-rk3568/install/lib") 

#opencv中的库不是在lib的第一个字文件夹中,可能有二三层字文件夹,所以这里自动搜索so文件,就不用一个个指定
file(GLOB_RECURSE Opencv_LIB_LIST      
    "${Opencv_LIB_DIR}/*.so"  
)    
# 包含ONNX的头文件目录  
include_directories(${ONNX_INCLUDE_DIR} )  
include_directories(${Opencv_INCLUDE_DIR})


add_executable(main ${SRC_LIST}) #可执行文件名为main,指定源文件所在目录 
  
# 链接ONNX和opencv库  
target_link_libraries(main ${LIBS}  ${ONNX_LIBRARY} ${Opencv_LIB_LIST})
 
set(DIR_Examples "${CMAKE_SOURCE_DIR}/build")  
# 设置输出目录为DIR_BIN(注意:CMake默认使用build目录,但可以通过设置CMAKE_ARCHIVE_OUTPUT_DIRECTORY等变量来改变)  
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${DIR_BIN})  
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${DIR_BIN})  
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${DIR_BIN})  
  

由于我们需要交叉编译,新建设置工具链文件toolchain.cmake

  1. toolchain.cmake
set(CMAKE_SYSTEM_NAME Linux)#当前虚拟机处理器
set(CMAKE_SYSTEM_PROCESSOR aarch64)#设置开发板处理器类型,RK3568是aarch64,Rv1103和STM32MP1是arm

set(tools /opt/atk-dlrk356x-toolchain/)#

#set(CMAKE_SYSROOT ${tools}/sysroot)设置各编译器命令路径
set(CMAKE_C_COMPILER ${tools}/bin/aarch64-buildroot-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER ${tools}/bin/aarch64-buildroot-linux-gnu-g++)
set(CMAKE_AR ${tools}/bin/aarch64-buildroot-linux-gnu-ar)
set(CMAKE_RANLIB ${tools}/bin/aarch64-buildroot-linux-gnu-ranlib)

set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)

3.2 开始编译

命令行中进入build目录

cmake -DCMAKE_TOOLCHAIN_FILE=../toolchain.cmake ..
make 

这里通过-DCMAKE_TOOLCHAIN_FILE变量 指定交叉编译工具链的文件,注意这里不能在CMakeLists.txt文件中指定:

#set(CMAKE_TOOLCHAIN_FILE "/home/tao/linux/rk3568/Linux_C_App/onnx/toolchain.cmake")  

因为CMake 的初始化顺序是先加载cmake工具链文件,然后才解析 CMakeLists.txt。如果在 CMakeLists.txt 中设置 CMAKE_TOOLCHAIN_FILE,此时工具链文件已经加载完毕,设置不会生效。成功编译后build文件夹中生成可执行文件main:
在这里插入图片描述
使用file命令查看main文件确实是arrch64文件,此时可以复制到开发板中运行,如果报错缺少库,就在opencv和onnx中的so文件复制到开发板/usr/lib64中
在这里插入图片描述

3.3 运行

将onnxruntime推理所需的图像和onnx模型文件复制到main同一个目录,为了不一个一个so文件复制,并且链接文件无法复制,我直接将所有库打包在lib文件夹中,复制到开发板,使用export指定库的路径
在这里插入图片描述
成功运行
在这里插入图片描述

在这里插入图片描述

问题

  1. QStandardPaths: runtime directory ‘/var/run’ is not a directory, but a symbolic link to a directory permissions 0755 owned by UID 0 GID 0
    由于/var/run是一个链接而不是一个实际的目录
    解决:查看/root/usr指向目录/run,里面有QT执行所需的platform文件wayland等,执行命令export XDG_RUNTIME_DIR=/run
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值