对于大多数图文验证码,均可以使用开源OCR识别库进行处理,比如ddddocr,接下来以ddddocr库进行示范
一、ddddocr库安装和使用
-
安装
pip install ddddocr
-
使用代码示例
import ddddocr ocr = ddddocr.DdddOcr(old=True) with open("test.jpg", 'rb') as f: image = f.read() res = ocr.classification(image) print(res)
old = True表示使用老版本模型,因为某些情况下老版本识别效果更佳
二、案例示范
-
制作验证码
import random import string from PIL import Image, ImageDraw, ImageFont def createCaptcha(imgname): ''' 生成5个字符长度的验证码 :param imgname: 生成验证码图片名 :return: 验证码字符串 ''' # 定义变量,用于画面的背景色、宽、高 # bgcolor = (random.randrange(255), random.randrange(255), 100) bgcolor = 'white' width = 100 height = 30 # 创建画面对象 im = Image.new('RGB', (width, height), bgcolor) # 创建画笔对象 draw = ImageDraw.Draw(im) # 绘制50个噪点 for i in range(50): point_position = (random.randrange(width), random.randrange(height)) point_color = (random.randrange(255), random.randrange(255), random.randrange(255)) draw.point(point_position, point_color) # 绘制2条干扰线 for i in range(2): start_position = (random.randrange(width), random.randrange(height)) end_position = (random.randrange(width), random.randrange(height)) line_color = (random.randrange(255), random.randrange(255), random.randrange(255)) draw.line([start_position, end_position], line_color) # 定义验证码的备选值 str1 = string.ascii_letters + string.digits # 大小写字母+数字共62个 # 随机选取5个值作为验证码 rand_str = random.sample(str1, 5) # 字体 font_type = 'arial.ttf' # 字体类型 font_size = 20 # 字体大小 # 构造字体对象 font = ImageFont.truetype(font_type, font_size) # 构造字体颜色 fontcolor = (random.randrange(255), random.randrange(255), random.randrange(255)) # 绘制5个字 for i in range(5): draw.text((20 * i + random.randint(1, 3), random.randint(0, 1)), rand_str[i], font=font, fill=fontcolor) im.save(imgname) # 验证码内容命名图片,保存在当前目录下 # 验证码字符串 captcha_str = "".join(rand_str) return captcha_str
View Code生成的验证码示例:
-
验证码预处理
import cv2 def pre_handler(ori_imgname, new_imgname): ''' 图片预处理 :param ori_imgname: 原验证码图片名 :param new_imgname: 预处理后图片名 :return: ''' im = cv2.imread(ori_imgname) # 转灰度图像 im = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY) height, width = im.shape # (30, 100) # 去噪线 # opencv库中的矩阵点x, y需要反着理解 for x in range(1, height - 1): for y in range(1, width - 1): count = 0 if im[x, y - 1] > 245: count += 1 if im[x, y + 1] > 245: count += 1 if im[x - 1, y] > 245: count += 1 if im[x + 1, y] > 245: count += 1 if count > 2: # 根据该点周围4个点中白点的个数,超过2个就转成白的 im[x, y] = 255 # 去噪点 for x in range(height): for y in range(width): # 第一排 if x == 0: # 左上角顶点 if y == 0: num = 3 if im[x, y + 1] > 245: num -= 1 if im[x + 1, y] > 245: num -= 1 if im[x + 1, y + 1] > 245: num -= 1 # 右上角顶点 elif y == width - 1: num = 3 if im[x, y - 1] > 245: num -= 1 if im[x + 1, y - 1] > 245: num -= 1 if im[x + 1, y] > 245: num -= 1 # 第一排中间点 else: num = 5 if im[x, y - 1] > 245: num -= 1 if im[x, y + 1] > 245: num -= 1 if im[x + 1, y - 1] > 245: num -= 1 if im[x + 1, y] > 245: num -= 1 if im[x + 1, y + 1] > 245: num -= 1 # 最后一排 elif x == height - 1: # 左下角顶点 if y == 0: num = 3 if im[x, y + 1] > 245: num -= 1 if im[x - 1, y] > 245: num -= 1 if im[x - 1, y + 1] > 245: num -= 1 # 右下角顶点 elif y == width - 1: num = 3 if im[x, y - 1] > 245: num -= 1 if im[x - 1, y - 1] > 245: num -= 1 if im[x - 1, y] > 245: num -= 1 # 最后一排中间点 else: num = 5 if im[x, y - 1] > 245: num -= 1 if im[x, y + 1] > 245: num -= 1 if im[x - 1, y - 1] > 245: num -= 1 if im[x - 1, y] > 245: num -= 1 if im[x - 1, y + 1] > 245: num -= 1 # 中间排 else: # 第一列中间点 if y == 0: num = 5 if im[x - 1, y] > 245: num -= 1 if im[x + 1, y] > 245: num -= 1 if im[x - 1, y + 1] > 245: num -= 1 if im[x, y + 1] > 245: num -= 1 if im[x + 1, y + 1] > 245: num -= 1 # 最后一列中间点 elif y == width - 1: num = 5 if im[x - 1, y] > 245: num -= 1 if im[x + 1, y] > 245: num -= 1 if im[x - 1, y - 1] > 245: num -= 1 if im[x, y - 1] > 245: num -= 1 if im[x + 1, y - 1] > 245: num -= 1 # 周围拥有8个点的点 else: num = 8 if im[x - 1, y - 1] > 245: num -= 1 if im[x - 1, y] > 245: num -= 1 if im[x - 1, y + 1] > 245: num -= 1 if im[x, y - 1] > 245: num -= 1 if im[x, y + 1] > 245: num -= 1 if im[x + 1, y - 1] > 245: num -= 1 if im[x + 1, y] > 245: num -= 1 if im[x + 1, y + 1] > 245: num -= 1 if num <= 1: im[x, y] = 255 cv2.imwrite(new_imgname, im)
View Code如上主要进行了灰度化、去噪线、去噪点,预处理后验证码示例:
-
验证码识别完整代码
import cv2 import random import string import ddddocr from PIL import Image, ImageDraw, ImageFont class CaptchaHandler: def main(self): ori_imgname = 'captcha.png' new_imgname = 'captcha_new.png' ori_str = self.createCaptcha(ori_imgname) # 制作验证码 self.pre_handler(ori_imgname, new_imgname) # 图片预处理(灰度化、去噪点、去噪线) ocr = ddddocr.DdddOcr(old=True) with open(ori_imgname, 'rb') as f: image1 = f.read() res1 = ocr.classification(image1) with open(new_imgname, 'rb') as f: image2 = f.read() res2 = ocr.classification(image2) if ori_str.lower() == res1.lower(): result1 = True else: result1 = False if ori_str.lower() == res2.lower(): result2 = True else: result2 = False print(f'验证码{ori_str} 原图识别结果:{res1},处理后识别结果:{res2}') return result1, result2 def pre_handler(self, ori_imgname, new_imgname): ''' 图片预处理 :param ori_imgname: 原验证码图片名 :param new_imgname: 预处理后图片名 :return: ''' im = cv2.imread(ori_imgname) # 转灰度图像 im = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY) height, width = im.shape # (30, 100) # 去噪线 # opencv库中的矩阵点x, y需要反着理解 for x in range(1, height - 1): for y in range(1, width - 1): count = 0 if im[x, y - 1] > 245: count += 1 if im[x, y + 1] > 245: count += 1 if im[x - 1, y] > 245: count += 1 if im[x + 1, y] > 245: count += 1 if count > 2: # 根据该点周围4个点中白点的个数,超过2个就转成白的 im[x, y] = 255 # 去噪点 for x in range(height): for y in range(width): # 第一排 if x == 0: # 左上角顶点 if y == 0: num = 3 if im[x, y + 1] > 245: num -= 1 if im[x + 1, y] > 245: num -= 1 if im[x + 1, y + 1] > 245: num -= 1 # 右上角顶点 elif y == width - 1: num = 3 if im[x, y - 1] > 245: num -= 1 if im[x + 1, y - 1] > 245: num -= 1 if im[x + 1, y] > 245: num -= 1 # 第一排中间点 else: num = 5 if im[x, y - 1] > 245: num -= 1 if im[x, y + 1] > 245: num -= 1 if im[x + 1, y - 1] > 245: num -= 1 if im[x + 1, y] > 245: num -= 1 if im[x + 1, y + 1] > 245: num -= 1 # 最后一排 elif x == height - 1: # 左下角顶点 if y == 0: num = 3 if im[x, y + 1] > 245: num -= 1 if im[x - 1, y] > 245: num -= 1 if im[x - 1, y + 1] > 245: num -= 1 # 右下角顶点 elif y == width - 1: num = 3 if im[x, y - 1] > 245: num -= 1 if im[x - 1, y - 1] > 245: num -= 1 if im[x - 1, y] > 245: num -= 1 # 最后一排中间点 else: num = 5 if im[x, y - 1] > 245: num -= 1 if im[x, y + 1] > 245: num -= 1 if im[x - 1, y - 1] > 245: num -= 1 if im[x - 1, y] > 245: num -= 1 if im[x - 1, y + 1] > 245: num -= 1 # 中间排 else: # 第一列中间点 if y == 0: num = 5 if im[x - 1, y] > 245: num -= 1 if im[x + 1, y] > 245: num -= 1 if im[x - 1, y + 1] > 245: num -= 1 if im[x, y + 1] > 245: num -= 1 if im[x + 1, y + 1] > 245: num -= 1 # 最后一列中间点 elif y == width - 1: num = 5 if im[x - 1, y] > 245: num -= 1 if im[x + 1, y] > 245: num -= 1 if im[x - 1, y - 1] > 245: num -= 1 if im[x, y - 1] > 245: num -= 1 if im[x + 1, y - 1] > 245: num -= 1 # 周围拥有8个点的点 else: num = 8 if im[x - 1, y - 1] > 245: num -= 1 if im[x - 1, y] > 245: num -= 1 if im[x - 1, y + 1] > 245: num -= 1 if im[x, y - 1] > 245: num -= 1 if im[x, y + 1] > 245: num -= 1 if im[x + 1, y - 1] > 245: num -= 1 if im[x + 1, y] > 245: num -= 1 if im[x + 1, y + 1] > 245: num -= 1 if num <= 1: im[x, y] = 255 cv2.imwrite(new_imgname, im) def createCaptcha(self, imgname): ''' 生成5个字符长度的验证码 :param imgname: 生成验证码图片名 :return: 验证码字符串 ''' # 定义变量,用于画面的背景色、宽、高 # bgcolor = (random.randrange(255), random.randrange(255), 100) bgcolor = 'white' width = 100 height = 30 # 创建画面对象 im = Image.new('RGB', (width, height), bgcolor) # 创建画笔对象 draw = ImageDraw.Draw(im) # 绘制50个噪点 for i in range(50): point_position = (random.randrange(width), random.randrange(height)) point_color = (random.randrange(255), random.randrange(255), random.randrange(255)) draw.point(point_position, point_color) # 绘制2条干扰线 for i in range(2): start_position = (random.randrange(width), random.randrange(height)) end_position = (random.randrange(width), random.randrange(height)) line_color = (random.randrange(255), random.randrange(255), random.randrange(255)) draw.line([start_position, end_position], line_color) # 定义验证码的备选值 str1 = string.ascii_letters + string.digits # 大小写字母+数字共62个 # 随机选取5个值作为验证码 rand_str = random.sample(str1, 5) # 字体 font_type = 'arial.ttf' # 字体类型 font_size = 20 # 字体大小 # 构造字体对象 font = ImageFont.truetype(font_type, font_size) # 构造字体颜色 fontcolor = (random.randrange(255), random.randrange(255), random.randrange(255)) # 绘制5个字 for i in range(5): draw.text((20 * i + random.randint(1, 3), random.randint(0, 1)), rand_str[i], font=font, fill=fontcolor) im.save(imgname) # 验证码内容命名图片,保存在当前目录下 # 验证码字符串 captcha_str = "".join(rand_str) return captcha_str if __name__ == '__main__': handler = CaptchaHandler() count1 = 0 count2 = 0 for i in range(200): result1, result2 = handler.main() if result1 is True: count1 += 1 if result2 is True: count2 += 1 print(f'识别200次,原图成功{count1}次,预处理后成功{count2}次')
View Code通过200次测试,在忽略字符大小写的情况下,ddddocr库识别原图和经过预处理后验证码的成功率都很高,接近90%的成功率