小伙伴们,终于到了实战部分了!今天给大家带来的项目是用PaddlePaddle进行车牌识别。车牌识别其实属于比较常见的图像识别的项目了,目前也属于比较成熟的应用,大多数老牌厂家能做到准确率99%+。传统的方法需要对图像进行多次预处理再用机器学习的分类算法进行分类识别,然而深度学习发展起来以后,我们可以通过用CNN来进行端对端的车牌识别。任何模型的训练都离不开数据,在车牌识别中,除了晚上能下载到的一些包含车牌的数据是不够的,本篇文章的主要目的是教大家如何批量生成车牌。


生成车牌数据

  1.定义车牌数据所需字符

  车牌中包括省份简称、大写英文字母和数字,我们首先定义需要的字符和字典,方便后面使用

 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, "": 31, "": 32, "": 33, "": 34, "": 35, "": 36,
"": 37, "": 38, "": 39, "": 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 = ["京", "沪", "津", "渝", "冀", "晋", "蒙", "辽", "吉", "黑", "苏", "浙", "皖", "闽", "赣", "鲁", "豫", "鄂", "湘", "粤", "桂",
"琼", "川", "贵", "云", "藏", "陕", "甘", "青", "宁", "新", "", "", "", "", "", "", "", "", "", "", "A",
"B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X",
"Y", "Z"
];

  

  2.生成中英文字符

 def GenCh(f,val):
"""
生成中文字符
"""
img=Image.new("RGB", (45,70),(255,255,255))
draw = ImageDraw.Draw(img)
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))
draw = ImageDraw.Draw(img)
draw.text((0, 2),val.decode('utf-8'),(0,0,0),font=f)
A = np.array(img)
return A

  

  3.对数据添加各种噪音和畸变,模糊处理

 def AddSmudginess(img, Smu):
rows = r(Smu.shape[0] - 50)
cols = r(Smu.shape[1] - 50)
adder = Smu[rows:rows + 50, cols:cols + 50];
adder = cv2.resize(adder, (50, 50));
#adder = cv2.bitwise_not(adder)
img = cv2.resize(img,(50,50))
img = cv2.bitwise_not(img)
img = cv2.bitwise_and(adder, img)
img = cv2.bitwise_not(img)
return img def rot(img,angel,shape,max_angel):
"""
添加放射畸变
img 输入图像
factor 畸变的参数
size 为图片的目标尺寸
"""
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):
"""
添加透视畸变
"""
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 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,data_set):
"""
添加自然环境的噪声
"""
index=r(len(data_set))
env = cv2.imread(data_set[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 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= 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

  

  4.加入背景图片,生成车牌字符串list和label,并存为图片格式,批量生成。

 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.bg = cv2.resize(cv2.imread("./images/template.bmp"),(226,70));
self.smu = cv2.imread("./images/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) == 9:
fg = self.draw(text.decode(encoding="utf-8"));
fg = cv2.bitwise_not(fg);
com = cv2.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 def genPlateString(self,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
#print plateStr
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 # 将生成的车牌图片写入文件夹,对应的label写入label.txt
def genBatch(self, batchSize,pos,charRange, outputPath,size):
if (not os.path.exists(outputPath)):
os.mkdir(outputPath)
outfile = open('label.txt','w')
for i in xrange(batchSize):
plateStr,plate = G.genPlateString(-1,-1)
print plateStr,plate
img = G.generate(plateStr);
img = cv2.resize(img,size);
cv2.imwrite(outputPath + "/" + str(i).zfill(2) + ".jpg", img);
outfile.write(str(plate)+"\n")
G = GenPlate("./font/platech.ttf",'./font/platechar.ttf',"./NoPlates")

  完整代码:

 #coding=utf-8
"""
genPlate.py:生成随机车牌
""" __author__ = "Huxiaoman"
__copyright__ = "Copyright (c) 2017 " import PIL
from PIL import ImageFont
from PIL import Image
from PIL import ImageDraw
import cv2;
import numpy as np;
import os;
from math import *
import sys 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, "": 31, "": 32, "": 33, "": 34, "": 35, "": 36,
"": 37, "": 38, "": 39, "": 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 = ["京", "沪", "津", "渝", "冀", "晋", "蒙", "辽", "吉", "黑", "苏", "浙", "皖", "闽", "赣", "鲁", "豫", "鄂", "湘", "粤", "桂",
"琼", "川", "贵", "云", "藏", "陕", "甘", "青", "宁", "新", "", "", "", "", "", "", "", "", "", "", "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):
rows = r(Smu.shape[0] - 50)
cols = r(Smu.shape[1] - 50)
adder = Smu[rows:rows + 50, cols:cols + 50];
adder = cv2.resize(adder, (50, 50));
#adder = cv2.bitwise_not(adder)
img = cv2.resize(img,(50,50))
img = cv2.bitwise_not(img)
img = cv2.bitwise_and(adder, img)
img = cv2.bitwise_not(img)
return img def rot(img,angel,shape,max_angel):
"""
添加放射畸变
img 输入图像
factor 畸变的参数
size 为图片的目标尺寸
"""
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):
"""
添加透视畸变
"""
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 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,data_set):
"""
添加自然环境的噪声
"""
index=r(len(data_set))
env = cv2.imread(data_set[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 GenCh(f,val):
"""
生成中文字符
"""
img=Image.new("RGB", (45,70),(255,255,255))
draw = ImageDraw.Draw(img)
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))
draw = ImageDraw.Draw(img)
draw.text((0, 2),val.decode('utf-8'),(0,0,0),font=f)
A = np.array(img)
return A def AddGauss(img, level):
"""
添加高斯模糊
"""
return cv2.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= 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.bg = cv2.resize(cv2.imread("./images/template.bmp"),(226,70));
self.smu = cv2.imread("./images/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) == 9:
fg = self.draw(text.decode(encoding="utf-8"));
fg = cv2.bitwise_not(fg);
com = cv2.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 def genPlateString(self,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
#print plateStr
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 # 将生成的车牌图片写入文件夹,对应的label写入label.txt
def genBatch(self, batchSize,pos,charRange, outputPath,size):
if (not os.path.exists(outputPath)):
os.mkdir(outputPath)
outfile = open('label.txt','w')
for i in xrange(batchSize):
plateStr,plate = G.genPlateString(-1,-1)
print plateStr,plate
img = G.generate(plateStr);
img = cv2.resize(img,size);
cv2.imwrite(outputPath + "/" + str(i).zfill(2) + ".jpg", img);
outfile.write(str(plate)+"\n")
G = GenPlate("./font/platech.ttf",'./font/platechar.ttf',"./NoPlates")
#G.genBatch(100,2,range(31,65),"./plate_100",(272,72)) if __name__=='__main__':
G.genBatch(int(sys.argv[1]),2,range(31,65),sys.argv[2],(272,72))

  运行时加生成数量和保存路径即可,如:

python genPlate.py 100 ./plate_100

  显示结果:

  上图即为生成的车牌数据,有清晰的有模糊的,有比较方正的,也有一些比较倾斜,生成完大量的车牌样张后就可以进行车牌识别了。下一小节将会讲如何用端对端的CNN进行车牌识别,不需要通过传统的ocr先对字符进行分割处理后再识别。

参考资料:

1.原来做的车牌识别项目:https://github.com/huxiaoman7/mxnet-cnn-plate-recognition

【深度学习系列】用PaddlePaddle进行车牌识别(一)的更多相关文章

  1. 【深度学习】用PaddlePaddle进行车牌识别(二)

    上节我们讲了第一部分,如何用生成简易的车牌,这节课中我们会用PaddlePaddle来识别生成的车牌. 数据读取 在上一节生成车牌时,我们可以分别生成训练数据和测试数据,方法如下(完整代码在这里): ...

  2. 【深度学习系列】PaddlePaddle垃圾邮件处理实战(二)

    PaddlePaddle垃圾邮件处理实战(二) 前文回顾   在上篇文章中我们讲了如何用支持向量机对垃圾邮件进行分类,auc为73.3%,本篇讲继续讲如何用PaddlePaddle实现邮件分类,将深度 ...

  3. 【深度学习系列】PaddlePaddle之手写数字识别

    上周在搜索关于深度学习分布式运行方式的资料时,无意间搜到了paddlepaddle,发现这个框架的分布式训练方案做的还挺不错的,想跟大家分享一下.不过呢,这块内容太复杂了,所以就简单的介绍一下padd ...

  4. 【深度学习系列】手写数字识别卷积神经--卷积神经网络CNN原理详解(一)

    上篇文章我们给出了用paddlepaddle来做手写数字识别的示例,并对网络结构进行到了调整,提高了识别的精度.有的同学表示不是很理解原理,为什么传统的机器学习算法,简单的神经网络(如多层感知机)都可 ...

  5. 【深度学习系列】PaddlePaddle垃圾邮件处理实战(一)

    PaddlePaddle垃圾邮件处理实战(一) 背景介绍   在我们日常生活中,经常会受到各种垃圾邮件,譬如来自商家的广告.打折促销信息.澳门博彩邮件.理财推广信息等,一般来说邮件客户端都会设置一定的 ...

  6. 【深度学习系列】PaddlePaddle之数据预处理

    上篇文章讲了卷积神经网络的基本知识,本来这篇文章准备继续深入讲CNN的相关知识和手写CNN,但是有很多同学跟我发邮件或私信问我关于PaddlePaddle如何读取数据.做数据预处理相关的内容.网上看的 ...

  7. 【深度学习系列】PaddlePaddle可视化之VisualDL

    上篇文章我们讲了如何对模型进行可视化,用的keras手动绘图输出CNN训练的中途结果,本篇文章将讲述如何用PaddlePaddle新开源的VisualDL来进行可视化.在讲VisualDL之前,我们先 ...

  8. 【AI开发】基于深度学习的卡口车型、车牌识别

    服务端代码后面给出 卡口车型.车牌识别demo截图 服务器:

  9. 【深度学习系列】关于PaddlePaddle的一些避“坑”技巧

    最近除了工作以外,业余在参加Paddle的AI比赛,在用Paddle训练的过程中遇到了一些问题,并找到了解决方法,跟大家分享一下: PaddlePaddle的Anaconda的兼容问题 之前我是在服务 ...

随机推荐

  1. 从js向Action传中文参数出现乱码问题的解决方法

    Action获取jsp表单中的中文参数,只要整个项目都采用UTF-8编码格式都不会出现乱码问题:但JSP中用到JS,并从JS向Action传中文参数,就会出现中文乱的现象     做项目的时候,发现A ...

  2. Android开发者必备的42个链接

    http://mobile.51cto.com/ahot-426035.htm Android开发者必备的42个链接 下面收集了42个帮助大家学习Android的内容链接,部分内容是面向初学者的,帮助 ...

  3. c++ ip地址的操作 c版

    http://blog.csdn.net/cpp_funs/article/details/6988154 1.htonl ()和ntohl( ) u_long PASCAL FAR ntohl (u ...

  4. [转载]App.Config详解及读写操作

    App.Config详解 应用程序配置文件是标准的 XML 文件,XML 标记和属性是区分大小写的.它是可以按需要更改的,开发人员可以使用配置文件来更改设置,而不必重编译应用程序.配置文件的根节点是c ...

  5. jQuery中on()方法用法实例详解

    这篇文章主要介绍了jQuery中on()方法用法,实例分析了on()方法的功能及各种常见的使用技巧,并对比分析了与bind(),live(),delegate()等方法的区别,需要的朋友可以参考下 本 ...

  6. 在Mac OS上配置Android开发环境

    1)安装配置NDK 1.1 下载NDK并解压缩 下载路径 https://developer.android.com/tools/sdk/ndk/index.html 在terminal运行: chm ...

  7. Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be

    有些操作只能回到主线程操作 比如: mbprogresshud只能在主线程中使用 而且注意凡是关于布局的代码也只能下载主线程

  8. gogogo

  9. 把项目运行到本地环境及mysql配置

    1. 添加本地域名C:\Windows\System32\drivers\etc\hosts 127.0.0.1 local.v7.com 2. 添加一个apache虚拟站点D:\wamp64\bin ...

  10. WIN10 ISO 官方

    WIN10   ISO  官方: https://www.microsoft.com/zh-cn/software-download/windows10ISO/