飞道的博客

RT-AK DEMO 实战教程,教你实现单个人像识别~

264人阅读  评论(0)

让 AI 在你的板子上尽情舞蹈~

本次实验的是识别摄像头中的人,就一个人,而不是多个。

从多类别检测模型改编而来,只保留 person 这个类别,根据各位看官的能力完全可以改成识别多类

项目开源地址:

https://github.com/EdgeAIWithRTT/Project4-Person_detection_RT-AK

本次项目的篇幅将会较长,请各位看官耐心看完

整体分为三部分:

  1. 模型

  2. RT-AK 使用

  3. 板子上的应用层代码实现

硬件平台 ART-Pi, 50M FLOPS。

我的模型(删减后的模型)最终部署在板子上是推理时间是56ms,不包括数据处理时间。

  • pc 端推理一张图片:


   
  1. 1$ pip install -r requirements.txt
  2. 2$ python inference_yolo-s.py

  • 以下是在 ART-PI 上的模型推理实现:


我的模型数据:map 21.58%

1 模型

1.1 参考项目

参考项目:

  1. Yolo-Fastest https://github.com/dog-qiuqiu/Yolo-Fastest

  2. keras-YOLOv3-model-set

    https://github.com/david8862/keras-YOLOv3-model-set

原因:目前了解的全网最轻量级的目标检测网络,没有之一

现在不是了,出现了一个 ppyolo,百度产

当然,Yolo Fastest 最小的模型也有 0.23 Bflops,想要在 ART-Pi 上顺利的跑起来,肉眼可见的丝滑程度,我是在做梦。

这时候有两个办法:

  1. 换一块板子,换一块算力更大的板子。

  2. 将模型改的小一点,能够在 ART-PI 上丝滑的跑起来。

这里我选择的是后者。

我改动的很简单,去掉特征金字塔输出,只保留一个输出,保证对大物体检测友好即可。同时删减网络结构。原来是109层,我是20+层网络结构。

纠正一个思想误区,由于一些很神奇的存在,网络并不是越深,FLOPS 就会越大,比如 DSCNN


第二个参考项目的意义是在于:将模型转变为 tflite 可食用模型

1.2 模型文件

  • 我改动的模型配置文件:./model/yolo-s_with_lrelu.cfg

原模型配置文件:./model/VOC

为了防止在后期模型转换的过程中遇到不支持的算子:leakyrelu,我这里提供了一份 relu 的模型训练配置文件

  • 预先训练好的模型:./model/yolo-s.h5 507 k,量化的 tflite 模型文件:./model/yolo-s.tflite 144 k

1.3 自己训练模型

请参考:dog-qiuqiu/Yolo-Fastest 项目

我自己也写过一份 快速上手 yolo-fastest 教程:

https://blog.csdn.net/weixin_37598106/article/details/112544854?spm=1001.2014.3001.5501

但是由于 up 主更新的比较快,可能有一些版本落后,仅供参考。

  1. 需要配置 darknet 训练环境,然后根据需求修改下  cfg 文件即可

  2. 准备数据集:VOC 2007 + VOC 2012

    
         
    1. 1wget https: //pjreddie.com/media/files/VOCtrainval_11-May-2012.tar
    2. 2wget https: //pjreddie.com/media/files/VOCtrainval_06-Nov-2007.tar
    3. 3wget https: //pjreddie.com/media/files/VOCtest_06-Nov-2007.tar
    4. 4tar xf VOCtrainval_11-May -2012.tar
    5. 5tar xf VOCtrainval_06-Nov -2007.tar
    6. 6tar xf VOCtest_06-Nov -2007.tar
    7. 7
    8. 8wget https: //pjreddie.com/media/files/voc_label.py
    9. 9# 修改文件,将里面的类别只保留 person 类别
    10. 10python voc_label.py
    11. 11
    12. 12cat  2007_train.txt  2007_val.txt  2012_*.txt > train.txt

修改 ./model/voc_person.data 中的 trainvalid 路径,注意,如果你不是训练 person  单类别,请一并修改 voc_person.names 文件

3.训练


   
  1. 1$ ./darknet detector train <data_path> <cfg_path> 
  2. 2
  3. 3# 举例, -dont_show 是不显示图片, -gpus 是指定 gpu 训练
  4. 4$ ./darknet detector train voc_person.data yolo-s.cfg -dont_show -gpus  01

4.测试

perons.jpg 位于 ./imgs


   
  1. 1# test  1 image
  2. 2./darknet detector test voc_person.data yolo-s.cfg yolo-s_last.weights person.jpg -thresh  0.5 -dont_show
  3. 3
  4. 4# mAP
  5. 5./darknet detector  map voc_person.data yolo-s.cfg yolo-s_last.weights -points  11

模型转换成 keras,最后转成 tflite

对应的代码仓库:

Lebhoryi/keras-YOLOv3-model-set 

https://github.com/Lebhoryi/keras-YOLOv3-model-set

转自 

david8862/keras-YOLOv3-model-set 

https://github.com/david8862/keras-YOLOv3-model-set,我做了一些修改,请按照我的来,否则出错请自负????????????

yolo-fastest to keras

python tools/model_converter/convert.py cfg/yolo-s.cfg weights/yolo-s_last.weights weights/yolo-s.h5  -f -c

keras to tflite


   
  1. python tools/model_converter/custom_tflite_convert.py --keras_model_file ./weights/yolo-s.h5 --output_file ./weights/yolo-s.tflite
  2. # keras to tflite; quantize
  3. python tools/model_converter/post_train_quant_convert.py --keras_model_file ./weights/yolo-s.h5 --annotation_file /home/lebhoryi/Data/VOC/ 2007_test.txt --model_input_shape 160x160 --sample_num 30 --output_file ./weights/yolo-s.tflite -c
  4. ```

2 RT-AK 使用

具体使用请查阅 RT-Thread/RT-AK https://github.com/RT-Thread/RT-AK 相关文档

准备:

  • ART-PI bsp

  • 模型

  • RT-AK

使用:


   
  1. 1$ git clone https: //github.com/RT-Thread/RT-AK
  2. 2
  3. 3$ cd RT-AK/RT-AK/rt_ai_tools
  4. 4
  5. 5# 只需要改动 --model、--project、--ext_tools 三个参数的路径即可
  6. 6$ python aitools.py --model=./yolo-s.h5 --model_name=person_yolo --project=D:\RT-ThreadStudio\workspace\art-pi --platform stm32 --ext_tools= "D:\Program Files (x86)\stm32ai-windows-5.2.0\windows" --clear

3 应用代码

我的输入是 160x160x1,为了减小模型参数大小,

先在 pc 端实现应用层的代码

  • 图片预处理:尺度缩放+灰度转化+归一化

  • yolo 解码

  • nms 处理

代码都在 inference_yolo-s.py 中。

手边没有 usb 摄像头,也就没有写视频的推理代码,只有图片的推理代码。

功能实现:

1.图片预处理

python 里头就很简单,调用 opencv 库,几行代码搞定


   
  1. 1img_raw = cv2.imread(str(img_path))
  2. 2img = cv2.cvtColor(img_raw, cv2.COLOR_BGR2GRAY)
  3. 3img = cv2.resize(img, ( 160160), interpolation=cv2.INTER_LINEAR)
  4. 4img = img /  255.0
  5. 5img = np.asarray(img).astype( 'float32')

2.yolo 解码

inference_yolo-s.py 中的 yolo_decode 函数

模型推理的是检测目标的 xywh 的偏移量,目的是将模型输出结果转换成真实世界的 xywh

这部分呢,我也写了一篇文章,感兴趣的可以看一下:

掌握 yolo - 解码核心思想 https://blog.csdn.net/weixin_37598106/article/details/113058426?spm=1001.2014.3001.5501

3.nms

inference_yolo-s.py 中的 non_max_suppress 函数,这个函数针对的是单类别的


c 代码的实现比较痛苦(痛苦面具 x3)

✔灰度转换

✔尺度缩放

✔yolo 解码

⚪nms (可能五一节后实现)

1、灰度转换

RGB转灰度,通常会使用下面的一个心理学公式:(Matlab和OpenCV中使用的也是该公式)


   
  1. 1Gray =  0.2989*R +  0.5870*G +  0.1140*B
  2. 2# 优化
  3. 3Gray = ( 2989*R +  5870*G +  1140*B)/  10000
  4. 4# 移位
  5. 5Gray = ( 4898*R +  9618*G +  1868*B)>>  14
  6. 68位精度
  7. 7Gray = ( 76*R +  150*G +  30*B)>>  8

   
  1. 1 // c 代码实现
  2. 2void rgb2gray(unsigned char *src,unsigned char *dst,  int width, int height)
  3. 3{
  4. 4    int r, g, b;
  5. 5    for ( int i= 0; i<width*height; ++i)
  6. 6  {
  7. 7       r = *src++;  // load red
  8. 8       g = *src++;  // load green
  9. 9       b = *src++;  // load blue
  10. 10        // build weighted average:
  11. 11       *dst++ = (r *  76 + g *  150 + b *  30) >>  8;
  12. 12  }
  13. 13}

   
  1. 1# python 代码实现
  2. 2# val_c_gray_scaling.py
  3. 3def img2gray(img_path):
  4. 4   # 读取第一张图像
  5. 5   img = cv2.imread(img_path)
  6. 6   # 获取图像尺寸
  7. 7   h, w = img.shape[ 0: 2]
  8. 8   # 自定义空白单通道图像,用于存放灰度图
  9. 9   gray = np.zeros((h, w), dtype=img.dtype)
  10. 10   # 对原图像进行遍历,然后分别对B\G\R按比例灰度化
  11. 11    for i in  range(h):
  12. 12        for j in  range(w):
  13. 13           gray[i, j] =  0.11 * img[i, j,  0] +  0.59 * img[i, j,  1] +  0.3 * img[i, j,  2]  # Y= 0.3R+ 0.59G+ 0.11B
  14. 14   show_img(gray)
  15. 15    return gray

2.尺度缩放

此处用的是双线性插值


   
  1. 1int is_in_array(short x, short y, short height, short width)
  2. 2{
  3. 3    if (x >=  0 && x < width && y >=  0 && y < height)
  4. 4        return  1;
  5. 5    else
  6. 6        return  0;
  7. 7}
  8. 8
  9. 9void bilinera_interpolation(rt_uint8_t* in_array, short height, short width,
  10. 10                           rt_uint8_t* out_array, short out_height, short out_width)
  11. 11{
  12. 12   double h_times = (double)out_height / (double)height,
  13. 13          w_times = (double)out_width / (double)width;
  14. 14   short  x1, y1, x2, y2, f11, f12, f21, f22;
  15. 15   double x, y;
  16. 16
  17. 17    for ( int i =  0; i < out_height; i++){
  18. 18        for ( int j =  0; j < out_width; j++){
  19. 19           x = j / w_times;
  20. 20           y = i / h_times;
  21. 21
  22. 22           x1 = (short)(x -  1);
  23. 23           x2 = (short)(x +  1);
  24. 24           y1 = (short)(y +  1);
  25. 25           y2 = (short)(y -  1);
  26. 26           f11 = is_in_array(x1, y1, height, width) ? in_array[y1*width+x1] :  0;
  27. 27           f12 = is_in_array(x1, y2, height, width) ? in_array[y2*width+x1] :  0;
  28. 28           f21 = is_in_array(x2, y1, height, width) ? in_array[y1*width+x2] :  0;
  29. 29           f22 = is_in_array(x2, y2, height, width) ? in_array[y2*width+x2] :  0;
  30. 30           out_array[i*out_width+j] = (rt_uint8_t)(((f11 * (x2 - x) * (y2 - y)) +
  31. 31                                      (f21 * (x - x1) * (y2 - y)) +
  32. 32                                      (f12 * (x2 - x) * (y - y1)) +
  33. 33                                      (f22 * (x - x1) * (y - y1))) / ((x2 - x1) * (y2 - y1)));
  34. 34      }
  35. 35  }
  36. 36}

python 代码实现:val_c_gray_scaling.py 中的 bilinera_interpolation 函数

3.yolo 解码


   
  1. 1 // c 代码实现
  2. 2 // applications/yolo.c
  3. 3int yolo_decode(float *out_data)
  4. 4{
  5. 5  int j= 0,k= 0,l= 0;
  6. 6  for( int i= 0; i< 5* 5* 5; i++)
  7. 7{
  8. 8   float x_tmp =  1 / ( 1 + exp(-out_data[i* 6+ 0]));
  9. 9   float y_tmp =  1 / ( 1 + exp(-out_data[i* 6+ 1]));
  10. 10   float box_x = (x_tmp + k) /  5;
  11. 11   float box_y = (y_tmp + l) /  5;
  12. 12
  13. 13   float box_w = (exp(out_data[i* 6+ 2])*anchor[j][ 0])/ input_dims[ 0];
  14. 14   float box_h = (exp(out_data[i* 6+ 3])*anchor[j][ 1])/ input_dims[ 1];
  15. 15
  16. 16   float objectness =  1 / ( 1 + exp(-out_data[i* 6+ 4]));
  17. 17
  18. 18   float class_scores =  1 / ( 1 + exp(-out_data[i* 6+ 5]));
  19. 19
  20. 20 //   printf("%d %d %d %f %f, %f %f, %f %f\n", j,k,l, box_x, box_y, box_w, box_h, objectness, class_scores);
  21. 21
  22. 22   out_data[i* 6+ 0] = box_x;
  23. 23   out_data[i* 6+ 1] = box_y;
  24. 24   out_data[i* 6+ 2] = box_w;
  25. 25   out_data[i* 6+ 3] = box_h;
  26. 26   out_data[i* 6+ 4] = objectness;
  27. 27   out_data[i* 6+ 5] = class_scores;
  28. 28
  29. 29    if(j++>= 4)
  30. 30  {
  31. 31     j =  0;
  32. 32      if(k++>= 4)
  33. 33    {
  34. 34       k =  0;
  35. 35        if(l++>= 4)
  36. 36      {
  37. 37         l =  0;
  38. 38      }
  39. 39    }
  40. 40  }
  41. 41}
  42. 42  return  0;
  43. 43}

python 代码实现:inference_yolo-s.py 中的 yolo_decode 函数

4.nms

没有 nms 的目标检测工程就等于没有灵魂,等后期来实现


编译报错以及解决

第二种解决方式:

4. 参考链接


  • C++ RGB转灰度图像 https://blog.csdn.net/martinkeith/article/details/104185635

  • GBeetle/c_image_processing https://github.com/GBeetle/c_image_processing/blob/4ceabf4959f455f5b7d1ee419aac25eccf231b3b/scaling/scaling.c#L155

  • https://github.com/dog-qiuqiu/Yolo-Fastest

  • https://github.com/david8862/keras-YOLOv3-model-set

你可以添加微信17775982065为好友,注明:公司+姓名,拉进 RT-Thread 官方微信交流群!

???????????? 点击阅读原文进入官网


转载:https://blog.csdn.net/rtthreadiotos/article/details/116311835
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场