Scarnox · Blog

图片文字验证码 OCR:如何识别扭曲的验证码

图片验证码会把一小段字母或数字以图片形式展示出来,有时还带有噪点、旋转和波浪线。光学字符识别(OCR)可以用 Tesseract 这类开源引擎读取其中较简单的版本。准确率取决于图片有多干净,所以大部分工作都在预处理上。本指南给出一套可用的 Python 方案,说明它在哪些地方会失败,并明确一点:Scarnox 目前的 API 识别的是 PopularCaptcha 控件的 token,而不是图片转文字的验证码。对于 OCR,你需要自己编写代码,而不是调用一个 API。

OCR 何时有效,何时无效

当文字满足以下条件时,OCR 是合理的首选方案:

遇到旋转的字母、粘连的字符、背景纹理以及穿过字形的线条时,OCR 就会吃力。每种情况都需要特定的预处理步骤,有些甚至需要完全不同的模型。要做好测量准确率的准备,而不是想当然地认为它可靠。

安装工具

你需要 Python、Tesseract 程序,以及两个 Python 包:

pip install pillow pytesseract

Tesseract 本身需要单独安装。在 Windows 上,使用 Tesseract 项目提供的安装程序,并确保可执行文件在 PATH 中,或者把 pytesseract.pytesseract.tesseract_cmd 指向它。

预处理,然后识别

准确率的大部分来自干净的输入。下面的流程依次执行:转为灰度、增强对比度、应用硬阈值、放大图片,并把输出限制在验证码使用的字符范围内:

from PIL import Image, ImageOps
import pytesseract

# 验证码可能使用的字符。如果验证码从不使用 0/O、1/I 之类容易混淆的字符,
# 可以把它们去掉。
ALLOWED = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"

def read_text(path: str) -> str:
    img = Image.open(path)
    gray = ImageOps.grayscale(img)
    gray = ImageOps.autocontrast(gray)
    # 硬阈值:亮度大于 140 的像素变为白色,其余变为黑色。
    bw = gray.point(lambda p: 255 if p > 140 else 0)
    # Tesseract 对较大的字符识别效果更好。
    bw = bw.resize((bw.width * 3, bw.height * 3))
    config = f"--psm 7 -c tessedit_char_whitelist={ALLOWED}"  # psm 7:单行文本
    return pytesseract.image_to_string(bw, config=config).strip()

if __name__ == "__main__":
    print(read_text("sample_captcha.png"))

阈值 140 只是一个起点。把处理后的图片打印出来,与原图对比,并不断调整,直到字母饱满、背景干净为止。

依赖之前先测量准确率

对单张图片的一次正确识别什么也证明不了。保存一组答案已知的验证码,对所有样本运行该函数,然后统计匹配数:

def accuracy(samples: dict[str, str]) -> float:
    # samples 把文件路径映射到正确的文本
    hits = sum(read_text(path) == truth for path, truth in samples.items())
    return hits / len(samples)

labelled = {
    "samples/0001.png": "K7PQ2",
    "samples/0002.png": "M3XN9",
}
print(f"accuracy: {accuracy(labelled):.0%}")

每次修改预处理时都要追踪这个数字。如果经过几次修改之后,这个数字仍然很低,那么 OCR 可能就是这种验证码的错误工具,你应该考虑其他方案。

常见失败场景

预处理检查清单

识别失败时,请按以下顺序逐项排查。每次只改动一个地方,这样才能看出哪一步起了作用:

  1. 检查输入尺寸。非常小的验证码在放大 3 倍后通常识别得更好。
  2. 转为灰度,并拉伸对比度,使字母更突出。
  3. 应用阈值,在确定一个值之前,先尝试几个不同的值。
  4. 用中值滤波去除斑点噪点,必要时再重新阈值化。
  5. 把图片裁剪到文字区域,避免背景干扰识别引擎。
  6. 把字符集限制为验证码实际使用的字符。
  7. 如果文字分散或分层排列,尝试其他页面分割模式。

把这些步骤的顺序和参数与带标注的样本一起纳入版本控制。预处理流程本身就是代码,应当像代码一样接受审查。

OCR 还是自己运行的模型

经典 OCR 速度快,在笔记本上就能运行,但验证码样式一变,它就很脆弱。基于你自己标注数据集训练的图像模型,可以应对更多的变化,但代价是需要收集数据和运行推理。两者都需要你自己搭建和维护。目前 Scarnox 并没有托管这类服务的端点,所以在选择方案之前,请先规划好基础设施。

Scarnox 在这里做什么,不做什么

Scarnox 的 API 目前识别的是 PopularCaptcha 控件的 token。请求格式为 POST https://api.scarnox.com/api/tasks/create,包含 PopularCaptcha 任务类型、sitekey 和页面 URL,响应则是用于表单字段的 token。这与读取图片中的文字是完全不同的工作,而且 Scarnox 目前并不提供图片转文字的接口。如果你的问题是图片验证码,请使用 OCR 或你自己运行的模型,并参阅其他指南了解 Scarnox 覆盖的范围。你可以注册账户,免费试用额度,无需信用卡,用于 Scarnox 目前支持的 PopularCaptcha 流程。当前价格请查看定价页面。

合法用途

不要用 OCR 绕过不允许自动化访问的网站上的验证码。图片验证码正是为了阻止这种行为而存在的。

下一步