当前位置:   article > 正文

基于Licheepi 4A的YOLOv5-Lite的部署_荔枝派4a应用

荔枝派4a应用

一、项目说明

YOLOv5-Lite

本项目采用荔枝派4a进行YOLOv5-Lite的部署,YOLOv5-Lite 牺牲了部分网络模型精度,但是极大的提升了模型的推理速度

值得一提的是,这款轻量化模型的制作者是中国ppogg大佬,原项目位于:

https://github.com/ppogg/YOLOv5-Lite

Licheepi 4A

LicheePi 4A 是基于 Lichee Module 4A 核心板的 高性能 RISC-V Linux 开发板,以 TH1520 为主控核心(4xC910@1.85G, RV64GCV,4TOPS@int8 NPU, 50GFLOP GPU),板载最大 16GB 64bit LPDDR4X,128GB eMMC,支持 HDMI+MIPI 双4K 显示输出,支持 4K 摄像头接入,双千兆网口(其中一个支持POE供电)和 4 个 USB3.0 接口,多种音频输入输出(由专用 C906 核心处理)

该pi使用的是国产的RISC-V架构,应该是目前较为强力的RISC-V SBC,未开启专用指令集加速的情况下,性能逼近基于 ARM A72 的树莓派 4,而且拥有16GB的内存(正式版,笔者使用的测试版只有8GB,有正式版16GB的同好可以直接烧录官方的FULL镜像,里面的工具一应俱全,不会再出现因为框架版本的问题导致大范围冒红,极度推荐)

二、模型训练

2.1 数据集制作

首先我们需要制作对应的数据集提供给模型进行训练,这需要我们通过荔枝派自行拍摄对应的照片,然后用labelme进行标注,手动制作数据集。

1.本项目采用SIPEED官方的USB摄像头,插上摄像头后,摄像头背面的灯会亮一会然后熄灭,这代表摄像头可以使用。

首先用指令查看摄像头是否能使用:

sudo apt-get install guvcview
guvcview

如果摄像头正常,那么屏幕上会显示出画面并且摄像头背面的灯会同时亮起两个。

2.安装Python和opencv:

安装Python是因为最适合新手入门,而OpenCV则是一个开源的跨平台计算机视觉库。

首先是更新下载器:

sudo apt update 
sudo apt upgrade 

然后安装Python和OpenCV:

sudo apt install python3 python3-pip 
sudo apt install python3-opencv         

安装所需要的依赖:

sudo apt install libqt5gui5-gles        

3.利用OpenCV调用摄像头并且拍照存为训练集的素材;

在usb_test.py中写入测试代码:

  1. import cv2
  2. from threading import Thread
  3. import uuid
  4. import os
  5. import time
  6. count = 0
  7. def image_collect(cap):
  8. global count
  9. while True:
  10. success, img = cap.read()
  11. if success:
  12. file_name = str(uuid.uuid4())+'.jpg'
  13. cv2.imwrite(os.path.join('images',file_name),img)
  14. count = count+1
  15. print("save %d %s"%(count,file_name))
  16. time.sleep(0.4)
  17. if __name__ == "__main__":
  18. os.makedirs("images",exist_ok=True)
  19. # 打开摄像头
  20. cap = cv2.VideoCapture(0)
  21. cv2.namedWindow("frame",cv2.WINDOW_NORMAL)
  22. cv2.setWindowProperty("frame",cv2.WINDOW_NORMAL,cv2.WINDOW_NORMAL)
  23. cap.set(cv2.CAP_PROP_FRAME_WIDTH,2560);
  24. cap.set(cv2.CAP_PROP_FRAME_HEIGHT,1440);
  25. m_thread = Thread(target=image_collect, args=([cap]),daemon=True)
  26. while True:
  27. # 读取一帧图像
  28. success, img = cap.read()
  29. if not success:
  30. continue
  31. cv2.imshow("frame",img)
  32. key = cv2.waitKey(1) & 0xFF
  33. # 按键 "q" 退出
  34. if key == ord('c'):
  35. m_thread.start()
  36. continue
  37. elif key == ord('q'):
  38. break
  39. cap.release()
 

运行代码后出现摄像头画面即为成功。

按下“c“键采集图像,采集完对应的数据集后按下”q“键退出图片采集:

4.将采集到的图片传输到PC端,用自己的电脑制作训练集。

5.在自己的电脑上利用Anaconda搭建一个Python的虚拟环境(推荐,避免各种依赖干扰其他的项目运行)

6.在Python虚拟环境中下载labelme软件,然后运行labelme对图片进行标注(Labelme的下载和使用参考网上的其他教程)

7.使用JSON转TXT的Python代码进行转换

首先自定义一个字典:

dic_lab.py:

  1. dic_labels= {'WhiteMouse':0,
  2. 'BlackMouse':1,#标签名称
  3. 'path_json':'labels',#格式
  4. 'ratio':0.9}#训练集和校对集的比例

然后写JSON转TXT格式的Python代码:

lablemetoyolo.py:

  1. import os
  2. import json
  3. import random
  4. import base64
  5. import shutil
  6. import argparse
  7. from pathlib import Path
  8. from glob import glob
  9. from dic_lab import dic_labels
  10. def generate_labels(dic_labs):
  11. path_input_json = dic_labels['path_json']
  12. ratio = dic_labs['ratio']
  13. for index, labelme_annotation_path in enumerate(glob(f'{path_input_json}/*.json')):
  14. # 读取文件名
  15. image_id = os.path.basename(labelme_annotation_path).rstrip('.json')
  16. # 计算是train 还是 valid
  17. train_or_valid = 'train' if random.random() < ratio else 'valid'
  18. # 读取labelme格式的json文件
  19. labelme_annotation_file = open(labelme_annotation_path, 'r')
  20. labelme_annotation = json.load(labelme_annotation_file)
  21. # yolo 格式的 lables
  22. yolo_annotation_path = os.path.join(train_or_valid, 'labels', image_id + '.txt')
  23. yolo_annotation_file = open(yolo_annotation_path, 'w')
  24. # yolo 格式的图像保存
  25. yolo_image = base64.decodebytes(labelme_annotation['imageData'].encode())
  26. yolo_image_path = os.path.join(train_or_valid, 'images', image_id + '.jpg')
  27. yolo_image_file = open(yolo_image_path, 'wb')
  28. yolo_image_file.write(yolo_image)
  29. yolo_image_file.close()
  30. # 获取位置信息
  31. for shape in labelme_annotation['shapes']:
  32. if shape['shape_type'] != 'rectangle':
  33. print(
  34. f'Invalid type `{shape["shape_type"]}` in annotation `annotation_path`')
  35. continue
  36. points = shape['points']
  37. scale_width = 1.0 / labelme_annotation['imageWidth']
  38. scale_height = 1.0 / labelme_annotation['imageHeight']
  39. width = (points[1][0] - points[0][0]) * scale_width
  40. height = (points[1][1] - points[0][1]) * scale_height
  41. x = ((points[1][0] + points[0][0]) / 2) * scale_width
  42. y = ((points[1][1] + points[0][1]) / 2) * scale_height
  43. object_class = dic_labels[shape['label']]
  44. yolo_annotation_file.write(f'{object_class} {x} {y} {width} {height}\n')
  45. yolo_annotation_file.close()
  46. print("creat lab %d : %s" % (index, image_id))
  47. if __name__ == "__main__":
  48. os.makedirs(os.path.join("train", 'images'), exist_ok=True)
  49. os.makedirs(os.path.join("train", 'labels'), exist_ok=True)
  50. os.makedirs(os.path.join("valid", 'images'), exist_ok=True)
  51. os.makedirs(os.path.join("valid", 'labels'), exist_ok=True)
  52. generate_labels(dic_labels)

执行完毕上述两份代码后就可以成功将JSON格式转化为TXT格式方便模型训练,执行完毕后的工程如下:

train:训练用数据
​
valid:验证用数据

2.2 YOLOv5-Lite训练

Yolov5-Lite 训练就是常规的神经网络模型训练,从 GitHub 上下载 Yolov5-Lite 的源代码

Yolov5-Lite 的目录下找到 train.py (训练文件)的 main 函数入口,进行如下配置:

1.将数据集存放在正确的位置并且填写到代码中:

2.安装对应的依赖(根据源代码中的requirements):

`matplotlib>=3.2.2`
`numpy>=1.18.5`
`opencv-python>=4.1.2`
`Pillow`
`PyYAML>=5.3.1`
`scipy>=1.4.1`
`torch>=1.8.0`
`torchvision>=0.9.0`
`tqdm>=4.41.0`
`tensorboard>=2.4.1`
`seaborn>=0.11.0`
`pandas`
`thop  # FLOPS computation`
`pycocotools>=2.0  # COCO mAP`

3.因为最新版本的依赖与旧版本的代码并不兼容,所以需要对代码进行修改:

特别注意配置train中的参数:

笔者遇到的一些BUG:

①numpy版本的原因,np.int全部无法使用,所以要去对应的报错地点修改np.int为np.int_

②数据格式转换有问题,因为新版的torch无法自行转换,所以需要在每个torch中手动添加一个.long()进行数据转换,ctrl+f搜索gain,找到gain = torch.ones(7, device=targets.device),将其修改为gain = torch.ones(7, device=targets.device).long()

③model的返回格式中,删除多余的不明符号

4.修改完上述bug后代码运行并且开始训练,等待训练完成。

训练完成后生成以下文件:

2.3 ONNX的部署

1.将exp最大的数字(此处的数字代表第几次训练)文件夹内下的pt文件放到模型主文件夹下,利用模型自带的export.py将模型训练后的文件转化为onnx格式。

2.exprot中也有很多地方需要修改:

①将初始设定的e.pt改为s.pt(根据你使用的模型来,笔者使用的是s)

②删除opt.ncnn这一选项

③下载onnx的依赖(上网搜索onnx的依赖安装,不再赘述)

3.将protohuf降低版本到3.19.0以下

(这个好像是版本的问题,总之如果前三步都做了还是报错,就降低版本试试)

pip install -U numpy==1.17.0 

或者

pip uninstall numpy
pip install numpy==1.17.1

运行成功后得到以下文件:

4.搭建onnx环境到荔枝派中

①首先搭建并进入一个Python的虚拟环境,方便使用pip(很重要)

sudo -i
apt install python3.11-venv
cd /root
python3 -m venv ort
source /root/ort/bin/activate

②然后下载SHL库

pip3 install shl-python
python3 -m shl --whereis th1520
//将whereis得到的地址的库复制到/usr/lib中
sudo cp {whereis的地址}/lib/* /usr/lib/

③最后下载onnxruntime (下载该库很有可能不成功,不成功的原因有很多,有可能是镜像问题,有可能是缺少某个库或者框架)

wget https://github.com/zhangwm-pt/onnxruntime/releases/download/riscv_whl_v2.6.0/hhb_onnxruntime_c920-2.6.0-cp311-cp311-linux_riscv64.whl
​
pip install hhb_onnxruntime_c920-2.6.0-cp311-cp311-linux_riscv64.whl

排雷:

解决方法:

Releases · T-head-Semi/csi-nn2 · GitHub下载对应的库,放到板子的/usr/lib或者/lib目录下

5.编写摄像头代码,此处代码中按下s代表开始识别,q代表关闭程序

  1. import cv2
  2. import numpy as np
  3. import onnxruntime as ort
  4. import time
  5. def plot_one_box(x, img, color=None, label=None, line_thickness=None):
  6. tl = (
  7. line_thickness or round(0.002 * (img.shape[0] + img.shape[1]) / 2) + 1
  8. ) # line/font thickness
  9. color = color or [random.randint(0, 255) for _ in range(3)]
  10. x = x.squeeze()
  11. c1, c2 = (int(x[0]), int(x[1])), (int(x[2]), int(x[3]))
  12. cv2.rectangle(img, c1, c2, color, thickness=tl, lineType=cv2.LINE_AA)
  13. if label:
  14. tf = max(tl - 1, 1) # font thickness
  15. t_size = cv2.getTextSize(label, 0, fontScale=tl / 3, thickness=tf)[0]
  16. c2 = c1[0] + t_size[0], c1[1] - t_size[1] - 3
  17. cv2.rectangle(img, c1, c2, color, -1, cv2.LINE_AA) # filled
  18. cv2.putText(
  19. img,
  20. label,
  21. (c1[0], c1[1] - 2),
  22. 0,
  23. tl / 3,
  24. [225, 255, 255],
  25. thickness=tf,
  26. lineType=cv2.LINE_AA,
  27. )
  28. def _make_grid( nx, ny):
  29. xv, yv = np.meshgrid(np.arange(ny), np.arange(nx))
  30. return np.stack((xv, yv), 2).reshape((-1, 2)).astype(np.float32)
  31. def cal_outputs(outs,nl,na,model_w,model_h,anchor_grid,stride):
  32. row_ind = 0
  33. grid = [np.zeros(1)] * nl
  34. for i in range(nl):
  35. h, w = int(model_w/ stride[i]), int(model_h / stride[i])
  36. length = int(na * h * w)
  37. if grid[i].shape[2:4] != (h, w):
  38. grid[i] = _make_grid(w, h)
  39. outs[row_ind:row_ind + length, 0:2] = (outs[row_ind:row_ind + length, 0:2] * 2. - 0.5 + np.tile(
  40. grid[i], (na, 1))) * int(stride[i])
  41. outs[row_ind:row_ind + length, 2:4] = (outs[row_ind:row_ind + length, 2:4] * 2) ** 2 * np.repeat(
  42. anchor_grid[i], h * w, axis=0)
  43. row_ind += length
  44. return outs
  45. def post_process_opencv(outputs,model_h,model_w,img_h,img_w,thred_nms,thred_cond):
  46. conf = outputs[:,4].tolist()
  47. c_x = outputs[:,0]/model_w*img_w
  48. c_y = outputs[:,1]/model_h*img_h
  49. w = outputs[:,2]/model_w*img_w
  50. h = outputs[:,3]/model_h*img_h
  51. p_cls = outputs[:,5:]
  52. if len(p_cls.shape)==1:
  53. p_cls = np.expand_dims(p_cls,1)
  54. cls_id = np.argmax(p_cls,axis=1)
  55. p_x1 = np.expand_dims(c_x-w/2,-1)
  56. p_y1 = np.expand_dims(c_y-h/2,-1)
  57. p_x2 = np.expand_dims(c_x+w/2,-1)
  58. p_y2 = np.expand_dims(c_y+h/2,-1)
  59. areas = np.concatenate((p_x1,p_y1,p_x2,p_y2),axis=-1)
  60. areas = areas.tolist()
  61. ids = cv2.dnn.NMSBoxes(areas,conf,thred_cond,thred_nms)
  62. if len(ids)>0:
  63. return np.array(areas)[ids],np.array(conf)[ids],cls_id[ids]
  64. else:
  65. return [],[],[]
  66. def infer_img(img0,net,model_h,model_w,nl,na,stride,anchor_grid,thred_nms=0.4,thred_cond=0.5):
  67. # 图像预处理
  68. img = cv2.resize(img0, [model_w,model_h], interpolation=cv2.INTER_AREA)
  69. img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
  70. img = img.astype(np.float32) / 255.0
  71. blob = np.expand_dims(np.transpose(img, (2, 0, 1)), axis=0)
  72. # 模型推理
  73. outs = net.run(None, {net.get_inputs()[0].name: blob})[0].squeeze(axis=0)
  74. # 输出坐标矫正
  75. outs = cal_outputs(outs,nl,na,model_w,model_h,anchor_grid,stride)
  76. # 检测框计算
  77. img_h,img_w,_ = np.shape(img0)
  78. boxes,confs,ids = post_process_opencv(outs,model_h,model_w,img_h,img_w,thred_nms,thred_cond)
  79. return boxes,confs,ids
  80. if __name__ == "__main__":
  81. # 模型加载
  82. model_pb_path = "best.onnx"
  83. so = ort.SessionOptions()
  84. net = ort.InferenceSession(model_pb_path, so)
  85. # 标签字典
  86. dic_labels= {0:'WhiteMouse',
  87. 1:'BlackMouse'}
  88. # 模型参数
  89. model_h = 320
  90. model_w = 320
  91. nl = 3
  92. na = 3
  93. stride=[8.,16.,32.]
  94. anchors = [[10, 13, 16, 30, 33, 23], [30, 61, 62, 45, 59, 119], [116, 90, 156, 198, 373, 326]]
  95. anchor_grid = np.asarray(anchors, dtype=np.float32).reshape(nl, -1, 2)
  96. video = 0
  97. cap = cv2.VideoCapture(video)
  98. flag_det = False
  99. while True:
  100. success, img0 = cap.read()
  101. if success:
  102. if flag_det:
  103. t1 = time.time()
  104. det_boxes,scores,ids = infer_img(img0,net,model_h,model_w,nl,na,stride,anchor_grid,thred_nms=0.4,thred_cond=0.5)
  105. t2 = time.time()
  106. for box,score,id in zip(det_boxes,scores,ids):
  107. label = '%s:%.2f'%(dic_labels[id.item()],score)
  108. plot_one_box(box.astype(np.int16), img0, color=(255,0,0), label=label, line_thickness=None)
  109. str_FPS = "FPS: %.2f"%(1./(t2-t1))
  110. cv2.putText(img0,str_FPS,(50,50),cv2.FONT_HERSHEY_COMPLEX,1,(0,255,0),3)
  111. cv2.imshow("video",img0)
  112. key=cv2.waitKey(1) & 0xFF
  113. if key == ord('q'):
  114. break
  115. elif key & 0xFF == ord('s'):
  116. flag_det = not flag_det
  117. print(flag_det)
  118. cap.release()

注意改正下面两个地方,改成自己数据集的Label和onnx文件名

6.最后将onnx文件和摄像头代码放到同一个文件夹下,运行test1.py就可以开启识别:

最后一个BUG:

解决方法:

这个错误的提示是numpy报错,shape有问题,但是我没找到原模型中的问题出在哪个地方,最后降低了模型的版本,就没有这个报错了

三、成果展示

新手第一次部署模型,最后的成果大概3-4FPS左右,听说还可以继续优化,请教一下评论区有懂这方面的大佬。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/正经夜光杯/article/detail/824927
推荐阅读
相关标签
  

闽ICP备14008679号