本项目基于百度飞桨AI Studio平台进行实现,百度出品的深度学习平台飞桨(PaddlePaddle)是主流深度学习框架中一款完全国产化的产品,与Google TensorFlow、Facebook Pytorch齐名。2016 年飞桨正式开源,是国内首个全面开源开放、技术领先、功能完备的产业级深度学习平台。相比国内其他平台,飞桨是一个功能完整的深度学习平台,也是唯一成熟稳定、具备大规模推广条件的深度学习平台。
文章目录
平台主界面如下:
![]()
在飞桨平台上,我们在使用paddle深度学习框架的基础上,可以免费使用平台的服务器,具体如下:
接下来我们进入正文~
背景介绍
车牌识别技术是一种利用计算机视觉技术对车辆车牌进行自动检测、识别的技术。它可以通过摄像头实时捕获车辆车牌信息,并经过算法处理后得到车牌号码信息,实现对车辆的自动识别、管理和监控。车牌识别技术被广泛应用于城市交通管理、安防监控行业、停车场管理等领域,可以提高交通管理效率,降低人工劳动成本,保障公共安全。
随着自动驾驶技术的快速发展,车辆自主性越来越高,丰富的实时数据也为车牌识别技术的运用提供了更多可能性。未来,车牌识别技术将会更加智能化、精准化,成为城市交通管理、智慧安防等领域的重要组成部分。
本项目主要基于PaddleOCR套件中的PP-OCR进行车牌的检测与识别,PP-OCR是PaddleOCR自研的实用的超轻量OCR系统。在实现前沿算法的基础上,考虑精度与速度的平衡,进行模型瘦身和深度优化,使其尽可能满足产业落地需求。该系统包含文本检测和文本识别两个阶段,其中文本检测算法选用DB,文本识别算法选用CRNN,并在检测和识别模块之间添加文本方向分类器,以应对不同方向的文本识别。我们这次使用的模块为PP-OCRv3,在PP-OCRv2的基础上,针对检测模型和识别模型,进行了共计9个方面的升级,进一步提升了模型效果。
一、PaddleOCR介绍
📑1.1 简介
PaddleOCR是一套丰富、领先、且实用的OCR工具库,旨在助力开发者训练出更好的模型,并应用落地。
部分应用场景如下:
🌟1.2 特性
PaddleOCR支持多种OCR相关前沿算法,在此基础上打造产业级特色模型PP-OCR和PP-Structure,并打通数据生产、模型训练、压缩、预测部署全流程。
⚡1.3 快速开始
- 在线网站体验:超轻量PP-OCR mobile模型体验地址:https://www.paddlepaddle.org.cn/hub/scene/ocr
- 移动端demo体验:安装包DEMO下载地址(基于EasyEdge和Paddle-Lite, 支持iOS和Android系统)
- 一行命令快速使用:快速开始(中英文/多语言/文档分析)
更多关于PaddleOCR套件的详细信息请访问此地址
二、 数据集简介和预处理
2.1 数据集简介
所使用的数据集为 CCPD2020 新能源车牌数据集,该数据集为
该数据集分布如下:
数据集类型 | 数量 |
---|---|
训练集 | 5769 |
验证集 | 1001 |
测试集 | 5006 |
数据集图片示例如下:
2.2 数据集处理
# 解压数据集
!tar -xvf /home/aistudio/data/data148343/CCPD2020.tar
import cv2
import os
import json
from tqdm import tqdm
import numpy as np
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 make_label(img_dir, save_gt_folder, phase):
crop_img_save_dir = os.path.join(save_gt_folder, phase, 'crop_imgs')
os.makedirs(crop_img_save_dir, exist_ok=True)
f_det = open(os.path.join(save_gt_folder, phase, 'det.txt'), 'w', encoding='utf-8')
f_rec = open(os.path.join(save_gt_folder, phase, 'rec.txt'), 'w', encoding='utf-8')
i = 0
for filename in tqdm(os.listdir(os.path.join(img_dir, phase))):
str_list = filename.split('-')
if len(str_list) < 5:
continue
coord_list = str_list[3].split('_')
txt_list = str_list[4].split('_')
boxes = []
for coord in coord_list:
boxes.append([int(x) for x in coord.split("&")])
boxes = [boxes[2], boxes[3], boxes[0], boxes[1]]
lp_number = provinces[int(txt_list[0])] + alphabets[int(txt_list[1])] + ''.join([ads[int(x)] for x in txt_list[2:]])
# det
det_info = [{'points':boxes, 'transcription':lp_number}]
f_det.write('{}\t{}\n'.format(os.path.join(phase, filename), json.dumps(det_info, ensure_ascii=False)))
# rec
boxes = np.float32(boxes)
img = cv2.imread(os.path.join(img_dir, phase, filename))
# crop_img = img[int(boxes[:,1].min()):int(boxes[:,1].max()),int(boxes[:,0].min()):int(boxes[:,0].max())]
crop_img = get_rotate_crop_image(img, boxes)
crop_img_save_filename = '{}_{}.jpg'.format(i,'_'.join(txt_list))
crop_img_save_path = os.path.join(crop_img_save_dir, crop_img_save_filename)
cv2.imwrite(crop_img_save_path, crop_img)
f_rec.write('{}/crop_imgs/{}\t{}\n'.format(phase, crop_img_save_filename, lp_number))
i+=1
f_det.close()
f_rec.close()
def get_rotate_crop_image(img, points):
'''
img_height, img_width = img.shape[0:2]
left = int(np.min(points[:, 0]))
right = int(np.max(points[:, 0]))
top = int(np.min(points[:, 1]))
bottom = int(np.max(points[:, 1]))
img_crop = img[top:bottom, left:right, :].copy()
points[:, 0] = points[:, 0] - left
points[:, 1] = points[:, 1] - top
'''
assert len(points) == 4, "shape of points must be 4*2"
img_crop_width = int(
max(
np.linalg.norm(points[0] - points[1]),
np.linalg.norm(points[2] - points[3])))
img_crop_height = int(
max(
np.linalg.norm(points[0] - points[3]),
np.linalg.norm(points[1] - points[2])))
pts_std = np.float32([[0, 0], [img_crop_width, 0],
[img_crop_width, img_crop_height],
[0, img_crop_height]])
M = cv2.getPerspectiveTransform(points, pts_std)
dst_img = cv2.warpPerspective(
img,
M, (img_crop_width, img_crop_height),
borderMode=cv2.BORDER_REPLICATE,
flags=cv2.INTER_CUBIC)
dst_img_height, dst_img_width = dst_img.shape[0:2]
if dst_img_height * 1.0 / dst_img_width >= 1.5:
dst_img = np.rot90(dst_img)
return dst_img
img_dir = '/home/aistudio/data/CCPD2020/ccpd_green'
save_gt_folder = '/home/aistudio/data/CCPD2020/PPOCR'
# phase = 'train' # change to val and test to make val dataset and test dataset
for phase in ['train','val','test']:
make_label(img_dir, save_gt_folder, phase)
以上是官网对该数据集的处理,本项目由于数据集已经处理好了,所以不再进行处理,这里仅分享。
数据集格式如下:
|- CCPD2020
|-ccpd_green
|-small
|-img1.jpg
|-img2.jpg
|-...
|-test
|-img1.jpg
|-img2.jpg
|-...
|-train
|-img1.jpg
|-img2.jpg
|-...
|-val
|-img1.jpg
|-img2.jpg
|-...
|-PPOCR
|-test
|-crop_imgs
|-det.txt
|-rec.txt
|-train
|-crop_imgs
|-det.txt
|-rec.txt
|-val
|-crop_imgs
|-det.txt
|-rec.txt
三、环境配置
在左边的套件里面下载PaddleOCR
套件。
# 安装相关依赖
!pip install -r /home/aistudio/requirements.txt
四、训练
4.1 检测模型训练
4.1.1 预训练模型直接预测
# 下载预训练模型
import os
!mkdir models
os.chdir('/home/aistudio/models')
!wget https://paddleocr.bj.bcebos.com/PP-OCRv3/chinese/ch_PP-OCRv3_det_distill_train.tar
!tar -xf ch_PP-OCRv3_det_distill_train.tar
os.chdir('/home/aistudio/PaddleOCR-2.6.0')
!python tools/eval.py -c /home/aistudio/ch_PP-OCRv3_det_student.yml -o \
Global.pretrained_model=/home/aistudio/models/ch_PP-OCRv3_det_distill_train/student.pdparams \
Eval.dataset.data_dir=/home/aistudio/CCPD2020/ccpd_green \
Eval.dataset.label_file_list=[/home/aistudio/CCPD2020/PPOCR/test/det.txt]
通过上述评估结果我们可以发现,直接使用预训练检测模型的hmean为0.76。
hmean是一个综合指标,从下面的表达式可以看出其值介于精度和回调之间,其表达式为:
h
m
e
a
n
=
2
×
p
r
e
c
i
s
i
o
n
×
r
e
c
a
l
l
p
r
e
c
i
s
i
o
n
+
r
e
c
a
l
l
hmean=\frac{2\times precision\times recall}{precision+recall}
hmean=precision+recall2×precision×recall
4.1.2 CCPD车牌数据集fine-tune训练
# 训练
!python tools/train.py -c /home/aistudio/ch_PP-OCRv3_det_student.yml -o \
Global.pretrained_model=/home/aistudio/models/ch_PP-OCRv3_det_distill_train/student.pdparams \
Global.save_model_dir=/home/aistudio/output/CCPD/det \
Global.eval_batch_step="[0, 772]" \
Optimizer.lr.name=Const \
Optimizer.lr.learning_rate=0.0005 \
Optimizer.lr.warmup_epoch=0 \
Train.dataset.data_dir=//home/aistudio/CCPD2020/ccpd_green \
Train.dataset.label_file_list=[/home/aistudio/CCPD2020/PPOCR/train/det.txt] \
Eval.dataset.data_dir=/home/aistudio/CCPD2020/ccpd_green \
Eval.dataset.label_file_list=[/home/aistudio/CCPD2020/PPOCR/test/det.txt]
!python tools/eval.py -c /home/aistudio/ch_PP-OCRv3_det_student.yml -o \
Global.pretrained_model=/home/aistudio/output/CCPD/det/best_accuracy.pdparams \
Eval.dataset.data_dir=/home/aistudio/CCPD2020/ccpd_green \
Eval.dataset.label_file_list=[/home/aistudio/CCPD2020/PPOCR/test/det.txt]
我训练了100轮左右,发现训练46轮后效果最好,这时候hmean达到了99.2%。
使用预训练模型和CCPD车牌数据集fine-tune,指标分别如下:
方案 | hmeans |
---|---|
PP-OCRv3中英文超轻量检测预训练模型直接预测 | 76.12% |
PP-OCRv3中英文超轻量检测预训练模型 fine-tune | 99.00% |
4.1.3 CCPD车牌数据集fine-tune+量化训练
!python3.7 deploy/slim/quantization/quant.py -c /home/aistudio/ch_PP-OCRv3_det_student.yml -o \
Global.pretrained_model=/home/aistudio/output/CCPD/det/best_accuracy.pdparams \
Global.save_model_dir=/home/aistudio/output/CCPD/det_quant \
Global.eval_batch_step="[0, 772]" \
Optimizer.lr.name=Const \
Optimizer.lr.learning_rate=0.0005 \
Optimizer.lr.warmup_epoch=0 \
Train.dataset.data_dir=/home/aistudio/CCPD2020/ccpd_green \
Train.dataset.label_file_list=[/home/aistudio/CCPD2020/PPOCR/train/det.txt] \
Eval.dataset.data_dir=/home/aistudio/CCPD2020/ccpd_green \
Eval.dataset.label_file_list=[/home/aistudio/CCPD2020/PPOCR/test/det.txt]
4.2 识别模型训练
4.2.1 预训练模型直接预测
import os
os.chdir('/home/aistudio/models')
!wget https://paddleocr.bj.bcebos.com/PP-OCRv3/chinese/ch_PP-OCRv3_rec_train.tar
!tar -xf ch_PP-OCRv3_rec_train.tar
PaddleOCR提供的PP-OCRv3识别模型采用蒸馏训练策略,因此提供的预训练模型中会包含Teacher和Student模型的参数,详细信息可参考knowledge_distillation.md。 因此,模型下载完成后需要使用如下代码提取Student模型的参数:
import paddle
# 加载预训练模型
all_params = paddle.load("../models/ch_PP-OCRv3_rec_train/best_accuracy.pdparams")
# 查看权重参数的keys
print(all_params.keys())
# 学生模型的权重提取
s_params = {key[len("Student."):]: all_params[key] for key in all_params if "Student." in key}
# 查看学生模型权重参数的keys
print(s_params.keys())
# 保存
paddle.save(s_params, "../models/ch_PP-OCRv3_rec_train/student.pdparams")
import os
os.chdir('/home/aistudio/PaddleOCR-2.6.0')
!python tools/eval.py -c configs/rec/PP-OCRv3/ch_PP-OCRv3_rec.yml -o \
Global.pretrained_model=/home/aistudio/models/ch_PP-OCRv3_rec_train/student.pdparams \
Eval.dataset.data_dir=/home/aistudio/CCPD2020/PPOCR \
Eval.dataset.label_file_list=[/home/aistudio/CCPD2020/PPOCR/test/rec.txt]
如果出现了acc精度为0的情况,那么我们需要到/home/aistudio/PaddleOCR-2.6.0/ppocr/postprocess/rec_postprocess.py
文件里,在第98行加入text = text.replace('·','')
代码,这是因为我们识别出的结果里面将车牌号字母和数字之间的点·
也给识别出来了,所以要将其替换掉进行评估。
具体操作如下:
我们可以先识别一张图片,比如:
import os
os.chdir('/home/aistudio/PaddleOCR-2.6.0')
!python tools/infer_rec.py -c /home/aistudio/ch_PP-OCRv3_rec.yml -o \
Global.pretrained_model=/home/aistudio/models/ch_PP-OCRv3_rec_train/student.pdparams \
Global.infer_img=/home/aistudio/car1.jpg
4.2.2 CCPD车牌数据集fine-tune训练
import os
os.chdir('/home/aistudio/PaddleOCR-2.6.0')
!python tools/train.py -c /home/aistudio/ch_PP-OCRv3_rec.yml -o \
Global.pretrained_model=/home/aistudio/models/ch_PP-OCRv3_rec_train/student.pdparams \
Global.save_model_dir=/home/aistudio/output/CCPD/rec/ \
Global.eval_batch_step="[0, 90]" \
Optimizer.lr.name=Const \
Optimizer.lr.learning_rate=0.0005 \
Optimizer.lr.warmup_epoch=0 \
Train.dataset.data_dir=/home/aistudio/CCPD2020/PPOCR \
Train.dataset.label_file_list=[/home/aistudio/CCPD2020/PPOCR/train/rec.txt] \
Eval.dataset.data_dir=/home/aistudio/CCPD2020/PPOCR \
Eval.dataset.label_file_list=[/home/aistudio/CCPD2020/PPOCR/test/rec.txt]
!python tools/eval.py -c /home/aistudio/ch_PP-OCRv3_rec.yml -o \
Global.pretrained_model=/home/aistudio/output/CCPD/rec/best_accuracy.pdparams \
Eval.dataset.data_dir=/home/aistudio/CCPD2020/PPOCR \
Eval.dataset.label_file_list=[/home/aistudio/CCPD2020/PPOCR/test/rec.txt]
4.2.3 CCPD车牌数据集fine-tune+量化训练
!python3.7 deploy/slim/quantization/quant.py -c /home/aistudio/ch_PP-OCRv3_rec.yml -o \
Global.pretrained_model=/home/aistudio/output/CCPD/rec/best_accuracy.pdparams \
Global.save_model_dir=/home/aistudio/output/CCPD/rec_quant/ \
Global.eval_batch_step="[0, 90]" \
Optimizer.lr.name=Const \
Optimizer.lr.learning_rate=0.0005 \
Optimizer.lr.warmup_epoch=0 \
Train.dataset.data_dir=/home/aistudio/CCPD2020/PPOCR \
Train.dataset.label_file_list=[/home/aistudio/CCPD2020/PPOCR/train/rec.txt] \
Eval.dataset.data_dir=/home/aistudio/CCPD2020/PPOCR \
Eval.dataset.label_file_list=[/home/aistudio/CCPD2020/PPOCR/test/rec.txt]
五、模型导出
5.1 检测非量化模型的导出
!python tools/export_model.py -c /home/aistudio/ch_PP-OCRv3_det_student.yml -o \
Global.pretrained_model=/home/aistudio/output/CCPD/det/best_accuracy.pdparams \
Global.save_inference_dir=/home/aistudio/output/det/infer
5.2 识别非量化模型的导出
!python tools/export_model.py -c /home/aistudio/ch_PP-OCRv3_rec.yml -o \
Global.pretrained_model=/home/aistudio/output/CCPD/rec/best_accuracy.pdparams \
Global.save_inference_dir=/home/aistudio/output/rec/infer
六、模型部署
!python tools/infer/predict_system.py \
--det_model_dir=/home/aistudio/output/det/infer/ \
--rec_model_dir=/home/aistudio/output/rec/infer/ \
--image_dir="/home/aistudio/CCPD2020/ccpd_green/test/04421336206896552-88_265-177&509_550&618-546&600_187&618_177&527_550&509-0_0_3_32_31_32_31_32-87-37.jpg" \
--rec_image_shape=3,48,320
推理结果保存在/home/aistudio/PaddleOCR-2.6.0/inference_results
路径下。
推理结果如下:
总结
- 本次车牌识别项目旨在设计和实现一个高效、准确的车牌检测与识别系统,能够在各种复杂的场景下自动完成车牌的识别和记录,并提高城市交通管理的效率。PadlleOCR套件里的PP-OCR在精度和速度上都已经达到了很不错的效果。
- 在该项目中,我们采用了PaddleOCR套件提供的车牌识别模型并进行了相关的优化和调整。同时,我们还采用了大量的车牌图像数据进行训练和测试,以确保系统的准确性和稳定性。
- 通过对算法的优化和实验验证,我们成功地实现了车牌号识别的自动化,取得了较高的识别率和较低的误识别率。接下来,我们将设计了一个友好的用户界面,可以方便地进行数据管理和查询,以提升车牌管理的智能化和便捷性。
- 总体而言,基于PaddleOCR套件的车牌号识别项目在技术和实用性方面取得了很好的成果,为城市交通管理和智慧安防等领域的相关应用提供了可靠的技术支持。未来,我们将继续优化和升级该系统,以满足不断变化的实际需求和挑战。
本项目基于百度AI Studio平台完成,如需要可以到基于PP-OCRv3的车牌检测和识别运行项目导出训练模型权重,并部署到本地使用。