6. 综合练习:批量图片转文本
这是什么:批量图片转文本 = 一个跑完就能用的命令行小工具,把整个文件夹的截图批量转成文本文件。
为什么用:前面五章都是一次处理一张图,手工一张张点既慢又容易漏。真正的活儿是几十上百张截图堆在一起,能不能一句话全转出来才是关键。
如何用:本章把遍历文件夹、预处理、识别、保存结果四步串起来,写成 python batch_ocr.py 一条命令跑完。
6.1 功能设计
动手写代码前,先把「输入什么、输出什么」定下来:
| 项 | 内容 |
|---|---|
| 输入 | 一个图片文件夹(含 .png / .jpg / .jpeg) |
| 处理 | 遍历 → 预处理 → 识别 → 取文字 |
| 输出 | 同目录下生成同名 .txt,每张图一个文件 |
| 关键约束 | 只处理图片后缀;跳过读不出的图;文件用 UTF-8 写 |
整个工具拆成三个函数,各管一件事:
preprocess() → 把图变成模型喜欢的干净图(灰度/放大/去噪/二值化)
ocr_single() → 把一张图变成一段文本
batch_ocr() → 把一个文件夹跑完并落盘
好处是:以后想换识别引擎(EasyOCR → PaddleOCR),只需改 ocr_single 一个函数。
6.2 完整脚本(基于 EasyOCR)
import os
import cv2
import easyocr
reader = easyocr.Reader(['ch_sim', 'en'])
def preprocess(img_path, scale=2, threshold=130):
"""灰度 → 放大 → 去噪 → 二值化,返回可直接喂给 OCR 的图片"""
img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)
if img is None:
raise FileNotFoundError(f'读不出图片:{img_path}')
img = cv2.resize(img, None, fx=scale, fy=scale, interpolation=cv2.INTER_CUBIC)
img = cv2.GaussianBlur(img, (5, 5), 0)
_, binary = cv2.threshold(img, threshold, 255, cv2.THRESH_BINARY)
return binary
def ocr_single(img_path):
img = preprocess(img_path)
# EasyOCR 直接吃 numpy 数组,不用先存成临时文件
return reader.readtext(img, detail=0, paragraph=True)
def batch_ocr(folder):
count = 0
for filename in sorted(os.listdir(folder)):
if not filename.lower().endswith(('.png', '.jpg', '.jpeg')):
continue
img_path = os.path.join(folder, filename)
text = '\n'.join(ocr_single(img_path))
txt_path = os.path.splitext(img_path)[0] + '.txt'
with open(txt_path, 'w', encoding='utf-8') as f:
f.write(text)
count += 1
print(f'✅ {filename} → {txt_path}')
print(f'共处理 {count} 张图片')
if __name__ == '__main__':
batch_ocr('screenshots/')
6.3 运行与验收
# 1. 准备测试图(5–10 张)放进 screenshots/
# 2. 运行脚本
python batch_ocr.py
# 3. 检查输出(Windows 用 type,macOS/Linux 用 cat)
type screenshots\test1.txt
你会看到:
✅ test1.png → screenshots\test1.txt
✅ test2.png → screenshots\test2.txt
共处理 2 张图片
每一张图对应一个文本文件,后续可批量导入数据库或做关键词搜索。
6.4 进阶:置信度过滤
段落模式下没有置信度(第 3 章讲过),所以要按置信度筛掉低分行,就老老实实开 detail=1、别开 paragraph:
def batch_ocr_with_conf(folder, conf_thresh=0.8):
count = 0
for filename in sorted(os.listdir(folder)):
if not filename.lower().endswith(('.png', '.jpg', '.jpeg')):
continue
img_path = os.path.join(folder, filename)
result = reader.readtext(img_path, detail=1) # 要置信度,就别开 paragraph
lines = [text for _, text, conf in result if conf >= conf_thresh]
if not lines:
print(f'⚠️ {filename}:没有一行过线(阈值 {conf_thresh})')
continue
txt_path = os.path.splitext(img_path)[0] + '.txt'
with open(txt_path, 'w', encoding='utf-8') as f:
f.write('\n'.join(lines))
count += 1
print(f'✅ {filename} → {txt_path}')
print(f'共处理 {count} 张图片')
低分行人工复核 → 识别质量更稳。阈值别一上来就调太高:0.6~0.8 是常用区间。
6.5 再往前一步:让结果真正有用
文本落盘只是起点,三个常见的下游:
| 需求 | 怎么接 |
|---|---|
| 存进数据库做全文检索 | 把 text 写成一条记录,配上文件名和时间戳 |
| 交给 AI 做摘要 / 分类 | 读出 .txt,拼进 Prompt 送给 LLM(LLM 教程) |
| 只挑关心的图片 | 用置信度阈值过滤,只处理高分图,省时间省算力 |
一句提醒:OCR 只负责「把字读出来」,读得准不准取决于图片质量和预处理(上一章);要更高精度就换 PaddleOCR 的中文模型。
6.6 常见翻车现场
| 报错 / 现象 | 原因 | 修法 |
|---|---|---|
ValueError: not enough values to unpack |
开了 paragraph=True 却按三元组解包 |
段落模式只有两项,取 item[1];或关掉 paragraph |
| 输出的 txt 是空的 | 图片没文字,或阈值太严 | 调低 conf_thresh,或先跑 6.2 原图版对照 |
FileNotFoundError |
路径带了尾斜杠或图片在别的目录 | 用 os.path.abspath() 打印确认 |
| 脚本跑完但没生成文件 | 文件夹里没有图片,或后缀不在白名单 | 打印 os.listdir(folder) 核对 |
| 中文变成乱码 | 写文件时没指定编码 | open(..., encoding='utf-8') |
6.7 动手改
- 给脚本加「跳过空图」逻辑(图片大小为 0 时跳过)
- 把
batch_ocr_with_conf改成「导出 JSON」,每条含{filename, text, conf} - 用 PaddleOCR 替换 EasyOCR,对比批量识别的准确率与耗时
6.8 小结
- 批量 OCR = 预处理 + 识别 + 保存,三步链
- 函数拆成三段,换引擎只改一处
- EasyOCR 能直接吃 numpy 数组,不必落临时文件
- 段落模式没有置信度,要复核就退回
detail=1 - 以后遇到「把几十张截图变文本」的需求,直接复用这套流程
OCR 完成!你可以把截图里的任意内容「读」进程序了 🐸