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

Author: Charlotte77

Reference: http://www.cnblogs.com/charlotte77/

This article is for study, research and sharing, if you need to reprint, please contact me, indicate the author and source, non-commercial purposes!