[Серия глубокого обучения] Распознавание номерных знаков с помощью PaddlePaddle (1)

машинное обучение глубокое обучение

Друзья, наконец-то пришло время настоящего боя! Проект, представленный вам сегодня, заключается в использовании 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

Добавить Автора

Источник: http://www.cnblogs.com/charlotte77/

Эта статья предназначена в основном для изучения, исследования и распространения.Если вам необходимо перепечатать, пожалуйста, свяжитесь со мной, укажите автора и источник, некоммерческое использование!