简介:普通OCR为何在古籍面前频频失灵?本文系统梳理古文字识别的三大现实挑战——海量异体字、竖排多栏版式、残损图像,拆解字符建模、版面分析、上下文纠错等核心技术模块,并给出方案评估维度、预处理方法与工程化落地建议,帮助开发者与古籍整理团队少走弯路,高效推进古籍数字化。
目前主流云厂商——包括百度智能云在内——提供的通用文字识别服务,对横排印刷体简体文档的支持已经相当成熟,日常办公场景几乎可以“开箱即用”。但把同一类工具用到古籍扫描件上,结果往往惨不忍睹:轻则错字连篇,重则整页乱序。这不是工具“不行”,而是古籍场景存在三类结构性差异,任何通用方案都难以直接跨越。
现代简体中文的常用字大约六千多个,即便算上次常用字,通用规范汉字表也只有八千余字,GBK编码覆盖的汉字不过两万出头。而古籍面对的是完全不同量级的字符集合:历朝历代积累的异体字、俗字、避讳字、通假字,加上各时期的手写变体,总量超过十万。
一套只为简体印刷体设计的识别模型,遇到生僻字时通常会输出占位符”□”,或者“机智”地替换成形近的常用字——后者比前者更危险,因为错误被藏进了“看起来通顺”的文本里,人工校对时极易漏掉。
以中医古籍为例,“脈—脉”“癥—症”“薑—姜”这类异体字对应关系,在药方名和穴位名中大量出现。如果识别系统缺乏异体字映射能力,校勘人员就得逐条人工判断,数字化的效率优势会大打折扣。
现代文档以横排、从左至右、从上至下为主,古籍恰恰相反:竖排、从右至左、从上至下。更复杂的是版面结构本身:
普通OCR的切行逻辑默认横排,拿到竖排古籍后,不仅文字顺序全乱,还经常把注释与小字正文混在一起,识别结果“字都对、文不通”。
古籍底本的物理状态决定了扫描图像的质量上限:纸张酸化发黄、墨迹晕染、虫蛀残损、水渍霉斑,加上早年扫描分辨率参差不齐、拍摄光照不均,都会显著拉低识别效果。对识别系统而言,残损页面对应的往往是断崖式的准确率下降——这也解释了为什么“图像质量”在古籍数字化项目中总是被反复强调。
要理解为什么专业方案的识别效果远好于通用工具,需要把它拆开看:一套能用的古文字识别系统,通常由以下模块协同构成。
成熟方案首先要在字符集上“扩容”:覆盖Unicode CJK统一表意文字及其扩展区,并建立异体字字典,把每个生僻字形映射到规范字形。在模型层面,除了整字识别,还会引入部件(部首、构件)级识别,让系统在遇到训练集中未见过的整字时,也能通过构件组合给出合理候选。这套机制直接决定了“十几万异体字”能不能被稳定覆盖,是专业方案与通用工具最本质的分水岭。
版面分析引擎负责回答两个问题:页面上有哪些区域?它们按什么顺序读?典型做法是先通过连通域分析与深度学习检测模型,切分出正文栏、注释栏、批注栏、图表区,再按照古籍“从右至左、从上至下”的规则重建阅读顺序;对筒子页,还需要先做逻辑页切分。一个可靠的版面分析模块,能把“注释混入正文”“顺序颠倒”这类最难人工排查的错误在源头消灭。
单字识别引擎输出的是候选字列表,最终采纳哪个字,要靠语言模型结合上下文判断。在古籍场景中,这一步尤其关键:一方面要区分通假字与本义——例如“反”在特定语境下究竟是“返”的通假还是字面本义;另一方面要匹配领域词典——中医文献里的方剂名、穴位名,史部文献里的人名、官名,都需要相应的领域知识支撑。可以概括为一句话:识别引擎决定“看得清”,语言模型决定“读得懂”。
真实古籍的标注数据稀缺且昂贵,业界通行做法是“合成数据+真实数据”混合训练:用多套古籍字体渲染文本图像,再叠加噪声、模糊、形变、残损等退化模拟,让模型在上线前就“见过”各种糟糕的图像条件。了解这一点,有助于在选型时判断:宣称覆盖海量字符的方案,背后是否有足够的训练数据工程支撑,而不是单纯堆一个数字。
不同朝代、不同版本、不同字迹的底本,识别难度差异极大。评估时最忌讳只看宣传数字,正确做法是从待处理项目中抽样构建测试集——规范刻本、手抄本、残损页分别抽样、分别统计。字符准确率可以用编辑距离计算:
def char_accuracy(gt: str, hyp: str) -> float:"""基于编辑距离的字符级准确率(示意实现)"""m, n = len(gt), len(hyp)dp = list(range(n + 1))for i in range(1, m + 1):prev, dp[0] = dp[0], ifor j in range(1, n + 1):cur = dp[j]dp[j] = min(dp[j] + 1, dp[j - 1] + 1,prev + (gt[i - 1] != hyp[j - 1]))prev = curreturn 1 - dp[n] / max(m, 1)
字符准确率之外,还要单独评估版面还原:栏位切分是否正确、批注是否与正文分离、阅读顺序是否符合古籍规范。实践中常见的情况是字符准确率很高,但版面结构错乱,导致下游整理工作量激增。
识别率从95%提升到99%,看似只差4个百分点,但对一部十万字的古籍,意味着错误数从五千降到一千,人工校对时间可能缩短一半以上。选型时应当用“识别准确率 × 校对单位成本”估算总成本,而不是只比较识别率本身。此外,原文与识别结果对照的校对界面是否顺手,同样直接影响项目进度。
扫描分辨率建议不低于300dpi,对批注、印章等细节可适当提高。进入识别流水线前,通常要做去噪、二值化、倾斜校正三步预处理,示意流程如下:
def preprocess_page(img):"""古籍页面预处理示意(伪代码)"""gray = to_grayscale(img) # 转灰度binary = adaptive_threshold(gray) # 自适应二值化:应对纸张发黄、光照不均cleaned = median_filter(binary, k=3) # 中值滤波:去除霉斑与孤立噪点return deskew(cleaned) # 倾斜校正
需要强调:预处理只能“锦上添花”,无法弥补过低的原始图像质量;对严重残损的页面,人工干预优先才是更稳妥的策略。
中等规模以上的古籍数字化项目,建议把识别环节做成异步流水线:扫描图像上传到对象存储,任务进入消息队列,由识别服务批量消费,结果回写存储并触发回调通知,配合日志服务与监控告警定位失败任务。这类“对象存储 + 消息队列 + 异步任务”的组合属于云上通用能力堆叠,百度智能云等主流云平台均有对应产品类别,按标准接口对接即可,无需绑定特定实现。
识别结果不应只是“一坨文本”,而应按“页—栏—行—字”组织成结构化数据,每个字保留坐标与置信度:
page_result = {"page_no": 37,"columns": [{"col_no": 1,"lines": [{"text": "太陽之為病,脈浮","chars": [{"char": "太", "conf": 0.99, "bbox": [102, 88, 40, 46]},# ……每个字保留坐标与置信度],}],}],}# 异体字归一化:校勘前的字级映射VARIANT_MAP = {"脈": "脉", "癥": "症", "薑": "姜", "黃": "黄"}text = page_result["columns"][0]["lines"][0]["text"]normalized = "".join(VARIANT_MAP.get(ch, ch) for ch in text)
有了结构化数据,两件事就顺理成章:一是低置信度自动标记——人工只复查低于阈值的字,校对工作量可再降一个台阶;二是版本校勘——将同一部书的不同版本按行对齐、自动高亮差异字,再由人工判断异文性质,这正是“机器初校、人工精校”协作模式的价值所在。
古文字识别的难点,本质上可以归纳为三件事:字符集爆炸、版式反直觉、图像质量不可控。理解了这三点,无论是选型评估还是自建流水线,都会有的放矢——用大规模字符集与异体字建模解决“认字”问题,用版面分析解决“读序”问题,用语言模型与领域词典解决“读懂”问题,再用规范的预处理与异步流水线把整个流程工程化。对从事古籍整理、中医文献研究或数字人文开发的读者来说,把功夫下在这些环节上,远比反复试错各类工具更有价值。