Wio Lite AI视觉开发套件——人脸识别快速实现, W- s) K5 w% r+ Y" P0 d2 ~ s
1、在完成基本硬件评测和软件开发环境配置之后,可以快速实现人脸识别的任务
! `0 n) G6 c! H! h5 @2、首先按照新的ST-link连接,把Nucleo的st-link调试如下图连接,
$ j/ U5 F* d8 `. E" n
: ^3 ?# A: O+ d+ Z8 j8 v
采用如下接口对应联络到WIO Lite AI开发板,
8 c4 O% G0 M9 P! S
7 Z; n1 x- ?6 L& c+ P: y1 k6 Z5 R
& m+ ]6 o. Y3 C
使用调试夹子快速连接,就可以,如下图,& l: \4 j) Y3 V, X9 d. Z) j) x
4 _2 g' `" {6 j: \" x" F5 s3 Q8 A1 {在项目的接口可以发现ST-LInk在Port Com8,另一个是COM5,连接WIO Lite AI
- f- {, h- O6 I
@2 A: e1 f6 @+ o1 C3 k% \
) u8 G1 s* C: H
2、创建模板项目,启动CubeIDE2 D8 p+ g$ r4 k& Z2 j
2 y4 k' o6 \0 i8 A- @% D- O0 s
可以找到人脸识别的模型,Person_detect.tflite,这个是用于人脸识别的tensorflow 模型。这个是压缩后的整数型数据,人脸识别在标准情况下,现在已经可以做到90%以上,那么压缩之后,精度下降,识别精度明显下降,但是仍然可以完美实现该功能。/ j, ~" k: m# P' N' j
完成编译,执行下载,显示成功完成。
. C f( y2 X1 q t1 C* y4 l! R* W3 u. A: S- ^
7 V7 h" I9 }4 [3、运行模拟" ?% |" d0 X1 Q" { |. H" ~
上电后,摄像头捕捉图像,并显示在LCD屏上,当图片是空白,或者没有发现人的时候,显示No-person,2 F2 c9 m( @. C; ?
这里用手机摄像头遮挡面部,就显示没发现人脸,显示准确度可以达到80%,
5 W- [* q: ~$ p, E5 G( r9 R
0 T ~' |9 d6 H% d- B) V
0 b/ g, T- R" d3 _' _
移开手机,露出人脸,就迅速监测到人脸,显示Person,同时计算出准确率61%。$ d7 F5 a& C9 M$ A5 s, }3 b8 f+ ~
+ [* V, I5 T& P: N0 V
5 Z( P, d# y5 v9 F+ Z
这样快速监测成功。
5 S$ }6 D6 `' X5 `3 x: b8 N# I3 r4、人工智能实现的分析+ A/ x0 Y8 k+ [5 y7 a
4.1 硬件平台的能力
# ]4 o& m6 ]1 b+ U; D' `WIO LiteAI采用的是STM32H725,工作频率高达550 MHz的Arm® Cortex®-M7内核(具有双精度浮点单元),可选扩展室温范围最高为125 °C (*),只有一个内核,仍然展现强大的计算能力,可以无延迟实现图像处理和人脸识别。在图形上采用的高性能的支持,具有以下两个图形实现和加速功能,1 I. m; _% u! u% k1 Z9 M6 L; F$ c
- LCD-TFT控制器接口支持双层图形
- Chrom-ART Accelerator™提高了图形内容创建速度,并为其它应用节省了MCU内核处理带宽# k( h. `" b* S; n" x. j2 Z* K4 i
: {9 M2 ]2 C; `4 ?% L( a4 E# X* S# [$ @
4.2 人脸识别的流程和库的支持
$ d0 ^: \6 m, w, nCubeAI的人脸识别是用如下层次实现+ A. d2 u( w* ]
- N+ ?3 w8 \! C) z+ e( f5 c
在底层硬件上,实现板级支持,更上一层是STM32的人脸识别模型,实现具体的应用,实现过程的数据流程如下5 }) |: @4 q8 i( f3 D
9 @8 a4 [# M! n% Q% y, P
数据依次在上述流程中逐级计算,根据人工智能模型实现人脸的识别精度计算,当大于计算值以后就迅速提出结果并显示出来2 I# ~# m/ K) U9 C$ _# O; e0 t1 y
4.3 程序代码分析 B. Z% ?; T: Y( U
首先定义图形数据的交换空间,是一个320x240的采样空间
& g# s- {# A) ~0 r. L! G- }- static Frame_TypeDef frame ={ .buffer = buffer,- A) @! e) d- s( O3 B' C
- .length = 320 * 240 * 2,
3 N; Y& @. l2 p3 N9 s6 S+ t* W - .width = 320,
0 D% y' f0 Q& @, a, l- k - .height = 240
$ S2 n& h- B8 P. q - };
复制代码 主程序代码非常精简
2 l9 m6 m6 O1 r U7 D- 3 M2 ]1 Y5 t1 `7 V& Q+ {% P
- int main(void)
/ H3 z" M9 j2 s3 p) S( k9 P - {% f z8 H: G: {6 F8 Y6 ]
- MPU_Config();6 w E, C. U: G* p3 g t7 V5 Z8 m% |
- SCB_EnableICache();
' H) W+ Q2 Q# X5 G/ {* \+ A5 V( Q - SCB_EnableDCache(); B8 H- k5 p# d `1 E
- HAL_Init();9 f8 n5 t; j# |3 ^
- SystemClock_Config();. n5 C R. d1 I
- $ f& o, \: B- w4 X
- /* Initialize all configured peripherals */
- J) K# S* p% d; D - MX_GPIO_Init();! U8 o( K3 Z. ?+ P5 ]
- MX_USART3_UART_Init();) x$ \( Z& E; j* X, J9 ?8 e3 t
- MX_LTDC_Init();4 X, }( [1 X( h; l7 B) I0 `
- MX_I2C4_Init();
& B0 S0 v7 h& ^4 [4 n4 ?( ? - MX_DMA_Init();
8 \: m& @$ B8 `* \7 M6 }$ T& i - MX_DCMI_Init();' ~( _4 E; V; [0 F2 A; ?, Y1 V
- MX_TIM2_Init(); d H6 X2 H0 \$ ^2 U7 H$ K) e
- MX_OCTOSPI1_Init();
: L7 k( W: j+ q3 k I p. {8 D( p - MX_CRC_Init();" ?: U# z6 \: T/ A1 P) X
- LOG("here");
% x/ @( R. t3 e6 j& t+ Y3 E - Psram_Init();
6 J. u2 I6 J5 R+ N. a+ c7 [ - & d0 y' F4 S# p9 A* b' x6 n8 R
- /*OV2640 Init*/
6 l3 X, Y8 }4 k$ ` - HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_3); //XCLK
9 N) N9 m, P2 J5 K+ C, D' A9 f- i - OV2640_Init(&OV2640);
0 B' c3 L6 l7 c/ @5 J: u6 t - OV2640_ReadID(&(OV2640.ID));& @$ @9 g% W' W5 u. f0 M- ?9 g% ~
- LOG("ID: %02X %02X \r\n", OV2640.ID.PIDH, OV2640.ID.PIDL);
3 V: K$ Y3 P+ L$ D* S6 } - OV2640_UXGAConfig(); //flip after all the camera setting ,or camera setting will change REG04 value.; G% j+ U5 Z* S4 v' `9 H4 \0 R
- OV2640_Flip();
5 n n6 n" O( \5 {$ w - OV2640_Start(); //initial IPL and AI( {- Y$ R+ S- f' X# B+ d
- STM32Ipl_InitLib(buffer_ipl, IPL_BUFFER_SIZE);
5 ]1 O" }/ ~4 t - /* The STM32 CRC IP clock should be enabled to use the network runtime library */
" J" q/ @& o' ?- I0 q/ R! q - __HAL_RCC_CRC_CLK_ENABLE();
1 @4 l( f4 F& ~* O - aiInit();: Z4 w; L# d3 _" F" j
- frame_rgb565.data = frame.buffer;, B7 ?" j" @8 G0 d/ w+ R
, a9 f& b3 b& A- while (1)3 B% R/ ?6 i; N& @+ }/ w
- {
" k: ~. x/ h, C, r) V - HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
6 [9 [3 p! N: ^' v/ A - HAL_GPIO_WritePin(GPIOF, GPIO_PIN_0, GPIO_PIN_SET);
7 m6 c- T9 [1 b7 P8 y4 b - . A- N/ }7 z! u2 _8 z
- // rgb565 320*240*2 to grayscale 320*240
4 r, \$ p1 C/ U! e3 t* a - if (STM32Ipl_Convert(&frame_rgb565, &frame_rgb565_grayscale) != stm32ipl_err_Ok) {
& a9 Z B+ X9 ]% U* w - while(1);
# Q4 r E4 R# ~7 \$ N - }
, U# X6 z! X4 B) B6 V - // grayscale 320*240 to grayscale 96x96x26 n" _, W, K0 u; q
- if (STM32Ipl_Resize(&frame_rgb565_grayscale, &frame_grayscale_resized, &roi) != stm32ipl_err_Ok) {5 H. T9 f8 V9 C
- while(1);
) R4 R9 t i" g - }
$ I8 h) T }$ P; i9 G
4 v6 c4 h+ a) N+ k4 R8 J: F- //AI/ ?9 ?% O8 ^, @. e% p \2 l% o
- aiRun(buffer_grayscale_resized, network_output);
4 V: b$ U: a6 O0 S3 O6 M; M - postprocess(network_output);4 v3 p4 e/ y1 m2 p& o: |
$ {/ d! l5 A) s; }- HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);# G5 l `5 G- n* y/ ?: E
- HAL_GPIO_WritePin(GPIOF, GPIO_PIN_0, GPIO_PIN_RESET);# D9 f) O+ M: v
7 ]7 w" X. J, g1 W, l- }
+ t8 L0 [- S6 w3 E: J; q - }
; A* ^7 V1 b4 H4 w( E
复制代码 在启动硬件初始化之后,就进入识别循环,当识别出人脸就直接把对应的LED点亮,! }' U. @! R1 N& B; _4 U: w
实现人脸识别的函数是aiRun,对图形交换空间的数据分析,并直接输出神经网络计算的结果
0 _# X$ m7 D# s( D7 m1 j' \
; v! g. Y2 ~, g+ `( M- aiRun(buffer_grayscale_resized, network_output);
: M9 U. y1 ^% Q$ {' s - postprocess(network_output);
复制代码 这个函数是在"ai_inference.h"文件中实现的,
$ [& h, j. U! q1 A3 I' k- void aiRun(ai_u8 *pIn, ai_u8 *pOut)
6 ?' p& P& Y7 V6 w1 Q5 n+ B - {
2 E9 t1 `& j/ T( Y- m4 _* | - ai_i32 batch;' ]& Y' G: R% z# [+ c
- ai_error err;
! U0 M; ~: w* H: \* ~( v0 W: J
8 {6 Y- e% q& }4 z$ }7 {- /* 1 - Create the AI buffer IO handlers with the default definition */
9 x1 s5 L) S" c; F3 o0 Q: n; x/ ` - ai_buffer ai_input[AI_NETWORK_IN_NUM] = AI_NETWORK_IN;
1 O# s2 f; N" b0 J, g - ai_buffer ai_output[AI_NETWORK_OUT_NUM] = AI_NETWORK_OUT;
; O1 k+ b. F, f* H" d
$ \5 x" {" C4 y/ c& L# u' o1 s- /* 2 - Update IO handlers with the data payload */
7 v# G7 O9 C1 h3 U& r - ai_input[0].n_batches = 1;
2 ^' J2 j1 u/ M4 y& C7 q$ w - ai_input[0].data = AI_HANDLE_PTR(pIn);
) Y( m3 j/ L$ p/ P7 h, E% e - ai_output[0].n_batches = 1;: ^/ X: S4 }" _3 g1 w7 R! O' { i
- ai_output[0].data = AI_HANDLE_PTR(pOut); Z$ U* C- H0 Q3 T4 l
- 0 N4 U5 }# X8 t& j& a& s
- batch = ai_network_run(network, ai_input, ai_output);# A6 V" E* |: N: Q( f
- if (batch != 1) {$ S( M8 Q/ X: x" C' R/ W( W8 h2 W
- err = ai_network_get_error(network);
1 f$ e& U# a# j0 \ - printf("AI ai_network_run error - type=%d code=%d\r\n", err.type, err.code); y. M1 S! W5 w$ F8 y- K; _
- Error_Handler();2 `4 e: M( T! b4 } D# b5 {
- }; P0 _' W4 v3 y- S& {9 e5 _
- }
) G, m- U6 T# q# {( a, U
复制代码 上述过程就是流程图数据流实现的过程,读取经过预处理的图像数据,然后再ai网络中计算,输出计算结果,: q8 B. |& s1 Q H3 [% J
- ai_i32 ai_network_run(
/ c5 y5 w0 B3 c - ai_handle network, const ai_buffer* input, ai_buffer* output)
1 b' x y1 ^1 C' _9 l( H5 f - {
/ x$ j% f1 I: e, u- |- Z - return ai_platform_network_process(network, input, output);/ l. }7 [7 s3 Y: w& J6 U* h
- }
复制代码 上述处理,需要计算网络,这个是再ai_model中定义的,然后读取输入数据,形成输出数据。3 b# v* f; i3 D; Z! ?
$ D! O3 y0 N6 o2 C# o5 小结
) G. F. ]3 N5 v: i4 U: c7 T 基于STM的cubeAI,通常已经提供了一个快速部署的流程框架。对于开发者,只需要自定义人工智能模型,然后导入到新建的工程中,按照提供的API逐步实现数据流程,就能够在项目中嵌入人工智能的功能。; a" |) I+ r, X1 I. u5 R
在上一个文件中提高的,对应模型的生产,采用CubeAI中的命令行指令,stm32ai就可以完整实现。这个CubeAI极其配套工具,就是一个完整实现人工智能嵌入的工具,虽然安装和使用流程比较多,但是能够实现这样的效果还是非常理想的,在硬件性能和软件功能,达到完美的结合。% T4 y1 ~( X; j$ L% ^
9 z. q- r$ m5 }
y3 [# q& T4 t. ^8 k6 D; b+ D- J$ I5 U! Y `3 D& G. s7 `5 @
8 y0 X* ?# ?' R
8 G/ U' T5 H# s2 N( f
6 ?+ K, R( C3 o9 @
9 |$ ^) C$ F& m: N4 I( T0 Y0 ~( v6 C% a% b0 e
8 Z" K1 a* V1 g% v/ V5 B- {( f' {& l1 m
|
6 q$ B, T$ F) w- @& M/ l
大佬求源码
UP求驱动程序