图片预处理

把验证码去噪、二值化、灰度处理

正方教务系统的二维码字符颜色都是蓝色。二维码转为RGB通道,遍历像素点,蓝色改为黑色,其他颜色改为白色。再转为灰度图片,方便后面识别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def stay_blue2gray(image):
image = image.convert('RGB')
height = image.size[0]
width = image.size[1]
for y in range(width):
for x in range(height):
pix_data = image.getpixel((x, y))
if pix_data[0] == 0 and pix_data[1] == 0 and pix_data[2] == 153:
image.putpixel((x, y), (0, 0, 0))
continue
else:
image.putpixel((x, y), (255, 255, 255))
image = image.convert('L')
return image

分割图片

验证码的字符总是出现在相同的位置,定长暴力切割

1
2
3
4
5
6
7
8
9
10
def split_image(image):
images = []
x = 5
y = 0
w = 12
h = 23
for i in range(4):
images.append(image.crop((x, y, x+w, y+h)))
x += w
return images

从本地加载字模

字模的文件名切片第一个就是字模对应的字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def ocr(images, model_path='./zfgetcode/data/model'):
result = ""
models = []
file_names = []

# 加载模型
for filename in os.listdir(model_path):
model = Image.open(model_path + "/" + filename)
file_names.append(filename[0:1])
models.append(model.convert('L'))

# 分别识别切割的单子字符
for image in images:
result += (single_char_ocr(image, models, file_names))
return result

OCR识别字符

遍历像素点,返回匹配度最高的结果,匹配算法还是大一程序设计课上,找最小数的穷举算法

avatar

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
def single_char_ocr(image, models, file_names):
# 识别一个字符
result = "#"
height = image.size[0]
width = image.size[1]
min_count = width * height
for i in range(len(models)):
model = models[i]
if model.size[1] - width > 2:
print("OCR_Code.py--71")
continue
count = 0
width_min = width if width < model.size[1] else model.size[1]
height_min = height if width < model.size[0] else model.size[0]
for y in range(width_min):
done = False
for x in range(height_min):
if model.getpixel((x, y)) != image.getpixel((x, y)):
count += 1
if count >= min_count:
done = True
break
if done:
break
if count <= 3:
result = file_names[i]
print(result)
elif count < min_count:
min_count = count
result = file_names[i]
return result

完整代码(OCR_code.py)

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
import os
from PIL import Image
import matplotlib.pyplot as plt


def show_code(image=None, path='./'):
if image:
image = Image.open(path + "code.gif")
plt.figure("CODE")
plt.imshow(image)
plt.show()
return image


def stay_blue2gray(image):
image = image.convert('RGB')
height = image.size[0]
width = image.size[1]
for y in range(width):
for x in range(height):
pix_data = image.getpixel((x, y))
if pix_data[0] == 0 and pix_data[1] == 0 and pix_data[2] == 153:
image.putpixel((x, y), (0, 0, 0))
continue
else:
image.putpixel((x, y), (255, 255, 255))
image = image.convert('L')
return image


def split_image(image):
images = []
x = 5
y = 0
w = 12
h = 23
for i in range(4):
images.append(image.crop((x, y, x+w, y+h)))
x += w
# images[i].save(str(i) + '.gif')
return images


def ocr(images, model_path='./zfgetcode/data/model'):
result = ""
models = []
file_names = []

# 加载模型
for filename in os.listdir(model_path):
model = Image.open(model_path + "/" + filename)
file_names.append(filename[0:1])
models.append(model.convert('L'))

# 分别识别切割的单子字符
for image in images:
result += (single_char_ocr(image, models, file_names))
return result


def single_char_ocr(image, models, file_names):
# 识别一个字符
result = "#"
height = image.size[0]
width = image.size[1]
min_count = width * height
for i in range(len(models)):
model = models[i]
if model.size[1] - width > 2:
print("OCR_Code.py--71")
continue
count = 0
width_min = width if width < model.size[1] else model.size[1]
height_min = height if width < model.size[0] else model.size[0]
for y in range(width_min):
done = False
for x in range(height_min):
if model.getpixel((x, y)) != image.getpixel((x, y)):
count += 1
if count >= min_count:
done = True
break
if done:
break
if count <= 3:
result = file_names[i]
print(result)
elif count < min_count:
min_count = count
result = file_names[i]
return result


def run(image_path='./'):
image = Image.open(image_path + "code.gif")
# show_code(image)
image = stay_blue2gray(image)
images = split_image(image)
result = ocr(images)
# print(result)
return result


if __name__ == "__main__":
run()

完整项目包括登录、获取信息和自动选课,其中登录模块的验证码识别采用OCR

OCR字模生成(Java实现)