[置顶] 泰晓 RISC-V 实验箱,配套 30+ 讲嵌入式 Linux 系统开发公开课
RISC-V AI 开发:D1 开发板实时人物检测推流的功能实现
Corrector: TinyCorrect v0.1-rc3 - [spaces toc comments codeblock urls pangu autocorrect epw] Author: Jinwen Zhou zhoujwtony@163.com Date: 2022/10/06 Revisor: Falcon falcon@tinylab.org Project: RISC-V Linux 内核剖析 Sponsor: PLCT Lab, ISCAS
前言
前面三篇文章介绍了 D1 开发板实现摄像头实时人物识别功能及 D1 开发板上使用 ffmpeg 进行视频推流的功能,此篇将二者进行整合,以实现一个完整可用功能:实时检测,当有人出现时将当前视频推流到服务器,可在客户端进行观看。此功能可用于对敏感地点的监控及记录。读者阅读完本文后,结合代码即可复现。
整体逻辑设计
开发板逻辑
D1 开发板通过摄像头模块需要完成两个关键逻辑:
- 画面进行实时采集并对采集的图像或视频进行人物识别,能够识别到人并通知视频推流模块。
- 能够在收到通知后开始同步录制并推送视频流到给定服务器地址。
以上两个关键逻辑在前两篇文章中已经分别实现,需将二者合并然后处理合并后的一些细节即可完成预期目标。
服务器及客户端部署
服务器用安装 Ubuntu 22.04 server 的虚拟机进行模拟,客户端使用 Windows 上的 VLC media player 拉起目标服务器上的视频流,具体流程已在上一篇文章中叙述。
需要补充说明一下网络配置:
- 虚拟机中选择桥接网卡桥接到主机的以太网卡并将混杂模式选择为全部允许
- 再将虚拟机桥接网卡对应的 IP 地址和主机以太网卡的 IP 设置为同一网段,开发板网口也设置为此网段
如果开发板和主机无法 Ping 通,建议关闭主机防火墙。
具体实现的细节处理
以下描述设计及整合代码时两处关键细节,为此做了一些适应性调整。
处理器性能制约
D1 处理器为单核处理器,主频 1GHz。前面在使用 ffmpeg 进行视频录制、编码、推流实验时延迟很大,约为 20 秒左右。采用对视频直接进行深度学习处理开销对于本芯片来说压力较大。所以本实验采用间隔 1s 拍照,拍摄的图片使用 ncnn 进行人物识别的方式判断有没有人,而没有采用直接从视频中动态识别人物的方式。保证实时性的同时也能在没有人的时候让 CPU 负载不至于过大。
摄像头模块无法复用
实验中发现,摄像头模块在同一个进程打开设备(open()
)并设置相关参数,使用完毕后,按照示例流程释放缓存,释放映射并关闭设备(close()
)后,在此进程中再次进行打开设备设置参数流程会报错,无法进行再次拍摄。只有该进程退出后,才可以继续使用摄像头模块。判断应该是相关驱动的一个 Bug。并且在拍照进程打开摄像头后,就无法对此摄像头使用 ffmpeg 进行录制和推流。
针对以上问题,对代码逻辑重新进行设计:
拍照程序拍照后将图片保存固定路径后通知 ncnn 图片识别模块,若收到 ncnn 反馈到图片有人(
buffer=1
)的通知后再反馈给 ncnn 已收到(buffer=2
)后退出程序。退出保证了摄像头可被推流进程使用。ncnn 程序对图片进行识别,若无人将结果(
buffer=0
)告知拍照程序即可。若有人,通知拍照程序结果后,收到对方发来的确认(
buffer=2
)后,即启动一个子进程执行 ffmpeg 推流录制的脚本(脚本的内容即为上一篇推流的命令)。原进程计时,2 分钟后执行关闭 ffmpeg 的命令,并将拍照程序再次启动。
拍照模块相关逻辑代码如下:
v4l2_init();
while(1)
{
v4l2Grab();
buffer[0] = 1;
/* 通知 ncnn 模块已经采集到图片并存储 */
datalen = write(fd_w,buffer,80);
if (datalen == -1)
continue;
/* 等待 ncnn 发回来的识别结果 */
if (read(fd_r,buffer,80))
{
if (buffer[1] == 1)
{
printf("person!\n");
buffer[0] = 2;
datalen = write(fd_w,buffer,80);
if (datalen == -1)
continue;
/* 主动退出,使摄像头可被推流模块用 */
v4l2_close();
return 0;
}
else
printf("no person!\n");
}
}
ncnn 程序关键相关逻辑代码如下:
while(1)
{
memset(buffer,0,80);
/* 收取客户端发来的识别请求 */
datalen = read(fd_r,buffer,80);
if(datalen ==-1)
continue;
if(buffer[0] == 1)
{
// start detect
if (argc != 2)
{
fprintf(stderr, "Usage: %s [imagepath]\n", argv[0]);
return -1;
}
/* 摄像头模块采集图片的保存路径 */
const char* imagepath = argv[1];
cv::Mat m = cv::imread(imagepath, 1);
if (m.empty())
{
fprintf(stderr, "cv::imread %s failed\n", imagepath);
return -1;
}
std::vector<Object> objects;
/* 使用 nanodet 框架进行识别,识别结果保存在 object 结构体中 */
detect_nanodet(m, objects);
ret = is_person(objects);
buffer[1] = ret;
write(fd_w,buffer,80);
}
if(buffer[0] == 2)
{
pid = fork();
if(pid)
{
system("./video.sh"); // (ffmpeg -f video4linux2 -s 1280x720 -i /dev/video0 -f flv rtmp://192.168.56.103/live/live &)
exit(0);
}
else
{
sleep(120);
system("killall -9 ffmpeg");
system("./cam1 &");
}
}
总结
实验效果
按照之前文章所述编译流程对上述代码进行编译,并实际测试。
首先开启服务器程序和客户端程序,等待视频流。
接着在开发板执行启动 nanodet 程序:
# ./nanodet 1.jpeg&
再启动 cam 程序即可:
#./cam &
通过实验,上述程序满足预期功能,在有人出现时会推流 2 分钟的视频,可在客户端观看。2 分钟后,推流程序退出,会重新进行人物检测。
展望
本实验仅实现了功能,有很多可以优化的地方:
实验中,可以看到推流延迟很大,约有 20 秒,实验用的 D1 开发板处理器单核以及其内存比较小制约了其性能。可以明显感受到用于视频的处理无论是处理器性能方面还是 ffmpeg 在该芯片上的优化都有很大进步空间。
客户端采用现成的拉流软件验证,若要有实际使用价值,需要在客户端增加视频保存回看功能。
图片的检测功能单一,本实践仅仅验证了 ncnn 的可用性。官方工具对算法模型的部署中关键的参数转换的工具支持希望能够更智能和平滑。
参考资料
猜你喜欢:
- 我要投稿:发表原创技术文章,收获福利、挚友与行业影响力
- 知识星球:独家 Linux 实战经验与技巧,订阅「Linux知识星球」
- 视频频道:泰晓学院,B 站,发布各类 Linux 视频课
- 开源小店:欢迎光临泰晓科技自营店,购物支持泰晓原创
- 技术交流:Linux 用户技术交流微信群,联系微信号:tinylab
支付宝打赏 ¥9.68元 | 微信打赏 ¥9.68元 | |
请作者喝杯咖啡吧 |
Read Album:
- TinyBPT 和面向 buildroot 的二进制包管理服务(3):服务端说明
- TinyBPT 和面向 buildroot 的二进制包管理服务(2):客户端说明
- TinyBPT 和面向 buildroot 的二进制包管理服务(1):设计简介与框架
- RISC-V Linux 内核及周边技术动态第 118 期
- RISC-V Linux 内核及周边技术动态第 117 期