基于深度学习的车牌识别(YOLOv5和CNN)

6,326 阅读8分钟

基于深度学习的车牌识别(YOLOv5和CNN)

目录

一、综述

二、车牌检测

一、综述

本篇文章是面向的是小白,想要学习深度学习上的应用,本文中目前应用了YOLO v5和CNN来对车牌进行处理,最终形成一个完整的车牌信息记录,如果我写的有什么不对或者需要改进的地方,可以私信给我。

我们假设已经获得包含有车牌的照片,那么我们

第一步:是车牌的检测获取车牌的那部分。

第二步:就是对车牌进行拆分成各个字符。

第三步:对于一张车牌的每个字符我们使用CNN得到的模型去预测每个字符

最终得到车牌信息。

二、车牌检测

概括

车牌检测的方法有许多,最初我用的是原始的图像形态学对图像的处理(基于opencv的形态学操作),发现这样的方法不能很好的应用到所有的车牌中。那么我在学习的过程中,其实已经有许多其他方法来实现。比如目标检测,它就是一个很好的方法,主要是可以把这样的方法应用到更多更广泛的应用场景。

目标领域中主要分为如下两个——One-stage和two-stage算法

他们的区别可以查看 目标检测算法基础介绍

首先YOLO系列下的模型都是属于One-stage,在One-stage包括SSD也是很常用的一种方法。

YOLOv5学习视频查看 火爆全网的YOLOv5目标检测项目 从P41开始学习就可以了。

我们这部分有几个前提必须准备好。

1.训练需要的车牌图片数据

车牌数据 我的来自于 中科大建立的CCPD数据集,其他的不要说,总之在学习阶段说一个不错的数据,论文和数据集下载地址:github.com/detectRecog… 以下说数据说明

截屏2022-06-10 22.23.27.png

2.安装YOLOv5模型 YOLO的安装可以直接 去GitHub上下载:github.com/ultralytics… 如果是第一次使用其他人模型,第一次都会有不知所措,但一旦掌握了其中一个,其他是类似的。比如图像分割中的U-net,ocr模型中PaddlePaddleOCR都是差不多的用法。

(1) YOLO文件中需要修改某些文件

在我的目录中,我将YOLO下载到与car文件夹的同目录下

我们需要关注YOLO文件夹下的几个文件,这是我的文件目录

截屏2022-06-10 21.02.39.png

我们要自己首先一个yaml文件来配置YOLO模型,图中化横线的文件 car.yaml

以下是我的简单的配置

截屏2022-06-10 21.03.06.png path:图片数据的根目录

train:训练集的绝对路径,这里存放是所有图片的绝对路径

val: 验证集的绝对路径,与train一样

nc:分类个数

names:列表类型 所有类别名字 license plate 车牌

(2)预使用模型 YOLO已经给出以下模型 我们可以在他们这些模型的基础上进行训练自己的模型

截屏2022-06-10 21.10.29.png

我使用的是占内存比较小,准确率不错的yolov5s.yaml 详细其他模型有什么区别可以去官网去查看:github.com/ultralytics…

yolo5s.yaml 文件我们需要简单的进行修改,从而满足我们自己项目的需求

截屏2022-06-10 21.15.32.png

在我的项目中只有一个车牌需要关注,所以我们需要将nc的个数改为1,其他参数我们不需要修改。

好,YOLO文件夹下内容,暂时不需要修改了。

图片数据分析

数据的目录是如图

截屏2022-06-10 14.54.54.png

我们简单的使用 ccpd_base 文件夹,图片很多,考虑电脑实际情况,我只选取其中任意的5000张照片来作为数据源。

ccpd_base文件内容如下

截屏2022-06-10 14.59.57.png

先建立文件夹结构

截屏2022-06-10 16.12.36.png

car
	images
		放入数据集照片5000张
	labels
		等会儿说到

详细查看第一个图片名字

数据中有详细的解释文件名字的意思 截屏2022-06-10 15.03.57.png

第一张图片名字是:01-86_91-298&341_449&414-458&394_308&410_304&357_454&341-0_0_14_28_24_26_29-124-24.jpg


1) 01为区域,

2) 86_91 对应两个角度, 水平86°, 竖直91°

3) 298&341_449&414对应边界框坐标:左上(298, 341), 右下(449, 414)

4) 458&394_308&410_304&357_454&341对应四个角点坐标

5) 0_0_14_28_24_26_29-124-24为车牌号码 映射关系如下: 第一个为省份0 对应省份字典皖, 后面的为字母和文字, 查看ads字典.如0A, 22Y....

现在对于我们这个阶段我们用到四个点的坐标。

代码如下 Car_Model:

import os
import numpy as np
# 访问图片获得图片名字的字符
def load_files(path, cuted=False): # cuted=False 可以不需要写
    total_info = []
    car_numbers = []   # 车牌号码
    angels = []         # 倾斜角度
    areas = []          # 区域
    positions = []      # 车牌四个角的位置
    areas_positions = []  # 车牌左上和右下区域位置
    for i, j, names in os.walk(path):
        # 假设现在已经获得了路径
        # 首先要删除前面没有用的路径,现在获得的是一个str
        for name in names:
            if len(name) < 50:
                continue
            if cuted is True:
                    path = name.split("-", 5)
                    areas.append(path[0])
                    angels.append(path[1])
                    areas_positions.append(path[2])
                    positions.append(path[3])
                    car_numbers.append(path[4])
            else:
                total_info.append(name)

    if cuted is True:    # 其他情况下可能需要得到每段信息,可以不需要
        total_info.append(areas)
        total_info.append(angels)
        total_info.append(areas_positions)
        total_info.append(positions)
        total_info.append(car_numbers)
        total_info.append(names)
    return total_info

因为YOLO对于图片有要求,我们要对图片相关对文件进行一定的改变。

通常我们需要对图片进行标注,通常使用labelme工具来进行图片标注 以下是常用图片标注工具的对比,对于目标检测我们使用labelme就可以了。

blog.csdn.net/sinat_29957…

这个标注是把图片中的车牌框选出来,从而成为一个json文件

截屏2022-06-10 21.30.03.png

框中的数据就是一个车牌左上点(x1,y1)和右下点数据(x2,y2),我们需要取出这些数据出来。

但对于我们的中科大CCPD数据而言,这些数据已经写在文件名字中了,我们只需对名字进行处理,满足YOLO的一个要求。

文件名Car_data.py

import os
import Car_Model
import numpy as np
from PIL import Image
DATA_PATH = "/Users/pengpeng/Resources/Python/ccpd_base/car"

# 处理文件名中的4个点坐标,并且满足YOLO要求
def process_position(data, data_name):
    for name, title in zip(data[3], data_name):
        yolo_data = []
        bounding_boxes = name.split("_", 4)
        real_positions = []
        image_name = []
        title1 = title.split('.', 2)
        image_name.append(title1[0])
        # 获得4个点数据
        for bbox in bounding_boxes:
            values = bbox.split("&", 1)
            for index in range(len(values)):
                values[index] = int(int(values[index]))
            real_positions.append(values)
        real_positions = np.int0(np.array(real_positions))
        # 得到整个最大的值
        Xs = [i[0] for i in real_positions]
        Ys = [i[1] for i in real_positions]
        x1 = abs(min(Xs))
        x2 = abs(max(Xs))
        y1 = abs(min(Ys))
        y2 = abs(max(Ys))
        '''
            YOLO要求这个框满足以下要求
            (1)需要这个框的中心坐标位置,宽度(width)和高度(height)
            (2)每个值必须在0-1之间的小数,所以需要进行归一化
            (3)还需要对每个框进行标注属于哪一个分类, 按照 分类 x, y,w,h 这五个值进行写入
            (4)需要对一张照片所有的框进行新建一个txt文件,存取以上内容
        '''
        # 获得图片信息,获得图片大小分别是width和height
        img_path = DATA_PATH+"/images/"+title
        img  = Image.open(img_path)
        imgSize = img.size
        img_width = imgSize[0]
        img_height = imgSize[1]

        # 进行归一化,直接把这些内容copy就可以了
        dw = 1. / img_width # 1/w
        dh = 1. / img_height  # 1/h
        x = (x1 + x2) / 2.0 - 1  # 物体在图中的中心点x坐标
        y = (y1 + y2) / 2.0 - 1  # 物体在图中的中心点y坐标
        w = x2 - x1  # 物体实际像素宽度
        h = y2 - y1  # 物体实际像素高度
        x = x * dw  # 物体中心点x的坐标比(相当于 x/原图w)
        w = w * dw  # 物体宽度的宽度比(相当于 w/原图w)
        y = y * dh  # 物体中心点y的坐标比(相当于 y/原图h)
        h = h * dh  # 物体宽度的宽度比(相当于 h/原图h)

        # 把这五个值写入到文件中
        yolo_data.append([0, x, y, w, h])
        yolo_data = np.array(yolo_data)
        # 保存bbox的图片信息
        np.savetxt(
            os.path.join(DATA_PATH, f'labels/{title1[0]}.txt'),
            yolo_data,
            fmt=['%d', '%f', '%f', '%f', '%f']
        )

if __name__ == '__main__':
    total_infos = Car_Model.load_files(DATA_PATH, True)
    title = Car_Model.load_files(DATA_PATH, False)
    process_position(total_infos, title)

另一文件processing_character.py

目的是将图片的绝对路径写入train.txt 和test.txt中

import os
import random

# train所占的比例 70%
train_percet = 0.7

# 文件绝对路径 Mac电脑与windows电脑的地址不同,它类似与Linux的地址
path = "/Users/pengpeng/Resources/Python/ccpd_base/car"

# car文件夹下的images文件夹
images = "images"

# 新建的train.txt和test.txt
names = ["train", "test"]

# 访问images文件夹并把绝对地址写入train和test文件中
images_path = os.path.join(path, images)
images_path = os.listdir(images_path)
total_labels = []
for label in images_path:
    # 因为Mac电脑比较特殊,在每个文件中都有一个名字叫.DS_Store的隐藏文件,必须排除掉这个文件
    if label ==".DS_Store":
        continue
    total_labels.append(label)
    
# 按照之前的概率,对数据进行随机
num = len(total_labels)
digit = range(num)
trd = int(num * train_percet)
ted = int(num * (1-train_percet))
trdr = random.sample(digit, trd)  # train_digit_random 
tedr = random.sample(trdr, ted)   # test_digit_random

# 分别写入以下两个文件中
ftrain = open(path+'/train.txt', 'w')
ftest = open(path+'/test.txt', 'w')

# 根据随机进行分成train和test比例 这个是按照机器学习对train和test进行安排,当然可以按照自己对要求进行调整
for i in digit:
    name_path = path + "/" + images + "/" + total_labels[i] + "\n"
    print(name_path)
    if i in trdr:
        ftrain.write(name_path)
    else:
        ftest.write(name_path)
# 写完文件,要关闭文件
ftrain.close()
ftest.close()

按照顺序执行完以上三个,我们可以得到以下文件包含它的相应内容

截屏2022-06-10 21.50.24.png 截屏2022-06-10 21.50.03.png

截屏2022-06-10 21.50.12.png

完成到这里,我们车牌的检测就已经完成了大半了。

接着是打开我们的terminal或cmd 找到yolo的根目录,我这使用的是anconda的虚拟环境

截屏2022-06-10 21.55.27.png

在命令行上输入以下命令

python train.py --data car.yaml --cfg models/yolov5s.yaml --weights weights/yolov5s.pt --batch-size 10 --epochs 3
# --data 后面是刚刚写的配置文件 以yolo为根本目录找的文件
# --cfg 使用配置,然后对这个文件进行修改
# --weights 使用模型的权制
# --batch-size 批量大小
# --epochs 简单理解为运行次数

因为使用的是mac m1pro 不能使用pytorch的gpu ,按照现在的2022 6月份 是不能使用的,只能使用cpu。计算时间很长,计算3次的时间是2个小时吧(不记得了)

最终进行简单的炼丹,也能达到一个还行的结果 val_batch0_pred.jpg

这些结果会最终得到一个文件夹存在以下中

截屏2022-06-10 22.15.35.png

这样我们简单的得到一个自己的车牌检测模型。

接着我们要做的就是预测我们检测车牌的结果。