脚本之家,脚本语言编程技术及教程分享平台!
分类导航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服务器之家 - 脚本之家 - Python - Python实现验证码识别

Python实现验证码识别

2020-06-16 10:03老板丶鱼丸粗面 Python

这篇文章主要介绍了Python实现验证码识别的方法,文中讲解非常详细,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下

大致介绍  

在python爬虫爬取某些网站的验证码的时候可能会遇到验证码识别的问题,现在的验证码大多分为四类:

    1、计算验证码

         2、滑块验证码

    3、识图验证码

    4、语音验证码

  这篇博客主要写的就是识图验证码,识别的是简单的验证码,要想让识别率更高,识别的更加准确就需要花很多的精力去训练自己的字体库。

  识别验证码通常是这几个步骤:

    1、灰度处理

    2、二值化

    3、去除边框(如果有的话)

    4、降噪

    5、切割字符或者倾斜度矫正

    6、训练字体库

    7、识别

  这6个步骤中前三个步骤是基本的,4或者5可根据实际情况选择是否需要,并不一定切割验证码,识别率就会上升很多有时候还会下降

  这篇博客不涉及训练字体库的内容,请自行搜索。同样也不讲解基础的语法。

  用到的几个主要的python库: Pillow(python图像处理库)、OpenCV(高级图像处理库)、pytesseract(识别库)

灰度处理&二值化

  灰度处理,就是把彩色的验证码图片转为灰色的图片。

  二值化,是将图片处理为只有黑白两色的图片,利于后面的图像处理和识别

  在OpenCV中有现成的方法可以进行灰度处理和二值化,处理后的效果:

  代码:

?
1
2
3
4
5
6
7
8
9
10
11
# 自适应阀值二值化
def _get_dynamic_binary_image(filedir, img_name):
 filename = './out_img/' + img_name.split('.')[0] + '-binary.jpg'
 img_name = filedir + '/' + img_name
 print('.....' + img_name)
 im = cv2.imread(img_name)
 im = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY) #灰值化
 # 二值化
 th1 = cv2.adaptiveThreshold(im, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 21, 1)
 cv2.imwrite(filename,th1)
 return th1

去除边框

  如果验证码有边框,那我们就需要去除边框,去除边框就是遍历像素点,找到四个边框上的所有点,把他们都改为白色,我这里边框是两个像素宽

  注意:在用OpenCV时,图片的矩阵点是反的,就是长和宽是颠倒的

  代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
# 去除边框
def clear_border(img,img_name):
 filename = './out_img/' + img_name.split('.')[0] + '-clearBorder.jpg'
 h, w = img.shape[:2]
 for y in range(0, w):
 for x in range(0, h):
  if y < 2 or y > w - 2:
  img[x, y] = 255
  if x < 2 or x > h -2:
  img[x, y] = 255
 
 cv2.imwrite(filename,img)
 return img

降噪

  降噪是验证码处理中比较重要的一个步骤,我这里使用了点降噪和线降噪

Python实现验证码识别

  线降噪的思路就是检测这个点相邻的四个点(图中标出的绿色点),判断这四个点中是白点的个数,如果有两个以上的白色像素点,那么就认为这个点是白色的,从而去除整个干扰线,但是这种方法是有限度的,如果干扰线特别粗就没有办法去除,只能去除细的干扰线

  代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 干扰线降噪
def interference_line(img, img_name):
 filename = './out_img/' + img_name.split('.')[0] + '-interferenceline.jpg'
 h, w = img.shape[:2]
 # !!!opencv矩阵点是反的
 # img[1,2] 1:图片的高度,2:图片的宽度
 for y in range(1, w - 1):
 for x in range(1, h - 1):
  count = 0
  if img[x, y - 1] > 245:
  count = count + 1
  if img[x, y + 1] > 245:
  count = count + 1
  if img[x - 1, y] > 245:
  count = count + 1
  if img[x + 1, y] > 245:
  count = count + 1
  if count > 2:
  img[x, y] = 255
 cv2.imwrite(filename,img)
 return img

  点降噪的思路和线降噪的差不多,只是会针对不同的位置检测的点不一样,注释写的很清楚了

  代码:

?
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
# 点降噪
def interference_point(img,img_name, x = 0, y = 0):
 """
 9邻域框,以当前点为中心的田字框,黑点个数
 :param x:
 :param y:
 :return:
 """
 filename = './out_img/' + img_name.split('.')[0] + '-interferencePoint.jpg'
 # todo 判断图片的长宽度下限
 cur_pixel = img[x,y]# 当前像素点的值
 height,width = img.shape[:2]
 
 for y in range(0, width - 1):
  for x in range(0, height - 1):
  if y == 0: # 第一行
   if x == 0: # 左上顶点,4邻域
    # 中心点旁边3个点
    sum = int(cur_pixel) \
      + int(img[x, y + 1]) \
      + int(img[x + 1, y]) \
      + int(img[x + 1, y + 1])
    if sum <= 2 * 245:
     img[x, y] = 0
   elif x == height - 1: # 右上顶点
    sum = int(cur_pixel) \
      + int(img[x, y + 1]) \
      + int(img[x - 1, y]) \
      + int(img[x - 1, y + 1])
    if sum <= 2 * 245:
     img[x, y] = 0
   else: # 最上非顶点,6邻域
    sum = int(img[x - 1, y]) \
      + int(img[x - 1, y + 1]) \
      + int(cur_pixel) \
      + int(img[x, y + 1]) \
      + int(img[x + 1, y]) \
      + int(img[x + 1, y + 1])
    if sum <= 3 * 245:
     img[x, y] = 0
  elif y == width - 1: # 最下面一行
   if x == 0: # 左下顶点
    # 中心点旁边3个点
    sum = int(cur_pixel) \
      + int(img[x + 1, y]) \
      + int(img[x + 1, y - 1]) \
      + int(img[x, y - 1])
    if sum <= 2 * 245:
     img[x, y] = 0
   elif x == height - 1: # 右下顶点
    sum = int(cur_pixel) \
      + int(img[x, y - 1]) \
      + int(img[x - 1, y]) \
      + int(img[x - 1, y - 1])
 
    if sum <= 2 * 245:
     img[x, y] = 0
   else: # 最下非顶点,6邻域
    sum = int(cur_pixel) \
      + int(img[x - 1, y]) \
      + int(img[x + 1, y]) \
      + int(img[x, y - 1]) \
      + int(img[x - 1, y - 1]) \
      + int(img[x + 1, y - 1])
    if sum <= 3 * 245:
     img[x, y] = 0
  else: # y不在边界
   if x == 0: # 左边非顶点
    sum = int(img[x, y - 1]) \
      + int(cur_pixel) \
      + int(img[x, y + 1]) \
      + int(img[x + 1, y - 1]) \
      + int(img[x + 1, y]) \
      + int(img[x + 1, y + 1])
 
    if sum <= 3 * 245:
     img[x, y] = 0
   elif x == height - 1: # 右边非顶点
    sum = int(img[x, y - 1]) \
      + int(cur_pixel) \
      + int(img[x, y + 1]) \
      + int(img[x - 1, y - 1]) \
      + int(img[x - 1, y]) \
      + int(img[x - 1, y + 1])
 
    if sum <= 3 * 245:
     img[x, y] = 0
   else: # 具备9领域条件的
    sum = int(img[x - 1, y - 1]) \
      + int(img[x - 1, y]) \
      + int(img[x - 1, y + 1]) \
      + int(img[x, y - 1]) \
      + int(cur_pixel) \
      + int(img[x, y + 1]) \
      + int(img[x + 1, y - 1]) \
      + int(img[x + 1, y]) \
      + int(img[x + 1, y + 1])
    if sum <= 4 * 245:
     img[x, y] = 0
 cv2.imwrite(filename,img)
 return img

  效果:

Python实现验证码识别

  其实到了这一步,这些字符就可以识别了,没必要进行字符切割了,现在这三种类型的验证码识别率已经达到50%以上了

字符切割   

       字符切割通常用于验证码中有粘连的字符,粘连的字符不好识别,所以我们需要将粘连的字符切割为单个的字符,在进行识别

  字符切割的思路就是找到一个黑色的点,然后在遍历与他相邻的黑色的点,直到遍历完所有的连接起来的黑色的点,找出这些点中的最高的点、最低的点、最右边的点、最左边的点,记录下这四个点,认为这是一个字符,然后在向后遍历点,直至找到黑色的点,继续以上的步骤。最后通过每个字符的四个点进行切割

  图中红色的点就是代码执行完后,标识出的每个字符的四个点,然后就会根据这四个点进行切割(图中画的有些误差,懂就好)

  但是也可以看到,m2是粘连的,代码认为他是一个字符,所以我们需要对每个字符的宽度进行检测,如果他的宽度过宽,我们就认为他是两个粘连在一起的字符,并将它在从中间切割

  确定每个字符的四个点代码:

?
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
def cfs(im,x_fd,y_fd):
 '''用队列和集合记录遍历过的像素坐标代替单纯递归以解决cfs访问过深问题
 '''
 
 # print('**********')
 
 xaxis=[]
 yaxis=[]
 visited =set()
 q = Queue()
 q.put((x_fd, y_fd))
 visited.add((x_fd, y_fd))
 offsets=[(1, 0), (0, 1), (-1, 0), (0, -1)]#四邻域
 
 while not q.empty():
  x,y=q.get()
 
  for xoffset,yoffset in offsets:
   x_neighbor,y_neighbor = x+xoffset,y+yoffset
 
   if (x_neighbor,y_neighbor) in (visited):
    continue # 已经访问过了
 
   visited.add((x_neighbor, y_neighbor))
 
   try:
    if im[x_neighbor, y_neighbor] == 0:
     xaxis.append(x_neighbor)
     yaxis.append(y_neighbor)
     q.put((x_neighbor,y_neighbor))
 
   except IndexError:
    pass
 # print(xaxis)
 if (len(xaxis) == 0 | len(yaxis) == 0):
 xmax = x_fd + 1
 xmin = x_fd
 ymax = y_fd + 1
 ymin = y_fd
 
 else:
 xmax = max(xaxis)
 xmin = min(xaxis)
 ymax = max(yaxis)
 ymin = min(yaxis)
 #ymin,ymax=sort(yaxis)
 
 return ymax,ymin,xmax,xmin
 
def detectFgPix(im,xmax):
 '''搜索区块起点
 '''
 
 h,w = im.shape[:2]
 for y_fd in range(xmax+1,w):
  for x_fd in range(h):
   if im[x_fd,y_fd] == 0:
    return x_fd,y_fd
 
def CFS(im):
 '''切割字符位置
 '''
 
 zoneL=[]#各区块长度L列表
 zoneWB=[]#各区块的X轴[起始,终点]列表
 zoneHB=[]#各区块的Y轴[起始,终点]列表
 
 xmax=0#上一区块结束黑点横坐标,这里是初始化
 for i in range(10):
 
  try:
   x_fd,y_fd = detectFgPix(im,xmax)
   # print(y_fd,x_fd)
   xmax,xmin,ymax,ymin=cfs(im,x_fd,y_fd)
   L = xmax - xmin
   H = ymax - ymin
   zoneL.append(L)
   zoneWB.append([xmin,xmax])
   zoneHB.append([ymin,ymax])
 
  except TypeError:
   return zoneL,zoneWB,zoneHB
 
 return zoneL,zoneWB,zoneHB

  分割粘连字符代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 切割的位置
  im_position = CFS(im)
 
  maxL = max(im_position[0])
  minL = min(im_position[0])
 
  # 如果有粘连字符,如果一个字符的长度过长就认为是粘连字符,并从中间进行切割
  if(maxL > minL + minL * 0.7):
  maxL_index = im_position[0].index(maxL)
  minL_index = im_position[0].index(minL)
  # 设置字符的宽度
  im_position[0][maxL_index] = maxL // 2
  im_position[0].insert(maxL_index + 1, maxL // 2)
  # 设置字符X轴[起始,终点]位置
  im_position[1][maxL_index][1] = im_position[1][maxL_index][0] + maxL // 2
  im_position[1].insert(maxL_index + 1, [im_position[1][maxL_index][1] + 1, im_position[1][maxL_index][1] + 1 + maxL // 2])
  # 设置字符的Y轴[起始,终点]位置
  im_position[2].insert(maxL_index + 1, im_position[2][maxL_index])
 
  # 切割字符,要想切得好就得配置参数,通常 1 or 2 就可以
  cutting_img(im,im_position,img_name,1,1)

  切割粘连字符代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
def cutting_img(im,im_position,img,xoffset = 1,yoffset = 1):
 filename = './out_img/' + img.split('.')[0]
 # 识别出的字符个数
 im_number = len(im_position[1])
 # 切割字符
 for i in range(im_number):
 im_start_X = im_position[1][i][0] - xoffset
 im_end_X = im_position[1][i][1] + xoffset
 im_start_Y = im_position[2][i][0] - yoffset
 im_end_Y = im_position[2][i][1] + yoffset
 cropped = im[im_start_Y:im_end_Y, im_start_X:im_end_X]
 cv2.imwrite(filename + '-cutting-' + str(i) + '.jpg',cropped)

识别

  识别用的是typesseract库,主要识别一行字符和单个字符时的参数设置,识别中英文的参数设置,代码很简单就一行,我这里大多是filter文件的操作

  代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 识别验证码
  cutting_img_num = 0
  for file in os.listdir('./out_img'):
  str_img = ''
  if fnmatch(file, '%s-cutting-*.jpg' % img_name.split('.')[0]):
   cutting_img_num += 1
  for i in range(cutting_img_num):
  try:
   file = './out_img/%s-cutting-%s.jpg' % (img_name.split('.')[0], i)
   # 识别字符
   str_img = str_img + image_to_string(Image.open(file),lang = 'eng', config='-psm 10') #单个字符是10,一行文本是7
  except Exception as err:
   pass
  print('切图:%s' % cutting_img_num)
  print('识别为:%s' % str_img)

  最后这种粘连字符的识别率是在30%左右,而且这种只是处理两个字符粘连,如果有两个以上的字符粘连还不能识别,但是根据字符宽度判别的话也不难,有兴趣的可以试一下

  无需切割字符识别的效果:

Python实现验证码识别

Python实现验证码识别

  需要切割字符的识别效果:

Python实现验证码识别

Python实现验证码识别

  这种只是能够识别简单验证码,复杂的验证码还要靠大家了

  参考资料:

  本来参考了挺多的资料,但是时间长了就找不到了,如果有人发现了,可以告诉我,我再添加

  使用方法:

   1、将要识别的验证码图片放入与脚本同级的img文件夹中,创建out_img文件夹
   2、python3 filename
     3、二值化、降噪等各个阶段的图片将存储在out_img文件夹中,最终识别结果会打印到屏幕上

  最后附上源码(带切割,不想要切割的就自己修改吧):

?
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
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
from PIL import Image
from pytesseract import *
from fnmatch import fnmatch
from queue import Queue
import matplotlib.pyplot as plt
import cv2
import time
import os
 
 
 
 
 
def clear_border(img,img_name):
 '''去除边框
 '''
 
 filename = './out_img/' + img_name.split('.')[0] + '-clearBorder.jpg'
 h, w = img.shape[:2]
 for y in range(0, w):
 for x in range(0, h):
  # if y ==0 or y == w -1 or y == w - 2:
  if y < 4 or y > w -4:
  img[x, y] = 255
  # if x == 0 or x == h - 1 or x == h - 2:
  if x < 4 or x > h - 4:
  img[x, y] = 255
 
 cv2.imwrite(filename,img)
 return img
 
 
def interference_line(img, img_name):
 '''
 干扰线降噪
 '''
 
 filename = './out_img/' + img_name.split('.')[0] + '-interferenceline.jpg'
 h, w = img.shape[:2]
 # !!!opencv矩阵点是反的
 # img[1,2] 1:图片的高度,2:图片的宽度
 for y in range(1, w - 1):
 for x in range(1, h - 1):
  count = 0
  if img[x, y - 1] > 245:
  count = count + 1
  if img[x, y + 1] > 245:
  count = count + 1
  if img[x - 1, y] > 245:
  count = count + 1
  if img[x + 1, y] > 245:
  count = count + 1
  if count > 2:
  img[x, y] = 255
 cv2.imwrite(filename,img)
 return img
 
def interference_point(img,img_name, x = 0, y = 0):
 """点降噪
 9邻域框,以当前点为中心的田字框,黑点个数
 :param x:
 :param y:
 :return:
 """
 filename = './out_img/' + img_name.split('.')[0] + '-interferencePoint.jpg'
 # todo 判断图片的长宽度下限
 cur_pixel = img[x,y]# 当前像素点的值
 height,width = img.shape[:2]
 
 for y in range(0, width - 1):
  for x in range(0, height - 1):
  if y == 0: # 第一行
   if x == 0: # 左上顶点,4邻域
    # 中心点旁边3个点
    sum = int(cur_pixel) \
      + int(img[x, y + 1]) \
      + int(img[x + 1, y]) \
      + int(img[x + 1, y + 1])
    if sum <= 2 * 245:
     img[x, y] = 0
   elif x == height - 1: # 右上顶点
    sum = int(cur_pixel) \
      + int(img[x, y + 1]) \
      + int(img[x - 1, y]) \
      + int(img[x - 1, y + 1])
    if sum <= 2 * 245:
     img[x, y] = 0
   else: # 最上非顶点,6邻域
    sum = int(img[x - 1, y]) \
      + int(img[x - 1, y + 1]) \
      + int(cur_pixel) \
      + int(img[x, y + 1]) \
      + int(img[x + 1, y]) \
      + int(img[x + 1, y + 1])
    if sum <= 3 * 245:
     img[x, y] = 0
  elif y == width - 1: # 最下面一行
   if x == 0: # 左下顶点
    # 中心点旁边3个点
    sum = int(cur_pixel) \
      + int(img[x + 1, y]) \
      + int(img[x + 1, y - 1]) \
      + int(img[x, y - 1])
    if sum <= 2 * 245:
     img[x, y] = 0
   elif x == height - 1: # 右下顶点
    sum = int(cur_pixel) \
      + int(img[x, y - 1]) \
      + int(img[x - 1, y]) \
      + int(img[x - 1, y - 1])
 
    if sum <= 2 * 245:
     img[x, y] = 0
   else: # 最下非顶点,6邻域
    sum = int(cur_pixel) \
      + int(img[x - 1, y]) \
      + int(img[x + 1, y]) \
      + int(img[x, y - 1]) \
      + int(img[x - 1, y - 1]) \
      + int(img[x + 1, y - 1])
    if sum <= 3 * 245:
     img[x, y] = 0
  else: # y不在边界
   if x == 0: # 左边非顶点
    sum = int(img[x, y - 1]) \
      + int(cur_pixel) \
      + int(img[x, y + 1]) \
      + int(img[x + 1, y - 1]) \
      + int(img[x + 1, y]) \
      + int(img[x + 1, y + 1])
 
    if sum <= 3 * 245:
     img[x, y] = 0
   elif x == height - 1: # 右边非顶点
    sum = int(img[x, y - 1]) \
      + int(cur_pixel) \
      + int(img[x, y + 1]) \
      + int(img[x - 1, y - 1]) \
      + int(img[x - 1, y]) \
      + int(img[x - 1, y + 1])
 
    if sum <= 3 * 245:
     img[x, y] = 0
   else: # 具备9领域条件的
    sum = int(img[x - 1, y - 1]) \
      + int(img[x - 1, y]) \
      + int(img[x - 1, y + 1]) \
      + int(img[x, y - 1]) \
      + int(cur_pixel) \
      + int(img[x, y + 1]) \
      + int(img[x + 1, y - 1]) \
      + int(img[x + 1, y]) \
      + int(img[x + 1, y + 1])
    if sum <= 4 * 245:
     img[x, y] = 0
 cv2.imwrite(filename,img)
 return img
 
def _get_dynamic_binary_image(filedir, img_name):
 '''
 自适应阀值二值化
 '''
 
 filename = './out_img/' + img_name.split('.')[0] + '-binary.jpg'
 img_name = filedir + '/' + img_name
 print('.....' + img_name)
 im = cv2.imread(img_name)
 im = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
 
 th1 = cv2.adaptiveThreshold(im, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 21, 1)
 cv2.imwrite(filename,th1)
 return th1
 
def _get_static_binary_image(img, threshold = 140):
 '''
 手动二值化
 '''
 
 img = Image.open(img)
 img = img.convert('L')
 pixdata = img.load()
 w, h = img.size
 for y in range(h):
 for x in range(w):
  if pixdata[x, y] < threshold:
  pixdata[x, y] = 0
  else:
  pixdata[x, y] = 255
 
 return img
 
 
def cfs(im,x_fd,y_fd):
 '''用队列和集合记录遍历过的像素坐标代替单纯递归以解决cfs访问过深问题
 '''
 
 # print('**********')
 
 xaxis=[]
 yaxis=[]
 visited =set()
 q = Queue()
 q.put((x_fd, y_fd))
 visited.add((x_fd, y_fd))
 offsets=[(1, 0), (0, 1), (-1, 0), (0, -1)]#四邻域
 
 while not q.empty():
  x,y=q.get()
 
  for xoffset,yoffset in offsets:
   x_neighbor,y_neighbor = x+xoffset,y+yoffset
 
   if (x_neighbor,y_neighbor) in (visited):
    continue # 已经访问过了
 
   visited.add((x_neighbor, y_neighbor))
 
   try:
    if im[x_neighbor, y_neighbor] == 0:
     xaxis.append(x_neighbor)
     yaxis.append(y_neighbor)
     q.put((x_neighbor,y_neighbor))
 
   except IndexError:
    pass
 # print(xaxis)
 if (len(xaxis) == 0 | len(yaxis) == 0):
 xmax = x_fd + 1
 xmin = x_fd
 ymax = y_fd + 1
 ymin = y_fd
 
 else:
 xmax = max(xaxis)
 xmin = min(xaxis)
 ymax = max(yaxis)
 ymin = min(yaxis)
 #ymin,ymax=sort(yaxis)
 
 return ymax,ymin,xmax,xmin
 
def detectFgPix(im,xmax):
 '''搜索区块起点
 '''
 
 h,w = im.shape[:2]
 for y_fd in range(xmax+1,w):
  for x_fd in range(h):
   if im[x_fd,y_fd] == 0:
    return x_fd,y_fd
 
def CFS(im):
 '''切割字符位置
 '''
 
 zoneL=[]#各区块长度L列表
 zoneWB=[]#各区块的X轴[起始,终点]列表
 zoneHB=[]#各区块的Y轴[起始,终点]列表
 
 xmax=0#上一区块结束黑点横坐标,这里是初始化
 for i in range(10):
 
  try:
   x_fd,y_fd = detectFgPix(im,xmax)
   # print(y_fd,x_fd)
   xmax,xmin,ymax,ymin=cfs(im,x_fd,y_fd)
   L = xmax - xmin
   H = ymax - ymin
   zoneL.append(L)
   zoneWB.append([xmin,xmax])
   zoneHB.append([ymin,ymax])
 
  except TypeError:
   return zoneL,zoneWB,zoneHB
 
 return zoneL,zoneWB,zoneHB
 
 
def cutting_img(im,im_position,img,xoffset = 1,yoffset = 1):
 filename = './out_img/' + img.split('.')[0]
 # 识别出的字符个数
 im_number = len(im_position[1])
 # 切割字符
 for i in range(im_number):
 im_start_X = im_position[1][i][0] - xoffset
 im_end_X = im_position[1][i][1] + xoffset
 im_start_Y = im_position[2][i][0] - yoffset
 im_end_Y = im_position[2][i][1] + yoffset
 cropped = im[im_start_Y:im_end_Y, im_start_X:im_end_X]
 cv2.imwrite(filename + '-cutting-' + str(i) + '.jpg',cropped)
 
 
 
def main():
 filedir = './easy_img'
 
 for file in os.listdir(filedir):
 if fnmatch(file, '*.jpeg'):
  img_name = file
 
  # 自适应阈值二值化
  im = _get_dynamic_binary_image(filedir, img_name)
 
  # 去除边框
  im = clear_border(im,img_name)
 
  # 对图片进行干扰线降噪
  im = interference_line(im,img_name)
 
  # 对图片进行点降噪
  im = interference_point(im,img_name)
 
  # 切割的位置
  im_position = CFS(im)
 
  maxL = max(im_position[0])
  minL = min(im_position[0])
 
  # 如果有粘连字符,如果一个字符的长度过长就认为是粘连字符,并从中间进行切割
  if(maxL > minL + minL * 0.7):
  maxL_index = im_position[0].index(maxL)
  minL_index = im_position[0].index(minL)
  # 设置字符的宽度
  im_position[0][maxL_index] = maxL // 2
  im_position[0].insert(maxL_index + 1, maxL // 2)
  # 设置字符X轴[起始,终点]位置
  im_position[1][maxL_index][1] = im_position[1][maxL_index][0] + maxL // 2
  im_position[1].insert(maxL_index + 1, [im_position[1][maxL_index][1] + 1, im_position[1][maxL_index][1] + 1 + maxL // 2])
  # 设置字符的Y轴[起始,终点]位置
  im_position[2].insert(maxL_index + 1, im_position[2][maxL_index])
 
  # 切割字符,要想切得好就得配置参数,通常 1 or 2 就可以
  cutting_img(im,im_position,img_name,1,1)
 
  # 识别验证码
  cutting_img_num = 0
  for file in os.listdir('./out_img'):
  str_img = ''
  if fnmatch(file, '%s-cutting-*.jpg' % img_name.split('.')[0]):
   cutting_img_num += 1
  for i in range(cutting_img_num):
  try:
   file = './out_img/%s-cutting-%s.jpg' % (img_name.split('.')[0], i)
   # 识别验证码
   str_img = str_img + image_to_string(Image.open(file),lang = 'eng', config='-psm 10') #单个字符是10,一行文本是7
  except Exception as err:
   pass
  print('切图:%s' % cutting_img_num)
  print('识别为:%s' % str_img)
 
if __name__ == '__main__':
 main()

以上就是Python实现验证码识别的详细内容,更多关于Python验证码识别的资料请关注服务器之家其它相关文章!

原文链接:https://www.cnblogs.com/qqandfqr/p/7866650.html

延伸 · 阅读

精彩推荐