Python OCR 技术入门与验证码识别实战
引言:当机器学会'阅读'
OCR(Optical Character Recognition,光学字符识别)是一种将图像中的文字转换为可编辑文本的技术。简单来说,就是让计算机'看懂'图片里的字。它的核心流程包括以下几个步骤:
- 图像预处理:对原始图像进行处理,提高文字的可识别性,包括灰度化、二值化、去噪、倾斜校正等。
- :识别图像中的文本区域,将文字与背景分离。
介绍 Python OCR 技术基础及实战应用。涵盖 Tesseract 引擎安装配置、图像预处理(灰度化、二值化、去噪)、字符分割与倾斜校正。提供基础文字识别、验证码识别(含缓存优化)及批量图片转 Excel 的完整代码示例。同时对比 PaddleOCR 方案,并总结常见错误解决方案,帮助开发者实现图像文字提取功能。
OCR(Optical Character Recognition,光学字符识别)是一种将图像中的文字转换为可编辑文本的技术。简单来说,就是让计算机'看懂'图片里的字。它的核心流程包括以下几个步骤:
在实际应用中,OCR 面临诸多挑战:
Tesseract 是一个开源的 OCR 引擎,最初由 HP 开发,后来由 Google 维护和赞助。它支持 100 多种语言的识别,是目前最流行、最成熟的 OCR 引擎之一。Tesseract 5.x 版本在 2025 年已支持 100+ 种语言,中文识别准确率达 89.7%。它的优势在于开源免费、多语言支持、可训练以及社区活跃。
Windows 系统:
tesseract-ocr-w64-setup-5.3.3.20231005.exe)C:\Program Files\Tesseract-OCR)添加到系统环境变量 PATH 中macOS 系统:
brew install tesseract # 安装中文语言包
brew install tesseract-lang
Linux 系统(Ubuntu/Debian):
sudo apt-get update
sudo apt-get install tesseract-ocr
sudo apt-get install tesseract-ocr-chi-sim # 中文简体语言包
sudo apt-get install tesseract-ocr-eng # 英文语言包
安装完成后,可以通过命令行验证安装是否成功:
tesseract --version
tesseract --list-langs # 查看已安装的语言包
pip install pytesseract pillow opencv-python numpy
各库的作用:
编写一个简单的测试脚本,验证 OCR 环境是否配置成功:
import pytesseract
from PIL import Image
import os
# 如果 Tesseract 未添加到系统 PATH,可以手动指定路径
# Windows 示例:
# pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'
def test_tesseract():
try:
# 尝试获取版本信息,说明环境正常
version = pytesseract.get_tesseract_version()
print(f"Tesseract 版本:{version}")
print("环境配置成功!")
except Exception as e:
print(f"环境配置失败:{e}")
print("请检查 Tesseract 是否正确安装并添加到 PATH")
test_tesseract()
我们先从一个最简单的例子开始:识别一张清晰的图片中的文字。
假设有一张图片 sample.png,内容为纯英文文本:
import pytesseract
from PIL import Image
def ocr_basic(image_path):
""" 基础 OCR 识别 """
# 打开图片
image = Image.open(image_path)
# 使用 Tesseract 进行识别
# lang='eng'指定使用英文语言包
text = pytesseract.image_to_string(image, lang='eng')
print("识别结果:")
print(text)
return text
# 使用示例
# ocr_basic('sample.png')
如果图片中包含中文,只需将 lang 参数改为 'chi_sim'(中文简体):
text = pytesseract.image_to_string(image, lang='chi_sim')
对于中英文混合的图片,可以使用 '+' 组合多个语言包:
text = pytesseract.image_to_string(image, lang='chi_sim+eng')
在实际应用中,我们遇到的图片往往不是理想状态的——可能有噪点、背景复杂、文字倾斜或光照不均。这时,直接使用 OCR 的准确率会大幅下降。图像预处理是提高 OCR 准确率最关键的一步。
常见的预处理技术包括:
下面我们使用 OpenCV 和 Pillow 实现一个完整的图像预处理流程:
import cv2
import numpy as np
from PIL import Image
import pytesseract
def preprocess_image(image_path):
""" 图像预处理:灰度化、二值化、去噪 """
# 读取图像(使用 OpenCV)
img = cv2.imread(image_path)
# 1. 灰度化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 2. 去噪(高斯模糊)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
# 3. 二值化(自适应阈值,处理光照不均的情况)
# 使用自适应阈值,根据局部像素分布自动确定阈值
binary = cv2.adaptiveThreshold(
blurred,
255,
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY,
11, # 块大小
2 # 常数
)
# 4. 可选:形态学操作,去除小的噪点
kernel = np.ones((1, 1), np.uint8)
opening = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
return opening
def ocr_with_preprocess(image_path):
""" 预处理后的 OCR 识别 """
# 预处理
processed_img = preprocess_image(image_path)
# OpenCV 图像(numpy 数组)转换为 PIL Image
pil_img = Image.fromarray(processed_img)
# OCR 识别
text = pytesseract.image_to_string(pil_img, lang='eng')
return text
# 对比实验:直接识别 vs 预处理后识别
def compare_ocr(image_path):
print("=== 直接识别 ===")
img_raw = Image.open(image_path)
text_raw = pytesseract.image_to_string(img_raw, lang='eng')
print(text_raw[:200]) # 只显示前 200 字符
print("\n=== 预处理后识别 ===")
text_processed = ocr_with_preprocess(image_path)
print(text_processed[:200])
# 使用示例
# compare_ocr('noisy_text.jpg')
预处理技术的选择需要根据具体图像的特点来决定。例如:
对于拍摄角度不正导致的文字倾斜,需要进行倾斜校正。常用的方法是利用霍夫变换检测直线,计算平均倾斜角度,然后进行旋转校正:
import cv2
import numpy as np
import math
def correct_skew(image):
""" 校正图像倾斜 """
# 转换为灰度图
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 二值化
_, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
# 检测边缘
edges = cv2.Canny(binary, 50, 150, apertureSize=3)
# 霍夫变换检测直线
lines = cv2.HoughLinesP(edges, 1, np.pi / 180, 100, minLineLength=100, maxLineGap=10)
if lines is None:
return image
# 计算所有检测到的直线的角度
angles = []
for line in lines:
x1, y1, x2, y2 = line[0]
angle = math.degrees(math.atan2(y2 - y1, x2 - x1))
angles.append(angle)
# 取中位数角度
median_angle = np.median(angles)
# 旋转校正
h, w = image.shape[:2]
center = (w // 2, h // 2)
M = cv2.getRotationMatrix2D(center, median_angle, 1.0)
rotated = cv2.warpAffine(image, M, (w, h), flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_REPLICATE)
return rotated
pytesseract允许用户自定义 OCR 配置,以提高识别效果。最常用的配置是 --psm(页面分割模式,Page Segmentation Mode),它告诉 Tesseract 如何分析图像中的文本布局。
常见的 PSM 模式:
--psm 6:将图像视为一个统一的文本块--psm 7:将图像视为单行文本--psm 8:将图像视为单个单词--psm 13:原始行处理(不进行额外的文本方向检测)def ocr_with_config(image_path, psm=6):
""" 使用自定义配置进行 OCR 识别 """
image = Image.open(image_path)
# 配置参数:--psm 6 表示将图像视为一个统一的文本块
# --oem 3 表示使用默认的 OCR 引擎模式
custom_config = r'--oem 3 --psm {}'.format(psm)
text = pytesseract.image_to_string(
image,
lang='eng',
config=custom_config
)
return text
# 不同 PSM 模式的对比
def test_psm_modes(image_path):
for psm in [6, 7, 8, 13]:
text = ocr_with_config(image_path, psm)
print(f"PSM 模式 {psm}:")
print(text[:100] + "...\n")
对于验证码识别,通常使用 --psm 8(单个单词)或 --psm 7(单行文本)效果更好。
验证码识别是 OCR 技术的一个重要应用场景。虽然现代验证码越来越复杂(如滑动验证码、点选验证码),但在简单场景下(如数字字母组合的静态验证码),OCR 仍然是一种有效的解决方案。
验证码通常包含以下干扰因素:
针对验证码的特点,我们需要更精细的预处理流程:
import cv2
import numpy as np
import pytesseract
from PIL import Image
def preprocess_captcha(image_path):
""" 验证码图像预处理 """
# 读取图像
img = cv2.imread(image_path)
# 1. 灰度化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 2. 去噪(中值滤波,对去除椒盐噪声效果好)
denoised = cv2.medianBlur(gray, 3)
# 3. 二值化(使用 OTSU 自动阈值)
_, binary = cv2.threshold(denoised, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
# 4. 形态学操作:去除小的噪点
# 定义结构元素
kernel = np.ones((2, 2), np.uint8)
# 开运算(先腐蚀后膨胀),去除小的白色噪点
opening = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
# 5. 可选:膨胀操作,连接断开的字符
# kernel_dilate = np.ones((2, 2), np.uint8)
# dilated = cv2.dilate(opening, kernel_dilate, iterations=1)
return opening
def recognize_captcha(image_path):
""" 识别验证码 """
# 预处理
processed = preprocess_captcha(image_path)
# 转换为 PIL 图像
pil_img = Image.fromarray(processed)
# OCR 配置:--psm 8(单个单词),只允许数字和大写字母
# -c tessedit_char_whitelist=0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ
custom_config = r'--psm 8 -c tessedit_char_whitelist=0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
text = pytesseract.image_to_string(
pil_img,
lang='eng',
config=custom_config
)
# 清理结果:去除空格和特殊字符
text = ''.join(filter(str.isalnum, text))
return text
# 使用示例
captcha_text = recognize_captcha('captcha.png')
print(f"验证码识别结果:{captcha_text}")
对于字符粘连的验证码,可以先进行字符分割,然后逐个识别,提高准确率:
def segment_and_recognize(image_path):
""" 字符分割后识别 """
# 预处理
img = cv2.imread(image_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
# 查找轮廓(每个字符的轮廓)
contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 筛选轮廓:根据宽高比和面积过滤
char_contours = []
for contour in contours:
x, y, w, h = cv2.boundingRect(contour)
# 过滤太小的噪声
if w > 5 and h > 10 and w < 50 and h < 50:
char_contours.append((x, y, w, h))
# 按 x 坐标排序(从左到右)
char_contours.sort(key=lambda x: x[0])
# 逐个识别
result = ""
for i, (x, y, w, h) in enumerate(char_contours):
# 提取单个字符区域
char_img = binary[y:y+h, x:x+w]
# 可选:为字符添加边框,方便识别
char_with_border = cv2.copyMakeBorder(
char_img, 5, 5, 5, 5, cv2.BORDER_CONSTANT, value=0
)
# 转换为 PIL 图像
pil_char = Image.fromarray(char_with_border)
# 识别单个字符
custom_config = r'--psm 10 -c tessedit_char_whitelist=0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
char_text = pytesseract.image_to_string(pil_char, config=custom_config).strip()
if char_text:
result += char_text
return result
对于需要频繁识别验证码的场景(如爬虫程序),性能优化非常重要:
from concurrent.futures import ThreadPoolExecutor
import hashlib
import pickle
import os
class CaptchaRecognizer:
""" 带缓存的验证码识别器 """
def __init__(self, cache_dir='captcha_cache'):
self.cache_dir = cache_dir
if not os.path.exists(cache_dir):
os.makedirs(cache_dir)
def _get_cache_key(self, image_path):
"""生成缓存键(基于图片内容的哈希)"""
with open(image_path, 'rb') as f:
img_data = f.read()
return hashlib.md5(img_data).hexdigest()
def _recognize(self, image_path):
"""实际的识别逻辑"""
processed = preprocess_captcha(image_path)
pil_img = Image.fromarray(processed)
custom_config = r'--psm 8 -c tessedit_char_whitelist=0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
text = pytesseract.image_to_string(pil_img, config=custom_config)
return text.strip()
def recognize(self, image_path):
"""带缓存的识别"""
cache_key = self._get_cache_key(image_path)
cache_file = os.path.join(self.cache_dir, cache_key)
# 检查缓存
if os.path.exists(cache_file):
with open(cache_file, 'rb') as f:
return pickle.load(f)
# 识别
result = self._recognize(image_path)
# 保存到缓存
with open(cache_file, 'wb') as f:
pickle.dump(result, f)
return result
def batch_recognize(self, image_paths, max_workers=4):
"""批量识别(并行处理)"""
with ThreadPoolExecutor(max_workers=max_workers) as executor:
results = list(executor.map(self.recognize, image_paths))
return results
将 OCR 技术与办公自动化结合,可以实现很多实用功能。下面我们实现一个批量图片文字识别工具,将识别结果保存到 Excel 文件中。
import os
import pytesseract
from PIL import Image
import pandas as pd
from concurrent.futures import ThreadPoolExecutor
import cv2
import numpy as np
from datetime import datetime
class BatchOCRProcessor:
""" 批量 OCR 识别处理器 """
def __init__(self, input_dir, output_excel, lang='chi_sim+eng', use_preprocess=True):
""" 初始化
:param input_dir: 输入图片目录
:param output_excel: 输出 Excel 文件路径
:param lang: OCR 语言包
:param use_preprocess: 是否使用预处理
"""
self.input_dir = input_dir
self.output_excel = output_excel
self.lang = lang
self.use_preprocess = use_preprocess
# 支持的图片格式
self.image_extensions = ('.png', '.jpg', '.jpeg', '.bmp', '.tiff')
def preprocess_image(self, image_path):
""" 图像预处理 """
# 读取图像
img = cv2.imread(image_path)
# 灰度化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 去噪
denoised = cv2.medianBlur(gray, 3)
# 二值化(自适应阈值)
binary = cv2.adaptiveThreshold(
denoised,
255,
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY,
11,
2
)
# 转换为 PIL 图像
return Image.fromarray(binary)
def process_single_image(self, filename):
""" 处理单张图片 """
file_path = os.path.join(self.input_dir, filename)
try:
if self.use_preprocess:
# 预处理后识别
img = self.preprocess_image(file_path)
else:
# 直接识别
img = Image.open(file_path)
# 执行 OCR 识别
# 配置:尝试不同的 PSM 模式,选择最佳结果
text = self.ocr_with_multiple_configs(img)
return {
'文件名': filename,
'识别内容': text,
'状态': '成功',
'处理时间': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
}
except Exception as e:
return {
'文件名': filename,
'识别内容': '',
'状态': f'失败:{str(e)}',
'处理时间': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
}
def ocr_with_multiple_configs(self, img):
""" 使用多种配置尝试识别,返回最佳结果 """
# 尝试不同的 PSM 模式
psm_modes = [6, 7, 8, 13]
results = []
for psm in psm_modes:
config = f'--psm {psm}'
text = pytesseract.image_to_string(img, lang=self.lang, config=config)
# 计算有效字符数(去除非字母数字字符)
valid_chars = sum(c.isalnum() for c in text)
results.append((valid_chars, text))
# 返回有效字符最多的结果
best_result = max(results, key=lambda x: x[0])
return best_result[1].strip()
def run(self, max_workers=4):
""" 运行批量处理 """
print(f"开始扫描目录:{self.input_dir}")
# 获取所有图片文件
image_files = [
f for f in os.listdir(self.input_dir)
if f.lower().endswith(self.image_extensions)
]
print(f"找到 {len(image_files)} 个图片文件")
if not image_files:
print("未找到图片文件")
return
# 批量处理(并行)
results = []
with ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = [executor.submit(self.process_single_image, f) for f in image_files]
for i, future in enumerate(futures):
result = future.result()
results.append(result)
print(f"进度:{i+1}/{len(image_files)} - 已处理 {result['文件名']}")
# 保存到 Excel
df = pd.DataFrame(results)
df.to_excel(self.output_excel, index=False, engine='openpyxl')
print(f"\n处理完成!结果已保存至:{self.output_excel}")
print(f"成功:{len(df[df['状态']=='成功'])} 个,失败:{len(df[df['状态']!='成功'])} 个")
# 返回统计信息
return df
# 使用示例
if __name__ == "__main__":
processor = BatchOCRProcessor(
input_dir='./images', # 图片目录
output_excel='./ocr_results.xlsx', # 输出 Excel
lang='chi_sim+eng', # 中英文混合
use_preprocess=True # 启用预处理
)
results_df = processor.run(max_workers=4)
如果 Tesseract 的识别效果不够理想,可以考虑使用 PaddleOCR。PaddleOCR 是百度开源的 OCR 工具包,在中文识别方面表现优异。
# 安装 PaddleOCR
# pip install paddlepaddle paddleocr
from paddleocr import PaddleOCR
def ocr_with_paddle(image_path):
""" 使用 PaddleOCR 识别 """
# 初始化 OCR(首次运行会下载模型)
ocr = PaddleOCR(use_angle_cls=True, lang='ch')
# 识别
result = ocr.ocr(image_path, cls=True)
# 提取文本
text = ''
for line in result:
for word_info in line:
text += word_info[1][0] + ' '
return text
根据实践经验,提升 OCR 准确率可以从以下几个方面入手:
def postprocess_text(text, known_words=None):
""" 后处理:清洗和校正识别结果 """
# 去除多余的空格和换行
text = ' '.join(text.split())
# 去除特殊字符,只保留字母、数字、中文和基本标点
import re
text = re.sub(r'[^\u4e00-\u9fff\u0041-\u005a\u0061-\u007a\u0030-\u0039\s\.,;:!?()]', '', text)
# 如果提供了已知词汇表,可以进行简单的纠错
if known_words:
words = text.split()
corrected = []
for word in words:
if word not in known_words:
# 简单纠错:查找最相似的已知词
# 这里可以集成更复杂的拼写检查算法
pass
corrected.append(word)
text = ' '.join(corrected)
return text
问题 1:TesseractNotFoundError
解决方案:确保 Tesseract 已正确安装并添加到系统 PATH,或在代码中手动指定路径:
pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'
问题 2:中文识别乱码或准确率低
解决方案:
tesseract-ocr-chi-sim)lang='chi_sim' 参数问题 3:识别结果包含大量噪声字符
解决方案:
问题 4:处理速度慢
解决方案:
问题 5:图像模糊或分辨率低
解决方案:
def enhance_image(image_path):
""" 图像增强:锐化 + 超分辨率 """
img = cv2.imread(image_path)
# 锐化
kernel_sharpen = np.array([[-1, -1, -1], [-1, 9, -1], [-1, -1, -1]])
sharpened = cv2.filter2D(img, -1, kernel_sharpen)
# 放大(如果图像太小)
height, width = sharpened.shape[:2]
if width < 800 or height < 600:
scale = max(800 / width, 600 / height)
new_width = int(width * scale)
new_height = int(height * scale)
enlarged = cv2.resize(sharpened, (new_width, new_height), interpolation=cv2.INTER_CUBIC)
return enlarged
return sharpened

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online