一、OCR 技术概述
1.1 什么是 OCR?
OCR(Optical Character Recognition)通过计算机视觉技术,将图像中的文字转换为可编辑文本。核心流程包括:
- 图像采集:获取包含文字的图像
- 预处理:灰度化、降噪、二值化等
- 特征提取:字符轮廓检测、分割
- 文本识别:字符分类与序列识别
1.2 适用场景
- 图形验证码识别(如本文案例)
- 文档数字化(扫描件转文本)
- 车牌识别、票据处理等工业场景
二、环境安装
2.1、安装Tesseract
python的OCR库叫做Tesserocr,但是我们需要先安装Tesseract,这是Tesserocr的核心。
-
下载地址:digi.bib.uni-mannheim.de/tesseract/,建议安装最新版
-
安装时勾选Additional language data选项,这个选项会在我们安装Tesseract时自动下载各国语言。
-
配置环境变量:
- 添加系统变量
PATH
:值为Tesseract的安装目录,例如D:\Tesseract-OCR
- 新增系统变量
TESSDATA_PREFIX
:值为tessdata的目录,例如D:\Tesseract-OCR
tessdata
- 添加系统变量
2.2、安装tesserocr和其他库
bash
# pip安装
pip install tesserocr
# 其他库,pillow和numpy用来处理图像,retrying用来重试操作,selenium用来模拟登陆
pip install pillow numpy retrying selenium
如果tesserocr
安装时一直出错,可以网上下载whl文件,地址是github.com/simonflueck...,下载对应电脑python版本的即可,比如我电脑的是python3.13,那么就下载tesserocr-2.8.0-cp313-cp313-win_amd64.whl
这个包。
pip install tesserocr-2.8.0-cp313-cp313-win_amd64.whl
2.3、验证
python
import tesserocr
from PIL import Image
image = Image.open('test.png')
print(tesserocr.image_to_text(image))
网上随便找张简单的验证码图片,能够正常识别即成功安装。
三、图像预处理
3.1、灰度化
将彩色图像转为单通道灰度图,减少计算量
arduino
image = image.convert('L') # 'L' 表示 8-bit 像素,黑白模式
此外,还有其他8种像素类型,以下是这9种类型的区别:
模式 | 通道数 | 含义 | 适用场景 | 示例代码 |
---|---|---|---|---|
L | 1 | 8-bit 灰度(0-255) | 灰度化处理、降噪、二值化 | image.convert('L') |
RGB | 3 | 24-bit 彩色(红、绿、蓝) | 彩色验证码识别 | image.convert('RGB') |
RGBA | 4 | 32-bit 彩色 + 透明度通道 | 含透明背景的图片处理 | image.convert('RGBA') |
CMYK | 4 | 印刷四色(青、品红、黄、黑) | 打印文档处理 | image.convert('CMYK') |
YCbCr | 3 | 电视广播色域(亮度、色度) | 视频图像处理 | image.convert('YCbCr') |
I | 1 | 32-bit 整数灰度 | 高精度灰度计算 | image.convert('I') |
F | 1 | 32-bit 浮点灰度 | 科学计算、图像分析 | image.convert('F') |
RGBX | 4 | RGB + 填充值(X 为填充字节) | 兼容某些特殊格式 | image.convert('RGBX') |
L (带 Alpha) | 2 | 灰度 + 8-bit Alpha 通道 | 半透明效果处理 | image.convert('LA') |
一般用L和RGB多一些,L模式适合边缘检测、二值化等;而RGB保留完整的色彩信息,适合复杂验证码。
3.2、降噪处理
- 中值滤波
arduino
# 去除椒盐噪声,size为奇数,例如3、5、7
image = image.filter(ImageFilter.MedianFilter(size=3))
- 高斯模糊
ini
# 平滑高斯噪声,radius控制模糊程度
image = image.filter(ImageFilter.GaussianBlur(radius=1))
如果需要处理简单验证码的干扰点,中值滤波是比较适合的,高斯模糊更是话语背景复杂但需要保留整体结构的图像。
3.3、二值化
通过阈值分割分离前景与背景:
ini
# 转换为灰度图像
image = image.convert('L')
# 设置阈值
threshold = 50
# 转换为numpy数组
array=numpy.array(image)
# 对数组进行筛选处理,将灰度大于阈值的像素点设为255,小于阈值的设为0,255是白色,0是黑色
array = numpy.where(array > threshold, 255, 0)
# 将处理后的数组转换为图像
image = Image.fromarray(array.astype('uint8'))
# 显示图像
image.show()
OpenCV 提供了一个自适应阈值化函数,用于图像二值化处理。它可以根据图像局部区域的像素值动态计算阈值,比全局阈值更适合处理光照不均的图像。
四、Tesserocr用法
Tesserocr用法很简单,使用image_to_text
方法即可将处理后的图像识别成文字
arduino
tesserocr.image_to_text(image)
五、实战
以下是一个测试网站的验证码识别,链接是captcha7.scrape.center/,我们需要识别出验证码并模拟登陆成功,这是源码:
python
import tesserocr
from PIL import Image,ImageFilter
import numpy
from selenium import webdriver
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
import time
from io import BytesIO
import re
from retrying import retry # 重试装饰器
from selenium.webdriver.chrome.options import Options
import logging
# 配置日志
logging.basicConfig(level=logging.INFO,format='%(asctime)s - %(levelname)s : %(message)s',datefmt = '%Y-%m-%d %A %H:%M:%S')
# 识别处理
def preposser(image):
image = image.convert('L') # 转换为灰度图像
# 去噪,去噪后的图像会更清晰
image = image.filter(ImageFilter.MedianFilter(size=3))
# # 显示处理后的验证码图片
# image.show() # 显示图像
# 去完噪后发现图片已经很清晰,不需要再进行二值化处理了
# array = numpy.array(image) # 转换为numpy数组
# array = numpy.where(array > 80, 255, 0) # 对数组进行筛选处理,将灰度大于阈值的像素点设为255,小于阈值的设为0,255是白色,0是黑色
# image = Image.fromarray(array.astype('uint8')) # 将处理后的数组转换为图像
return image
# 重试装饰器,最多重试10次,重试条件是登录失败返回FALSE,每次重试间隔1秒
@retry(stop_max_attempt_number=10, retry_on_result=lambda x: x is False, wait_fixed=1000)
def main():
brower.get('https://captcha7.scrape.center/')
# 获取用户名输入框
username = brower.find_element(By.CSS_SELECTOR, '.username input[type="text"]')
username.send_keys('admin')
# 获取密码输入框
password = brower.find_element(By.CSS_SELECTOR, '.password input[type="password"]')
password.send_keys('admin')
# 获取验证码输入框
captcha_input = brower.find_element(By.CSS_SELECTOR, '.captcha input[type="text"]')
# 获取验证码图片
captcha_image = brower.find_element(By.CSS_SELECTOR, '#captcha')
image= captcha_image.screenshot_as_png # 获取验证码图片的二进制数据
# 将二进制数据转换为图像,这里需要用到io模块的BytesIO函数,用来读写二进制数据,二进制数据是不能直接转为文件的
image = Image.open(BytesIO(image))
# 处理验证码图片
image = preposser(image)
# 验证码识别并输入输入框
captcha=tesserocr.image_to_text(image)
# 去掉空格和特殊字符
captcha=re.sub('[^A-Za-z0-9]', '', captcha)
if not captcha or len(captcha) != 4:
logging.error("验证码识别失败")
return False
logging.info(f"识别的验证码:{captcha}")
captcha_input.send_keys(captcha)
button=brower.find_element(By.CSS_SELECTOR, '.login')
time.sleep(1)
# 点击登录按钮
button.click()
try:
# 显式等待,判断登录是否成功
wait=WebDriverWait(brower,5)
# 这里只需要判断登录成功是否出现在HTML结构中,用xpath判断,.表示当前节点的文本内容
condition=EC.presence_of_element_located((By.XPATH,'//h2[contains(.,"登录成功")]'))
wait.until(condition)
logging.info("登录成功")
time.sleep(3)
return True
except Exception as e:
logging.error("登录失败")
return False
if __name__ == '__main__':
# 配置忽略 SSL 错误
chrome_options = Options()
chrome_options.add_argument('--ignore-certificate-errors')
chrome_options.add_argument('--ignore-ssl-errors')
# 根据自己电脑的环境来定义,如果不是虚拟环境,可以不需要指定
service = Service(r'....\myenv\Scripts\chromedriver.exe')
brower = webdriver.Chrome(service=service)
try:
main()
except Exception as e:
logging.error(f"程序运行出错:{e}")
finally:
# 确保浏览器在异常情况下也能关闭
brower.quit()
单纯的靠Tesseract和numpy,整体的识别率其实是不高的,因此我们更多的是配合OpenCV或者其他方法来进行预处理。