[原创] #AI挑战营终点站# luckfox手写体识别部署rknn

hollyedward   2024-5-31 21:38 楼主

镜像烧录

按照官方教程,将最新的开发板镜像烧录https://wiki.luckfox.com/zh/Luckfox-Pico/Luckfox-Pico-quick-start

需要安装驱动,使用烧录软件。

 

安装SDK

有几种安装方式,也有docker打包的环境镜像

image.png  

在服务器上的虚拟机内下载官方的sdk

 

相关文件目录

image.png  

首先要,导出编译链地址至环境变量

然后编译的时候cmake才能找到

 

export LUCKFOX_SDK_PATH=/home/user/luckfox-pico

执行命令对项目进行编译,生成最后的可执行文件

// 创建build目录
mkdir build
cd build
// 生成makefile文件
cmake ..
// 使用make工具进行编译
make && make install

image.png  

 

 

image.png  

烧录镜像

通过ssh连接开发板

ssh root@172.32.0.93

用scp将可执行文件和相关库传至开发板

scp -r ./luckfox_mnist_demo root@172.32.0.93:/root

给可执行文件或者文件夹赋权

chmod -R 777 ./luckfox_mnist_demo

运行脚本关闭系统默认 rkipc 程序

RkLunch-stop.sh

image.png  

运行可执行文件

./luck_mnist_rtsp_demo ./model.rknn

使用开源软件vlc拉取视频流

image.png  

 

 

参考官方的一些例程

通过 Rockchip 多媒体处理平台 (RKMPPI) 捕获和流式传输 H.264 视频。都有相应的接口。

//h264_frame	
	VENC_STREAM_S stFrame;	
	stFrame.pstPack = (VENC_PACK_S *)malloc(sizeof(VENC_PACK_S));
 	VIDEO_FRAME_INFO_S h264_frame;
 	VIDEO_FRAME_INFO_S stVpssFrame;

	// rkaiq init
	RK_BOOL multi_sensor = RK_FALSE;	
	const char *iq_dir = "/etc/iqfiles";
	rk_aiq_working_mode_t hdr_mode = RK_AIQ_WORKING_MODE_NORMAL;
	//hdr_mode = RK_AIQ_WORKING_MODE_ISP_HDR2;
	SAMPLE_COMM_ISP_Init(0, hdr_mode, multi_sensor, iq_dir);
	SAMPLE_COMM_ISP_Run(0);

	// rkmpi init
	if (RK_MPI_SYS_Init() != RK_SUCCESS) {
		RK_LOGE("rk mpi sys init fail!");
		return -1;
	}

	// rtsp init	
	rtsp_demo_handle g_rtsplive = NULL;
	rtsp_session_handle g_rtsp_session;
	g_rtsplive = create_rtsp_demo(554);
	g_rtsp_session = rtsp_new_session(g_rtsplive, "/live/0");
	rtsp_set_video(g_rtsp_session, RTSP_CODEC_ID_VIDEO_H264, NULL, 0);
	rtsp_sync_video_ts(g_rtsp_session, rtsp_get_reltime(), rtsp_get_ntptime());

	// vi init
	vi_dev_init();
	vi_chn_init(0, width, height);

	// vpss init
	vpss_init(0, width, height);

	// bind vi to vpss
	MPP_CHN_S stSrcChn, stvpssChn;
	stSrcChn.enModId = RK_ID_VI;
	stSrcChn.s32DevId = 0;
	stSrcChn.s32ChnId = 0;

	stvpssChn.enModId = RK_ID_VPSS;
	stvpssChn.s32DevId = 0;
	stvpssChn.s32ChnId = 0;

参考官方opencv处理帧的代码

while(1)
	{   
		// get vpss frame
		s32Ret = RK_MPI_VPSS_GetChnFrame(0,0, &stVpssFrame,-1);
		if(s32Ret == RK_SUCCESS)
		{
			void *data = RK_MPI_MB_Handle2VirAddr(stVpssFrame.stVFrame.pMbBlk);

			cv::Mat frame(height,width,CV_8UC3, data);

			// Preprocess the image
			cv::Mat gray_frame;
			preprocess(frame, gray_frame);

			// Run inference
			int digit = run_inference(gray_frame);

			// Draw rectangle around detected area (for demonstration, let's assume the area is at the center)
			int rect_size = 100;
			cv::Rect rect((frame.cols - rect_size) / 2, (frame.rows - rect_size) / 2, rect_size, rect_size);
			cv::rectangle(frame, rect, cv::Scalar(0, 255, 0), 2);

			// Draw the inference result on the frame
			char fps_text[16];
			sprintf(fps_text, "Digit: %d", digit);        
			cv::putText(frame, fps_text, 
						cv::Point(40, 40), 
						cv::FONT_HERSHEY_SIMPLEX, 1, 
						cv::Scalar(0, 255, 0), 2);

			// Display the frame
			cv::imshow("Detection", frame);
			cv::waitKey(1);

			// Encode and send the frame
			RK_MPI_VENC_SendFrame(0, &stVpssFrame,-1);
			s32Ret = RK_MPI_VENC_GetStream(0, &stFrame, -1);
			if(s32Ret == RK_SUCCESS)
			{
				if(g_rtsplive && g_rtsp_session)
				{
					void *pData = RK_MPI_MB_Handle2VirAddr(stFrame.pstPack->pMbBlk);
					rtsp_tx_video(g_rtsp_session, (uint8_t *)pData, stFrame.pstPack->u32Len,
								stFrame.pstPack->u64PTS);
					rtsp_do_event(g_rtsplive);
				}
				RK_U64 nowUs = TEST_COMM_GetNowUs();
				fps = (float) 1000000 / (float)(nowUs - stVpssFrame.stVFrame.u64PTS);            
			}

			// Release frame 
			s32Ret = RK_MPI_VPSS_ReleaseChnFrame(0, 0, &stVpssFrame);
			if (s32Ret != RK_SUCCESS) {
				RK_LOGE("RK_MPI_VI_ReleaseChnFrame fail %x", s32Ret);
			}
			s32Ret = RK_MPI_VENC_ReleaseStream(0, &stFrame);
			if (s32Ret != RK_SUCCESS) {
				RK_LOGE("RK_MPI_VENC_ReleaseStream fail %x", s32Ret);
			}
		}
int sX,sY,eX,eY;
	int width    = 720;
    int height   = 480;

	char fps_text[16];
	float fps = 0;
	memset(fps_text,0,16);

所用分辨不需要太高,太高反而影响速度

所以我们所需要做的就是,将摄像头获取到的帧进行前处理,处理为28*28的输入。

image.png  

一开始使用原来的模型推理精度较低,需要使用更深层以及带卷积的网络层。以及在前处理部分,需要将摄像头获取的图像,尽可能处理到和数据集里的一致,这样就能提高推理的精确度。这也是为何用更粗的线条会提高识别率,而细的笔迹识别率较低一点,所以在前处理的代码可以加入一些膨胀操作,边缘不用很锐化,做平滑过渡的处理。

 

使用top命令查看手写体任务所占内存

image.png  

内存只占了三分之一的样子,所以还是有很大优化空间的。

后续可以加入多线程的识别。

代码处理框架参考了论坛内大佬的开源,处理流程都大差不差,主要就是前处理和后处理的区别,很多嵌入式ai板卡跑模型一般都会给好你处理框架以及调用npu啥的驱动代码,自己需要补充和优化前后处理部分的代码。

result

 

 

本帖最后由 hollyedward 于 2024-5-31 22:19 编辑

回复评论

暂无评论,赶紧抢沙发吧
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复