小夥伴們,終於到了實戰部分了!今天給你們帶來的項目是用PaddlePaddle進行車牌識別。車牌識別其實屬於比較常見的圖像識別的項目了,目前也屬於比較成熟的應用,大多數老牌廠家能作到準確率99%+。傳統的方法須要對圖像進行屢次預處理再用機器學習的分類算法進行分類識別,然而深度學習發展起來之後,咱們能夠經過用CNN來進行端對端的車牌識別。任何模型的訓練都離不開數據,在車牌識別中,除了晚上能下載到的一些包含車牌的數據是不夠的,本篇文章的主要目的是教你們如何批量生成車牌。python
生成車牌數據git
1.定義車牌數據所需字符github
車牌中包括省份簡稱、大寫英文字母和數字,咱們首先定義須要的字符和字典,方便後面使用算法
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.生成中英文字符app
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.對數據添加各類噪音和畸變,模糊處理dom
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")
完整代碼:ide
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))
運行時加生成數量和保存路徑便可,如:學習
1 python genPlate.py 100 ./plate_100 ui
顯示結果:
上圖即爲生成的車牌數據,有清晰的有模糊的,有比較方正的,也有一些比較傾斜,生成完大量的車牌樣張後就能夠進行車牌識別了。下一小節將會講如何用端對端的CNN進行車牌識別,不須要經過傳統的ocr先對字符進行分割處理後再識別。
參考資料:
1.原來作的車牌識別項目:https://github.com/huxiaoman7/mxnet-cnn-plate-recognition