随着社会的发展人工智能已经逐渐走进并融入我们的生活,且应用在各个行业领域,AI不仅给许多行业带来了巨大的经济效益,同时也为我们的生活带来了许多改变和便利.在AI技术的加持下,传统消费电子产品向智能化转变,本篇就以V833为例,介绍在其上开发物体检测应用的具体步骤。
方案运行平台:
支持IPC开发的平台有Melis和Tina,过程是类似的,这里以Tina为例介绍。
方案软件架构:
方案软件设计:
方案数据通路:
一路sensor图像经过四路VIPP缩放操作后,送给四个不同应用,分别是H265编码存盘,LCD预览以及以及NPU人形检测,还有一路照片抓拍。
NPU人形检测原理如下:
NPU线程抓取352*198(VIPP支持的输出大小)像素,格式为NV21的YUV原始帧数据,喂给人形检测算法网络,得到类别和坐标数据后,反馈给VIPP1 预览通道画框。
最后,将模型文件和模型数据放到路径/mnt/sdcard/object_det/model/下,因为程序会固定的到这个位置读取网络结构文件和网络数据文件。
模型是基于darknet yolov4-tiny.cfg的基础上优化得到的。
方案调试以及问题记录:
运行过程中,利用如下命令查看数据通路:
cat /sys/kernel/debug/mpp/vi
root@(none):/# cat /sys/kernel/debug/mpp/vi
*****************************************************
VIN hardware feature list:
mcsi 1, ncsi 1, parser 2, isp 1, vipp 4, dma 4
CSI_VERSION: CSI230_200, ISP_VERSION: ISP521_100
CSI_CLK: 336000000, ISP_CLK: 300000000
*****************************************************
vi0:
imx386_mipi => mipi0 => csi0 => isp0 => vipp0
input => hoff: 0, voff: 2, w: 1920, h: 1080, fmt: RGGB10
output => width: 1920, height: 1080, fmt: NV21M
interface: MIPI, isp_mode: NORMAL, hflip: 0, vflip: 0
prs_in => x: 1920, y: 1082, hb: 397, hs: 2183
buf => cnt: 5 size: 3133440 rest: 5, mode: software_update
frame => cnt: 1033, lost_cnt: 1, error_cnt: 1
internal => avg: 16(ms), max: 30(ms), min: 1(ms)
*****************************************************
vi1:
imx386_mipi => mipi0 => csi0 => isp0 => vipp1
input => hoff: 0, voff: 2, w: 1920, h: 1080, fmt: RGGB10
output => width: 1080, height: 720, fmt: NV21M
interface: MIPI, isp_mode: NORMAL, hflip: 0, vflip: 0
prs_in => x: 1920, y: 1082, hb: 397, hs: 2183
buf => cnt: 5 size: 1179648 rest: 3, mode: software_update
frame => cnt: 4369922, lost_cnt: 2643, error_cnt: 0
internal => avg: 16(ms), max: 24(ms), min: 2(ms)
*****************************************************
vi2:
imx386_mipi => mipi0 => csi0 => isp0 => vipp2
input => hoff: 0, voff: 2, w: 1920, h: 1080, fmt: RGGB10
output => width: 352, height: 198, fmt: NV21M
interface: MIPI, isp_mode: NORMAL, hflip: 0, vflip: 0
prs_in => x: 1920, y: 1082, hb: 401, hs: 2183
buf => cnt: 5 size: 110592 rest: 2, mode: software_update
frame => cnt: 4369922, lost_cnt: 3963615, error_cnt: 0
internal => avg: 16(ms), max: 24(ms), min: 8(ms)
*****************************************************
vi3:
(null) => csi1 => isp1 => vipp3
input => hoff: 0, voff: 0, w: 0, h: 0, fmt: NULL
output => width: 0, height: 0, fmt: NULL
interface: PARALLEL, isp_mode: NORMAL, hflip: 0, vflip: 0
prs_in => x: 0, y: 0, hb: 0, hs: 0
buf => cnt: 0 size: 0 rest: 0, mode: software_update
frame => cnt: 0, lost_cnt: 0, error_cnt: 0
internal => avg: 0(ms), max: 0(ms), min: 0(ms)
*****************************************************
root@(none):/# cat /sys/kernel/debug/mpp/vi
cat /sys/kernel/debug/mpp/vi
*****************************************************
VIN hardware feature list:
mcsi 1, ncsi 1, parser 2, isp 1, vipp 4, dma 4
CSI_VERSION: CSI230_200, ISP_VERSION: ISP521_100
CSI_CLK: 336000000, ISP_CLK: 300000000
*****************************************************
vi0:
imx386_mipi => mipi0 => csi0 => isp0 => vipp0
input => hoff: 0, voff: 2, w: 1920, h: 1080, fmt: RGGB10
output => width: 1920, height: 1080, fmt: NV21M
interface: MIPI, isp_mode: NORMAL, hflip: 0, vflip: 0
prs_in => x: 1920, y: 1082, hb: 397, hs: 2183
buf => cnt: 5 size: 3133440 rest: 5, mode: software_update
frame => cnt: 1033, lost_cnt: 1, error_cnt: 1
internal => avg: 16(ms), max: 30(ms), min: 1(ms)
*****************************************************
vi1:
imx386_mipi => mipi0 => csi0 => isp0 => vipp1
input => hoff: 0, voff: 2, w: 1920, h: 1080, fmt: RGGB10
output => width: 1080, height: 720, fmt: NV21M
interface: MIPI, isp_mode: NORMAL, hflip: 0, vflip: 0
prs_in => x: 1920, y: 1082, hb: 398, hs: 2183
buf => cnt: 5 size: 1179648 rest: 3, mode: software_update
frame => cnt: 4370069, lost_cnt: 2643, error_cnt: 0
internal => avg: 16(ms), max: 24(ms), min: 2(ms)
*****************************************************
vi2:
imx386_mipi => mipi0 => csi0 => isp0 => vipp2
input => hoff: 0, voff: 2, w: 1920, h: 1080, fmt: RGGB10
output => width: 352, height: 198, fmt: NV21M
interface: MIPI, isp_mode: NORMAL, hflip: 0, vflip: 0
prs_in => x: 1920, y: 1082, hb: 398, hs: 2182
buf => cnt: 5 size: 110592 rest: 2, mode: software_update
frame => cnt: 4370069, lost_cnt: 3963749, error_cnt: 0
internal => avg: 18(ms), max: 24(ms), min: 8(ms)
*****************************************************
vi3:
(null) => csi1 => isp1 => vipp3
input => hoff: 0, voff: 0, w: 0, h: 0, fmt: NULL
output => width: 0, height: 0, fmt: NULL
interface: PARALLEL, isp_mode: NORMAL, hflip: 0, vflip: 0
prs_in => x: 0, y: 0, hb: 0, hs: 0
buf => cnt: 0 size: 0 rest: 0, mode: software_update
frame => cnt: 0, lost_cnt: 0, error_cnt: 0
internal => avg: 0(ms), max: 0(ms), min: 0(ms)
*****************************************************
root@(none):/#
这样的设计方案包含一个问题,NPU这边获得帧并进行网络推理得到坐标结果并反馈给VIPP1通道是需要时间的,这段时间物体有可能已经发生移动,所以画框的图像帧并非是和原始检测帧同一个场景,所以绘的框可能不太准确,不过如果物体运动速度不太快的话,误差也不会太大,消费类设备是可以接受的。
这里遇到的问题是,在开发画框流程的时候,发现对设置的框坐标无法和LCD显示器上的框坐标对应上,例如,box的坐标有四个参数来描述,定义如下:
当设置x,y坐标为0,0时,发现绘出来的框缺失这个点,显示在屏幕上就是框不完整,缺失了一部分。经过不断尝试,标定,最终发现了屏幕大小的矩形框对应的box的坐标,x,y,width,height的关系,下面是分析过程:
VIPP1的原图格式设置的是1080 * 720, 但是LCD的显示分辨率是 480 * 640,如下图:
由于显示前,DE如果发现源分辨率和目标分辨率不同,会自动做scale操作,而我们设置的BOX坐标是按照源图给的,所以如果scale过程中发生裁剪,那么最终会出来的矩形框肯定是不对的,经过分析,这恰恰是遇到的情况。
经过反复调试,得到的坐标原点和屏幕宽高给
大概的处理过程如下:
当x,y,width,height分别为140,0,800,720时的框图像:
对照录制的视频文件和预览的文件显示区域范围的区别,可以发现如上规律:
宽高的大小关系发生了易位!
一个框从最大逐渐缩小的例子:
效果是这个样子的,可以看到框在逐渐缩小:
MPP Tina调试方案:
VE会在下面注册:
VO也在下面注册:
当然还有VI
如何有效提高NPU检测帧率
NPU检测帧率是测量NPU性能的关键指标,检测帧率越大,画框表现的越流畅,越自然,用户体验越好,当然,技术发展也是符合辩证法的,达到这种满意状态的代价就是你需要更好的硬件,更高的算力,但是如何在给定的硬件,给定的系统上尽可能大的压榨NPU的性能呢?这里记录一些最最佳实践。
1.关闭动态调压调频,参考.
Tina关闭动态调压调频_tugouxp的专栏-CSDN博客
2.提高NPU检测线程的优先级.
这里主要记录这种方式:
打开CONFIG_SCHED_DEBUG的主要目的是确认优先级改成功了,policy为0表示默认的CFS调度器,默认优先级为120.
0:SCHED_OTHER
1: SCHED_FIFO
2: SCHED_RR
提升 优先级的代码,采用继承策略,所有被主动提升优先级创建的新线程都会提升到相同的优先级和调度策略.
在提优先级之前的任务状态:
root@(none):/mnt/sdcard# pidof sample_run_nna
924
root@(none):/mnt/sdcard# cd /proc/924/task/
root@(none):/proc/924/task# cat ./9
924/ 931/ 932/ 933/ 934/ 935/ 936/ 937/ 938/ 939/ 940/ 941/ 942/
root@(none):/proc/924/task# cat ./*/sched
sample_run_nna (924, #threads: 13)
-------------------------------------------------------------------
se.exec_start : 97028.161170
se.vruntime : 9498.540895
se.sum_exec_runtime : 322.772674
se.nr_migrations : 0
nr_switches : 325
nr_voluntary_switches : 161
nr_involuntary_switches : 164
se.load.weight : 1024
policy : 0
prio : 120
clock-delta : 625
isp_thread (931, #threads: 13)
-------------------------------------------------------------------
se.exec_start : 105481.353013
se.vruntime : 10706.565309
se.sum_exec_runtime : 1614.484623
se.nr_migrations : 0
nr_switches : 1626
nr_voluntary_switches : 1007
nr_involuntary_switches : 619
se.load.weight : 1024
policy : 0
prio : 120
clock-delta : 125
ViComponentThre (932, #threads: 13)
-------------------------------------------------------------------
se.exec_start : 105500.862346
se.vruntime : 10706.495724
se.sum_exec_runtime : 14.819088
se.nr_migrations : 0
nr_switches : 550
nr_voluntary_switches : 548
nr_involuntary_switches : 2
se.load.weight : 1024
policy : 0
prio : 120
clock-delta : 125
VICaptureThread (933, #threads: 13)
-------------------------------------------------------------------
se.exec_start : 105500.748138
se.vruntime : 10706.530808
se.sum_exec_runtime : 35.223204
se.nr_migrations : 0
nr_switches : 546
nr_voluntary_switches : 546
nr_involuntary_switches : 0
se.load.weight : 1024
policy : 0
prio : 120
clock-delta : 84
CDX_VRender (934, #threads: 13)
-------------------------------------------------------------------
se.exec_start : 105501.137846
se.vruntime : 10706.651099
se.sum_exec_runtime : 110.490325
se.nr_migrations : 0
nr_switches : 551
nr_voluntary_switches : 547
nr_involuntary_switches : 4
se.load.weight : 1024
policy : 0
prio : 120
clock-delta : 292
sample_run_nna (935, #threads: 13)
-------------------------------------------------------------------
se.exec_start : 105801.617512
se.vruntime : 10756.689146
se.sum_exec_runtime : 65.801465
se.nr_migrations : 0
nr_switches : 589
nr_voluntary_switches : 584
nr_involuntary_switches : 5
se.load.weight : 1024
policy : 0
prio : 120
clock-delta : 375
sample_run_nna (936, #threads: 13)
-------------------------------------------------------------------
se.exec_start : 105818.973803
se.vruntime : 10760.648644
se.sum_exec_runtime : 313.491813
se.nr_migrations : 0
nr_switches : 1676
nr_voluntary_switches : 1649
nr_involuntary_switches : 27
se.load.weight : 1024
policy : 0
prio : 120
clock-delta : 83
ViComponentThre (937, #threads: 13)
-------------------------------------------------------------------
se.exec_start : 105801.500345
se.vruntime : 10756.642146
se.sum_exec_runtime : 25.299997
se.nr_migrations : 0
nr_switches : 544
nr_voluntary_switches : 542
nr_involuntary_switches : 2
se.load.weight : 1024
policy : 0
prio : 120
clock-delta : 250
ViComponentThre (938, #threads: 13)
-------------------------------------------------------------------
se.exec_start : 87057.498791
se.vruntime : 8002.817188
se.sum_exec_runtime : 0.055333
se.nr_migrations : 0
nr_switches : 4
nr_voluntary_switches : 2
nr_involuntary_switches : 2
se.load.weight : 1024
policy : 0
prio : 120
clock-delta : 208
VICaptureThread (939, #threads: 13)
-------------------------------------------------------------------
se.exec_start : 105801.454803
se.vruntime : 10756.644229
se.sum_exec_runtime : 29.591133
se.nr_migrations : 0
nr_switches : 560
nr_voluntary_switches : 560
nr_involuntary_switches : 0
se.load.weight : 1024
policy : 0
prio : 120
clock-delta : 250
CDX_VRender (940, #threads: 13)
-------------------------------------------------------------------
se.exec_start : 87057.579416
se.vruntime : 8005.820814
se.sum_exec_runtime : 0.056417
se.nr_migrations : 0
nr_switches : 5
nr_voluntary_switches : 1
nr_involuntary_switches : 4
se.load.weight : 1024
policy : 0
prio : 120
clock-delta : 375
VICaptureThread (941, #threads: 13)
-------------------------------------------------------------------
se.exec_start : 106135.636594
se.vruntime : 10802.278307
se.sum_exec_runtime : 90.035334
se.nr_migrations : 0
nr_switches : 570
nr_voluntary_switches : 570
nr_involuntary_switches : 0
se.load.weight : 1024
policy : 0
prio : 120
clock-delta : 125
VEncComp (942, #threads: 13)
-------------------------------------------------------------------
se.exec_start : 106151.919010
se.vruntime : 10805.540306
se.sum_exec_runtime : 1871.900509
se.nr_migrations : 0
nr_switches : 2988
nr_voluntary_switches : 1102
nr_involuntary_switches : 1886
se.load.weight : 1024
policy : 0
prio : 120
clock-delta : 125
root@(none):/proc/924/task#
NPU优先级提升后的线程状态:
root@(none):/mnt/sdcard# pidof sample_run_nna
923
root@(none):/mnt/sdcard# cd /proc/923
root@(none):/proc/923# ls
auxv limits oom_adj status
cmdline map_files oom_score syscall
comm maps oom_score_adj task
coredump_filter mem personality time_in_state
cwd mountinfo root timerslack_ns
environ mounts sched wchan
exe mountstats stack
fd net stat
fdinfo ns statm
root@(none):/proc/923# cd task/9
-/bin/sh: cd: can't cd to task/9
root@(none):/proc/923# cd task/
root@(none):/proc/923/task# cat ./*/sched
sample_run_nna (923, #threads: 13)
-------------------------------------------------------------------
se.exec_start : 81454.674063
se.vruntime : 9205.329910
se.sum_exec_runtime : 328.087841
se.nr_migrations : 0
nr_switches : 334
nr_voluntary_switches : 155
nr_involuntary_switches : 179
se.load.weight : 1024
policy : 0
prio : 120
clock-delta : 500
isp_thread (929, #threads: 13)
-------------------------------------------------------------------
se.exec_start : 89798.691073
se.vruntime : 10412.040559
se.sum_exec_runtime : 1604.900473
se.nr_migrations : 0
nr_switches : 1552
nr_voluntary_switches : 971
nr_involuntary_switches : 581
se.load.weight : 1024
policy : 0
prio : 120
clock-delta : 125
ViComponentThre (930, #threads: 13)
-------------------------------------------------------------------
se.exec_start : 89788.183281
se.vruntime : 10405.992058
se.sum_exec_runtime : 14.439583
se.nr_migrations : 0
nr_switches : 546
nr_voluntary_switches : 544
nr_involuntary_switches : 2
se.load.weight : 1024
policy : 0
prio : 120
clock-delta : 125
VICaptureThread (931, #threads: 13)
-------------------------------------------------------------------
se.exec_start : 89821.514281
se.vruntime : 10417.390891
se.sum_exec_runtime : 30.870623
se.nr_migrations : 0
nr_switches : 544
nr_voluntary_switches : 544
nr_involuntary_switches : 0
se.load.weight : 1024
policy : 0
prio : 120
clock-delta : 709
CDX_VRender (932, #threads: 13)
-------------------------------------------------------------------
se.exec_start : 89822.039656
se.vruntime : 10417.633141
se.sum_exec_runtime : 112.393517
se.nr_migrations : 0
nr_switches : 545
nr_voluntary_switches : 543
nr_involuntary_switches : 2
se.load.weight : 1024
policy : 0
prio : 120
clock-delta : 334
sample_run_nna (933, #threads: 13)
-------------------------------------------------------------------
se.exec_start : 90122.059697
se.vruntime : 1.999999
se.sum_exec_runtime : 74.959755
se.nr_migrations : 0
nr_switches : 570
nr_voluntary_switches : 570
nr_involuntary_switches : 0
se.load.weight : 1024
policy : 2
prio : 0
clock-delta : 250
sample_run_nna (934, #threads: 13)
-------------------------------------------------------------------
se.exec_start : 90139.016030
se.vruntime : 10461.141308
se.sum_exec_runtime : 308.006983
se.nr_migrations : 0
nr_switches : 1702
nr_voluntary_switches : 1660
nr_involuntary_switches : 42
se.load.weight : 1024
policy : 0
prio : 120
clock-delta : 250
ViComponentThre (935, #threads: 13)
-------------------------------------------------------------------
se.exec_start : 71440.589685
se.vruntime : 0.000000
se.sum_exec_runtime : 0.049709
se.nr_migrations : 0
nr_switches : 2
nr_voluntary_switches : 2
nr_involuntary_switches : 0
se.load.weight : 1024
policy : 2
prio : 0
clock-delta : 209
VICaptureThread (936, #threads: 13)
-------------------------------------------------------------------
se.exec_start : 90121.954780
se.vruntime : 0.000000
se.sum_exec_runtime : 86.315416
se.nr_migrations : 0
nr_switches : 557
nr_voluntary_switches : 557
nr_involuntary_switches : 0
se.load.weight : 1024
policy : 2
prio : 0
clock-delta : 125
CDX_VRender (937, #threads: 13)
-------------------------------------------------------------------
se.exec_start : 71440.608226
se.vruntime : 0.000000
se.sum_exec_runtime : 0.044791
se.nr_migrations : 0
nr_switches : 2
nr_voluntary_switches : 2
nr_involuntary_switches : 0
se.load.weight : 1024
policy : 2
prio : 0
clock-delta : 125
ViComponentThre (938, #threads: 13)
-------------------------------------------------------------------
se.exec_start : 90456.480320
se.vruntime : 10502.731807
se.sum_exec_runtime : 22.858504
se.nr_migrations : 0
nr_switches : 553
nr_voluntary_switches : 550
nr_involuntary_switches : 3
se.load.weight : 1024
policy : 0
prio : 120
clock-delta : 375
VICaptureThread (939, #threads: 13)
-------------------------------------------------------------------
se.exec_start : 90456.263862
se.vruntime : 10502.758224
se.sum_exec_runtime : 42.512623
se.nr_migrations : 0
nr_switches : 567
nr_voluntary_switches : 567
nr_involuntary_switches : 0
se.load.weight : 1024
policy : 0
prio : 120
clock-delta : 292
VEncComp (940, #threads: 13)
-------------------------------------------------------------------
se.exec_start : 90470.809987
se.vruntime : 10509.307349
se.sum_exec_runtime : 1860.196328
se.nr_migrations : 0
nr_switches : 2982
nr_voluntary_switches : 1098
nr_involuntary_switches : 1884
se.load.weight : 1024
policy : 0
prio : 120
clock-delta : 250
root@(none):/proc/923/task#
可以看到,有多个线程的调度策略被设置为2(SCHED_RR),优先级为0,最高.
我们再来看看帧率的变化,可以清楚的看到OSD图层和视频图层双图层显示:
ffmpeg -ss 00:00:13 -t 5 -i 11.mp4 -s 360x640 -r 15 dongtu.gif
最后看一下编码路的输出,使用的工具是potplayer.
提高帧率的另外一个措施是提高CPU主频,也就是超频,手操超频方法请参考:
Tina关闭动态调压调频_tugouxp的专栏-CSDN博客结束!https://blog.csdn.net/tugouxp/article/details/120527174
一个库串用问题的澄清过程:
开发是基于一个旧一些的SDK上进行的,完成后,测试10类物体检测都很正常,识别率也比较高,后面将整个demo方案移植到比较新的SDK上的时候,遇到了一个困扰我半天的问题,问题大概表现是,移植后的demo只能检测到人形,其它9类目标均无法检测。针对此问题做过的试验包括:
1.用原版的算法库离线包(构建环境不是基于tina,而是离线的绿色版)算法库环境编译的测试程序,在新的SDK上可以正确完成10类目标的识别. --------说明模型文件和权重没有问题,SDK也没问题。
2.同样的测试用例,将其移植到Tina构建环境中,使用同样的测试图片,测试发现只能识别人形,原来离线方式用例能检测出的其它物体钧检测不到了。
3.测试用新SDK的工具链编译绿色版算法程序,编译出来的程序仍然能检测10类物体,说明不是工具链的问题。
4.这就奇怪了,排除了系统问题,模型问题程序逻辑问题,和工具链的问题之后,就只剩下运行时环境问题了。但是对比绿色版算法包和Tina环境中的三个运行库是binary identical的,这点希望也破灭了。
5.经过反复排查,终于通过查看链接map文件找到线索,原来,新的SDK增加了一个测试用例,也是用于人形检测的,这个用例的运行库和ai demo的运行库名称相同,目录不同,查看map文件,发现在编译的时候,两笔运行库之间都有用到。
找到了问题根源,就是运行时库的串扰引起的这个问题,解决方法就很简单了,直接关闭人形检测的用例,这样构建系统就不会找人形库去链接,而是清一色的我们预期的libnna目录中的库。
关闭body detect function,因为它只有body detection 功能。
至于为何决定检测多少类的不是通过模型,而是通过运行库有所区别,还不清楚为何?
V831上的移植问题:
v831上的开发和V833基本一样,需要注意的只有预览这一路,LCD的分辨率和V833不同,V833是640*480,而V831上是其1/4,为320*240,基本上软甲软件只要配套改VO预览这一路的输出分辨率就可以了,NPU,编码的分辨率,由于是软件内部行为,和输出LCD分辨率无关,所以可以不用动。
这里遇到的一个主要问题是,开发完成后,用手机百度的人物头像图片进行检测,无论怎样都没有框画出来,试了很多办法,包括尝试对显示的图像做旋转都无效,后面讲NPU这一路的图像看才找到问题原因,原来平台摄像头和LCD都是挂载裸板上,没有整机外壳,无法分摄像头的坐标系在是怎样的,导致送给NPU这一路的图像是头朝下的,当然无法检测出来 :(, 如下图所示:
OSD显示:
OSD显示使用基于DirectFB的多进程方式,用例将坐标数据发送给独立的进程,此进程再调用DirectFB将信息输出在屏幕上,如下图所示:
Buffer共享:
为了避免在目标检测库内分配内存,可以直接将从MPP获取到的planer布局的YUV数据三成员直接赋值给人形库的输入数据结构体,这样避免了调用目标检测库的makebuf函数重复申请结构图,毕竟NPU物体检测和还帧的动作是同步进行的,一帧不会二用,所以这样做是完全安全的。
优化效果,不同场景下的检测帧率统计:
优化总结:
个人看法,如果是从算力角度不是存储角度,感觉方案功能定义不变的话,可能的思路有:
- 从算法角度优化,流程上避免不必要的CPU计算和循环,优化计算密集模块的算法,减少占用率.
- 内存够用的话,针对个别场景,空间换时间,用多一点的内存减少计算上的辗转。
- 适当提高算法密集模块的优先级,减少计算过程中不必要的系统调度,把算力用在刀刃上。
- 充分利用硬件,超频,加速,一起上.
关于SDK的环境配置,可参考: