【深度学习系列】车牌识别实践(一)

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


生成车牌数据

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

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

 1 index = {"": 0, "": 1, "": 2, "": 3, "": 4, "": 5, "": 6, "": 7, "": 8, "": 9, "": 10, "": 11, "": 12,
 2          "": 13, "": 14, "": 15, "": 16, "": 17, "": 18, "": 19, "": 20, "": 21, "": 22, "": 23, "": 24,
 3          "": 25, "": 26, "": 27, "": 28, "": 29, "": 30, "0": 31, "1": 32, "2": 33, "3": 34, "4": 35, "5": 36,
 4          "6": 37, "7": 38, "8": 39, "9": 40, "A": 41, "B": 42, "C": 43, "D": 44, "E": 45, "F": 46, "G": 47, "H": 48,
 5          "J": 49, "K": 50, "L": 51, "M": 52, "N": 53, "P": 54, "Q": 55, "R": 56, "S": 57, "T": 58, "U": 59, "V": 60,
 6          "W": 61, "X": 62, "Y": 63, "Z": 64};
 7 
 8 chars = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
 9              "", "", "", "", "", "", "", "", "", "", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A",
10              "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X",
11              "Y", "Z"
12              ];

  

  2.生成中英文字符

 1 def GenCh(f,val):
 2     """
 3     生成中文字符
 4     """
 5     img=Image.new("RGB", (45,70),(255,255,255))
 6     draw = ImageDraw.Draw(img)
 7     draw.text((0, 3),val,(0,0,0),font=f)
 8     img =  img.resize((23,70))
 9     A = np.array(img)
10     return A
11 
12 def GenCh1(f,val):
13     """
14     生成英文字符
15     """
16     img=Image.new("RGB", (23,70),(255,255,255))
17     draw = ImageDraw.Draw(img)
18     draw.text((0, 2),val.decode('utf-8'),(0,0,0),font=f)
19     A = np.array(img)
20     return A

  

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

 1 def AddSmudginess(img, Smu):
 2     rows = r(Smu.shape[0] - 50)
 3     cols = r(Smu.shape[1] - 50)
 4     adder = Smu[rows:rows + 50, cols:cols + 50];
 5     adder = cv2.resize(adder, (50, 50));
 6     #adder = cv2.bitwise_not(adder)
 7     img = cv2.resize(img,(50,50))
 8     img = cv2.bitwise_not(img)
 9     img = cv2.bitwise_and(adder, img)
10     img = cv2.bitwise_not(img)
11     return img
12 
13 
14 def rot(img,angel,shape,max_angel):
15     """
16         添加放射畸变
17         img 输入图像
18         factor 畸变的参数
19         size 为图片的目标尺寸
20     """
21     size_o = [shape[1],shape[0]]
22     size = (shape[1]+ int(shape[0]*cos((float(max_angel )/180) * 3.14)),shape[0])
23     interval = abs( int( sin((float(angel) /180) * 3.14)* shape[0]));
24     pts1 = np.float32([[0,0],[0,size_o[1]],[size_o[0],0],[size_o[0],size_o[1]]])
25     if(angel>0):
26         pts2 = np.float32([[interval,0],[0,size[1]  ],[size[0],0  ],[size[0]-interval,size_o[1]]])
27     else:
28         pts2 = np.float32([[0,0],[interval,size[1]  ],[size[0]-interval,0  ],[size[0],size_o[1]]])
29     M  = cv2.getPerspectiveTransform(pts1,pts2);
30     dst = cv2.warpPerspective(img,M,size);
31     return dst
32 
33 
34 def rotRandrom(img, factor, size):
35     """
36     添加透视畸变
37     """
38     shape = size;
39     pts1 = np.float32([[0, 0], [0, shape[0]], [shape[1], 0], [shape[1], shape[0]]])
40     pts2 = np.float32([[r(factor), r(factor)], [ r(factor), shape[0] - r(factor)], [shape[1] - r(factor),  r(factor)],
41                        [shape[1] - r(factor), shape[0] - r(factor)]])
42     M = cv2.getPerspectiveTransform(pts1, pts2);
43     dst = cv2.warpPerspective(img, M, size);
44     return dst
45 
46 def tfactor(img):
47     """
48     添加饱和度光照的噪声
49     """
50     hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV);
51     hsv[:,:,0] = hsv[:,:,0]*(0.8+ np.random.random()*0.2);
52     hsv[:,:,1] = hsv[:,:,1]*(0.3+ np.random.random()*0.7);
53     hsv[:,:,2] = hsv[:,:,2]*(0.2+ np.random.random()*0.8);
54 
55     img = cv2.cvtColor(hsv,cv2.COLOR_HSV2BGR);
56     return img
57 
58 def random_envirment(img,data_set):
59     """
60     添加自然环境的噪声
61     """
62     index=r(len(data_set))
63     env = cv2.imread(data_set[index])
64     env = cv2.resize(env,(img.shape[1],img.shape[0]))
65     bak = (img==0);
66     bak = bak.astype(np.uint8)*255;
67     inv = cv2.bitwise_and(bak,env)
68     img = cv2.bitwise_or(inv,img)
69     return img
70 
71 def AddGauss(img, level):
72     """
73     添加高斯模糊
74     """
75     return cv2.blur(img, (level * 2 + 1, level * 2 + 1));
76 
77 def r(val):
78     return int(np.random.random() * val)
79 
80 def AddNoiseSingleChannel(single):
81     """
82     添加高斯噪声
83     """
84     diff = 255-single.max();
85     noise = np.random.normal(0,1+r(6),single.shape);
86     noise = (noise - noise.min())/(noise.max()-noise.min())
87     noise= diff*noise;
88     noise= noise.astype(np.uint8)
89     dst = single + noise
90     return dst
91 
92 def addNoise(img,sdev = 0.5,avg=10):
93     img[:,:,0] =  AddNoiseSingleChannel(img[:,:,0]);
94     img[:,:,1] =  AddNoiseSingleChannel(img[:,:,1]);
95     img[:,:,2] =  AddNoiseSingleChannel(img[:,:,2]);
96     return img

  

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

 1 class GenPlate:
 2 
 3     def __init__(self,fontCh,fontEng,NoPlates):
 4         self.fontC =  ImageFont.truetype(fontCh,43,0);
 5         self.fontE =  ImageFont.truetype(fontEng,60,0);
 6         self.img=np.array(Image.new("RGB", (226,70),(255,255,255)))
 7         self.bg  = cv2.resize(cv2.imread("./images/template.bmp"),(226,70));
 8         self.smu = cv2.imread("./images/smu2.jpg");
 9         self.noplates_path = [];
10         for parent,parent_folder,filenames in os.walk(NoPlates):
11             for filename in filenames:
12                 path = parent+"/"+filename;
13                 self.noplates_path.append(path);
14 
15 
16     def draw(self,val):
17         offset= 2 ;
18         self.img[0:70,offset+8:offset+8+23]= GenCh(self.fontC,val[0]);
19         self.img[0:70,offset+8+23+6:offset+8+23+6+23]= GenCh1(self.fontE,val[1]);
20         for i in range(5):
21             base = offset+8+23+6+23+17 +i*23 + i*6 ;
22             self.img[0:70, base  : base+23]= GenCh1(self.fontE,val[i+2]);
23         return self.img
24     
25     def generate(self,text):
26         if len(text) == 9:
27             fg = self.draw(text.decode(encoding="utf-8"));
28             fg = cv2.bitwise_not(fg);
29             com = cv2.bitwise_or(fg,self.bg);
30             com = rot(com,r(60)-30,com.shape,30);
31             com = rotRandrom(com,10,(com.shape[1],com.shape[0]));
32             com = tfactor(com)
33             com = random_envirment(com,self.noplates_path);
34             com = AddGauss(com, 1+r(4));
35             com = addNoise(com);
36             return com
37 
38     def genPlateString(self,pos,val):
39         '''
40     生成车牌String,存为图片
41         生成车牌list,存为label
42         '''
43         plateStr = "";
44         plateList=[]
45         box = [0,0,0,0,0,0,0];
46         if(pos!=-1):
47             box[pos]=1;
48         for unit,cpos in zip(box,range(len(box))):
49             if unit == 1:
50                 plateStr += val
51                 #print plateStr
52                 plateList.append(val)
53             else:
54                 if cpos == 0:
55                     plateStr += chars[r(31)]
56                     plateList.append(plateStr)
57                 elif cpos == 1:
58                     plateStr += chars[41+r(24)]
59                     plateList.append(plateStr)
60                 else:
61                     plateStr += chars[31 + r(34)]
62                     plateList.append(plateStr)
63         plate = [plateList[0]]
64         b = [plateList[i][-1] for i in range(len(plateList))]
65         plate.extend(b[1:7])
66         return plateStr,plate
67 
68     # 将生成的车牌图片写入文件夹,对应的label写入label.txt
69     def genBatch(self, batchSize,pos,charRange, outputPath,size):
70         if (not os.path.exists(outputPath)):
71             os.mkdir(outputPath)
72     outfile = open('label.txt','w')
73         for i in xrange(batchSize):
74                 plateStr,plate = G.genPlateString(-1,-1)
75                 print plateStr,plate
76         img =  G.generate(plateStr);
77                 img = cv2.resize(img,size);
78                 cv2.imwrite(outputPath + "/" + str(i).zfill(2) + ".jpg", img);
79         outfile.write(str(plate)+"\n")
80 G = GenPlate("./font/platech.ttf",'./font/platechar.ttf',"./NoPlates")

  完整代码:

  1 #coding=utf-8
  2 """ 
  3    genPlate.py:生成随机车牌
  4 """
  5 
  6 __author__ = "Huxiaoman"
  7 __copyright__ = "Copyright (c) 2017 "
  8 
  9 import PIL
 10 from PIL import ImageFont
 11 from PIL import Image
 12 from PIL import ImageDraw
 13 import cv2;
 14 import numpy as np;
 15 import os;
 16 from math import *
 17 import sys
 18 
 19 
 20 index = {"": 0, "": 1, "": 2, "": 3, "": 4, "": 5, "": 6, "": 7, "": 8, "": 9, "": 10, "": 11, "": 12,
 21          "": 13, "": 14, "": 15, "": 16, "": 17, "": 18, "": 19, "": 20, "": 21, "": 22, "": 23, "": 24,
 22          "": 25, "": 26, "": 27, "": 28, "": 29, "": 30, "0": 31, "1": 32, "2": 33, "3": 34, "4": 35, "5": 36,
 23          "6": 37, "7": 38, "8": 39, "9": 40, "A": 41, "B": 42, "C": 43, "D": 44, "E": 45, "F": 46, "G": 47, "H": 48,
 24          "J": 49, "K": 50, "L": 51, "M": 52, "N": 53, "P": 54, "Q": 55, "R": 56, "S": 57, "T": 58, "U": 59, "V": 60,
 25          "W": 61, "X": 62, "Y": 63, "Z": 64};
 26 
 27 chars = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
 28              "", "", "", "", "", "", "", "", "", "", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A",
 29              "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X",
 30              "Y", "Z"
 31              ];
 32 
 33 def AddSmudginess(img, Smu):
 34     rows = r(Smu.shape[0] - 50)
 35     cols = r(Smu.shape[1] - 50)
 36     adder = Smu[rows:rows + 50, cols:cols + 50];
 37     adder = cv2.resize(adder, (50, 50));
 38     #adder = cv2.bitwise_not(adder)
 39     img = cv2.resize(img,(50,50))
 40     img = cv2.bitwise_not(img)
 41     img = cv2.bitwise_and(adder, img)
 42     img = cv2.bitwise_not(img)
 43     return img
 44 
 45 def rot(img,angel,shape,max_angel):
 46     """ 
 47         添加放射畸变
 48         img 输入图像
 49         factor 畸变的参数
 50         size 为图片的目标尺寸
 51     """
 52     size_o = [shape[1],shape[0]]
 53     size = (shape[1]+ int(shape[0]*cos((float(max_angel )/180) * 3.14)),shape[0])
 54     interval = abs( int( sin((float(angel) /180) * 3.14)* shape[0]));
 55     pts1 = np.float32([[0,0],[0,size_o[1]],[size_o[0],0],[size_o[0],size_o[1]]])
 56     if(angel>0):
 57         pts2 = np.float32([[interval,0],[0,size[1]  ],[size[0],0  ],[size[0]-interval,size_o[1]]])
 58     else:
 59         pts2 = np.float32([[0,0],[interval,size[1]  ],[size[0]-interval,0  ],[size[0],size_o[1]]])
 60     M  = cv2.getPerspectiveTransform(pts1,pts2);
 61     dst = cv2.warpPerspective(img,M,size);
 62     return dst
 63 
 64 def rotRandrom(img, factor, size):
 65     """
 66     添加透视畸变
 67     """
 68     shape = size;
 69     pts1 = np.float32([[0, 0], [0, shape[0]], [shape[1], 0], [shape[1], shape[0]]])
 70     pts2 = np.float32([[r(factor), r(factor)], [ r(factor), shape[0] - r(factor)], [shape[1] - r(factor),  r(factor)],
 71                        [shape[1] - r(factor), shape[0] - r(factor)]])
 72     M = cv2.getPerspectiveTransform(pts1, pts2);
 73     dst = cv2.warpPerspective(img, M, size);
 74     return dst
 75 
 76 def tfactor(img):
 77     """
 78     添加饱和度光照的噪声
 79     """
 80     hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV);
 81     hsv[:,:,0] = hsv[:,:,0]*(0.8+ np.random.random()*0.2);
 82     hsv[:,:,1] = hsv[:,:,1]*(0.3+ np.random.random()*0.7);
 83     hsv[:,:,2] = hsv[:,:,2]*(0.2+ np.random.random()*0.8);
 84 
 85     img = cv2.cvtColor(hsv,cv2.COLOR_HSV2BGR);
 86     return img
 87 
 88 def random_envirment(img,data_set):
 89     """
 90     添加自然环境的噪声    
 91     """
 92     index=r(len(data_set))
 93     env = cv2.imread(data_set[index])
 94     env = cv2.resize(env,(img.shape[1],img.shape[0]))
 95     bak = (img==0);
 96     bak = bak.astype(np.uint8)*255;
 97     inv = cv2.bitwise_and(bak,env)
 98     img = cv2.bitwise_or(inv,img)
 99     return img
100 
101 def GenCh(f,val):
102     """
103     生成中文字符
104     """
105     img=Image.new("RGB", (45,70),(255,255,255))
106     draw = ImageDraw.Draw(img)
107     draw.text((0, 3),val,(0,0,0),font=f)
108     img =  img.resize((23,70))
109     A = np.array(img)
110     return A
111 
112 def GenCh1(f,val):
113     """
114     生成英文字符
115     """
116     img=Image.new("RGB", (23,70),(255,255,255))
117     draw = ImageDraw.Draw(img)
118     draw.text((0, 2),val.decode('utf-8'),(0,0,0),font=f)
119     A = np.array(img)
120     return A
121 
122 def AddGauss(img, level):
123     """
124     添加高斯模糊
125     """ 
126     return cv2.blur(img, (level * 2 + 1, level * 2 + 1));
127 
128 def r(val):
129     return int(np.random.random() * val)
130 
131 def AddNoiseSingleChannel(single):
132     """
133     添加高斯噪声
134     """
135     diff = 255-single.max();
136     noise = np.random.normal(0,1+r(6),single.shape);
137     noise = (noise - noise.min())/(noise.max()-noise.min())
138     noise= diff*noise;
139     noise= noise.astype(np.uint8)
140     dst = single + noise
141     return dst
142 
143 def addNoise(img,sdev = 0.5,avg=10):
144     img[:,:,0] =  AddNoiseSingleChannel(img[:,:,0]);
145     img[:,:,1] =  AddNoiseSingleChannel(img[:,:,1]);
146     img[:,:,2] =  AddNoiseSingleChannel(img[:,:,2]);
147     return img
148 
149 
150 class GenPlate:
151 
152     def __init__(self,fontCh,fontEng,NoPlates):
153         self.fontC =  ImageFont.truetype(fontCh,43,0);
154         self.fontE =  ImageFont.truetype(fontEng,60,0);
155         self.img=np.array(Image.new("RGB", (226,70),(255,255,255)))
156         self.bg  = cv2.resize(cv2.imread("./images/template.bmp"),(226,70));
157         self.smu = cv2.imread("./images/smu2.jpg");
158         self.noplates_path = [];
159         for parent,parent_folder,filenames in os.walk(NoPlates):
160             for filename in filenames:
161                 path = parent+"/"+filename;
162                 self.noplates_path.append(path);
163 
164 
165     def draw(self,val):
166         offset= 2 ;
167         self.img[0:70,offset+8:offset+8+23]= GenCh(self.fontC,val[0]);
168         self.img[0:70,offset+8+23+6:offset+8+23+6+23]= GenCh1(self.fontE,val[1]);
169         for i in range(5):
170             base = offset+8+23+6+23+17 +i*23 + i*6 ;
171             self.img[0:70, base  : base+23]= GenCh1(self.fontE,val[i+2]);
172         return self.img
173     
174     def generate(self,text):
175         if len(text) == 9:
176             fg = self.draw(text.decode(encoding="utf-8"));
177             fg = cv2.bitwise_not(fg);
178             com = cv2.bitwise_or(fg,self.bg);
179             com = rot(com,r(60)-30,com.shape,30);
180             com = rotRandrom(com,10,(com.shape[1],com.shape[0]));
181             com = tfactor(com)
182             com = random_envirment(com,self.noplates_path);
183             com = AddGauss(com, 1+r(4));
184             com = addNoise(com);
185             return com
186 
187     def genPlateString(self,pos,val):
188         '''
189     生成车牌String,存为图片
190         生成车牌list,存为label
191         '''
192         plateStr = "";
193         plateList=[]
194         box = [0,0,0,0,0,0,0];
195         if(pos!=-1):
196             box[pos]=1;
197         for unit,cpos in zip(box,range(len(box))):
198             if unit == 1:
199                 plateStr += val
200                 #print plateStr
201                 plateList.append(val)
202             else:
203                 if cpos == 0:
204                     plateStr += chars[r(31)]
205                     plateList.append(plateStr)
206                 elif cpos == 1:
207                     plateStr += chars[41+r(24)]
208                     plateList.append(plateStr)
209                 else:
210                     plateStr += chars[31 + r(34)]
211                     plateList.append(plateStr)
212         plate = [plateList[0]]
213         b = [plateList[i][-1] for i in range(len(plateList))]
214         plate.extend(b[1:7])
215         return plateStr,plate
216 
217     # 将生成的车牌图片写入文件夹,对应的label写入label.txt
218     def genBatch(self, batchSize,pos,charRange, outputPath,size):
219         if (not os.path.exists(outputPath)):
220             os.mkdir(outputPath)
221     outfile = open('label.txt','w')
222         for i in xrange(batchSize):
223                 plateStr,plate = G.genPlateString(-1,-1)
224                 print plateStr,plate
225         img =  G.generate(plateStr);
226                 img = cv2.resize(img,size);
227                 cv2.imwrite(outputPath + "/" + str(i).zfill(2) + ".jpg", img);
228         outfile.write(str(plate)+"\n")
229 G = GenPlate("./font/platech.ttf",'./font/platechar.ttf',"./NoPlates")
230 #G.genBatch(100,2,range(31,65),"./plate_100",(272,72))
231 
232 if __name__=='__main__':
233     G.genBatch(int(sys.argv[1]),2,range(31,65),sys.argv[2],(272,72))
View Code

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

 1 python genPlate.py 100 ./plate_100 

  显示结果:

 

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

 

参考资料:

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

posted @ 2018-02-26 10:42  Charlotte77  阅读(25877)  评论(18编辑  收藏  举报