Commit bc2ea059 authored by liuhy's avatar liuhy
Browse files

Merge branch 'dev' into 'master'

Dev

See merge request modelzoo/lpr!1
parents b44aeb9e b1430a7d
File added
import onnxruntime as ort import onnxruntime as ort
import cv2 import cv2
import numpy as np import numpy as np
import argparse
import os
import time
print('Runing Based On:', ort.get_device()) print('Runing Based On:', ort.get_device())
...@@ -40,24 +43,39 @@ def LPRNetPostprocess(infer_res): ...@@ -40,24 +43,39 @@ def LPRNetPostprocess(infer_res):
result = ''.join(list(map(lambda x: CHARS[x], no_repeat_blank_label))) result = ''.join(list(map(lambda x: CHARS[x], no_repeat_blank_label)))
return result return result
def LPRNetInference(model, imgs): def LPRNetInference(args):
img = LPRNetPreprocess(imgs) if ort.get_device() == "GPU-MIGRAPHX":
sess = ort.InferenceSession(args.model, providers=['ROCMExecutionProvider'],) #DCU版本
if ort.get_device() == "GPU":
# sess = ort.InferenceSession(model, providers=['CUDAExecutionProvider'],) #GPU版本
sess = ort.InferenceSession(model, providers=['ROCMExecutionProvider'],) #DCU版本
else: else:
sess = ort.InferenceSession(model, providers=['CPUExecutionProvider']) # CPU版本 sess = ort.InferenceSession(args.model, providers=['CPUExecutionProvider']) # CPU版本
print(sess.get_providers())
intput = sess.get_inputs()[0].shape
preb = sess.run(None, input_feed={sess.get_inputs()[0].name: img})[0]
result = LPRNetPostprocess(preb) if os.path.isdir(args.imgpath):
return result images = os.listdir(args.imgpath)
count = 0
time1 = time.perf_counter()
for image in images:
img = LPRNetPreprocess(os.path.join(args.imgpath, image))
intput = sess.get_inputs()[0].shape
preb = sess.run(None, input_feed={sess.get_inputs()[0].name: img})[0]
result = LPRNetPostprocess(preb)
if result == image[:-4]:
count += 1
print('Inference Result:', result)
time2 = time.perf_counter()
print('accuracy rate:', count / len(images))
print('average time', (time2 - time1)/count*1000)
else:
img = LPRNetPreprocess(args.imgpath)
intput = sess.get_inputs()[0].shape
preb = sess.run(None, input_feed={sess.get_inputs()[0].name: img})[0]
result = LPRNetPostprocess(preb)
print('Inference Result:', result)
if __name__ == '__main__': if __name__ == '__main__':
model_name = 'model/LPRNet.onnx' parser = argparse.ArgumentParser(description='parameters to vaildate net')
# model_name = 'LPRNet.onnx' parser.add_argument('--model', default='model/LPRNet.onnx', help='model path to vaildate')
image = 'imgs/川JK0707.jpg' parser.add_argument('--imgpath', default='imgs', help='the image path')
InferRes = LPRNetInference(model_name, image) args = parser.parse_args()
print(image, 'Inference Result:', InferRes)
LPRNetInference(args)
...@@ -5,6 +5,9 @@ MIGraphX示例程序 ...@@ -5,6 +5,9 @@ MIGraphX示例程序
import cv2 import cv2
import numpy as np import numpy as np
import migraphx import migraphx
import argparse
import os
import time
CHARS = ['京', '沪', '津', '渝', '冀', '晋', '蒙', '辽', '吉', '黑', CHARS = ['京', '沪', '津', '渝', '冀', '晋', '蒙', '辽', '吉', '黑',
'苏', '浙', '皖', '闽', '赣', '鲁', '豫', '鄂', '湘', '粤', '苏', '浙', '皖', '闽', '赣', '鲁', '豫', '鄂', '湘', '粤',
...@@ -42,30 +45,47 @@ def LPRNetPostprocess(infer_res): ...@@ -42,30 +45,47 @@ def LPRNetPostprocess(infer_res):
result = ''.join(list(map(lambda x: CHARS[x], no_repeat_blank_label))) result = ''.join(list(map(lambda x: CHARS[x], no_repeat_blank_label)))
return result return result
def LPRNetInference(model_name, imgs): def LPRNetInference(args):
img = LPRNetPreprocess(imgs)
# 加载模型 # 加载模型
if model_name[-3:] == 'mxr': if args.model[-3:] == 'mxr':
model = migraphx.load(model_name) model = migraphx.load(args.model)
else: else:
model = migraphx.parse_onnx(model_name) print('convert onnx to mxr...')
# migraphx.quantize_fp16(model) model = migraphx.parse_onnx(args.model)
model.compile(t=migraphx.get_target("gpu"),device_id=0) # device_id: 设置GPU设备,默认为0号设备(>=1.2版本中支持) model.compile(t=migraphx.get_target("gpu"),device_id=0) # device_id: 设置GPU设备,默认为0号设备(>=1.2版本中支持)
# migraphx.save(model, 'LPRNet.mxr') migraphx.save(model, args.savepath)
inputName=model.get_parameter_names()[0] if os.path.isdir(args.imgpath):
inputShape=model.get_parameter_shapes()[inputName].lens() images = os.listdir(args.imgpath)
print("inputName:{0} \ninputShape:{1}".format(inputName,inputShape)) count = 0
time1 = time.perf_counter()
results = model.run({inputName: migraphx.argument(img)}) for image in images:
img = LPRNetPreprocess(os.path.join(args.imgpath, image))
result = LPRNetPostprocess(np.array(results[0])) inputName = model.get_parameter_names()[0]
return result inputShape = model.get_parameter_shapes()[inputName].lens()
# print("inputName:{0} \ninputShape:{1}".format(inputName,inputShape))
results = model.run({inputName: migraphx.argument(img)})
result = LPRNetPostprocess(np.array(results[0]))
if result == image[:-4]:
count += 1
print('Inference Result:', result)
time2 = time.perf_counter()
print('accuracy rate:', count / len(images))
print('average time', (time2 - time1)/count*1000)
else:
img = LPRNetPreprocess(args.imgpath)
inputName=model.get_parameter_names()[0]
inputShape=model.get_parameter_shapes()[inputName].lens()
# print("inputName:{0} \ninputShape:{1}".format(inputName,inputShape))
results = model.run({inputName: migraphx.argument(img)})
result = LPRNetPostprocess(np.array(results[0]))
print('Inference Result:', result)
if __name__ == '__main__': if __name__ == '__main__':
# model_name = 'LPRNet.onnx' parser = argparse.ArgumentParser(description='parameters to vaildate net')
model_name = 'model/LPRNet.mxr' parser.add_argument('--model', default='model/LPRNet.onnx', help='model path to inference')
image = 'imgs/川JK0707.jpg' parser.add_argument('--imgpath', default='imgs', help='the image path')
InferRes = LPRNetInference(model_name, image) parser.add_argument('--savepath', default='model/LPRNet.mxr', help='mxr model save path and name')
print(image, 'Inference Result:', InferRes) args = parser.parse_args()
LPRNetInference(args)
# license-plate-detect-recoginition-pytorch <!--
深度学习车牌检测与识别,检测结果包含车牌矩形框和4个角点,基于pytorch框架运行, * @Author: liuhy
主程序是detect_rec_img.py,运行程序前需要确保您的机器安装了pytorch * @email: liuhy6@sugon.com
* @Date: 2023-03-03 10:17:07
* @LastEditTime: 2023-03-03 15:38:01
* @FilePath: \lpr\README.md
-->
# License-Plate-Recoginition(LPR)
## 模型介绍
LPR是一个基于深度学习技术的车牌识别模型,主要识别目标是自然场景的车牌图像。
## 模型结构
模型采用LPRNet,模型结构主要包含三部分:一个轻量级CNN主干网络、基于预定位置的字符分类头部、基于贪婪算法的序列解码。此外模型使用CTC Loss和RMSprop优化器。参考论文[LPRNet: License Plate Recognition via Deep Neural Networks](https://arxiv.org/pdf/1806.10447v1.pdf)
## 数据集
推荐使用一个车牌数据集[CCPD](https://github.com/detectRecog/CCPD "CCPD官网GitHub"),该数据集由中科大收集,可用于车牌的检测与识别。
车牌识别模块,可以更换成crnn网络做识别,也可以更换到传统图像处理方法分割字符后逐个字符识别, 我们提供了一个脚本cut_ccpd.py用于剪裁出CCPD数据集中的车牌位置,以便用于LPR模型的训练,在cut_ccpd.py中修改img_path和save_path即可,分别是CCPD数据集中ccpd_base文件夹的路径和剪裁出的图像保存路径。LPR用于训练的数据文件名就是图像的标签。**数据集使用固定的大小94x24。** 使用方法:
在这个车牌检测和识别系统里,我觉得最重要的是前面的车牌检测与矫正模块,因为如果前面没做好,
那么后面输入到车牌识别模块里的图片是一个倾斜的车牌,这时候输出结果就出错了。
对于车牌检测,也可以使用图像分割的思想,例如使用UNet语义分割网络,分割出车牌, python cut_ccpd.py \
二值化然后查找连通域,计算4个顶点 --ccpdpath CCPD数据集下ccpd_base文件夹路径 \
--savepath 保存切割图像的路径
## 训练及推理
### 环境配置
[光源](https://www.sourcefind.cn/#/service-details)可拉取训练以及推理的docker镜像,在[光合开发者社区](https://cancon.hpccube.com:65024/4/main/)可下载Migraphx和ONNXruntime安装包。LPR推荐的镜像如下:
* 训练镜像:docker pull image.sourcefind.cn:5000/dcu/admin/base/vscode-pytorch:1.10.0-centos7.6-dtk-22.10-py37-patch4
* 推理镜像:docker pull image.sourcefind.cn:5000/dcu/admin/base/custom:migraphx2.5.0_centos7.6-dtk-22.10.1
### 训练与Fine-tunning
LPR模型的训练程序是train.py,初次训练模型使用以下命令:
python train.py \
--train_img_dirs 训练集文件夹路径 \
--test_img_dirs 验证集文件夹路径
Fine-tunning使用以下命令:
python train.py \
--train_img_dirs 训练集文件夹路径 \
--test_img_dirs 验证集文件夹路径 \
--pretrained_model 预训练模型路径 \
--resume_epoch Fine-tuning训练的起始epoch \
--max_epoch 训练的最大epoch
Fine-tuning时只训练从起始epoch到最大epoch。
### 预训练模型
在model文件夹下我们提供了一个预训练模型以及对应的onnx模型和mxr模型,以下是相关子目录的介绍:
LPR
├── imgs #测试图像
├── model
│   ├── lprnet.pth #基于pytorch框架训练出的LPR预训练模型
│   ├── LPRNet.onnx #由lprnet.pth转换的onnx模型
└── └── LPRNet.mxr #用migraphx编译LPRNet.onnx得到的离线推理模型
### 测试
LPR模型用test.py对训练出的模型进行测试,使用方法如下:
python test.py \
--model 需要测试的pth模型路径 \
--imgpath 测试集路径(文件夹或图像皆可) \
--export_onnx True/False(该参数用于选择是否需要将pth模型转为onnx模型) \
--dynamic True/False(该参数用于选择onnx模型是否使用动态的batch size)
### 推理
我们分别提供了基于ONNXruntime(ORT)和Migraphx的推理脚本,版本依赖:
* ONNXRuntime(DCU版本) >= 1.14.0
* Migraphx(DCU版本) >= 2.5.0
#### ORT
LPRNet_ORT_infer.py是基于ORT的的推理脚本,使用方法:
python LPRNet_ORT_infer.py \
--model onnx模型路径 \
--imgpath 数据路径(文件夹或图像皆可)
#### Migraphx
LPRNet_migraphx_infer.py是基于Migraphx的推理脚本,使用需安装好Migraphx,支持onnx模型和mxr模型推理,mxr模型是migraphx将onnx模型保存成的离线推理引擎,初次使用onnx模型会保存对应的mxr模型。使用方法:
python LPRNet_migraphx_infer.py \
--model mxr/onnx模型路径 \
--imgpath 数据路径(文件夹或图像皆可) \
--savepath mxr模型的保存路径以及模型名称
## 性能和准确率数据
测试数据使用的是[LPRNet_Pytorch](https://github.com/sirius-ai/LPRNet_Pytorch/tree/master/data/test),使用的加速卡是DCU Z100。**mxr格式的模型是migraphx创建的onnx模型的离线引擎。**
| Engine | Model Path| Model Format | Accuracy(%) | Speed(ms) |
| :------: | :------: | :------: | :------: |:------: |
| ONNXRuntime | model/LPRNet.onnx | onnx | 91.0 | 2.59 |
| Migraphx | model/LPRNet.onnx |onnx | 91.0 | 2.66 |
| Migraphx | model/LPRNet.mxr |mxr | 91.0 | 2.49 |
## 参考
* [LPRNet_Pytorch](https://github.com/sirius-ai/LPRNet_Pytorch)
* [license-plate-detect-recoginition](https://github.com/qzpzd/license-plate-detect-recoginition)
'''
Author: liuhy
email: liuhy6@sugon.com
Date: 2023-03-03 10:17:07
LastEditTime: 2023-03-03 11:28:33
FilePath: \lpr\cut_ccpd.py
'''
# -*- coding: utf-8 -*-
# @Author: liuhy
# @Email: 17603873430@163.com
# @Date: 2023-02-28 15:14:13
# @Last Modified time: 2023-03-01 17:52:20
# coding:utf-8
import os
import cv2
import time
import argparse
provinces = ['皖', '沪', '津', '渝', '冀', '晋', '蒙', '辽', '吉', '黑', '苏', '浙', '京', '闽', '赣', '鲁', '豫', '鄂', '湘', '粤', '桂', '琼', '川', '贵', '云', '藏', '陕', '甘', '青', '宁', '新', '警', '学', 'O']
alphabets = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W','X', 'Y', 'Z', 'O']
ads = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X','Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'O']
def cut_img(img_path, save_path):
images = os.listdir(img_path)
for i, image in enumerate(images):
try:
print('\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\bProcessing:%s'%i, end='',flush=True)
img = cv2.imread(os.path.join('ccpd_base', image))
info = image.split('-')
r1, r2, l1, l2 = info[3].split('_')
rx1, ry1 = r1.split('&')
lx1, ly1 = l1.split('&')
im = img[int(ly1):int(ry1), int(lx1):int(rx1)]
label_index = info[4].split('_')
label = provinces[int(label_index[0])]
label += alphabets[int(label_index[1])]
if label_index[2].isalnum():
label += ads[int(label_index[2])]
else:
label += alphabets[int(label_index[2])]
label += ads[int(label_index[3])]
label += ads[int(label_index[4])]
label += ads[int(label_index[5])]
label += ads[int(label_index[6])]
im = cv2.resize(im, (94, 24))
if os.path.exists(save_path) is False:
os.mkdir(save_path)
cv2.imencode('.jpg', im)[1].tofile(save_path + '/' + label + '.jpg')
except Exception as e:
print('wrong image:', image)
if __name__=='__main__':
parser = argparse.ArgumentParser(description='parameters to vaildate net')
parser.add_argument('--ccpdpath', default='CCPD/ccpd_base', help='model path to vaildate')
parser.add_argument('--savepath', default='CCPD/lpr', help='the image path')
args = parser.parse_args()
cut_img(args.ccpdpath, args.savepath)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment