• 【深度学习实践】基于深度学习的车牌识别(python,车牌检测+车牌识别)


    车牌识别具有广泛的应用前景,基于传统方法的车牌识别效果一般比较差,随着计算机视觉技术的快速发展,深度学习的方法能够更好的完成车牌识别任务。

     本文提供了车牌识别方案的部署链接,您可以在网页上体验该模型的效果:车牌识别方案在线体验

    本文介绍了使用PaddleOCR完成车牌识别任务的方法,其检测效果如下图:

    原图如下:

     检测结果如下:

    目录

    一、概述 

    二、使用

    1、数据集准备

    2、检测模型

    3、识别模型

    4、模型导出

    5、联合推理

     三、总结

    附录:


    一、概述 

    基于深度学习的车牌识别任务可以拆解为2个步骤:车牌检测-车牌识别。其中车牌检测的目的是确认图片中车牌的位置,根据检测到的车牌位置把图片中的ROI裁剪出来,车牌识别算法用于识别裁剪出的车牌图像中的具体内容。

    本文使用PaddleOCR工具实现了车牌识别任务,首先使用PaddleOCR的检测算法DBNet检测出车牌位置,再将车牌位置裁剪送入文本识别算法CRNN来识别车牌的具体内容。

    PaddleOCR github:https://github.com/PaddlePaddle/PaddleOCR.git

    二、使用

    1、数据集准备

    本文选择的数据集为CCPD2020,下载链接为:CCPD2020(New energy plate) - 飞桨AI Studio

    CPPD数据集的图片文件名具有特殊规则,具体规则如下:

    例如: 025-95_113-154&383_386&473-386&473_177&454_154&383_363&402-0_0_22_27_27_33_16-37-15.jpg

    每个名称可以分为七个字段,以-符号作为分割。这些字段解释如下。

    • 025:车牌面积与整个图片区域的面积比。025 (25%)

    • 95_113:水平倾斜程度和垂直倾斜度。水平 95度 垂直 113度

    • 154&383_386&473:左上和右下顶点的坐标。左上(154,383) 右下(386,473)

    • 386&473_177&454_154&383_363&402:整个图像中车牌的四个顶点的精确(x,y)坐标。这些坐标从右下角顶点开始。(386,473) (177,454) (154,383) (363,402)

    • 0_0_22_27_27_33_16:CCPD中的每个图像只有一个车牌。每个车牌号码由一个汉字,一个字母和五个字母或数字组成。有效的中文车牌由七个字符组成:省(1个字符),字母(1个字符),字母+数字(5个字符)。“ 0_0_22_27_27_33_16”是每个字符的索引。这三个数组定义如下。每个数组的最后一个字符是字母O,而不是数字0。我们将O用作“无字符”的符号,因为中文车牌字符中没有O。因此以上车牌拼起来即为 皖AY339S

    • 37:牌照区域的亮度。 37 (37%)

    • 15:车牌区域的模糊度。15 (15%)

    下载好了数据集,需要把数据集转换为PaddleOCR需要的标注格式,代码如下(修改图片的存储路径为自己的路径):

    1. import cv2
    2. import os
    3. import json
    4. from tqdm import tqdm
    5. import numpy as np
    6. provinces = ["皖", "沪", "津", "渝", "冀", "晋", "蒙", "辽", "吉", "黑", "苏", "浙", "京", "闽", "赣", "鲁", "豫", "鄂", "湘", "粤", "桂", "琼", "川", "贵", "云", "藏", "陕", "甘", "青", "宁", "新", "警", "学", "O"]
    7. 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']
    8. 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']
    9. def make_label(img_dir, save_gt_folder, phase):
    10. crop_img_save_dir = os.path.join(save_gt_folder, phase, 'crop_imgs')
    11. os.makedirs(crop_img_save_dir, exist_ok=True)
    12. f_det = open(os.path.join(save_gt_folder, phase, 'det.txt'), 'w', encoding='utf-8')
    13. f_rec = open(os.path.join(save_gt_folder, phase, 'rec.txt'), 'w', encoding='utf-8')
    14. i = 0
    15. for filename in tqdm(os.listdir(os.path.join(img_dir, phase))):
    16. str_list = filename.split('-')
    17. if len(str_list) < 5:
    18. continue
    19. coord_list = str_list[3].split('_')
    20. txt_list = str_list[4].split('_')
    21. boxes = []
    22. for coord in coord_list:
    23. boxes.append([int(x) for x in coord.split("&")])
    24. boxes = [boxes[2], boxes[3], boxes[0], boxes[1]]
    25. lp_number = provinces[int(txt_list[0])] + alphabets[int(txt_list[1])] + ''.join([ads[int(x)] for x in txt_list[2:]])
    26. # det
    27. det_info = [{'points':boxes, 'transcription':lp_number}]
    28. f_det.write('{}\t{}\n'.format(os.path.join(phase, filename), json.dumps(det_info, ensure_ascii=False)))
    29. # rec
    30. boxes = np.float32(boxes)
    31. img = cv2.imread(os.path.join(img_dir, phase, filename))
    32. # crop_img = img[int(boxes[:,1].min()):int(boxes[:,1].max()),int(boxes[:,0].min()):int(boxes[:,0].max())]
    33. crop_img = get_rotate_crop_image(img, boxes)
    34. crop_img_save_filename = '{}_{}.jpg'.format(i,'_'.join(txt_list))
    35. crop_img_save_path = os.path.join(crop_img_save_dir, crop_img_save_filename)
    36. cv2.imwrite(crop_img_save_path, crop_img)
    37. f_rec.write('{}/crop_imgs/{}\t{}\n'.format(phase, crop_img_save_filename, lp_number))
    38. i+=1
    39. f_det.close()
    40. f_rec.close()
    41. def get_rotate_crop_image(img, points):
    42. '''
    43. img_height, img_width = img.shape[0:2]
    44. left = int(np.min(points[:, 0]))
    45. right = int(np.max(points[:, 0]))
    46. top = int(np.min(points[:, 1]))
    47. bottom = int(np.max(points[:, 1]))
    48. img_crop = img[top:bottom, left:right, :].copy()
    49. points[:, 0] = points[:, 0] - left
    50. points[:, 1] = points[:, 1] - top
    51. '''
    52. assert len(points) == 4, "shape of points must be 4*2"
    53. img_crop_width = int(
    54. max(
    55. np.linalg.norm(points[0] - points[1]),
    56. np.linalg.norm(points[2] - points[3])))
    57. img_crop_height = int(
    58. max(
    59. np.linalg.norm(points[0] - points[3]),
    60. np.linalg.norm(points[1] - points[2])))
    61. pts_std = np.float32([[0, 0], [img_crop_width, 0],
    62. [img_crop_width, img_crop_height],
    63. [0, img_crop_height]])
    64. M = cv2.getPerspectiveTransform(points, pts_std)
    65. dst_img = cv2.warpPerspective(
    66. img,
    67. M, (img_crop_width, img_crop_height),
    68. borderMode=cv2.BORDER_REPLICATE,
    69. flags=cv2.INTER_CUBIC)
    70. dst_img_height, dst_img_width = dst_img.shape[0:2]
    71. if dst_img_height * 1.0 / dst_img_width >= 1.5:
    72. dst_img = np.rot90(dst_img)
    73. return dst_img
    74. img_dir = 'CCPD2020/ccpd_green' # 改成自己的路径
    75. save_gt_folder = 'CCPD2020/PPOCR' # 改成自己的路径
    76. # phase = 'train' # change to val and test to make val dataset and test dataset
    77. for phase in ['train','val','test']:
    78. make_label(img_dir, save_gt_folder, phase)

    2、检测模型

    准备好了数据集,首先需要训练车牌检测模型,这里我们使用PaddleOCR提供的文本检测预训练模型进行fine-tuning,这样可以减少训练时间,首先下载预训练检测模型(先进入PaddleOCR文件夹):

    1. mkdir models
    2. cd models
    3. wget https://paddleocr.bj.bcebos.com/PP-OCRv3/chinese/ch_PP-OCRv3_det_distill_train.tar
    4. tar -xf ch_PP-OCRv3_det_distill_train.tar
    5. cd PaddleOCR

    下载好了预训练模型,下面训练检测模型(其中的data_dir和label_file_list换成自己的数据集路径):

    1. python tools/train.py -c configs/det/ch_PP-OCRv3/ch_PP-OCRv3_det_student.yml -o \
    2. Global.pretrained_model=models/ch_PP-OCRv3_det_distill_train/student.pdparams \
    3. Global.save_model_dir=output/CCPD/det \
    4. Global.eval_batch_step="[0, 772]" \
    5. Optimizer.lr.name=Const \
    6. Optimizer.lr.learning_rate=0.0005 \
    7. Optimizer.lr.warmup_epoch=0 \
    8. Train.dataset.data_dir=CCPD2020/ccpd_green \
    9. Train.dataset.label_file_list=[CCPD2020/PPOCR/train/det.txt] \
    10. Eval.dataset.data_dir=CCPD2020/ccpd_green \
    11. Eval.dataset.label_file_list=[CCPD2020/PPOCR/test/det.txt]

    训练好了模型以后,可以使用下面的命令验证一下精度(此步可以跳过,也要更换data_dir和label_file_list路径):

    1. python tools/eval.py -c configs/det/ch_PP-OCRv3/ch_PP-OCRv3_det_student.yml -o \
    2. Global.pretrained_model=output/CCPD/det/best_accuracy.pdparams \
    3. Eval.dataset.data_dir=CCPD2020/ccpd_green \
    4. Eval.dataset.label_file_list=[CCPD2020/PPOCR/test/det.txt]

    可以使用如下命令来实现检测模型推理(路径修改为自己需要的路径):

    1. python3 tools/infer_det.py -c configs/det/ch_PP-OCRv3/ch_PP-OCRv3_det_student.yml \
    2. -o Global.infer_img="src.jpg" Global.pretrained_model="./output/CCPD/det/best_accuracy" \
    3. PostProcess.box_thresh=0.6 PostProcess.unclip_ratio=2.0

    3、识别模型

    训练好了检测模型,再来训练识别模型,同样先下载预训练权重再fine-tuning,下载权重命令如下:

    1. mkdir models
    2. cd models
    3. wget https://paddleocr.bj.bcebos.com/PP-OCRv3/chinese/ch_PP-OCRv3_rec_train.tar
    4. tar -xf ch_PP-OCRv3_rec_train.tar
    5. cd PaddleOCR

    这个权重中包含不需要的内容(Teacher的权重),需要提取需要的权重:

    1. import paddle
    2. # 加载预训练模型
    3. all_params = paddle.load("models/ch_PP-OCRv3_rec_train/best_accuracy.pdparams")
    4. # 查看权重参数的keys
    5. print(all_params.keys())
    6. # 学生模型的权重提取
    7. s_params = {key[len("Student."):]: all_params[key] for key in all_params if "Student." in key}
    8. # 查看学生模型权重参数的keys
    9. print(s_params.keys())
    10. # 保存
    11. paddle.save(s_params, "models/ch_PP-OCRv3_rec_train/student.pdparams")

    开启训练(注意路径):

    1. python tools/train.py -c configs/rec/PP-OCRv3/ch_PP-OCRv3_rec.yml -o \
    2. Global.pretrained_model=models/ch_PP-OCRv3_rec_train/student.pdparams \
    3. Global.save_model_dir=output/CCPD/rec/ \
    4. Global.eval_batch_step="[0, 90]" \
    5. Optimizer.lr.name=Const \
    6. Optimizer.lr.learning_rate=0.0005 \
    7. Optimizer.lr.warmup_epoch=0 \
    8. Train.dataset.data_dir=CCPD2020/PPOCR \
    9. Train.dataset.label_file_list=[PPOCR/train/rec.txt] \
    10. Eval.dataset.data_dir=CCPD2020/PPOCR \
    11. Eval.dataset.label_file_list=[PPOCR/test/rec.txt]

    验证精度:

    1. python tools/eval.py -c configs/rec/PP-OCRv3/ch_PP-OCRv3_rec.yml -o \
    2. Global.pretrained_model=output/CCPD/rec/best_accuracy.pdparams \
    3. Eval.dataset.data_dir=CCPD2020/PPOCR \
    4. Eval.dataset.label_file_list=[PPOCR/test/rec.txt]

    使用如下命令测试识别模型的效果(需要注意的是,识别模型的输入是车牌号图片,不是完整的图片,可以使用数据集处理时的PPOCR文件夹内生成的裁剪后的车牌图片):

    python3 tools/infer/predict_det.py --det_algorithm="DB" --det_model_dir="output/CCPD/det/infer" --image_dir="/home/aistudio/src.jpg" --use_gpu=True

    4、模型导出

    上面训练好的模型都是动态图模型,将他们导出为静态图模型来部署,可以加快速度,首先导出检测模型:

    1. python tools/export_model.py -c configs/det/ch_PP-OCRv3/ch_PP-OCRv3_det_student.yml -o \
    2. Global.pretrained_model=output/CCPD/det/best_accuracy.pdparams \
    3. Global.save_inference_dir=output/CCPD/det/infer

    测试一下导出的检测模型推理效果(注意图片路径):

    python3 tools/infer/predict_det.py --det_algorithm="DB" --det_model_dir="output/CCPD/det/infer" --image_dir="src.jpg" --use_gpu=True

    下面导出识别模型:

    1. python tools/export_model.py -c configs/rec/PP-OCRv3/ch_PP-OCRv3_rec.yml -o \
    2. Global.pretrained_model=output/CCPD/rec/best_accuracy.pdparams \
    3. Global.save_inference_dir=output/CCPD/rec/infer

    测试一下导出的识别模型推理效果(注意图片路径):

    1. python3 tools/infer/predict_rec.py --image_dir="PPOCR/test/crop_imgs" \
    2. --rec_model_dir="output/CCPD/rec/infer" --rec_image_shape="3, 48, 320" --rec_char_dict_path="ppocr/utils/ppocr_keys_v1.txt"

    5、联合推理

    训练好了检测和识别模型,下面就是联合推理,测试效果,命令如下(det_model_dir和rec_model_dir是上面导出的模型文件夹):

    1. python tools/infer/predict_system.py \
    2. --det_model_dir=output/CCPD/det/infer/ \
    3. --rec_model_dir=output/CCPD/rec/infer/ \
    4. --image_dir="src.jpg" \
    5. --rec_image_shape=3,48,320

    这是识别的结果:

     三、总结

    本文总结了PaddleOCR提供的车牌识别方案,并进行了简化,根据识别的结果来看可以很好地检测车牌图像。

    附录:

    PaddleOCR轻量级车牌识别方案

    本文提供了车牌识别方案的部署链接,您可以再网页上体验该模型的效果:

    车牌识别方案在线体验

  • 相关阅读:
    PHP 5 SimpleXML 函数
    HCIA自学笔记01-冲突域
    龙芯 Loongson 架构 UOS 系统编译 Qt 5.15.2 源码
    咖啡机、电热水壶、豆浆机上架亚马逊美国站UL1082认证标准
    C#写一个UDP程序判断延迟并运行在Centos上
    淘宝API接口,获取数据
    python的paramiko实现ssh登录
    关于python上的一个坑——reload导致模块重置
    elasticsearch 之 histogram 直方图聚合
    【第28天】给定一个字符串S,请你判断它是否为回文字符串 | 回文的判断
  • 原文地址:https://blog.csdn.net/qq_40035462/article/details/127709177