Guys, it’s finally time for the real thing! Today’s project is license plate recognition using PaddlePaddle. License plate recognition actually belongs to the more common image recognition project, currently also belongs to the more mature application, most of the old manufacturers can do 99% accuracy. Traditional methods require multiple pre-processing of images and then use machine learning classification algorithm for classification recognition. However, with the development of deep learning, we can use CNN to carry out end-to-end license plate recognition. The training of any model is inseparable from data. In license plate recognition, it is not enough to download some data containing license plates at night. The main purpose of this article is to teach you how to generate license plates in batches.
Generating license plate data
1. Define the characters required for license plate data
The license plate includes the abbreviation of the province, uppercase English letters and numbers. We first define the required characters and dictionaries for later use
1 index = {" jing ": 0," Shanghai ": 1," jin ": 2," chongqing ": 3," ji ": 4," jin ": 5," meng ": 6," liao ": 7," auspicious ": 8," black ": 9," su ": 10," zhe ": 11," anhui ": 13, 12, 2 "min" : "jiangxi" : 14, "lu" : 15, "yu" : 16, "e" : 17, "xiang" : 18, "yue" : 19, "guangxi" : 20, "Joan" : 21, "chuan" : 22, "expensive", 23, "cloud" : 3 "hidden" : 24, 25, "shan" : 26, "f" : 27, "green" : 28, "ning" : 29, "new" : 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 = [" Beijing ", "Shanghai", "jin", "chongqing", "ji", "jin", ""," liao ", "auspicious", "black", "su", "zhejiang", "anhui", "min", "jiangxi", "lu", "yu", "e", "xiang", "yue", "guangxi", 9 "Joan", "sichuan", "expensive", "cloud", "hidden", "shan", "f", "green", "better", "new", "0", "1", "2", "3", "4", "5", "6", "7", "eight" and "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 ];Copy the code
2. Generate Chinese and English characters
1 def GenCh(f,val): Img = image.new ("RGB", (50,70),(255,255,255)) 6 draw = ImageDraw.Draw(img) 7 draw. Text ((0, 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): New ("RGB", (23,70),(255,255,255)) 17 draw = ImageDraw.Draw(img) 18 draw. Text ((0, 0) 2),val.decode('utf-8'),(0,0,0),font=f) 19 A = np.array(img) 20 return ACopy the code
3. Add all kinds of noise and distortion to the data and fuzzy processing
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_not(img) 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 Add radiation distortion 17 img Input image 18 Factor distortion parameter 19 size is the target size of the image 20 "" 21 size_o = [shape[1], Shape [0]] 22 size = (shape[1]+ int(shape[0]*cos((float(max_angel)/180)),shape[0]) 23 interval = abs(int(sin((float(angel) /180) * 3.14) * shape [0])); 24 pts1 = np. Float32 ([[0, 0] to [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], [the interval, the size of [1]], [size [0] - interval, 0], [size [0], size_o [1]]]) M = 29 cv2.getPerspectiveTransform(pts1,pts2); 30 dst = cv2.warpPerspective(img,M,size); 31 return DST 32 33 34 def rotRandrom(img, factor, size): 30 """ 31 shape = size; 31 return DST 32 33 34 def rotRandrom(img, factor, size): 35 "" 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 """ 50 HSV = cv2.cvtcolor (img, cv2.color_bgr2hsv); 51 HSV [:, :, 0] = HSV [:, :, 0] * (0.8 + np. Random. The random () * 0.2); 52 HSV [:, :, 1) = HSV [:, :, 1) * (0.3 + np. Random. The random () * 0.7); 53 HSV [:, :, 2) = HSV [:, :, 2) * (0.2 + np. Random. The 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 Add ambient noise 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): Blur (img, (level * 2 + 1, level * 2 + 1)); blur(level * 2 + 1, level * 2 + 1)); 76 77 def r(val): 78 return int(np.random.random() * val) 79 80 def AddNoiseSingleChannel(single): 82 Add gaussian noise 83 """ 84 diff = 255-single.max(); 85 whose = np. Random. Normal (0, 1 + r (6), single shape); 86 noise = (noise - noise.min())/(noise.max()-noise.min()) 87 noise= diff*noise; Sdev =1,sdev =1,sdev =1,sdev =1,sdev =1,sdev =1,sdev =1,sdev =1 93 img[:,:,0] = AddNoiseSingleChannel(img[:,:,0]); 94 img[:,:,1] = AddNoiseSingleChannel(img[:,:,1]); 95 img[:,:,2] = AddNoiseSingleChannel(img[:,:,2]); 96 return imgCopy the code
4. Add background images to generate license plate strings list and label and save them in image format and generate them in batches.
1 class GenPlate: 2, 3 def __init__ (self, fontCh fontEng, NoPlates) : 4 self. FontC = ImageFont. Truetype (fontCh, 43, 0); 5 the self. FontE = ImageFont. Truetype (fontEng, 60, 0). 6 self.img=np.array(Image.new("RGB", (226), (255255255))) 7 self. Bg = cv2. Resize (cv2. Imread (". / images/template. BMP "), (226)); 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): PlateStr = ""; plateStr = ""; 44 plateList=[] 45 box =[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 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")Copy the code
Complete code:
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))Copy the code
Run time plus the number of generated and save path can be, such as:
1 python genPlate.py 100 ./plate_100
Display result:
The figure above is the generated license plate data, there are clear and fuzzy, there are more square, there are some more inclined, generated after a large number of license plate proofs can be license plate recognition. The next section will explain how to use end-to-end CNN for license plate recognition, which does not require traditional OCR to segment characters and then recognize them.
References:
1. The original license plate recognition project: https://github.com/huxiaoman7/mxnet-cnn-plate-recognition