车牌识别的N种办法——从OCR到深度学习

bt365体育平台3 2025-07-15 17:14:53 admin 1675 621
车牌识别的N种办法——从OCR到深度学习

一、车牌识别简介

随着科学技术的发展,人工智能技术在我们的生活中的应用越来越广泛,人脸识别、车牌识别、目标识别等众多场景已经落地应用,给我们的生活品质得到很大的提升,办事效率大大提高,同时也节约了大量的劳动力。

今天我们来讲一讲车牌识别这个任务,车牌识别技术经过多年的发展,技术路线也呈现多样化,我们来介绍一下其中的两种:

第一种,单个字符识别

首先,在地面上有传感器感应区域,当有车辆经过时自动进行拍照,然后对拍的照片进行预处理,变成灰度图像,去除噪声等,把一些干扰因素去掉,同时降低图像的大小,便于后期进行边缘提取;

其次,利用边缘提取技术,比如canny算子、sobel算子,把图像轮廓提取出来,根据车牌长宽比大概为1:2.5左右把大部分不疑似的去掉;然后,再利用图像灰度值在水平方向和竖直方向上的投影,将字符切割出来;

最后,利用模板匹配或者是已经训练好的深度学习模型进行识别,单独把每个字符识别后进行串联得到整张车牌号码。

这种方法的瓶颈在于怎么样把每一个字符准确地切割出来,这涉及到很多图像处理技术,比如高斯模糊、图像锐化、图像膨胀、伽马变化、仿射变换等等,目前对于英文字符和数字的识别准确度在96%左右,汉字识别的准确度在95%左右。

第二种办法,整张车牌识别

首先,与第一种车牌识别一样,利用传感器进行车辆感应,把整辆车进行拍照;

其次,利用训练好的车牌检测算法,比如YOLO算法或者其他的目标检测算法把车牌检测出来。为了提升车牌识别的准确度,可以利用图像预处理技术对图像进行处理,降低干扰项;

最后,利用已经训练好的深度学习模型对整张车牌进行识别。比如LPRNet、Darknet等

二、车牌识别技术实现

## 第一种方法 ——利用 pytesseract 进行识别

pytesseract是一个OCR识别工具,具体的 pytesseract 安装可百度安装,直接使用这个工具进行识别得到的准确度还有点低,达不到理想的效果,需要利用车牌数据进行训练才可以使用。

import cv2 as cv

from PIL import Image

import pytesseract as tess

def recoginse_text(image):

"""

步骤:

1、灰度,二值化处理

2、形态学操作去噪

3、识别

:param image:

:return:

"""

# 灰度 二值化

gray = cv.cvtColor(image,cv.COLOR_BGR2GRAY)

# 如果是白底黑字 建议 _INV

ret,binary = cv.threshold(gray,0,255,cv.THRESH_BINARY_INV| cv.THRESH_OTSU)

# 形态学操作 (根据需要设置参数(1,2))

kernel = cv.getStructuringElement(cv.MORPH_RECT,(1,2)) #去除横向细线

morph1 = cv.morphologyEx(binary,cv.MORPH_OPEN,kernel)

kernel = cv.getStructuringElement(cv.MORPH_RECT, (2, 1)) #去除纵向细线

morph2 = cv.morphologyEx(morph1,cv.MORPH_OPEN,kernel)

cv.imshow("Morph",morph2)

# 黑底白字取非,变为白底黑字(便于pytesseract 识别)

cv.bitwise_not(morph2,morph2)

textImage = Image.fromarray(morph2)

# 图片转文字

text=tess.image_to_string(textImage)

print("识别结果:%s"%text)

def main():

# 读取需要识别的数字字母图片,并显示读到的原图

src = cv.imread(r'C:\Users\lenovo\Desktop\00A8CX87_5.jpg')

cv.imshow("src",src)

# 识别

recoginse_text(src)

cv.waitKey(0)

cv.destroyAllWindows()

if __name__=="__main__":

main()

## 第二种识别办法——PaddleOCR

PaddleOCR 是百度paddlepaddle下的OCR模块,可以用它来识别车牌,直接下载预训练模型进行识别,对于数字和子母都得到不错的准确率,但是针对中文识别的效果并不理想,需要自己的数据集进行再训练。

要想利用这个模型进行训练车牌识别,需要大量的车牌数据,人工收集不仅耗费时间长,要涉及每个省份的车牌需要比较大的成本。

另外的办法是利用图像处理技术进行数据生成,加上图像增强技术来制作数据集,这将在我们的第三章识别方法中讲到。

from paddleocr import PaddleOCR

import os

# ocr = PaddleOCR(use_gpu=False, use_angle_cls=True, lang="ch")

ocr = PaddleOCR(use_gpu=True, use_angle_cls=True, lang="ch")

path=r'E:\车牌数据'

results=[]

for file in os.listdir(path):

img_path =os.path.join(path,file)

result = ocr.ocr(img_path, cls=True)

if len(result)>0:

results.append([img_path,result[0][1][0]])

else:

results.append([img_path])

将结果导出到Excel文件夹

import pandas as pd

resultss=pd.DataFrame(results)

resultss.to_excel(r'C:\Users\lenovo\Desktop\11.xlsx')

## 第三种办法 ——LPRNet算法

由于要收集满足深度学习的数据需要花费大量的时间,有一种办法是通过数据生成的办法生成大量的模拟数据集。

紧接着,我们可以利用LPRNet算法进行车牌识别,从GitHub上下载有tensorflow、torch两个版本。

第一步,生成蓝色车牌数据集

第二步,根据算法模型要求将图像名称修改并放到指定的文件夹中

第三步,模型训练和测试

## (1) 蓝色车牌生成

import os

import cv2 as cv

import numpy as np

from math import *

from PIL import ImageFont

from PIL import Image

from PIL import ImageDraw

index = {"京": 0, "沪": 1, "津": 2, "渝": 3, "冀": 4, "晋": 5, "蒙": 6, "辽": 7, "吉": 8, "黑": 9,

"苏": 10, "浙": 11, "皖": 12, "闽": 13, "赣": 14, "鲁": 15, "豫": 16, "鄂": 17, "湘": 18, "粤": 19,

"桂": 20, "琼": 21, "川": 22, "贵": 23, "云": 24, "藏": 25, "陕": 26, "甘": 27, "青": 28, "宁": 29,

"新": 30, "0": 31, "1": 32, "2": 33, "3": 34, "4": 35, "5": 36, "6": 37, "7": 38, "8": 39,

"9": 40, "A": 41, "B": 42, "C": 43, "D": 44, "E": 45, "F": 46, "G": 47, "H": 48, "J": 49,

"K": 50, "L": 51, "M": 52, "N": 53, "P": 54, "Q": 55, "R": 56, "S": 57, "T": 58, "U": 59,

"V": 60, "W": 61, "X": 62, "Y": 63, "Z": 64}

chars = ["京", "沪", "津", "渝", "冀", "晋", "蒙", "辽", "吉", "黑",

"苏", "浙", "皖", "闽", "赣", "鲁", "豫", "鄂", "湘", "粤",

"桂", "琼", "川", "贵", "云", "藏", "陕", "甘", "青", "宁",

"新", "0", "1", "2", "3", "4", "5", "6", "7", "8",

"9", "A", "B", "C", "D", "E", "F", "G", "H", "J",

"K", "L", "M", "N", "P", "Q", "R", "S", "T", "U",

"V", "W", "X", "Y", "Z"]

def AddSmudginess(img, Smu):

"""

模糊处理

:param img: 输入图像

:param Smu: 模糊图像

:return: 添加模糊后的图像

"""

rows = r(Smu.shape[0] - 50)

cols = r(Smu.shape[1] - 50)

adder = Smu[rows:rows + 50, cols:cols + 50]

adder = cv.resize(adder, (50, 50))

img = cv.resize(img,(50,50))

img = cv.bitwise_not(img)

img = cv.bitwise_and(adder, img)

img = cv.bitwise_not(img)

return img

def rot(img, angel, shape, max_angel):

"""

添加透视畸变

"""

size_o = [shape[1], shape[0]]

size = (shape[1]+ int(shape[0] * cos((float(max_angel ) / 180) * 3.14)), shape[0])

interval = abs(int(sin((float(angel) / 180) * 3.14) * shape[0]))

pts1 = np.float32([[0, 0], [0, size_o[1]], [size_o[0], 0], [size_o[0], size_o[1]]])

if angel > 0:

pts2 = np.float32([[interval, 0], [0, size[1]], [size[0], 0], [size[0] - interval, size_o[1]]])

else:

pts2 = np.float32([[0, 0], [interval, size[1]], [size[0] - interval, 0], [size[0], size_o[1]]])

M = cv.getPerspectiveTransform(pts1, pts2)

dst = cv.warpPerspective(img, M, size)

return dst

def rotRandrom(img, factor, size):

"""

添加放射畸变

:param img: 输入图像

:param factor: 畸变的参数

:param size: 图片目标尺寸

:return: 放射畸变后的图像

"""

shape = size

pts1 = np.float32([[0, 0], [0, shape[0]], [shape[1], 0], [shape[1], shape[0]]])

pts2 = np.float32([[r(factor), r(factor)], [r(factor), shape[0] - r(factor)], [shape[1] - r(factor), r(factor)],

[shape[1] - r(factor), shape[0] - r(factor)]])

M = cv.getPerspectiveTransform(pts1, pts2)

dst = cv.warpPerspective(img, M, size)

return dst

def tfactor(img):

"""

添加饱和度光照的噪声

"""

hsv = cv.cvtColor(img,cv.COLOR_BGR2HSV)

hsv[:, :, 0] = hsv[:, :, 0] * (0.8 + np.random.random() * 0.2)

hsv[:, :, 1] = hsv[:, :, 1] * (0.3 + np.random.random() * 0.7)

hsv[:, :, 2] = hsv[:, :, 2] * (0.2 + np.random.random() * 0.8)

img = cv.cvtColor(hsv, cv.COLOR_HSV2BGR)

return img

def random_envirment(img, noplate_bg):

"""

添加自然环境的噪声, noplate_bg为不含车牌的背景图

"""

bg_index = r(len(noplate_bg))

env = cv.imread(noplate_bg[bg_index])

env = cv.resize(env, (img.shape[1], img.shape[0]))

bak = (img == 0)

bak = bak.astype(np.uint8) * 255

inv = cv.bitwise_and(bak, env)

img = cv.bitwise_or(inv, img)

return img

def GenCh(f, val):

"""

生成中文字符

"""

img = Image.new("RGB", (45, 70), (255, 255, 255)) #白色

# img = Image.new("RGB", (45, 70), (0, 0, 0)) #黑色

draw = ImageDraw.Draw(img)

# draw.text((0, 3), val, (0, 0, 0), font=f)

draw.text((0, 3), val, (0, 0, 0), font=f)

img = img.resize((23, 70))

A = np.array(img)

return A

def GenCh1(f, val):

"""

生成英文字符

"""

img =Image.new("RGB", (23, 70), (255,255,255))#白色

# img =Image.new("RGB", (23, 70), (0, 0, 0))#黑色

draw = ImageDraw.Draw(img)

draw.text((0, 2), val, (0,125,125), font=f) # val.decode('utf-8')

A = np.array(img)

return A

def AddGauss(img, level):

"""

添加高斯模糊

"""

return cv.blur(img, (level * 2 + 1, level * 2 + 1))

def r(val):

return int(np.random.random() * val)

def AddNoiseSingleChannel(single):

"""

添加高斯噪声

"""

diff = 255 - single.max()

noise = np.random.normal(0, 1 + r(6), single.shape)

noise = (noise - noise.min()) / (noise.max() - noise.min())

noise *= diff

# noise= noise.astype(np.uint8)

dst = single + noise

return dst

def addNoise(img): # sdev = 0.5,avg=10

img[:, :, 0] = AddNoiseSingleChannel(img[:, :, 0])

img[:, :, 1] = AddNoiseSingleChannel(img[:, :, 1])

img[:, :, 2] = AddNoiseSingleChannel(img[:, :, 2])

return img

class GenPlate:

def __init__(self, fontCh, fontEng, NoPlates):

self.fontC = ImageFont.truetype(fontCh, 43, 0)

self.fontE = ImageFont.truetype(fontEng, 60, 0)

self.img = np.array(Image.new("RGB", (226, 70),(255, 255, 255)))

# self.img = np.array(Image.new("RGB", (226, 70),(0, 0, 0)))

self.bg = cv.resize(cv.imread(r"C:\Users\lenovo\Desktop\Plate_Recognition-LPRnet-master\Plate_Recognition-LPRnet-master\generate_picture\images\blue.bmp"), (226, 70))

# template.bmp:车牌背景图

self.smu = cv.imread(r"E:\AI_projects\vehicle license plate recognition\car_recognition\data\images\\smu2.jpg")

# smu2.jpg:模糊图像

self.noplates_path = []

for parent, parent_folder, filenames in os.walk(NoPlates):

for filename in filenames:

path = parent + "\\" + filename

self.noplates_path.append(path)

def draw(self, val):

offset = 2

self.img[0:70, offset+8:offset+8+23] = GenCh(self.fontC, val[0])

self.img[0:70, offset+8+23+6:offset+8+23+6+23] = GenCh1(self.fontE, val[1])

for i in range(5):

base = offset + 8 + 23 + 6 + 23 + 17 + i * 23 + i * 6

self.img[0:70, base:base+23] = GenCh1(self.fontE, val[i+2])

return self.img

def generate(self, text):

if len(text) == 7:

fg = self.draw(text) # decode(encoding="utf-8")

fg = cv.bitwise_not(fg)

com = cv.bitwise_or(fg, self.bg)

com = rot(com, r(30)-10, com.shape,30)

com = rotRandrom(com, 5, (com.shape[1], com.shape[0]))

com = tfactor(com)

com = random_envirment(com, self.noplates_path)

com = AddGauss(com, 1+r(4))

com = addNoise(com)

return com

@staticmethod

def genPlateString(pos, val):

"""

生成车牌string,存为图片

生成车牌list,存为label

"""

plateStr = ""

plateList=[]

box = [0, 0, 0, 0, 0, 0, 0]

if pos != -1:

box[pos] = 1

for unit, cpos in zip(box, range(len(box))):

if unit == 1:

plateStr += val

plateList.append(val)

else:

if cpos == 0:

plateStr += chars[r(31)]

plateList.append(plateStr)

elif cpos == 1:

plateStr += chars[41 + r(24)]

plateList.append(plateStr)

else:

plateStr += chars[31 + r(34)]

plateList.append(plateStr)

plate = [plateList[0]]

b = [plateList[i][-1] for i in range(len(plateList))]

plate.extend(b[1:7])

return plateStr, plate

@staticmethod

def genBatch(batchsize, outputPath, size):

"""

将生成的车牌图片写入文件夹,对应的label写入label.txt

:param batchsize: 批次大小

:param outputPath: 输出图像的保存路径

:param size: 输出图像的尺寸

:return: None

"""

if not os.path.exists(outputPath):

os.mkdir(outputPath)

outfile = open(r'C:\Users\lenovo\Desktop\Plate_Recognition-LPRnet-master\Plate_Recognition-LPRnet-master\label_blue.txt',

'w', encoding='utf-8')

for i in range(batchsize):

plateStr, plate = G.genPlateString(-1, -1)

img = G.generate(plateStr)

img = cv.resize(img, size)

content = "".join(plate)

cv.imwrite(outputPath + "\\" + str(i).zfill(2) + ".jpg", img)

outfile.write("./train/"+str(i).zfill(2)+".jpg"+'\t'+content +"\n")

G = GenPlate(r"E:\AI_projects\vehicle license plate recognition\car_recognition\data\font\platech.ttf",

r'E:\AI_projects\vehicle license plate recognition\car_recognition\data\font\platechar.ttf',

r"E:\AI_projects\vehicle license plate recognition\car_recognition\data\NoPlates")

G.genBatch(1000, r'C:\Users\lenovo\Desktop\Plate_Recognition-LPRnet-master\Plate_Recognition-LPRnet-master\test_in_blue', (272, 72))

需要生成多少张车牌数据可通过修改最后一行代码的数字即可

## (2) 生产黄色车牌

生成车牌的步骤是把字符写到纯净的背景图片上,蓝色的车牌是把白色的字写到蓝色底的车牌上,可以直接把字符写到底牌上去,但是黄色底的车牌不能简单把黑色字写上去,写上去的结果是字符被隐藏起来,需要特别处理。

正确的做法是:

第一步,构造一张车牌大小的纯黑色车牌,往上面写白色字符

第二步,将黑白色进行反转,把这牌的字符和背景进行翻转

第三步,将一张没有字符的黄色底牌和反转后的车牌进行结合,然后经过高斯模糊、改变光线、旋转角度等各种随机组合模拟出于生活中拍摄处理的图像。

#黄色车牌生成

import os

import cv2 as cv

import numpy as np

from math import *

from PIL import ImageFont

from PIL import Image

from PIL import ImageDraw

index = {"京": 0, "沪": 1, "津": 2, "渝": 3, "冀": 4, "晋": 5, "蒙": 6, "辽": 7, "吉": 8, "黑": 9,

"苏": 10, "浙": 11, "皖": 12, "闽": 13, "赣": 14, "鲁": 15, "豫": 16, "鄂": 17, "湘": 18, "粤": 19,

"桂": 20, "琼": 21, "川": 22, "贵": 23, "云": 24, "藏": 25, "陕": 26, "甘": 27, "青": 28, "宁": 29,

"新": 30, "0": 31, "1": 32, "2": 33, "3": 34, "4": 35, "5": 36, "6": 37, "7": 38, "8": 39,

"9": 40, "A": 41, "B": 42, "C": 43, "D": 44, "E": 45, "F": 46, "G": 47, "H": 48, "J": 49,

"K": 50, "L": 51, "M": 52, "N": 53, "P": 54, "Q": 55, "R": 56, "S": 57, "T": 58, "U": 59,

"V": 60, "W": 61, "X": 62, "Y": 63, "Z": 64}

chars = ["京", "沪", "津", "渝", "冀", "晋", "蒙", "辽", "吉", "黑",

"苏", "浙", "皖", "闽", "赣", "鲁", "豫", "鄂", "湘", "粤",

"桂", "琼", "川", "贵", "云", "藏", "陕", "甘", "青", "宁",

"新", "0", "1", "2", "3", "4", "5", "6", "7", "8",

"9", "A", "B", "C", "D", "E", "F", "G", "H", "J",

"K", "L", "M", "N", "P", "Q", "R", "S", "T", "U",

"V", "W", "X", "Y", "Z"]

def AddSmudginess(img, Smu):

"""

模糊处理

:param img: 输入图像

:param Smu: 模糊图像

:return: 添加模糊后的图像

"""

rows = r(Smu.shape[0] - 50)

cols = r(Smu.shape[1] - 50)

adder = Smu[rows:rows + 50, cols:cols + 50]

adder = cv.resize(adder, (50, 50))

img = cv.resize(img,(50,50))

img = cv.bitwise_not(img)

img = cv.bitwise_and(adder, img)

img = cv.bitwise_not(img)

return img

def rot(img, angel, shape, max_angel):

"""

添加透视畸变

"""

size_o = [shape[1], shape[0]]

size = (shape[1]+ int(shape[0] * cos((float(max_angel ) / 180) * 3.14)), shape[0])

interval = abs(int(sin((float(angel) / 180) * 3.14) * shape[0]))

pts1 = np.float32([[0, 0], [0, size_o[1]], [size_o[0], 0], [size_o[0], size_o[1]]])

if angel > 0:

pts2 = np.float32([[interval, 0], [0, size[1]], [size[0], 0], [size[0] - interval, size_o[1]]])

else:

pts2 = np.float32([[0, 0], [interval, size[1]], [size[0] - interval, 0], [size[0], size_o[1]]])

M = cv.getPerspectiveTransform(pts1, pts2)

dst = cv.warpPerspective(img, M, size)

return dst

def rotRandrom(img, factor, size):

"""

添加放射畸变

:param img: 输入图像

:param factor: 畸变的参数

:param size: 图片目标尺寸

:return: 放射畸变后的图像

"""

shape = size

pts1 = np.float32([[0, 0], [0, shape[0]], [shape[1], 0], [shape[1], shape[0]]])

pts2 = np.float32([[r(factor), r(factor)], [r(factor), shape[0] - r(factor)], [shape[1] - r(factor), r(factor)],

[shape[1] - r(factor), shape[0] - r(factor)]])

M = cv.getPerspectiveTransform(pts1, pts2)

dst = cv.warpPerspective(img, M, size)

return dst

def tfactor(img):

"""

添加饱和度光照的噪声

"""

hsv = cv.cvtColor(img,cv.COLOR_BGR2HSV)

hsv[:, :, 0] = hsv[:, :, 0] * (0.8 + np.random.random() * 0.2)

hsv[:, :, 1] = hsv[:, :, 1] * (0.3 + np.random.random() * 0.7)

hsv[:, :, 2] = hsv[:, :, 2] * (0.2 + np.random.random() * 0.8)

img = cv.cvtColor(hsv, cv.COLOR_HSV2BGR)

return img

def random_envirment(img, noplate_bg):

"""

添加自然环境的噪声, noplate_bg为不含车牌的背景图

"""

bg_index = r(len(noplate_bg))

env = cv.imread(noplate_bg[bg_index])

env = cv.resize(env, (img.shape[1], img.shape[0]))

bak = (img == 0)

bak = bak.astype(np.uint8) * 255

inv = cv.bitwise_and(bak, env)

img = cv.bitwise_or(inv, img)

return img

def GenCh(f, val):

"""

生成中文字符

"""

img = Image.new("RGB", (45, 70), (255, 255, 255)) #白色

# img = Image.new("RGB", (45, 70), (0, 0, 0)) #黑色

draw = ImageDraw.Draw(img)

# draw.text((0, 3), val, (0, 0, 0), font=f)

draw.text((0, 3), val, (0, 0, 0), font=f)

img = img.resize((23, 70))

A = np.array(img)

return A

def GenCh1(f, val):

"""

生成英文字符

"""

img =Image.new("RGB", (23, 70), (255,255,255))#白色

# img =Image.new("RGB", (23, 70), (0, 0, 0))#黑色

draw = ImageDraw.Draw(img)

draw.text((0, 2), val, (0,0,0), font=f) # val.decode('utf-8')

A = np.array(img)

return A

def AddGauss(img, level):

"""

添加高斯模糊

"""

return cv.blur(img, (level * 2 + 1, level * 2 + 1))

def r(val):

return int(np.random.random() * val)

def AddNoiseSingleChannel(single):

"""

添加高斯噪声

"""

diff = 255 - single.max()

noise = np.random.normal(0, 1 + r(6), single.shape)

noise = (noise - noise.min()) / (noise.max() - noise.min())

noise *= diff

# noise= noise.astype(np.uint8)

dst = single + noise

return dst

def addNoise(img): # sdev = 0.5,avg=10

img[:, :, 0] = AddNoiseSingleChannel(img[:, :, 0])

img[:, :, 1] = AddNoiseSingleChannel(img[:, :, 1])

img[:, :, 2] = AddNoiseSingleChannel(img[:, :, 2])

return img

class GenPlate:

def __init__(self, fontCh, fontEng, NoPlates):

self.fontC = ImageFont.truetype(fontCh, 43, 0)

self.fontE = ImageFont.truetype(fontEng, 60, 0)

self.img = np.array(Image.new("RGB", (226, 70),(255, 255, 255)))

# self.img = np.array(Image.new("RGB", (226, 70),(0, 0, 0)))

self.bg = cv.resize(cv.imread(r"C:\Users\lenovo\Desktop\Plate_Recognition-LPRnet-master\Plate_Recognition-LPRnet-master\generate_picture\images\black.jpg"), (226, 70))

# template.bmp:车牌背景图

self.smu = cv.imread(r"E:\AI_projects\vehicle license plate recognition\car_recognition\data\images\\smu2.jpg")

# smu2.jpg:模糊图像

self.noplates_path = []

for parent, parent_folder, filenames in os.walk(NoPlates):

for filename in filenames:

path = parent + "\\" + filename

self.noplates_path.append(path)

def draw(self, val):

offset = 2

self.img[0:70, offset+8:offset+8+23] = GenCh(self.fontC, val[0])

self.img[0:70, offset+8+23+6:offset+8+23+6+23] = GenCh1(self.fontE, val[1])

for i in range(5):

base = offset + 8 + 23 + 6 + 23 + 17 + i * 23 + i * 6

self.img[0:70, base:base+23] = GenCh1(self.fontE, val[i+2])

return self.img

def generate(self, text):

if len(text) == 7:

fg = self.draw(text) # decode(encoding="utf-8")

fg = cv.bitwise_not(fg)

com = cv.bitwise_or(fg, self.bg)

# com = rot(com, r(60)-30, com.shape,30)

# com = rotRandrom(com, 10, (com.shape[1], com.shape[0]))

# com = tfactor(com)

# com = random_envirment(com, self.noplates_path)

# com = AddGauss(com, 1+r(4))

# com = addNoise(com)

return com

@staticmethod

def genPlateString(pos, val):

"""

生成车牌string,存为图片

生成车牌list,存为label

"""

plateStr = ""

plateList=[]

box = [0, 0, 0, 0, 0, 0, 0]

if pos != -1:

box[pos] = 1

for unit, cpos in zip(box, range(len(box))):

if unit == 1:

plateStr += val

plateList.append(val)

else:

if cpos == 0:

plateStr += chars[r(31)]

plateList.append(plateStr)

elif cpos == 1:

plateStr += chars[41 + r(24)]

plateList.append(plateStr)

else:

plateStr += chars[31 + r(34)]

plateList.append(plateStr)

plate = [plateList[0]]

b = [plateList[i][-1] for i in range(len(plateList))]

plate.extend(b[1:7])

return plateStr, plate

@staticmethod

def genBatch(batchsize, outputPath, size):

"""

将生成的车牌图片写入文件夹,对应的label写入label.txt

:param batchsize: 批次大小

:param outputPath: 输出图像的保存路径

:param size: 输出图像的尺寸

:return: None

"""

if not os.path.exists(outputPath):

os.mkdir(outputPath)

outfile = open(r'C:\Users\lenovo\Desktop\Plate_Recognition-LPRnet-master\Plate_Recognition-LPRnet-master\label_yellow.txt',

'w', encoding='utf-8')

for i in range(batchsize):

plateStr, plate = G.genPlateString(-1, -1)

img = G.generate(plateStr)

img = cv.resize(img, size)

content = "".join(plate)

cv.imwrite(outputPath + "\\" + str(i).zfill(2) + ".jpg", img)

outfile.write("./train/"+str(i).zfill(2)+".jpg"+'\t'+content +"\n")

G = GenPlate(r"E:\AI_projects\vehicle license plate recognition\car_recognition\data\font\platech.ttf",

r'E:\AI_projects\vehicle license plate recognition\car_recognition\data\font\platechar.ttf',

r"E:\AI_projects\vehicle license plate recognition\car_recognition\data\NoPlates")

G.genBatch(1000, r'C:\Users\lenovo\Desktop\Plate_Recognition-LPRnet-master\Plate_Recognition-LPRnet-master\test_in_yellow', (272, 72))

字符和背景反转

#将车牌黑色反转

import cv2

import random

def addNoise(img): # sdev = 0.5,avg=10

img[:, :, 0] = AddNoiseSingleChannel(img[:, :, 0])

img[:, :, 1] = AddNoiseSingleChannel(img[:, :, 1])

img[:, :, 2] = AddNoiseSingleChannel(img[:, :, 2])

return img

sample_cp=cv.resize(cv2.imread(r"C:\Users\lenovo\Desktop\Plate_Recognition-LPRnet-master\Plate_Recognition-LPRnet-master\generate_picture\images\yellow.bmp"), (272, 72))

path=r'C:\Users\lenovo\Desktop\Plate_Recognition-LPRnet-master\Plate_Recognition-LPRnet-master\test_in_yellow'

for file in os.listdir(path):

if file.endswith('.jpg'):

image=os.path.join(path,file)

img=cv2.imread(image)

for i in range(72):

for j in range(272):

if img[i][j][0]>50:

img[i][j][0]=0 #黑色

img[i][j][1]=0

img[i][j][2]=0

else:

img[i][j][0]=255 #白色

img[i][j][1]=255

img[i][j][2]=255

for i in range(72):

for j in range(272):

if img[i][j][0]<50 and img[i][j][1]<50 and img[i][j][2]<50:

img[i][j][0]=img[i][j][0]

img[i][j][1]=img[i][j][1]

img[i][j][2]=img[i][j][2]

else:

img[i][j][0]=sample_cp[i][j][0] #+random.randint(0,9)

img[i][j][1]=sample_cp[i][j][1] #+random.randint(0,5)

img[i][j][2]=sample_cp[i][j][2] #+random.randint(0,10)

addNoise(img)

images=os.path.join(path,file)

cv2.imwrite(images,img)

字符和底牌相结合,并进行畸变、模糊、旋转等各种方法,构造出多种多样的车牌图像。

import cv2

import numpy as np

from math import cos,sin

import os

def rot(img, angel, shape, max_angel):

"""

添加透视畸变

"""

size_o = [shape[1], shape[0]]

size = (shape[1]+ int(shape[0] * cos((float(max_angel ) / 180) * 3.14)), shape[0])

interval = abs(int(sin((float(angel) / 180) * 3.14) * shape[0]))

pts1 = np.float32([[0, 0], [0, size_o[1]], [size_o[0], 0], [size_o[0], size_o[1]]])

if angel > 0:

pts2 = np.float32([[interval, 0], [0, size[1]], [size[0], 0], [size[0] - interval, size_o[1]]])

else:

pts2 = np.float32([[0, 0], [interval, size[1]], [size[0] - interval, 0], [size[0], size_o[1]]])

M = cv2.getPerspectiveTransform(pts1, pts2)

dst = cv2.warpPerspective(img, M, size)

return dst

def rotRandrom(img, factor, size):

"""

添加放射畸变

:param img: 输入图像

:param factor: 畸变的参数

:param size: 图片目标尺寸

:return: 放射畸变后的图像

"""

shape = size

pts1 = np.float32([[0, 0], [0, shape[0]], [shape[1], 0], [shape[1], shape[0]]])

pts2 = np.float32([[r(factor), r(factor)], [r(factor), shape[0] - r(factor)], [shape[1] - r(factor), r(factor)],

[shape[1] - r(factor), shape[0] - r(factor)]])

M = cv2.getPerspectiveTransform(pts1, pts2)

dst = cv2.warpPerspective(img, M, size)

return dst

def r(val):

return int(np.random.random() * val)

def tfactor(img):

"""

添加饱和度光照的噪声

"""

hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)

hsv[:, :, 0] = hsv[:, :, 0] * (0.8 + np.random.random() * 0.2)

hsv[:, :, 1] = hsv[:, :, 1] * (0.3 + np.random.random() * 0.7)

hsv[:, :, 2] = hsv[:, :, 2] * (0.2 + np.random.random() * 0.8)

img = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)

return img

def random_envirment(img, noplate_bg):

"""

添加自然环境的噪声, noplate_bg为不含车牌的背景图

"""

bg_index = r(len(noplate_bg))

env = cv2.imread(noplate_bg[bg_index])

env = cv2.resize(env, (img.shape[1], img.shape[0]))

bak = (img == 0)

bak = bak.astype(np.uint8) * 255

inv = cv2.bitwise_and(bak, env)

img = cv2.bitwise_or(inv, img)

return img

def AddGauss(img, level):

return cv2.blur(img, (level * 2 + 1, level * 2 + 1))

def AddNoiseSingleChannel(single):

"""

添加高斯噪声

"""

diff = 255 - single.max()

noise = np.random.normal(0, 1 + r(6), single.shape)

noise = (noise - noise.min()) / (noise.max() - noise.min())

noise *= diff

# noise= noise.astype(np.uint8)

dst = single + noise

return dst

def addNoise(img): # sdev = 0.5,avg=10

img[:, :, 0] = AddNoiseSingleChannel(img[:, :, 0])

img[:, :, 1] = AddNoiseSingleChannel(img[:, :, 1])

img[:, :, 2] = AddNoiseSingleChannel(img[:, :, 2])

return img

path0=r"C:\Users\lenovo\Desktop\Plate_Recognition-LPRnet-master\Plate_Recognition-LPRnet-master\generate_picture\NoPlates"

noplates_path=[]

for file in os.listdir(path0):

noplates_path.append(os.path.join(path0,file))

# bg=cv2.resize(cv2.imread(r"C:\Users\lenovo\Desktop\Plate_Recognition-LPRnet-master\Plate_Recognition-LPRnet-master\generate_picture\images\new.jpg"), (272, 72))

# fg=cv2.imread(r'C:\Users\lenovo\Desktop\Plate_Recognition-LPRnet-master\Plate_Recognition-LPRnet-master\picture_new\00.jpg')

# com=fg

# com = rot(com, r(30)-10, com.shape,30)

# com = rotRandrom(com, 5, (com.shape[1], com.shape[0]))

# com = tfactor(com)

# # com = random_envirment(com,noplates_path)

# # com = AddGauss(com, 1+r(4))

# com = addNoise(com)

# cv2.imshow('com',com)

# cv2.waitKey(0)

# cv2.destroyAllWindows()

# fg = cv2.bitwise_not(fg)

# com = cv2.bitwise_or(fg, bg)

path=r"C:\Users\lenovo\Desktop\Plate_Recognition-LPRnet-master\Plate_Recognition-LPRnet-master\test_in_yellow"

path1=r"C:\Users\lenovo\Desktop\Plate_Recognition-LPRnet-master\Plate_Recognition-LPRnet-master\test_in_yellow"

for file in os.listdir(path):

if file.endswith('.jpg'):

fg=cv2.imread(os.path.join(path,file))

com=fg

com = rot(com, r(30)-10, com.shape,30)

com = rotRandrom(com, 5, (com.shape[1], com.shape[0]))

com = tfactor(com)

com = random_envirment(com,noplates_path)

com = AddGauss(com, 1+r(4))

com = addNoise(com)

images=os.path.join(path1,file)

cv2.imwrite(images,com)

## (3) 生产新能源车牌

新能源车牌的生成办法与黄色车牌的生成办法是一样的,但是新能源车牌长度比蓝牌、黄牌多一位,而且指定第三个字符为D或者F,但是我们我们在生成时不考虑,只是修改代码让程序生成同样长度的车牌即可。

#黄色车牌生成

import os

import cv2 as cv

import numpy as np

from math import *

from PIL import ImageFont

from PIL import Image

from PIL import ImageDraw

index = {"京": 0, "沪": 1, "津": 2, "渝": 3, "冀": 4, "晋": 5, "蒙": 6, "辽": 7, "吉": 8, "黑": 9,

"苏": 10, "浙": 11, "皖": 12, "闽": 13, "赣": 14, "鲁": 15, "豫": 16, "鄂": 17, "湘": 18, "粤": 19,

"桂": 20, "琼": 21, "川": 22, "贵": 23, "云": 24, "藏": 25, "陕": 26, "甘": 27, "青": 28, "宁": 29,

"新": 30, "0": 31, "1": 32, "2": 33, "3": 34, "4": 35, "5": 36, "6": 37, "7": 38, "8": 39,

"9": 40, "A": 41, "B": 42, "C": 43, "D": 44, "E": 45, "F": 46, "G": 47, "H": 48, "J": 49,

"K": 50, "L": 51, "M": 52, "N": 53, "P": 54, "Q": 55, "R": 56, "S": 57, "T": 58, "U": 59,

"V": 60, "W": 61, "X": 62, "Y": 63, "Z": 64}

chars = ["京", "沪", "津", "渝", "冀", "晋", "蒙", "辽", "吉", "黑",

"苏", "浙", "皖", "闽", "赣", "鲁", "豫", "鄂", "湘", "粤",

"桂", "琼", "川", "贵", "云", "藏", "陕", "甘", "青", "宁",

"新", "0", "1", "2", "3", "4", "5", "6", "7", "8",

"9", "A", "B", "C", "D", "E", "F", "G", "H", "J",

"K", "L", "M", "N", "P", "Q", "R", "S", "T", "U",

"V", "W", "X", "Y", "Z"]

def AddSmudginess(img, Smu):

"""

模糊处理

:param img: 输入图像

:param Smu: 模糊图像

:return: 添加模糊后的图像

"""

rows = r(Smu.shape[0] - 20) #50

cols = r(Smu.shape[1] - 20)

adder = Smu[rows:rows + 20, cols:cols + 20]

adder = cv.resize(adder, (20, 20))

img = cv.resize(img,(20,20))

img = cv.bitwise_not(img)

img = cv.bitwise_and(adder, img)

img = cv.bitwise_not(img)

return img

def rot(img, angel, shape, max_angel):

"""

添加透视畸变

"""

size_o = [shape[1], shape[0]]

size = (shape[1]+ int(shape[0] * cos((float(max_angel ) / 180) * 3.14)), shape[0])

interval = abs(int(sin((float(angel) / 180) * 3.14) * shape[0]))

pts1 = np.float32([[0, 0], [0, size_o[1]], [size_o[0], 0], [size_o[0], size_o[1]]])

if angel > 0:

pts2 = np.float32([[interval, 0], [0, size[1]], [size[0], 0], [size[0] - interval, size_o[1]]])

else:

pts2 = np.float32([[0, 0], [interval, size[1]], [size[0] - interval, 0], [size[0], size_o[1]]])

M = cv.getPerspectiveTransform(pts1, pts2)

dst = cv.warpPerspective(img, M, size)

return dst

def rotRandrom(img, factor, size):

"""

添加放射畸变

:param img: 输入图像

:param factor: 畸变的参数

:param size: 图片目标尺寸

:return: 放射畸变后的图像

"""

shape = size

pts1 = np.float32([[0, 0], [0, shape[0]], [shape[1], 0], [shape[1], shape[0]]])

pts2 = np.float32([[r(factor), r(factor)], [r(factor), shape[0] - r(factor)], [shape[1] - r(factor), r(factor)],

[shape[1] - r(factor), shape[0] - r(factor)]])

M = cv.getPerspectiveTransform(pts1, pts2)

dst = cv.warpPerspective(img, M, size)

return dst

def tfactor(img):

"""

添加饱和度光照的噪声

"""

hsv = cv.cvtColor(img,cv.COLOR_BGR2HSV)

hsv[:, :, 0] = hsv[:, :, 0] * (0.8 + np.random.random() * 0.2)

hsv[:, :, 1] = hsv[:, :, 1] * (0.3 + np.random.random() * 0.7)

hsv[:, :, 2] = hsv[:, :, 2] * (0.2 + np.random.random() * 0.8)

img = cv.cvtColor(hsv, cv.COLOR_HSV2BGR)

return img

def random_envirment(img, noplate_bg):

"""

添加自然环境的噪声, noplate_bg为不含车牌的背景图

"""

bg_index = r(len(noplate_bg))

env = cv.imread(noplate_bg[bg_index])

env = cv.resize(env, (img.shape[1], img.shape[0]))

bak = (img == 0)

bak = bak.astype(np.uint8) * 255

inv = cv.bitwise_and(bak, env)

img = cv.bitwise_or(inv, img)

return img

def GenCh(f, val):

"""

生成中文字符

"""

img = Image.new("RGB", (45, 70), (255, 255, 255)) #白色

# img = Image.new("RGB", (45, 70), (0, 0, 0)) #黑色

draw = ImageDraw.Draw(img)

# draw.text((0, 3), val, (0, 0, 0), font=f)

draw.text((0, 2), val, (0, 0, 0), font=f)

img = img.resize((19, 70))

A = np.array(img)

return A

def GenCh1(f, val):

"""

生成英文字符

"""

img =Image.new("RGB", (19, 70), (255,255,255))#白色

# img =Image.new("RGB", (23, 70), (0, 0, 0))#黑色

draw = ImageDraw.Draw(img)

draw.text((0, 2), val, (0,0,0), font=f) # val.decode('utf-8')

A = np.array(img)

return A

def AddGauss(img, level):

"""

添加高斯模糊

"""

return cv.blur(img, (level * 2 + 1, level * 2 + 1))

def r(val):

return int(np.random.random() * val)

def AddNoiseSingleChannel(single):

"""

添加高斯噪声

"""

diff = 255 - single.max()

noise = np.random.normal(0, 1 + r(6), single.shape)

noise = (noise - noise.min()) / (noise.max() - noise.min())

noise *= diff

# noise= noise.astype(np.uint8)

dst = single + noise

return dst

def addNoise(img): # sdev = 0.5,avg=10

img[:, :, 0] = AddNoiseSingleChannel(img[:, :, 0])

img[:, :, 1] = AddNoiseSingleChannel(img[:, :, 1])

img[:, :, 2] = AddNoiseSingleChannel(img[:, :, 2])

return img

class GenPlate:

def __init__(self, fontCh, fontEng, NoPlates):

self.fontC = ImageFont.truetype(fontCh, 43, 0)

self.fontE = ImageFont.truetype(fontEng, 52, 0) #60--52

self.img = np.array(Image.new("RGB", (226, 70),(255, 255, 255)))

# self.img = np.array(Image.new("RGB", (226, 70),(0, 0, 0)))

self.bg = cv.resize(cv.imread(r"C:\Users\lenovo\Desktop\Plate_Recognition-LPRnet-master\Plate_Recognition-LPRnet-master\generate_picture\images\black.jpg"), (226, 70))

# template.bmp:车牌背景图

self.smu = cv.imread(r"E:\AI_projects\vehicle license plate recognition\car_recognition\data\images\\smu2.jpg")

# smu2.jpg:模糊图像

self.noplates_path = []

for parent, parent_folder, filenames in os.walk(NoPlates):

for filename in filenames:

path = parent + "\\" + filename

self.noplates_path.append(path)

def draw(self, val):

offset = 2

self.img[0:70, offset+6:offset+6+19] = GenCh(self.fontC, val[0])

self.img[0:70, offset+6+19+7:offset+6+19+7+19] = GenCh1(self.fontE, val[1])

for i in range(6):

base = offset + 6 + 19 + 7 + 19 + 17 + i * 19 + i * 7

self.img[0:70, base:base+19] = GenCh1(self.fontE, val[i+2]) #23--19 6--7 8--6

return self.img

def generate(self, text):

if len(text) == 8:

fg = self.draw(text) # decode(encoding="utf-8")

fg = cv.bitwise_not(fg)

com = cv.bitwise_or(fg, self.bg)

# com = rot(com, r(60)-30, com.shape,30)

# com = rotRandrom(com, 10, (com.shape[1], com.shape[0]))

# com = tfactor(com)

# com = random_envirment(com, self.noplates_path)

# com = AddGauss(com, 1+r(4))

# com = addNoise(com)

return com

@staticmethod

def genPlateString(pos, val):

"""

生成车牌string,存为图片

生成车牌list,存为label

"""

plateStr = ""

plateList=[]

box = [0, 0, 0, 0, 0, 0, 0,0]

if pos != -1:

box[pos] = 1

for unit, cpos in zip(box, range(len(box))):

if unit == 1:

plateStr += val

plateList.append(val)

else:

if cpos == 0:

plateStr += chars[r(31)] #31

plateList.append(plateStr)

elif cpos == 1:

plateStr += chars[41 + r(24)] #41 24

plateList.append(plateStr)

else:

plateStr += chars[31 + r(34)] #31 34

plateList.append(plateStr)

plate = [plateList[0]]

b = [plateList[i][-1] for i in range(len(plateList))]

plate.extend(b[1:8])

return plateStr, plate

@staticmethod

def genBatch(batchsize, outputPath, size):

"""

将生成的车牌图片写入文件夹,对应的label写入label.txt

:param batchsize: 批次大小

:param outputPath: 输出图像的保存路径

:param size: 输出图像的尺寸

:return: None

"""

if not os.path.exists(outputPath):

os.mkdir(outputPath)

outfile = open(r'C:\Users\lenovo\Desktop\Plate_Recognition-LPRnet-master\Plate_Recognition-LPRnet-master\label_new.txt',

'w', encoding='utf-8')

for i in range(batchsize):

plateStr, plate = G.genPlateString(-1, -1)

img = G.generate(plateStr)

img = cv.resize(img, size)

content = "".join(plate)

cv.imwrite(outputPath + "\\" + str(i).zfill(2) + ".jpg", img)

outfile.write("./train/"+str(i).zfill(2)+".jpg"+'\t'+content +"\n")

G = GenPlate(r"E:\AI_projects\vehicle license plate recognition\car_recognition\data\font\platech.ttf",

r'E:\AI_projects\vehicle license plate recognition\car_recognition\data\font\platechar.ttf',

r"E:\AI_projects\vehicle license plate recognition\car_recognition\data\NoPlates")

G.genBatch(1000, r'C:\Users\lenovo\Desktop\Plate_Recognition-LPRnet-master\Plate_Recognition-LPRnet-master\test_in_new', (272, 72))

以上为车牌生成部分代码,其他代码参考之前的部分

(4) 重命名各种车牌名称,使得它适合训练便签生成要求

#将以数字命名的图片转换为省份编号+车牌命名

import os

import pandas as pd

path=r'C:\Users\lenovo\Desktop\Plate_Recognition-LPRnet-master\Plate_Recognition-LPRnet-master\label_yellow.txt'

num=[]

with open(path,'rb') as f:

lines=f.readlines()

for line in lines:

data=line.decode('utf-8')#.encode('gb2312')

data1=data.split('\t')[0]

data2=data.split('\t')[1].split('\r')[0]

num.append([data1,data2])

number=pd.DataFrame(num,columns=['p','j'])

CHARS = ['京', '沪', '津', '渝', '冀', '晋', '蒙', '辽', '吉', '黑',

'苏', '浙', '皖', '闽', '赣', '鲁', '豫', '鄂', '湘', '粤',

'桂', '琼', '川', '贵', '云', '藏', '陕', '甘', '青', '宁',

'新',

'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',

'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K',

'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',

'W', 'X', 'Y', 'Z','_'

]

dicts = {'A01':'京','A02':'津','A03':'沪','B02':'蒙',

'S01':'皖','S02':'闽','S03':'粤','S04':'甘',

'S05': '贵', 'S06': '鄂', 'S07': '冀', 'S08': '黑', 'S09': '湘',

'S10': '豫', 'S12': '吉', 'S13': '苏', 'S14': '赣', 'S15': '辽',

'S17': '川', 'S18': '鲁', 'S22': '浙',

'S30':'渝', 'S31':'晋', 'S32':'桂', 'S33':'琼', 'S34':'云', 'S35':'藏',

'S36':'陕','S37':'青', 'S38':'宁', 'S39':'新'}

path_root=r'C:\Users\lenovo\Desktop\Plate_Recognition-LPRnet-master\Plate_Recognition-LPRnet-master\test_in_yellow'

for k in range(len(number)):

for t in range(len(CHARS)):

pp=number['p'][k].split('/train/')[1]

if number['j'][k][0]==CHARS[t]:

original_name=os.path.join(path_root,pp)

d=list(dicts.keys())[t]

pd=number['j'][k]

dd=d+'_'+pd[1:]+'_0'+'.jpg'

post_name=os.path.join(path_root,dd)

if os.path.exists(original_name):

if not os.path.exists(post_name):

os.rename(original_name,post_name)

(5) 统计各个省份的车牌数量

主要是确认中文字符数量均衡

import os

path=r'C:\Users\lenovo\Desktop\Plate_Recognition-LPRnet-master\Plate_Recognition-LPRnet-master\picture_real'

S=[]

for file in os.listdir(path):

a=file.split('_')[0]

S.append(a)

Set=set(S)

Dict={}

for item in Set:

Dict.update({item:S.count(item)})

print(Dict)

(6) 把车牌移动到训练和测试两个文件夹中

import os

import shutil

path_from=r'C:\Users\lenovo\Desktop\Plate_Recognition-LPRnet-master\Plate_Recognition-LPRnet-master\test_in_new'

path_to=r'C:\Users\lenovo\Desktop\Plate_Recognition-LPRnet-master\Plate_Recognition-LPRnet-master\test'

for file in os.listdir(path_from):

image=os.path.join(path_from,file)

# if not os.path.join(path_to,file):

shutil.move(image,path_to)

(7) 模型训练及测试

接下来进行模型训练。。。 (略)

总结

用第三种方法 LPRNet 来进行车牌识别这个项目是一个很不错的,涉及到图像处理,深度学习等各种图像相关的知识,完成这个项目训练后,项目工程能力得到较大提升,对于以后找工作和工作中遇到的问题处理都有比较大的帮助。

能认真看到这的小伙伴帮忙点个赞!

相关推荐

1200千米开车多长时间
365皇冠体育网址

1200千米开车多长时间

07-02 586
男人性生活正常多长时间
365皇冠体育网址

男人性生活正常多长时间

07-05 368