本帖最后由 hollyedward 于 2024-5-31 22:19 编辑
镜像烧录
按照官方教程,将最新的开发板镜像烧录https://wiki.luckfox.com/zh/Luckfox-Pico/Luckfox-Pico-quick-start
需要安装驱动,使用烧录软件。
安装SDK
有几种安装方式,也有docker打包的环境镜像
在服务器上的虚拟机内下载官方的sdk
相关文件目录
首先要,导出编译链地址至环境变量
然后编译的时候cmake才能找到
export LUCKFOX_SDK_PATH=/home/user/luckfox-pico
执行命令对项目进行编译,生成最后的可执行文件
// 创建build目录
mkdir build
cd build
// 生成makefile文件
cmake ..
// 使用make工具进行编译
make && make install
烧录镜像
通过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
运行可执行文件
./luck_mnist_rtsp_demo ./model.rknn
使用开源软件vlc拉取视频流
参考官方的一些例程
通过 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的输入。
一开始使用原来的模型推理精度较低,需要使用更深层以及带卷积的网络层。以及在前处理部分,需要将摄像头获取的图像,尽可能处理到和数据集里的一致,这样就能提高推理的精确度。这也是为何用更粗的线条会提高识别率,而细的笔迹识别率较低一点,所以在前处理的代码可以加入一些膨胀操作,边缘不用很锐化,做平滑过渡的处理。
使用top命令查看手写体任务所占内存
内存只占了三分之一的样子,所以还是有很大优化空间的。
后续可以加入多线程的识别。
代码处理框架参考了论坛内大佬的开源,处理流程都大差不差,主要就是前处理和后处理的区别,很多嵌入式ai板卡跑模型一般都会给好你处理框架以及调用npu啥的驱动代码,自己需要补充和优化前后处理部分的代码。
result