当前位置:   article > 正文

Python使用pytesseract进行验证码图像识别

pytesseract

目录

图像读取主要用了两个库,不同的库是不同的对象:

本次图像识别测试需要以下两个条件:

1. 验证码获取

2. 登录网站

3. 图像处理

4. 验证码识别测试

测试说明

测试代码

测试结果

5. 成功示例的再识别测试

测试说明

测试代码

测试结果

测试注意事项

6. 集成融合投票模型,并使用多进程机制运行程序

测试说明

测试代码

测试结果

单进程运行程序的结果

并行运行程序时的效果及结果​

7. 失败示例的再识别

8. 其他

图像读取主要用了两个库,不同的库是不同的对象:

  1. # plt.imread和PIL.Image.open读入的都是RGB顺序
  2. from PIL import Image
  3. img = Image.open('xxxx.png') # 读取Image对象
  4. img.save('xxx.png')
  5. '''
  6. print(img.mode) # 有'1', 'L', 'P', 'RGB', 'RGBA'等
  7. '1': 表示黑白模式照片
  8. 'L': 表示灰度模式照片
  9. 'RGB': 表示RGB通道模式的彩色照片
  10. 'RGBA': 表示RGB通道及Alpha通道的照片
  11. '''
  12. img.show() # 显示图片
  13. img.convert('L') # 转换为'L'模式
  14. img.crop((20,30,300,200)) # 裁剪
  15. # Image.eval(img, function) # 对每个像素/通道进行函数处理
  16. import cv2
  17. # opencv中cv2.imread读入的是BGR通道顺序
  18. # flags=0是灰度模式,flags=1是默认的彩色模式
  19. # im = cv2.imread('xxxx.png', flags=0) # 读取图像array对象、
  20. im = cv2.imread("imgCode_grey200.jpg", flags=cv2.IMREAD_GRAYSCALE)
  21. cv2.imwrite('imgCode_grey200.jpg', im)
  22. plt.imshow(im) # 显示图片
  23. # plt.show()
  24. # plt.close()
  25. # cv2.imshow('im', im) # 显示图片
  26. ## PIL.Image.open和cv2.imread的比较与相互转换的方法
  27. # 当图片是png格式,读取结果是一致的;
  28. # 当图片是jpg格式时,读取结果是不一致的。
  29. # 这可能是因为Image.open 与 cv2.imread 在解码jpg时运算有差异。
  30. # 简单转换
  31. # im = np.array(img, np.uint8) # copy=True
  32. # im = np.asarray(img, np.uint8) # copy=False
  33. # 不设置dtype为数值的话,得到的可能是布尔值的数组,比如二值化后的图片
  34. im = np.asarray(img)
  35. # img = Image.fromarray(np.uint8(im))
  36. img = Image.fromarray(im)
  37. # 标准转换
  38. def PILImageToCV(imagePath):
  39. # PIL Image转换成OpenCV格式
  40. img = Image.open(imagePath)
  41. plt.imshow(img)
  42. img = cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)
  43. plt.imshow(img)
  44. plt.show()
  45. def CVImageToPIL(imagePath):
  46. # OpenCV图片转换为PIL image
  47. img = cv2.imread(imagePath)
  48. plt.imshow(img)
  49. img2 = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
  50. plt.imshow(img2)
  51. plt.show()

本次图像识别测试需要以下两个条件:

OCR软件:tesseract.exe,通过命令行调用来识别字符。

OCR软件的Python接口:pytesseract,内核是OCR软件tesseract

OCR:Optical Character Recognition (光学字符识别)

备注:另外的一个接口PyOCR,内核可包括tesseract或其他,但也得提前安装OCR软件。

  1. import pytesseract
  2. def get_result_by_imgCode_recognition(img):
  3. # 进行验证码识别
  4. result = pytesseract.image_to_string(img) # 接口默认返回的是字符串
  5. # ''.join(result.split()) # 去掉全部空格和\n\t等
  6. result = ''.join(list(filter(str.isalnum, result))) # 只保留字母和数字
  7. return result
  8. def pass_counter(img, img_value):
  9. # 辨别是否识别正确
  10. rst = get_result_by_imgCode_recognition(img)
  11. if rst == img_value:
  12. return 1
  13. else:
  14. return 0
  15. def most_frequent(lst):
  16. # 获取列表最频繁的元素,可用于集成投票获得识别结果
  17. # return max(lst, key=lst.count)
  18. return max(set(lst), key=lst.count)

1. 验证码获取

通过浏览器的开发者工具,发现验证码图片为base64编码的文件,通过解码后写入文件。

 

 

  1. def fetch_imgCode():
  2. # 获取验证码
  3. url_imgCode = 'xxxx'
  4. html = requests.post(url_imgCode)
  5. '''
  6. print(f'imgCode rsp: {html.text}')
  7. imgCode rsp: {
  8. "data":
  9. {"image_buf_str": "/9j/4AAQ....KAP/9k=",
  10. "image_code": "16501881494161"},
  11. "error_code": 0, "msg": {"en-us": "Success", "zh-CN": "\u6210\u529f"},
  12. "request": "POST /public/verificationCode/imgCode"}
  13. '''
  14. html = html.json()
  15. image_buf_str = html['data']['image_buf_str']
  16. image_code = html['data']['image_code']
  17. # 保存base64编码的图片为图片文件
  18. with open(f'./imgCode_png_raw/imgCode_{image_code}.png', 'wb') as f:
  19. f.write(base64.b64decode(image_buf_str))
  20. return image_code

2. 登录网站

通过向网站发起post请求,可登录网站,一般情况下:

输入image_code对应的正确的验证码的值image_value,即可登录成功。

反过来,如果登录成功,也意味着我们识别出来的验证码值image_value是正确。

  1. HEADERS_PORTAL = {
  2. 'User-Agent': 'xxxx',
  3. "Content-Type": "application/json",
  4. }
  5. def login(image_code, image_value):
  6. login_flag = False
  7. url_login = 'xxxx'
  8. data_login = {"account": "DEMO_Tong",
  9. "password": "9xdsaGcy",
  10. "image_code": image_code,
  11. "captcha": image_value,
  12. "nickname": "DEMO_Tong", "client_type": 100}
  13. html = requests.post(url_login, data=json.dumps(data_login), headers=HEADERS_PORTAL)
  14. # print(f'login info: {html.text}')
  15. html = html.json()
  16. if html.get('data'):
  17. if html.get('data').get('token'):
  18. login_flag = True
  19. return login_flag

3. 图像处理

灰度处理、二值处理、去噪、膨胀及腐蚀、倾斜矫正、字符切割、归一化等

  1. # 灰度处理和二值处理
  2. # lookup_table = [0 if i < 200 else 1 for i in range(256)]
  3. def gray_processing(img, threshold = 127):
  4. # 转为灰度模式
  5. img = img.convert('L')
  6. # 转为二值模式,阈值默认是 127,大于为白色,否则黑色。
  7. # 为什么127呢,256/2=128, 2^8=256, 一个字节byte是8个比特bit
  8. # image.convert('1') # 即 threshold = 127
  9. # threshold = 125
  10. lookup_table = [0 if i < threshold else 1 for i in range(256)]
  11. img = img.point(lookup_table, '1')
  12. return img
  13. # 膨胀腐蚀法
  14. def erode_dilate(im, threshold=2):
  15. # im = cv2.imread('xxx.jpg', 0)
  16. # cv2.imshow('xxx.jpg', im)
  17. # (threshold, threshold) 腐蚀矩阵大小
  18. kernel = np.ones((threshold, threshold), np.uint8)
  19. # 膨胀
  20. erosion = cv2.erode(im, kernel, iterations=1)
  21. # cv2.imwrite('imgCode_erosion.jpg', erosion)
  22. # Image.open('imgCode_erosion.jpg').show()
  23. # # 腐蚀
  24. # eroded = cv2.dilate(erosion, kernel, iterations=1)
  25. # cv2.imwrite('imgCode_eroded.jpg', eroded)
  26. # Image.open('imgCode_eroded.jpg').show()
  27. return erosion

 

4.验证码识别测试

测试说明

根据不同的图像处理方式,进行验证码识别测试,积累成功识别示例的同时,观察不同处理方式的识别效果。测试中获取的验证码具有随机性,有容易识别的,也有不容易识别的,但客观上属于同一难度的验证码。

本次识别测试将分为3组,每次识别10000张,通过模拟登录网站来验证是否识别正确。

        第一组直接识别原图片文件,标签为“raw”

        第二组识别灰度处理和阈值为200的二值处理后的图片对象,标签为“gray”

        第三组识别经灰度、二值和膨胀处理后的图片对象,标签为“erosion”

识别的结果根据图像处理方式和识别正确与否,放在不同文件夹,识别结果也追加到文件名:

        imgCode_png_raw:存放从网站保存下来的原始图片

        imgCode_png_raw_pass:存放raw测试识别正确的原始图片

        imgCode_png_raw_fail:存放raw测试识别失败的原始图片

        imgCode_png_raw_gray_pass:存放gray测试识别正确的原始图片

        imgCode_png_raw_gray_fail:存放gray测试识别失败的已处理后的图片

        imgCode_png_raw_gray_erosion_pass:存放erosion测试识别正确的原始图片

        imgCode_png_raw_gray_erosion_fail:存放erosion测试识别失败的已处理后的图片

 

 

 

 注意:通过浏览器的开发工具可以发现,验证码使用的字体应该是 element-icons.535877f5.woff

测试代码

  1. from tqdm import tqdm, trange
  2. from tqdm.contrib import tzip # tqdm是进度条模块,为了便于观察处理进度
  3. TEST_TOTAL = 10000 # 测试数量1万张
  4. def test_raw():
  5. print('raw: ')
  6. pass_count = 0
  7. # for _ in range(TEST_TOTAL):
  8. for _ in trange(TEST_TOTAL):
  9. try:
  10. image_code = fetch_imgCode()
  11. img = Image.open(f'./imgCode_png_raw/imgCode_{image_code}.png')
  12. result = get_result_by_imgCode_recognition(img)
  13. login_flag = login(image_code, result)
  14. if login_flag:
  15. img.save(f'./imgCode_png_raw_pass/imgCode_{image_code}_{result}.png')
  16. pass_count += 1
  17. else:
  18. img.save(f'./imgCode_png_raw_fail/imgCode_{image_code}_{result}.png')
  19. except:
  20. info = sys.exc_info()
  21. print(info)
  22. print(f'pass_rate: {pass_count/TEST_TOTAL*100}')
  23. def test_gray():
  24. print('gray: ')
  25. pass_count = 0
  26. for _ in trange(TEST_TOTAL):
  27. try:
  28. image_code = fetch_imgCode()
  29. img = Image.open(f'./imgCode_png_raw/imgCode_{image_code}.png')
  30. img_gray = gray_processing(img, threshold=200)
  31. result = get_result_by_imgCode_recognition(img_gray)
  32. login_flag = login(image_code, result)
  33. if login_flag:
  34. img.save(f'./imgCode_png_raw_gray_pass/imgCode_{image_code}_{result}.png')
  35. pass_count += 1
  36. else:
  37. img_gray.save(f'./imgCode_png_raw_gray_fail/imgCode_{image_code}_{result}.png')
  38. except:
  39. info = sys.exc_info()
  40. print(info)
  41. print(f'pass_rate: {pass_count/TEST_TOTAL*100}')
  42. def test_erosion():
  43. print('erosion: ')
  44. pass_count = 0
  45. for _ in trange(TEST_TOTAL):
  46. try:
  47. image_code = fetch_imgCode()
  48. img = Image.open(f'./imgCode_png_raw/imgCode_{image_code}.png')
  49. img_gray = gray_processing(img, threshold=200)
  50. im = np.asarray(img_gray, np.uint8) # gray之后变成array,值变为0和1,有效去噪点
  51. erosion = erode_dilate(im, threshold=2)
  52. img1 = Image.fromarray(erosion*255) # 值为0到1,整个图片都是黑色的。
  53. result = get_result_by_imgCode_recognition(img1) # 这里用array也可以
  54. login_flag = login(image_code, result)
  55. if login_flag:
  56. img.save(f'./imgCode_png_raw_gray_erosion_pass/imgCode_{image_code}_{result}.png')
  57. pass_count += 1
  58. else:
  59. img1.save(f'./imgCode_png_raw_gray_erosion_fail/imgCode_{image_code}_{result}.png')
  60. except:
  61. info = sys.exc_info()
  62. print(info)
  63. print(f'pass_rate: {pass_count/TEST_TOTAL*100}')

测试结果

 

5. 成功示例的再识别测试

测试说明

将通过raw、gray、erosion识别测试正确的示例按照1:1:1的样本比例拷贝到imgCode_pass文件夹,此时的验证码样本都是有正确识别结果的,且数量一定和样本比例均衡,可以用三种处理方式进行再识别,比较三种处理方式的识别效果。

此次再识别测试的样本比例1:1:1,各8844张,共26532张。

测试代码

  1. def test_pass_raw():
  2. pass_list = os.listdir('./imgCode_pass')
  3. pass_value_list = [img_file[-8:-4] for img_file in pass_list]
  4. pass_cnt1 = 0
  5. pass_amt = len(pass_list)
  6. print(f'pass_amt: {pass_amt}')
  7. # for img_file, img_value in zip(pass_list, pass_value_list):
  8. for img_file, img_value in tzip(pass_list, pass_value_list):
  9. # raw
  10. img = Image.open(f'./imgCode_pass/{img_file}')
  11. pass_cnt1 += pass_counter(img, img_value)
  12. print(f'raw: \npass_rate:{pass_cnt1 / pass_amt * 100}')
  13. def test_pass_gray():
  14. pass_list = os.listdir('./imgCode_pass')
  15. pass_value_list = [img_file[-8:-4] for img_file in pass_list]
  16. pass_cnt2 = 0
  17. pass_amt = len(pass_list)
  18. print(f'pass_amt: {pass_amt}')
  19. # for img_file, img_value in zip(pass_list, pass_value_list):
  20. for img_file, img_value in tzip(pass_list, pass_value_list):
  21. # raw
  22. img = Image.open(f'./imgCode_pass/{img_file}')
  23. # raw + grey200
  24. img = gray_processing(img, threshold=200)
  25. pass_cnt2 += pass_counter(img, img_value)
  26. print(f'raw + grey200: \npass_rate:{pass_cnt2/pass_amt*100}')
  27. def test_pass_erosion():
  28. pass_list = os.listdir('./imgCode_pass')
  29. pass_value_list = [img_file[-8:-4] for img_file in pass_list]
  30. pass_cnt3 = 0
  31. pass_amt = len(pass_list)
  32. print(f'pass_amt: {pass_amt}')
  33. # for img_file, img_value in zip(pass_list, pass_value_list):
  34. for img_file, img_value in tzip(pass_list, pass_value_list):
  35. # raw
  36. img = Image.open(f'./imgCode_pass/{img_file}')
  37. # raw + grey200
  38. img = gray_processing(img, threshold=200)
  39. # raw + grey200 + erosion
  40. im = np.asarray(img, np.uint8) # gray之后变成array,值变为0和1,有效去噪点
  41. erosion = erode_dilate(im, threshold=2)
  42. img1 = Image.fromarray(erosion*255) # 值为0到1,整个图片都是黑色的。
  43. pass_cnt3 += pass_counter(img1, img_value)
  44. print(f'raw + grey200 + erosion(Image): \npass_rate:{pass_cnt3/pass_amt*100}')

测试结果

 

测试注意事项

此次测试特别需要注意样本比例,如果样本全为通过raw识别测试正确的来进行再识别,使用raw方式将为100%识别正确。下图是使用大部分为raw识别成功的示例来进行再识别的结果,发现不同处理方式的识别模型的识别能力呈下降趋势,越接近raw识别模型的模型精度越好,反正越差。

6. 集成融合投票模型,并使用多进程机制运行程序

测试说明

基于不同的模型的识别效果不一,考虑集成学习的模型融合,使用投票法,通过raw、gray、erosion三种模型进行识别预测投票,将票数多的识别结果作为集成融合投票模型的识别结果,来进行登录验证。

基于集成融合投票模型需要对同一张验证码示例进行3次识别,比较耗时,故使用多进程机制并行地运行程序,减少程序所消耗的时间。

测试代码

  1. def test_ensemble_vote(kwargs):
  2. result_list = []
  3. image_code = fetch_imgCode()
  4. img = Image.open(f'./imgCode_png_raw/imgCode_{image_code}.png')
  5. result_list.append(get_result_by_imgCode_recognition(img))
  6. img_gray = gray_processing(img, threshold=200)
  7. result_list.append(get_result_by_imgCode_recognition(img_gray))
  8. im = np.asarray(img_gray, np.uint8) # gray之后变成array,值变为0和1,有效去噪点
  9. erosion = erode_dilate(im, threshold=2)
  10. img1 = Image.fromarray(erosion*255) # 值为0到1,整个图片都是黑色的。
  11. result_list.append(get_result_by_imgCode_recognition(img1))
  12. result = max(result_list, key=result_list.count)
  13. login_flag = login(image_code, result)
  14. return login_flag
  15. def test_ensemble_vote_multi():
  16. print('test_ensemble_vote_multi: ')
  17. from multiprocessing import Pool
  18. pool = Pool()
  19. pool_result_list = pool.map(test_ensemble_vote, trange(TEST_TOTAL))
  20. pool.close()
  21. pool.terminate()
  22. pool.join()
  23. pass_count = pool_result_list.count(True)
  24. print(f'pass_rate: {pass_count/TEST_TOTAL*100}')

测试结果

单进程运行程序的结果

 并行运行程序时的效果及结果

7. 失败示例的再识别

使用不同二值化阈值识别的融合投票模型对元模型(raw、gray或erosion)识别失败的例子进行再识别。 

  1. def test_fail():
  2. ## 单独一张图片,不同的二值化阈值,最频繁预测结果
  3. # img = Image.open(f'./imgCode_fail/imgCode_16501101286728_359.png')
  4. # img.show()
  5. # result_list = []
  6. # for i in trange(120,200,1):
  7. # img_gray = gray_processing(img, threshold=i)
  8. # img_gray.show()
  9. # result = get_result_by_imgCode_recognition(img_gray)
  10. # result_list.append(result)
  11. # print(f'most_frequent(lst): {most_frequent(result_list)}')
  12. ## 多张图片,不同灰度阈值,最频繁预测结果,目的是寻找最佳阈值
  13. fail_list = os.listdir('./imgCode_fail')
  14. result_list_1 = []
  15. for img_file in fail_list:
  16. img = Image.open(f'./imgCode_fail/{img_file}')
  17. result_list_2 = []
  18. for i in trange(120,200,10):
  19. img_gray = gray_processing(img, threshold=i)
  20. result = get_result_by_imgCode_recognition(img_gray)
  21. result_list_2.append(result)
  22. result_list_1.append(result_list_2)
  23. for img_file, lst in zip(fail_list, result_list_1):
  24. print(f'{img_file}, most_frequent(lst): {most_frequent(lst)}')

8.其他

 

 

 

 

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/148716?site
推荐阅读
相关标签
  

闽ICP备14008679号