去年我们处理一批老图纸时遇到一个很现实的问题:CAD源文件在供应商那边,我们手里只有PDF扫描件和打印稿。人眼扫一眼就能分出轮廓线、尺寸线、中心线,但要让程序自动把这些线认出来、把尺寸数字读出来,再组成可校验的尺寸链,就不是写几个图像处理函数能糊弄过去的了。这个项目代号我起的比较随意,叫MDAIOD(Multi-Dimension Automatic Intelligent Object Dimensioning),核心目标就一句话:把平面上散落的尺寸线段,变成机器可读、可校验、可下发给下游系统的结构化几何信息。
这篇文章我打算把整个项目的设计思路、算法选型、实测数据和踩坑经验完整复盘一遍。适合正在做图纸自动识别、CAD数据提取、或者想在CV方向找落地场景的朋友参考,哪怕是刚入门的小白,也能从预处理到语义复原这条链路里抄到能直接用的处理流程。
1. 为什么先做“尺寸线段”而不是整体识图
1.1 企业图纸现状:矢量数据留不住,图片图纸一大堆
市面上的CAD软件都能导出矢量格式,理论上只要拿到DWG或者DXF,尺寸信息就是现成的,根本不需要去“识别”。但现实场景里,大量图纸以打印稿、PDF、照片的形式存在。有的源头厂商只发扫描件,有的老图纸只有蓝图,还有的PDF本身就是图片型PDF,里面没有任何矢量对象。这时候想批量提取尺寸做合规检查,就绕不开图像识别这条路。
我见过不少团队一上来就上深度学习目标检测,试图把箭头、数字、轮廓一次性“框”出来。这条路不是走不通,而是成本太高:标注一套完整的工程图纸数据集非常痛苦,一个图框里密密麻麻几十条标注,框到想吐。而且检测模型只能告诉你“这里有箭头”“那里有文字”,它不告诉你这些箭头和文字之间的几何关系,也不告诉你这条尺寸线到底是标注哪个特征。所以我在MDAIOD里坚持一个原则:先做像素级和连通域级的几何分析,把可靠的线段和文字块提取出来,再用规则去组合它们的语义关系。深度学习只作为OCR等局部环节的辅助工具。
这套思路的好处是,核心逻辑完全可解释,每一条输出都能回溯到具体像素证据。出了问题,你能明确知道是哪一步判断错了,而不是面对一个端到端黑盒模型束手无策。
1.2 把制图规范翻译成可计算的几何规则
工程制图里,尺寸标注不是随便画的,它由几个固定成员组成:尺寸线、尺寸界线、箭头、尺寸文字。制图标准对这些元素的线型、方向、相对位置都有约束。在算法眼里,这些约束就是现成的先验规则。
我从标准里提取了几条最关键的规则,作为整个系统的骨架:
- 尺寸线是细实线,两端有终端符号(最常见的实心三角箭头),中间或上方承载尺寸文字。
- 尺寸界线也是细实线,从被标注的轮廓边引出,通常垂直于尺寸线,两端分别指向两个测量特征点。
- 箭头的指向有规律:线性尺寸的箭头指向外侧的尺寸界线,半径标注只有一个箭头指向圆心,直径标注可以是两个箭头分别指向两个对边。
- 尺寸文字一般位于尺寸线中部上方或者尺寸线中断处,文字高度远大于线宽。
这些规则听上去平平无奇,但它们能直接转化成几何判断条件,比如“候选尺寸线的两个端点附近必须存在三角形的箭头形态特征”“文字块中心到尺寸线的垂直距离应该在字高的0.5到1.5倍范围内”。我把整条分析链路的所有判断,都建立在这些可计算的规则之上。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 像素到矢量:骨干线段提取的完整流程
2.1 预处理与线宽分离:先按线宽把图分成两层
图纸线条是有标准线宽系列的,粗线通常是细线的两倍。以国标常用配置为例,轮廓线用0.5mm或0.7mm,尺寸线、尺寸界线、中心线、剖面线用0.25mm或0.35mm。扫描成300dpi图片后,0.25mm的线约等于3个像素,0.5mm的线约等于6个像素。这个差距在像素层面非常明显,所以第一步我就把二值图按线宽拆成“粗线掩膜”和“细线掩膜”,尺寸线只可能出现在细线掩膜里。
具体做法是这样的:先灰度化、自适应阈值二值化,把白底黑线的图变成黑底白线,然后对前景像素做距离变换,得到每个前景点到背景的最近距离。这一步用OpenCV的cv2.distanceTransform就能算。距离值乘以2,就是这个位置线的近似宽度。接着统计所有前景像素的宽度直方图,正常图纸会形成两个峰,一个在细线宽度附近,一个在粗线宽度附近,用峰谷阈值切割,就能把像素分成粗线像素集和细线像素集。
这里有一个必须提醒的细节:图纸上还有波浪线、中心线(点划线)、剖面线,它们都属于细线,会一起进入细线掩膜。所以后续的箭头检测和几何过滤是必须的,线宽分离只负责缩小候选范围,不负责最终判断。
2.2 线宽聚类的工作细节与参数建议
距离变换后的直方图切割,听起来简单,实际处理时有几个坑必须注意。
第一个坑是扫描分辨率不统一。同一张图纸的不同扫描件,300dpi和600dpi下同样的0.25mm线宽会差出一倍的像素数。我的处理办法是先做全局尺度归一化:统计图中所有文字连通域的中等高度,把它映射到标准字高。工程图里字高和线宽存在比例关系,用字高反推线宽阈值,比写死像素值靠谱得多。
第二个坑是灰度不均。老蓝图上某些区域底色发黄或者反光,二值化后线条会断裂。自适应阈值比全局阈值好很多,但遇到大块脏污还是会出问题。我额外加了一步形态学闭运算,用3x3的核把轻微的线缝补上,代价是部分小箭头可能被“糊”在一起,但相对收益更大。
第三个坑是线宽双峰的谷很浅。打印质量差的图纸,粗线细线的宽度差异不一定明显。为了稳健,我没有完全依赖峰谷,而是同时用连通域的外轮廓周长与面积之比来估计线型:细线连通域的周长面积比明显大于粗线。两个特征投票决定像素归属,误分类率会低很多。
2.3 直线段提取:为什么我用连通域外包矩形而非纯霍夫变换
很多做直线检测的教程上来就是HoughLinesP,但工程图纸场景下纯霍夫变换是个灾难。尺寸线、中心线、剖面线交织在一起,加上箭头、文字、扫描噪声,霍夫变换会给出大量虚假短线,而且它的输出是极坐标参数化的线段端点,端点的像素精度不高,对后续箭头尖端定位来说误差太大。
我走的路线是:在细线掩膜上做连通域分析,然后对每个连通域求最小外接矩形。如果矩形长边足够长、短边接近线宽,就把它当成一条候选直线段,保存它的两个长边中点作为端点、角度、长度和线宽。这个方法的优点是完全保留了连通域的像素级端点信息,而且天然把箭头和文字排除在外——箭头区域的外接矩形短边会显著大于线宽,文字区域的面积极小但短边也很宽,它们都不会通过直线段筛选。
中心线是点划线,断开成好几段。我先把方向一致、端点间距在合理范围内的短线段聚类,再按间隙规律合并。代码逻辑不算复杂,核心就两步:
python复制# 线段合并示意:按角度和端点距离聚类
for seg in segments:
keys = []
for s in segments:
angle_gap = abs(seg.angle - s.angle)
end_dist = min(dist(seg.p1, s.p1), dist(seg.p1, s.p2),
dist(seg.p2, s.p1), dist(seg.p2, s.p2))
if angle_gap < 5 and end_dist < max_gap:
keys.append(s)
# 合并同一聚类中的线段,取覆盖了整段的长线
这一段做完,我就拿到了所有候选细线段的几何参数,接下来要处理的是“谁和谁组合成一条完整尺寸标注”的问题。
3. 箭头、文字与尺寸界线:语义复原的三个关键动作
3.1 箭头识别的形态学特征与方向判定
图纸标准里,实心三角箭头的张角大约在15度到60度之间,长度约4到6倍线宽,高度约等于线宽。但在扫描件上,一个小箭头可能就是12到20个像素的一块三角形区域,轮廓早被磨得不规则了。直接找三角形不现实,我用的是更鲁棒的局部形态学判断:
对每个候选直线段,取两个端点的局部ROI窗口,窗口内的前景像素若满足“从端点向外呈扇形发散”的特征,就认为该端存在箭头。具体做法是从端点出发,沿着线段方向向外延伸一个箭头长度的范围,统计这个扇形区域内的前景像素数量,再和线段尾部等面积的矩形区域像素数量做对比。扇形区域密度显著偏高,且扇形张开方向与线段方向一致,就标记为有箭头。
箭头方向决定了尺寸线的内外朝向。线性尺寸里,两条尺寸线通常夹在两条尺寸界线之间,箭头尖端指向外侧;半径标注只有一个箭头,箭头尖端指向圆心侧。我把箭头方向作为一个强特征存下来,后面做尺寸链归类时很有用。
有个细节值得单独说一下:箭头尖端点就是尺寸线与尺寸界线的交点,它的定位精度直接影响后续尺寸界线搜索。所以箭头尖端我在三角形ROI里再做一次亚像素级角点提取,用灰度质心法把尖端修正到0.5像素以内。这一步对干净导出图可能影响不大,但对扫描件很关键。
3.2 文字定位与OCR可信度控制
尺寸文字的目标,一是读数值,二是帮我们确定这条尺寸线的从属关系。文字定位我优先用连通域特征做粗筛,再用OCR模型识别。
尺寸数字在细线掩膜上通常会形成独立的连通域,因为数字笔画和尺寸线之间存在间隙。除非文字直接压在线上,否则用字符尺寸过滤就能把它们找出来。字符高度先验我用线宽的5倍左右做参考,再结合整张图的文字连通域高度直方图做中位数校准。这一步比盲目跑一个文本检测器要快得多,精度也够。
OCR环节,我对比过Tesseract和PaddleOCR。Tesseract对数字和小数点的识别在干净图上表现还行,但扫描件上一旦有噪点或轻微倾斜,错误率就上来了。PaddleOCR的PP-OCRv4在数字识别上明显更稳,所以我最后选用了PaddleOCR的检测加识别管线。
OCR输出必须做格式校验,不能直接把原始输出当尺寸值。我在代码里加了一层正则约束:
python复制import re
pattern = re.compile(
r'^[⌀RMRCST]?\s?'
r'(\d+(\.\d+)?|\.\d+)'
r'\s?([±]\s?\d+(\.\d+)?)?'
r'(\s?[Hh]\d)?$'
)
匹配不上的识别结果直接丢弃,或者标低置信度,宁可不识别也不要识别成错数字。尺寸值一旦出错,后面所有校验都会跟着错,而且这种错误非常隐蔽。
3.3 尺寸界线归属判定:这条尺寸量的是什么
有了箭头,有了尺寸线,有了文字,最后一环是把“这条尺寸”和“被标注的轮廓特征点”绑定起来。这个绑定关系就是尺寸界线归属判定。
工程图里,尺寸界线从箭头尖端引出,方向近似垂直于尺寸线,一直延伸到轮廓边。所以我的流程是:从箭头尖端出发,在垂直尺寸线的方向上搜索细线掩膜中的连通域,找到符合条件的线段后,沿着它的延伸方向继续找粗线掩膜的交点。如果能找到粗线轮廓,说明这条尺寸界线确实连到了零件实体边缘。
两个箭头尖端各自对应一个轮廓交点,把这两个交点和尺寸文字放在一起,就形成了一条完整的线性尺寸记录。如果只找到一个交点和一条指向图内的尺寸线,就要进入半径/直径分支处理。这一步的难点在于,复杂的零件轮廓上距离很近的边角会同时出现在搜索范围内,容易选错目标。我在搜索目标选择上加了一个评分函数,综合考量距离、角度垂直度和连接连续性,把连接分数最高的轮廓点作为归属目标。
这套方法对简单的矩形板、轴类件、法兰盘都非常好用。对复杂异形件,偶尔会有归属错误,但至少每个错误都能定位到具体评分项,方便人工复核。
4. 尺寸链与几何校验:从“看到线”到“看懂图画”
4.1 尺寸链闭合检查与漏标发现
单条尺寸识别得再准,如果不做整体校验,系统输出对用户的价值也有限。工程图里最经典的整体约束就是尺寸链:同一个方向上的分段尺寸加起来应该等于总尺寸,或者存在合法的基准偏移关系。
我提取完尺寸记录后,会把它们组成一个图结构,节点是轮廓特征点,边是尺寸线段。然后对每个主方向做闭合约束求解:遍历所有尺寸链路径,检查分段尺寸之和与总尺寸的差值。如果差值在容差范围内,说明这部分尺寸体系是自洽的;如果存在冲突,优先怀疑识别错误,其次是原图纸本身标错。
这套机制的实际价值是查漏。很多图纸上特征很多,人工漏标一个孔到基准的距离是常有的事。我的图结构里,如果某个轮廓特征点没有任何尺寸边连接,而且它不是对称点,就会自动报一条“可疑漏标”记录。我们拿一批真实图纸测过,这个功能比单纯识别尺寸数值更能打动现场工程师。
4.2 线性/径向/引线标注的分支判断
不同标注类型的语义差异很大,判断分支主要靠文字前缀和箭头指向组合:
- 尺寸文字以
⌀开头且尺寸线两端都接触轮廓线,通常是直径标注的线性形式,常见于轴类零件的侧视图。 - 文字以
R开头且只有一个箭头,箭头指向轮廓线上的一点,通常是半径标注。 - 文字以
⌀开头且箭头指向圆/圆弧轮廓中心,是真正的径向直径标注,多见于俯视图。 - 文字后带
×45°之类后缀的,是倒角标注,识别的时候不要和普通线性尺寸混在一起。
代码实现上就是一个if-else分支树,但每个分支都对应一种真实的图纸表达。我踩过的坑是:早期版本只按箭头数量判断线性还是径向,结果遇到轴类侧视图的直径标注时,两个箭头齐全却被当成了普通线性尺寸。后来加上文字前缀校验,准确率才起来。
4.3 数据结构设计:把结果交给下游之前
识别结果最终要交给下游系统使用,数据格式我设计成了JSON,保留原始像素坐标也保留换算后的工程坐标。每条尺寸记录包含类型、标称值、公差、尺寸线端点、尺寸界线交点、关联特征点、置信度,以及原始图像证据截图路径。设计要点是:所有几何数据都保留像素坐标和逻辑坐标两份,像素坐标用于人工复核定位,逻辑坐标用于下游建模。
json复制{
"id": "dim_0047",
"type": "linear",
"value": 50.0,
"tolerance": {"nominal": 50.0, "upper": 0.02, "lower": -0.01},
"unit": "mm",
"start": {"x": 188.2, "y": 336.5, "feature": "edge_a"},
"end": {"x": 128.2, "y": 336.5, "feature": "edge_b"},
"arrow_start_direction": "outward",
"arrow_end_direction": "outward",
"text_content": "50±0.02",
"text_box": [180.5, 330.1, 196.5, 342.8],
"confidence": 0.92,
"evidence_image": "dims/dim_0047.png"
}
下游的自动审查模块可以直接拿这个JSON算几何约束,完全不用关心底层图像。这个格式也方便扩展,以后加形位公差标注,只需增加一个“gdt”字段,不用推倒重来。
5. 实测效果与高频误判的规避经验
5.1 验证集组成与指标说明
我在内部建了一个验证集,包含三类样本:数字图纸直接导出的高清PNG、数字图纸打印后扫描的回扫件、纯扫描的老蓝图。样本总数不大但覆盖了常见标注类型。评估指标分两个层面:线段级指标只关心尺寸线和箭头有没有找全找对;语义级指标要求尺寸数值、尺寸界线归属、标注类型全部正确才算一条有效记录。
干净导出图上,线段级召回率能做到93%左右,精确率88%左右,语义级正确率约84%。扫描件上数据掉得很明显,线段级召回率降到78%,精确率74%,语义级正确率只有62%左右。落差主要来自箭头破损、文字粘连和线条断裂。这四个数据说明一个道理:这类项目里,扫描件才是真正的修罗场,工程化调优的重心必须压在抗噪能力上。
5.2 高频误判场景一:剖面线与中心线干扰
剖面线在图纸上是一族等间距平行细线,角度通常45度。它们进入细线掩膜后,经常被误当成尺寸线候选,尤其是当某个尺寸线正好和剖面线方向差不多的时候。我的对策是先用角度直方图检测出图中是否存在明显的平行线族,如果存在且间距均匀,就把这些线先剔除出尺寸线候选集。这个判断对包含多个方向剖面线的图纸也有效,因为剖面线族的方向能量在直方图上一定是突刺。
中心线的干扰方式不同。中心线是点划线,由长划、短划交替构成,不会形成完整长线,但容易被霍夫家族检测成一堆共线碎段。因为我在直线提取阶段用的是连通域外加矩形,碎段合并逻辑就是专门防这个问题的。合并的关键不是简单拼接,而是要验证碎段之间的间隙是否符合“短划间隙”的先验,否则会把两条平行的相邻尺寸线误并成一条。
5.3 高频误判场景二:文字吸附与线段断裂
尺寸文字和尺寸线靠得太近时,OCR检测框会把一部分线段像素划进文字区域,导致尺寸线被“啃”掉一截,断裂成两段。这个问题在扫描件上特别常见。我的处理方法分三步:先把OCR检测到的文字区域在掩膜上抠掉,再做闭运算把断口搭起来,最后根据文字框位置把断裂的线段重新关联。闭运算的核大小要按线宽动态调整,核太小断口接不上,核太大箭头就会被抹平。这里没有通用参数,我是按“核边长=线宽像素数的3倍”来设置的,实测效果还可以。
文字吸附还会造成另一个问题:两个数字离得近,OCR把它们合并识别成一个错误字符串。比如“12”和“30”中间的空隙太小,被读成“1230”。我用连通域先做了字符级分割,每个独立字符连通域单独过OCR,再把同一条尺寸线附近的字符按位置顺序拼成数值。这对小数点、正负号的处理也更干净。
5.4 高频误判场景三:扫描件箭头破损
箭头是整条分析链路里最脆弱的环节。扫描件上小箭头经常缺角、模糊,甚至变成一个圆点。一旦箭头检测失败,后面的尺寸界线归属和类型判断就全断了。针对这个问题,我引入了一个“无箭头降级恢复”机制:对没有箭头但长度合适、且两个端点附近存在垂直短线的细线段,把它们标记为候选尺寸线,结合文字位置和尺寸界线交点做二次确认。如果文字块到该线段的距离符合标注规范,且两端都能搜到垂直延伸线,就按尺寸线恢复处理,只是置信度要打折。
这个降级机制让扫描件的线段级召回率从70%左右拉回到了78%附近,但它也引入了一些误检,所以我在输出JSON里对这类恢复记录加了confidence字段,下游系统可以据此决定是否走人工复核。
6. 后续能扩展的方向(含我的遗憾)
6.1 圆弧标注、角度标注与组合标注的升级路线
现在的MDAIOD还只能覆盖线性尺寸和一部分径向直径尺寸,圆弧半径标注、角度标注、引线标注还没有完全纳入。技术上不是做不到,比如半径标注的文字前缀R是强信号,角度标注的尺寸线是一段弧而不是直线,处理逻辑要增加圆弧检测分支。工具层面RANSAC圆拟合和弧段分段都很成熟,难点在于把圆弧段和文字、圆心、边界点的关联逻辑设计好。这部分我早期没做,等回头要补的时候发现如果在一开始的数据结构里就把尺寸线设计成type: line|arc的泛型,后面扩展会顺畅很多,现在只能加兼容字段。
6.2 从尺寸分析走向参数化重建
尺寸线段的语义分析做完之后,一个更诱人的延伸方向是参数化重建:把识别到的轮廓线段当成未知参数,把尺寸数值当成等量约束,用一个平面几何求解器去求解所有轮廓点的精确位置。这件事的价值在于,它能把“读图”变成“建模”,下游可以直接基于重建结果做数控加工辅助编程、公差分析或者装配仿真。
这个方向卡点是比较多的,比如约束方程可能超定或者欠定,轮廓圆角过渡段的处理,还有尺寸之间互相矛盾的情况怎么仲裁。我的体会是,尺寸仲裁规则应该优先采信置信度高、能和多个邻接尺寸形成闭合链的数值,而不是图省事取平均值。这个问题值得单开一个项目去研究,MDAIOD目前的成果已经可以给它的约束端提供相对干净的数据输入了。
回头复盘这个项目,我最大的感受是:图形尺寸分析这类任务,算法的复杂度和工程琐碎度远超预期,但它的落地价值也很直接。我们拿这套流程跑完一批历史图纸的资料审核,确实能帮工程师省掉大量重复工作。如果你也在做类似的图纸识别项目,建议先把线宽分离和箭头定位这两环做扎实,它们虽然技术含量看起来不如模型高,但整套系统最后扛不扛得住扫描件噪声,全看这两环的底子。
