Друзья, наконец-то пришло время настоящего боя! Проект, представленный вам сегодня, заключается в использовании 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. Добавьте фоновые изображения, сгенерируйте список строк номерного знака и метку, сохраните их в формате изображения и сгенерируйте их в пакетном режиме.
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 питон genPlate.py 100 ./plate_100
показать результат:
Вышеприведенное изображение представляет собой сгенерированные данные номерного знака, некоторые четкие, некоторые нечеткие, некоторые относительно квадратные, а некоторые более наклонные.После создания большого количества доказательств номерного знака можно выполнить распознавание номерного знака. В следующем разделе будет рассказано о том, как использовать сквозную CNN для распознавания номерных знаков без необходимости использования традиционного OCR для сегментации символов перед их распознаванием.
Использованная литература:
1. Оригинальный проект распознавания номерных знаков: https://github.com/huxiaoman7/mxnet-cnn-plate-recognition