OCR 文字识别【选修】
本节定位
OCR 很容易被说成一句话:
- 把图片里的字识别出来
但真实项目里,问题会更细:
- 字在哪里
- 顺序是什么
- 多栏版面怎么读
- 倾斜和模糊怎么办
所以 OCR 更像一条流水线,而不是单个模型。
学习目标
- 理解 OCR 中“检测”和“识别”的区别
- 理解文档版面为什么会进一步增加复杂度
- 通过可运行示例建立 OCR 流水线直觉
- 理解 OCR 在表单、票据、文档场景中的特殊难点
先建立一张地图
OCR 这节最适合新人的理解顺序不是“先看识别结果”,而是先看清流水线:
所以这节真正想解决的是:
- 为什么 OCR 不是单一模型
- 为什么检测、识别、版面理解要拆开看
一、OCR 通常分哪几步?
1. 文本检测
先找出文字区域在哪里。
2. 文本识别
再把每块文字区域转成字符序列。
3. 版面与结构理解
在复杂文档场景里,还要回答:
- 哪段先读
- 哪段属于标题
- 哪段属于表格或正文
1.4 一个更适合新人的总类比
你可以把 OCR 想成一个三人小组在处理一张发票:
- 第一个人先拿笔把所有文字圈出来
- 第二个人把每一块圈出来的文字读出来
- 第三个人再决定这些文字谁是标题、谁是金额、谁是日期
这样理解后,OCR 就不会再像:
- 一个神秘的大模型黑盒
而更像:
- 一条有明确分工的流水线
二、先看一个最小 OCR 流水线示例
image_blocks = [
{"box": (0, 0, 50, 20), "pixels": "INV-001"},
{"box": (0, 30, 80, 50), "pixels": "TOTAL 299"},
]
def detect_text_regions(image_blocks):
return [block["box"] for block in image_blocks]
def recognize_text(image_blocks):
return [{"box": block["box"], "text": block["pixels"]} for block in image_blocks]
regions = detect_text_regions(image_blocks)
texts = recognize_text(image_blocks)
print("regions:", regions)
print("texts:", texts)
2.1 这个例子最关键的地方是什么?
它清楚分开了:
- 找文字在哪里
- 把文字读出来
这正是 OCR 最基础的两阶段结构。
2.2 为什么很多 OCR 错误不在“识别字本身”?
因为如果检测阶段就把文字框切错:
- 漏了一半
- 顺序乱了
后面的识别再强也没法完全补救。
2.3 新人第一次学 OCR,最该先记什么?
最值得先记住的是:
- 检测负责“字在哪”
- 识别负责“字是什么”
- 文档场景里还常常要回答“先读哪、属于哪一块”
三、OCR 为什么经常比想象中更难?
3.1 文字不总是规则排版
可能会遇到:
- 倾斜
- 透视变形
- 模糊
- 遮挡
3.2 文档不总是单栏单行
例如:
- 表格
- 发票
- 医疗单据
这时“识别文字”只是第一步,
真正难的是结构理解。
3.3 字符级别错误会影响下游业务
像编号、金额、日期这种字段,
识错一位就可能直接影响业务。
3.4 再看一个最小“阅读顺序恢复”示例
lines = [
{"y": 80, "text": "TOTAL 299"},
{"y": 20, "text": "INVOICE"},
{"y": 50, "text": "INV-001"},
]
def restore_reading_order(lines):
return [item["text"] for item in sorted(lines, key=lambda x: x["y"])]
print(restore_reading_order(lines))
这个例子很小,但它能帮新人先建立一个关键直觉:
- OCR 做完识别,不代表任务结束
- 文字顺序和结构恢复,常常同样重要
四、一个新人可直接照抄的项目推进顺序
更稳的顺序通常是:
- 先做清晰单栏小样本
- 再看倾斜和模糊样本
- 再补版面顺序和结构理解
- 最后再进入票据、表格这类更复杂文档
这样会比一开始就做复杂票据系统更容易稳住。