尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

甲骨拓片文字提取实战:边缘检测、多边形拟合与分割的完整流程

发布时间:2026/9/7 23:51:11

资讯中心
01
ARTICLE

甲骨拓片文字提取实战:边缘检测、多边形拟合与分割的完整流程

甲骨拓片文字提取实战:边缘检测、多边形拟合与分割的完整流程
简介一份基于Matlab的甲骨文图符提取与数字图像处理实验方案面向图像处理研究者与文化遗产数字化工作者系统覆盖边缘检测、质心定位、Hough变换纠偏、多边形拟合及文字分割等完整技术链路。文档从原始甲骨图像出发详述二值化、形态学膨胀腐蚀去噪、连通域分析提取甲骨区域并利用Canny边缘检测与Hough变换确定文字走向实现图像校正随后通过ROI干扰剔除和bwlabel、regionprops等函数完成字符分离每一步均配有代码和结果分析。压缩包内为1个docx文档共1个文件约795KB篇幅凝练但实验步骤完整附有流程图与效果对比既可用于教学演示也能为后续文字识别与古文字数字化研究提供可复现的基础方法。目前已有202人浏览学习适合考古数字化、古籍OCR及图像预处理相关领域的师生和技术人员参考。 甲骨文数字化研究做了这么久我最常被问到的不是这个字形念什么而是你是怎么把这些图符从拓片里抠出来的。看起来是个小问题真动手才知道疼——拓片上裂纹和笔画混在一起噪声点蚀跟文字笔触的灰度几乎一样用常规方法跑一遍出来的轮廓线能把人看崩溃。这篇文章是我实际处理一批甲骨拓片的完整记录走的是一条非常传统但能落地的技术路线边缘检测、多边形拟合、文字分割三步走把图像变成一组干净、可量化的矢量轮廓。适合做古籍数字化、文物图像处理的朋友参考也适合刚学完数字图像处理、想知道理论怎么落到真实项目里的同学。文中涉及的代码都用Python加OpenCV实现参数都是我实测调出来的可以直接抄。1. 整体思路拆解为什么是检测-拟合-分割三步走1.1 甲骨拓片的图像特点决定了处理策略先说图像本身。甲骨拓片和普通文档扫描件最大的区别在于它没有干净的背景。拓片上的字形是墨色拓印上来的但龟甲兽骨本身的纹理、裂痕、钻孔痕迹、年代久远产生的霉点和颗粒感全部混在同一张灰度图里。而且墨色不均匀同一个笔画中间出现断档、边缘出现虚影是常态。这意味着任何单一算法都搞不定整件事。如果你上来直接做二值化试图靠灰度阈值把字形分离出来大概率得到一张芝麻糊——因为裂纹和笔画在灰度直方图上有大量重叠区域。正确思路是先找结构性的轮廓信息再做几何化简最后才能谈分割。这套检测-拟合-分割的链条本质上是把从复杂背景中提取符号这个大问题拆成了三个可分别调优的小问题每一环的输出都是下一环的输入哪一步出了问题都能单独排查。1.2 为什么不做端到端分割偏要用传统方法肯定会有人问现在深度学习分割模型那么成熟为什么还要用边缘检测加多边形拟合这种老办法我的回答是两个原因。第一是数据量。甲骨文字体多变、拓片风格各异公开的标注数据集非常有限训练一个能泛化的分割网络数据根本不够。哪怕是做迁移学习也得先有大量精细标注的轮廓数据。如果先用传统方法自动提取一批轮廓再人工修正反而能快速积累训练集。第二是可解释性和精度可控。考古研究对轮廓的准确性要求极高哪怕一个笔画的夹角差了几度都可能影响对字形的判断。传统方法每一步都能可视化检查、参数微调出了问题能明确知道是滤波过重还是阈值太狠而这个特性是端到端黑盒模型很难给到的。当然传统方法也有自己的短板比如对严重残缺的笔画鲁棒性不足但这恰恰可以通过合理的流程设计来弥补后面我在分割环节会专门讲。2. 边缘检测从灰度图到字形轮廓2.1 三种边缘检测算子的实测对比边缘检测是整个流程的地基。这里我实测对比了三种常见算子Prewitt、Sobel和Canny。Prewitt算子的原理很直观它用两个3x3的卷积核分别计算水平和垂直方向的灰度差分差分值大的地方就是边缘。说得直白一点它就是在看旁边像素和我的亮度差多少差得多就认为这里是边界。Sobel算子在Prewitt基础上做了改进离中心近的像素权重更大对噪声稍微钝感一点但仍然只用了两个方向的模板。Canny则是把梯度计算、非极大值抑制、双阈值检测和滞后连接组合成一套完整流水线输出的边缘是细线而且能有效过滤掉部分弱纹理。我直接用同一张拓片区域跑了三种算法效果差异非常明显算子输出特点在拓片上的表现耗时512x512灰度图Prewitt梯度幅值图边缘粗、双像素宽对裂纹和墨点噪声响应强烈字形轮廓淹没在杂讯里约3msSobel梯度幅值图边缘稍细比Prewitt稍干净但仍有大量噪声边缘约3msCanny二值细线边缘图字形主轮廓清晰配合参数调整可压制大部分纹理噪声约12ms结论很明确在甲骨拓片这种噪声密集的场景直接用Prewitt或Sobel做边缘图输出效果惨不忍睹。它们更适合做边缘幅值计算的中间步骤而不是最终结果。Canny因为有双阈值机制能把强的真实边缘和弱的噪声响应区分开所以胜出。网上搜索prewitt边缘检测原理时能看到大量理论推导但真实项目里用它直接出结果的情况真的很少这个坑我先替你踩了。2.2 Canny参数怎么调才不出花图Canny用得好不好参数占七成。核心参数有三个高斯滤波的sigma值、两个滞后阈值low_threshold和high_threshold。我先说sigma。sigma控制高斯滤波的平滑程度sigma越大图像越模糊细微纹理越容易被抹平。甲骨拓片的笔画边缘本身有虚影sigma太小等于没滤波sigma太大又会让真实字形边缘也跟着模糊甚至断裂。我实测下来sigma取1.0到1.5之间比较合适既能压掉大部分粒状噪声字形主轮廓也不会断。双阈值是Canny的灵魂。我见过很多人直接抄例程里的100和200结果边缘断得七零八落。正确的做法是理解两个值的逻辑关系high_threshold决定什么边缘必定是边缘low_threshold决定什么边缘可能是边缘。凡是高于高阈值的直接保留高于低阈值的只有与高阈值边缘相连才保留。所以它俩的比值比绝对值更重要。在拓片上我一般这样定import cv2 import numpy as np img cv2.imread(oracle_bone.tif, cv2.IMREAD_GRAYSCALE) # 1. 高斯滤波sigma1.2 blurred cv2.GaussianBlur(img, (5, 5), 1.2) # 2. 根据图像梯度幅值统计信息自适应设定双阈值 grad_x cv2.Sobel(blurred, cv2.CV_64F, 1, 0, ksize3) grad_y cv2.Sobel(blurred, cv2.CV_64F, 0, 1, ksize3) grad_mag np.sqrt(grad_x**2 grad_y**2) high_threshold int(np.percentile(grad_mag, 90)) low_threshold int(high_threshold * 0.4) # 3. Canny边缘检测 edges cv2.Canny(blurred, low_threshold, high_threshold)这里用梯度幅值的90分位数作为高阈值低阈值取高阈值的0.4倍对不同拓片的明暗对比变化有天然的自适应能力。实测下来比固定写死100、200稳定很多。如果你处理的拓片对比度特别低可以把percentile的分位数降到80低阈值比例升到0.5让Canny更愿意保留边缘片段。3. 多边形拟合让像素轮廓变成可计算的矢量3.1 为什么输出必须是多边形而不是像素链边缘检测出来的是一个二值像素图就像用铅笔描出的线稿有的连成片有的断成点。但后续如果要测量笔画角度、计算字形面积、对比不同图符的几何相似度这种像素级的表达完全不够用。你没法用一行像素算夹角也没法直接拿两个二值图像做仿射变换对比。所以必须把像素链转成矢量多边形也就是用一个点序列去逼近原来的轮廓。这一步的价值用一句话概括把看起来像什么变成能被坐标描述成什么。一旦轮廓变成了点序列就可以做几何比对、匹配、尺寸分析甚至可以导出成SVG格式直接进入排版或3D建模流程。可以说多边形拟合是从图像领域跨入几何分析领域的桥梁。3.2 Douglas-Peucker算法的实践中多边形拟合最常用的算法是Douglas-PeuckerDPOpenCV里封装成approxPolyDP。它的逻辑一句话就能说清找一条过轮廓首尾两端的直线计算所有中间点到这条直线的最大距离如果最大距离大于阈值epsilon就把这个点切成两条线段继续递归如果小于阈值就认为这些点可以用一条直线段近似舍弃中间点。这里最容易踩的坑是epsilon取值。取太小多边形顶点数量爆炸起不到化简效果取太大笔画转角会被磨平字形细节丢失比如本来有折角的笔画被拟合成弧线。我试过几种epsilon定义方式最稳定的是按轮廓周长的比例来而不是固定像素值因为不同字形大小差异很大。# 边缘图 - 连通轮廓 contours, hierarchy cv2.findContours( edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE ) # 按面积排序过滤掉噪点小区域 contours sorted(contours, keylambda c: cv2.contourArea(c), reverseTrue) contours [c for c in contours if cv2.contourArea(c) 60] polygons [] for c in contours: # 计算轮廓周长 perimeter cv2.arcLength(c, True) # 经验值epsilon取周长的1.2%~2% epsilon 0.015 * perimeter approx cv2.approxPolyDP(c, epsilon, True) polygons.append(approx)epsilon系数0.015是我在大量拓片上试出来的折中点。写代码时有个细节cv2.arcLength的第二个参数要传True表示轮廓是闭合的否则计算出来的周长会少算一段闭合边epsilon也会偏小。另外在拟合前用RETR_EXTERNAL只取最外层轮廓可以过滤掉字形内部因为墨色不均产生的洞对后期分割是好事。3.3 拟合之后的轮廓后处理多边形拟合完不代表万事大吉还要做两步收尾。第一步是去除面积过小的碎多边形这些往往是拓片上的噪点或裂纹边缘残留通过面积阈值就能清掉。第二步是平滑处理有些多边形相邻顶点之间角度突变非常明显明显是噪声点没被DP化简掉可以用简单的角度阈值把这些异常顶点剔除。我习惯把后处理合成一个函数输出一个干净的polygon列表每个polygon就是一串有序点坐标同时把它的最小外接矩形也一并算出来后面分割要用processed [] for poly in polygons: area cv2.contourArea(poly) if area 60 or len(poly) 3: continue x, y, w, h cv2.boundingRect(poly) processed.append({ contour: poly, bbox: (x, y, w, h), area: area })到这里图像层面的工作已经基本完成每一个可能的字形都有了一个轮廓坐标集合和一块候选区域坐标接下来进入分割阶段。4. 文字分割把粘连的图符逐个抠出来4.1 连通域分析最直接的分割入口分割的目标简单说就是一个萝卜一个坑——每个图符对应一个独立区域。前一步的边缘检测和多边形拟合适用于单个图符的轮廓但一张拓片上通常有多个字而且它们之间可能挨得很近笔画甚至黏在一起这就需要先把文字区域从整图里分离出来。最基础的方法是连通域分析。一张二值化的字形图像里每个独立的白色连通块就是一个候选字符区域。在OpenCV里connectedComponentsWithStats可以一次性拿到每个连通域的位置、面积、外接矩形等信息。但甲骨拓片的特殊性在于笔画残缺或者拓印不清晰会导致同一个字断成多个连通域这就不能直接按连通域当一个字了。我的做法是连通域投影法双保险。先用连通域拿到候选再用水平投影和垂直投影找出字符行和字符列的空白间隔两个结果互相印证才能确定分割边界。4.2 投影分割和形态学处理投影法的思路特别直观把图像分别向水平和垂直方向压扁统计每一行或每一列有多少个白色像素得到的投影曲线上波谷就是字符间隙波峰就是字符主体。如果是规矩的印刷体文字投影法的效果可以称得上完美但甲骨字形歪歪扭扭、笔画忽粗忽细投影曲线往往杂乱无章直接找波谷不靠谱。所以我的实际流程是先做形态学操作再投影。形态学闭运算可以填平笔画内部的断裂把同一个字的离散部分牵在一起然后再做投影分割。这一步是整条流水线里最需要经验的环节因为结构元素的大小直接决定文字会不会被连错。我常用一个矩形结构元素做闭运算尺寸取单个笔画宽度的1.5到2倍然后用垂直投影定位列的间隙把每一列再交给连通域做精确分割。kernel np.ones((5, 5), np.uint8) closed cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) # 垂直方向投影 vertical_proj np.sum(closed, axis0) // 255 # 找出投影值接近0的列区间作为分割缝隙 gaps np.where(vertical_proj 0)[0]这一步跑完之后你会得到一组列边界。每一列的宽度如果落在合理范围内我一般统计所有列宽的中位数允许上下浮动比如中位数的0.5倍到2倍都保留就把它当做一个候选文字区域超出范围的要先放大检查避免把半个字框进去或者把两个字框在一起。4.3 分割结果如何与轮廓数据配合画框只是第一步分割得稳不稳要看它能不能跟前两步的数据对应起来。我的建议是分割出来的候选区域坐标要回回去与多边形拟合阶段生成的bbox做交集匹配。如果一个候选区域内部包含至少一个完整多边形才认为它真的是一个字否则就可能是残留墨块或者装饰性纹样。这套交叉验证的思路是整条流水线的精髓也是我能稳定处理低质量拓片的关键。分割模块输出的不只是一张框好红线的图片而是一份结构化的JSON每个图符带上了轮廓点、外接矩形、面积等几何特征。后续无论是做字形比对、编目存档还是喂给深度学习模型做训练都可以直接使用。5. 常见问题与排查技巧实录5.1 高频问题速查表这几个月来回跑流程我把踩过的坑整理成了一张速查表遇到问题时先对号入座。现象可能原因排查顺序字形边缘大量断裂sigma偏大或高阈值偏高先降sigma到1.0再看阈值分位数是否超过95边缘图整幅开花噪声多低阈值和高阈值比例失调把low_threshold提高到high_threshold的0.5倍多边形拟合后字形棱角被磨平epsilon取值过大将epsilon系数从0.015降到0.008左右相邻两字边缘黏连分不开拓片笔画太靠近闭运算结构元素调小或改用垂直投影加形态学腐蚀同一个字被拆成两半笔画中段墨色太淡边缘未连接闭运算结构元素加大或者改用条带区域生长法分割框把背景裂纹框进来面积阈值偏低噪声连通域混入提高面积阈值并检查连通域长宽比是否异常5.2 我踩过几次坑之后的避坑清单最后分享几个必须遵守的操作纪律全是从低级错误里换回来的教训。第一千万不要跳过高斯滤波直接做Canny。虽然Canny内部自带高斯平滑但那一个模糊参数完全不够用尤其在拓片这种粒状噪声突出的图像里跳过高斯滤波边缘图会粗一倍。先单独滤波再进Canny边缘细度立刻不一样。第二多边形拟合的epsilon参数宁可小一点也不能贪大。因为后续如果发现顶点过多可以用简化算法再抽稀一次但如果顶点已经被删没了原始信息就找不回来了。切记拟合是信息压缩过程一次删过头就补不回来了。第三所有缩放操作必须保持等比。为了快速调试很多人会把原图resize到很小的尺寸这在边缘检测阶段没大问题但到了多边形拟合阶段epsilon按周长比例计算时会跟着缩小而实际图像处理误差是像素级的两者的矛盾会突然爆发表现为小图上拟合完美的轮廓放大到原图上却歪歪扭扭。所以调试时用小图没问题最终输出前一定要回到原分辨率跑一遍全流程。第四不同年代的拓片建议分开调参数。我处理的这批拓片里商代早期的拓片墨色深、笔画粗晚期的则浅淡纤细。同一套阈值处理这两类结果天差地别。批量处理项目开工前先抽三到五张样图按类型分别标定参数能省后面一整周的返工时间。这套流程跑下来我对传统图像处理方法的看法从最初的怀疑变成了实打实的依赖。它不炫技但每一步都稳出了问题你永远知道该去调哪里。如果再进行扩展我会把多尺度分析加进去解决笔画粗细差异更大的场景也会尝试把分割出来的多边形轮廓直接做成字形匹配的特征向量让检索和比对自动化。甲骨文数字化这条路还很长希望这篇流程记录能给同样在做文字提取的朋友省下一些试错的成本。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。