本篇文章主要基于python语言和opencv库(cv2)进行车牌区域识别和字符分割,开篇之前针对在python中安装opencv的环境这里不做介绍,可以自行安装配置!
车牌号检测需要大致分为四个部分:
1.车辆图像获取
2.车牌定位、
3.车牌字符分割
4.车牌字符识别
具体介绍
车牌定位需要用到的是图片二值化为黑白后进canny边缘检测后多次进行开运算与闭运算用于消除小块的区域,保留大块的区域,后用cv2.rectangle选取矩形框,从而定位车牌位置
车牌字符的分割前需要准备的是只保留车牌部分,将其他部分均变为黑色背景。这里我采用cv2.grabcut方法,可将图像分割成前景与背景。分割完成后,再经过二值化为黑白图后即可进行字符分割。由于图像中只有黑色和白色像素,因此我们需要通过图像的白色像素和黑色像素来分割开字符。即分别通过判断每一行每一列的黑色白色像素值的位置,来定位出字符。
具体步骤如下:
1.灰度转换:将彩色图片转换为灰度图像,常见的r=g=b=像素平均值。
2.高斯平滑和中值滤波:去除噪声。
3.sobel算子:提取图像边缘轮廓,x方向和y方向平方和开跟。
4.二值化处理:图像转换为黑白两色,通常像素大于127设置为255,小于设置为0。
5.膨胀和细化:放大图像轮廓,转换为一个个区域,这些区域内包含车牌。
6.通过算法选择合适的车牌位置,通常将较小的区域过滤掉或寻找蓝色底的区域。
7.标注车牌位置
8.图像切割和识别
通过代码实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
|
# -*- coding: utf-8 -*- """ @email:cuiran2001@163.com @author: cuiran """ import cv2 import numpy as np from pil import image import os.path from skimage import io,data def stretch(img): ''' 图像拉伸函数 ''' maxi = float (img. max ()) mini = float (img. min ()) for i in range (img.shape[ 0 ]): for j in range (img.shape[ 1 ]): img[i,j] = ( 255 / (maxi - mini) * img[i,j] - ( 255 * mini) / (maxi - mini)) return img def dobinaryzation(img): ''' 二值化处理函数 ''' maxi = float (img. max ()) mini = float (img. min ()) x = maxi - ((maxi - mini) / 2 ) #二值化,返回阈值ret 和 二值化操作后的图像thresh ret,thresh = cv2.threshold(img,x, 255 ,cv2.thresh_binary) #返回二值化后的黑白图像 return thresh def find_rectangle(contour): ''' 寻找矩形轮廓 ''' y,x = [],[] for p in contour: y.append(p[ 0 ][ 0 ]) x.append(p[ 0 ][ 1 ]) return [ min (y), min (x), max (y), max (x)] def locate_license(img,afterimg): ''' 定位车牌号 ''' img,contours,hierarchy = cv2.findcontours(img,cv2.retr_external,cv2.chain_approx_simple) #找出最大的三个区域 block = [] for c in contours: #找出轮廓的左上点和右下点,由此计算它的面积和长度比 r = find_rectangle(c) a = (r[ 2 ] - r[ 0 ]) * (r[ 3 ] - r[ 1 ]) #面积 s = (r[ 2 ] - r[ 0 ]) * (r[ 3 ] - r[ 1 ]) #长度比 block.append([r,a,s]) #选出面积最大的3个区域 block = sorted (block,key = lambda b: b[ 1 ])[ - 3 :] #使用颜色识别判断找出最像车牌的区域 maxweight,maxindex = 0 , - 1 for i in range ( len (block)): b = afterimg[block[i][ 0 ][ 1 ]:block[i][ 0 ][ 3 ],block[i][ 0 ][ 0 ]:block[i][ 0 ][ 2 ]] #bgr转hsv hsv = cv2.cvtcolor(b,cv2.color_bgr2hsv) #蓝色车牌的范围 lower = np.array([ 100 , 50 , 50 ]) upper = np.array([ 140 , 255 , 255 ]) #根据阈值构建掩膜 mask = cv2.inrange(hsv,lower,upper) #统计权值 w1 = 0 for m in mask: w1 + = m / 255 w2 = 0 for n in w1: w2 + = n #选出最大权值的区域 if w2>maxweight: maxindex = i maxweight = w2 return block[maxindex][ 0 ] def find_license(img): ''' 预处理函数 ''' m = 400 * img.shape[ 0 ] / img.shape[ 1 ] #压缩图像 img = cv2.resize(img,( 400 , int (m)),interpolation = cv2.inter_cubic) #bgr转换为灰度图像 gray_img = cv2.cvtcolor(img,cv2.color_bgr2gray) #灰度拉伸 stretchedimg = stretch(gray_img) '''进行开运算,用来去除噪声''' r = 16 h = w = r * 2 + 1 kernel = np.zeros((h,w),np.uint8) cv2.circle(kernel,(r,r),r, 1 , - 1 ) #开运算 openingimg = cv2.morphologyex(stretchedimg,cv2.morph_open,kernel) #获取差分图,两幅图像做差 cv2.absdiff('图像1','图像2') strtimg = cv2.absdiff(stretchedimg,openingimg) #图像二值化 binaryimg = dobinaryzation(strtimg) #canny边缘检测 canny = cv2.canny(binaryimg,binaryimg.shape[ 0 ],binaryimg.shape[ 1 ]) '''消除小的区域,保留大块的区域,从而定位车牌''' #进行闭运算 kernel = np.ones(( 5 , 19 ),np.uint8) closingimg = cv2.morphologyex(canny,cv2.morph_close,kernel) #进行开运算 openingimg = cv2.morphologyex(closingimg,cv2.morph_open,kernel) #再次进行开运算 kernel = np.ones(( 11 , 5 ),np.uint8) openingimg = cv2.morphologyex(openingimg,cv2.morph_open,kernel) #消除小区域,定位车牌位置 rect = locate_license(openingimg,img) return rect,img def cut_license(afterimg,rect): ''' 图像分割函数 ''' #转换为宽度和高度 rect[ 2 ] = rect[ 2 ] - rect[ 0 ] rect[ 3 ] = rect[ 3 ] - rect[ 1 ] rect_copy = tuple (rect.copy()) rect = [ 0 , 0 , 0 , 0 ] #创建掩膜 mask = np.zeros(afterimg.shape[: 2 ],np.uint8) #创建背景模型 大小只能为13*5,行数只能为1,单通道浮点型 bgdmodel = np.zeros(( 1 , 65 ),np.float64) #创建前景模型 fgdmodel = np.zeros(( 1 , 65 ),np.float64) #分割图像 cv2.grabcut(afterimg,mask,rect_copy,bgdmodel,fgdmodel, 5 ,cv2.gc_init_with_rect) mask2 = np.where((mask = = 2 )|(mask = = 0 ), 0 , 1 ).astype( 'uint8' ) img_show = afterimg * mask2[:,:,np.newaxis] return img_show def deal_license(licenseimg): ''' 车牌图片二值化 ''' #车牌变为灰度图像 gray_img = cv2.cvtcolor(licenseimg,cv2.color_bgr2gray) #均值滤波 去除噪声 kernel = np.ones(( 3 , 3 ),np.float32) / 9 gray_img = cv2.filter2d(gray_img, - 1 ,kernel) #二值化处理 ret,thresh = cv2.threshold(gray_img, 120 , 255 ,cv2.thresh_binary) return thresh def find_end(start,arg,black,white,width,black_max,white_max): end = start + 1 for m in range (start + 1 ,width - 1 ): if (black[m] if arg else white[m])>( 0.98 * black_max if arg else 0.98 * white_max): end = m break return end if __name__ = = '__main__' : img = cv2.imread( 'test_images/car001.jpg' ,cv2.imread_color) #预处理图像 rect,afterimg = find_license(img) #框出车牌号 cv2.rectangle(afterimg,(rect[ 0 ],rect[ 1 ]),(rect[ 2 ],rect[ 3 ]),( 0 , 255 , 0 ), 2 ) cv2.imshow( 'afterimg' ,afterimg) #分割车牌与背景 cutimg = cut_license(afterimg,rect) cv2.imshow( 'cutimg' ,cutimg) #二值化生成黑白图 thresh = deal_license(cutimg) cv2.imshow( 'thresh' ,thresh) cv2.waitkey( 0 ) #分割字符 ''' 判断底色和字色 ''' #记录黑白像素总和 white = [] black = [] height = thresh.shape[ 0 ] #263 width = thresh.shape[ 1 ] #400 #print('height',height) #print('width',width) white_max = 0 black_max = 0 #计算每一列的黑白像素总和 for i in range (width): line_white = 0 line_black = 0 for j in range (height): if thresh[j][i] = = 255 : line_white + = 1 if thresh[j][i] = = 0 : line_black + = 1 white_max = max (white_max,line_white) black_max = max (black_max,line_black) white.append(line_white) black.append(line_black) print ( 'white' ,white) print ( 'black' ,black) #arg为true表示黑底白字,false为白底黑字 arg = true if black_max<white_max: arg = false n = 1 start = 1 end = 2 s_width = 28 s_height = 28 while n<width - 2 : n + = 1 #判断是白底黑字还是黑底白字 0.05参数对应上面的0.95 可作调整 if (white[n] if arg else black[n])>( 0.02 * white_max if arg else 0.02 * black_max): start = n end = find_end(start,arg,black,white,width,black_max,white_max) n = end if end - start> 5 : cj = thresh[ 1 :height,start:end] # new_image = cj.resize((s_width,s_height),image.bilinear) # cj=cj.reshape(28, 28) print ( "result/%s.jpg" % (n)) #保存分割的图片 by cayden # cj.save("result/%s.jpg" % (n)) infile = "result/%s.jpg" % (n) io.imsave(infile,cj) # im = image.open(infile) # out=im.resize((s_width,s_height),image.bilinear) # out.save(infile) cv2.imshow( 'cutlicense' ,cj) cv2.waitkey( 0 ) cv2.waitkey( 0 ) cv2.destroyallwindows() |
运行效果如图所示
车牌定位并进行处理
车牌分割如图所示
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:https://blog.csdn.net/cuiran/article/details/86706441