在python存在一个比较好用的简单验证码识别库ddddocr
import ddddocr
# 需要识别图片的路径
with open("captcha.jpg", 'rb') as f:
img_bytes = f.read()
res = ocr.classification(img_bytes)
print(res) # 识别后的结果
这种操作简单,易上手,是各大爬虫编写者的利器
有识别就有反识别,这里提供一种操作用于反ddddocr的识别
我们先了解图形学中膨胀和腐蚀是什么
归根到底,膨胀就是把细节放大,腐蚀就是把细节忽略
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 1 0 0 0 0 0 -> 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
(把单独零散的1去除了)
这种操作便是针对验证码图片中的噪点,通过腐蚀操作减少噪点的出现
而在许多ocr中,会采取一种叫闭运算的操作,即腐蚀后膨胀,这样不仅能减少干扰,还能扩大重要的识别轮廓
我们从闭运算下手突破
问:为什么我们的噪点会被消除?
答:噪点比例(噪点的大小 / 字符的粗细)不够大
在ocr中,一般会通过图片的比例进行一定程度的闭运算,为了消除噪点而保留原有的字符,就会寻找一个合适程度使得可以消除噪点且保留字符
问:我们是否可以通过一种方法,使得噪点和字符同时存在呢?
答:可以,把噪点大小设置为字符粗细即可
通过这种方式,ocr就无法找到一种方法使得可以消除噪点且保留字符,噪点和字符同时存在
显然,这种方法还不足够,许多ocr已经具有对颜色的敏感识别,不再是一味的转为灰度图再识别
我们在此基础上,设置噪点颜色与字符颜色相同
使用pillow库
# 生成验证码图片
from PIL import Image, ImageDraw, ImageFont
import random, ddddocr
# 字符组
# 有些字母或数字比较具有迷惑性,比如:L I l 1 i O o 0,这里进行了排除
chars = "ABCDEFHJKLMNPQRSTUVXYZ23456789"
# ddddocr识别
ocr = ddddocr.DdddOcr()
# 生成验证码
def generate_code(length):
# 随机取出
char_list = random.choices(chars, k=length)
code = "".join(char_list)
return code
# 随机生成颜色
def get_color(min_value=0, max_value=255):
return (
random.randint(min_value, max_value),
random.randint(min_value, max_value),
random.randint(min_value, max_value)
)
def main(width=120, height=50, code_length=4):
"""
width,height为生成图片的宽高
code_length为生成验证码的长度
"""
# 新建一个图像对象
image = Image.new(
'RGB',
(width, height),
get_color(64, 255) # 使用一个比较明亮的颜色作为背景
)
# 设置字体和字号
font = ImageFont.truetype("C:\\Windows\\Fonts\\arial.ttf", size=40)
draw = ImageDraw.Draw(image)
# 获取一个随机验证码
text = generate_code(code_length)
# 获取字符串的宽度和高度
bbox = draw.textbbox(xy=(width, height), text=text, font=font)
text_width = bbox[2] - bbox[0]
text_heigtht = bbox[3] - bbox[1]
# 绘画验证码的起始位置(居中)
offset_x = (width - text_width) // 2
offset_y = (height - text_heigtht) // 2
# 每个字符的间隔
width_gap = text_width // code_length
# 对每个字符操作
for char in text:
# 生成一个比较暗的颜色
color = get_color(0, 64)
draw.text(
xy=(
# 每个字符的随机偏移量
offset_x + random.randint(-4, 4),
offset_y + random.randint(-4, 4)
),
text=char,
font=font,
fill=color
)
# 绘制干扰噪点像素 概率是0.025,可以自行更改
for j in range(int(width * height * 0.025)):
# 生成噪点的随机位置
pos_x, pos_y = random.randint(0, width), random.randint(0, height)
draw.rectangle(
# 噪点的大小,这里需要根据字符的粗细来调整
(pos_x, pos_y, pos_x + random.randint(0, 3), pos_y + random.randint(0, 3)),
# 这里一定要使用与字符相同的颜色
fill=color
)
# 下一个字符的位置
offset_x += width_gap
res = ocr.classification(image)
# 一般验证码对大小写不敏感
print("your's code:", text.lower())
print("ocr's result:", res.lower())
# 展示图像
image.show()
if __name__ == "__main__":
main()
如果代码出现:
AttributeError: module 'PIL.Image' has no attribute 'ANTIALIAS'
解决方案:
解决ddddocr AttributeError: module ‘PIL.Image‘ has no attribute ‘ANTIALIAS‘报错
可以见到,迷惑性还是很强的,而且图像也很清楚
测试识别成功率,把代码更改为
# 一般验证码对大小写不敏感
# print("your's code:", text.lower())
# print("ocr's result:", res.lower())
# 展示图像
# image.show()
#
return res.lower() == text.lower()
if __name__ == "__main__":
total = 10000
couter = 0
for i in range(total):
print("NO.",i)
if main():
couter += 1
print(couter*100/total,"%")
不添加噪点
97.36 %
添加噪点后
2.11 %
有了大幅度提升
之后再更新其他迷惑操作