新闻详情

新闻详情

首页 / 资讯中心 / 详情

umeditor集成OCR:机械图纸截图自动识别插件开发实践

发布时间:2026/10/2 3:11:48来源:尧图网络
umeditor集成OCR:机械图纸截图自动识别插件开发实践
做机械行业信息化的朋友十有八九都被同一个问题磨过车间提交过来的图纸、铭牌、合格证要录入系统时全靠手工敲。老师傅看得懂图纸但让他把那一长串型号编码一个个敲进文本框效率低不说还经常把O和0、I和1搞混。我最近在给单位的材料管理系统做升级就接到这样的需求在常用的umeditor编辑器里做一个截图就能自动识别文字的插件把图纸上的参数直接变成可编辑的文本同时截图本身也留在文档里。这篇文章把整个开发过程复盘一遍内容包括OCR引擎选型、umeditor插件机制、前后端交互链路以及在机械行业实际部署中踩过的坑。想给后台编辑器加OCR能力的、或者正在做图纸信息采集系统的人可以直接参考这里的代码和思路。1. 机械行业的真实痛点编辑器、图纸与OCR之间的“最后一公里”1.1 一张截图里到底藏着什么信息机械行业后台系统有个共通点大量结构化信息并不在系统里而在纸面和电子图纸上。以一张最普通的零件加工图纸为例标题栏里有图号、材料牌号、设计者、审核者、日期技术要求栏里有热处理要求、表面处理要求、公差等级物料清单里则是密密麻麻的零件编码和数量。这些字段一旦要录入后台系统传统做法就是对着图纸抄。抄的过程看起来只是打字实际包含很重的注意力负担要在图形、尺寸线、标注文字之间分辨哪些是需要录入的字段还要抵抗疲劳带来的抄错风险。更重要的是这些信息往往是“混合密度”的。一个图号可能长成这样DX-6001-E材质写40Cr热处理要求写调质HB220-250表面处理写Cr12MoV轴承规格写6205-2RS。数字、大写字母、小写字母、连字符、单位符号全部混在短短几个字符里。普通OCR工具拿去识别图书封面、车牌、身份证这种规范文本效果不错但拿到机械图纸上面对这种小字号、细笔画、密集排列的工程字体识别率会明显掉一截。这也是为什么不能直接丢一个通用OCR应用给车间用必须自己在业务系统里做定制插件的原因。1.2 为什么是“截图OCR”而不是“全图识别”刚开始接到需求时我也考虑过是不是做一个“上传图纸全图自动识别”的功能。真做起来才发现方向偏了一张A3扫描图纸8000x6000像素里面图形线条、剖面线、虚线一大堆真正的有效文字只集中在标题栏、技术要求和明细表几个区域。全图识别有几个问题一是识别耗时太长二是图形线条会和文字产生大量粘连干扰三是识别出的坐标、尺寸标注未必是业务系统需要的字段。与其费劲做版面分析不如让用户自己圈出目标区域截个图再识别。这个交互模型非常接近机械工程师的日常工作习惯——他们本来就用CAD、看图软件里的框选功能框哪块看哪块。截图OCR还有一个额外好处识别之后截图本身可以作为原始凭证跟着文档走。后面就算识别结果被修改了审单的人也能看到原图长什么样追溯起来非常方便。综合下来“截图录入”比“全图识别”在准确率、响应速度、业务可溯源性上都要实用得多。2. OCR引擎选型本地部署与线上API的取舍2.1 Tesseract、PaddleOCR与线上云OCR的对比选型阶段我实际测过三类方案Tesseract 5.x、PaddleOCR、百度智能云OCR。测试样本是我从车间要来的80张真实图纸截图包含标题栏、铭牌、合格证三类图片。结果很有意思Tesseract对中文长句的识别率还行但一遇到“DX-6001-E”这种混合编码就频频出错O识别成0、1识别成I的问题很突出而且Tesseract对反向、倾斜的文字基本没有修正能力。云OCR的识别精度最高尤其对印刷体效果很好但机械行业的图纸有保密要求不少图纸是供应商内部编号往外传在合规上就有风险。PaddleOCR在这三者里属于中间最优解识别精度接近云厂商模型完全可以本地离线部署支持方向分类能自动把倒置或旋转90度的图片转正而且开源免费。这里有个容易忽略的成本项云OCR是按调用次数计费的一张图纸截图识别一次就是一次计费如果一个材料管理系统有几十个用户每天都在录入一年下来的费用不是小数目。本地部署虽然要一台机器但都是一次性投入。考虑到机械行业后台系统通常跑在内网服务器上本地部署更顺理成章。2.2 PaddleOCR新版调用形态从PaddleOCR到PaddleX PipelinePaddleOCR现在主推PaddleX Pipeline方式底层还是OCR能力但调用代码更简洁。老的写法是初始化PaddleOCR类一遍遍传参数新版直接用create_pipeline创建OCR管线预测时一行搞定from paddlex import create_pipeline pipeline create_pipeline(pipelineOCR) result pipeline.predict(snapshot.jpg) for res in result: # res里的字段在不同版本略有差异一般包含识别文本和置信度 print(res)我在搭建OCR服务时用FastAPI把这段能力包成一个HTTP接口。机械行业的业务后台一般用PHP或Java写前端是HTMLJavaScriptOCR能力必须解耦成独立服务这样后端语言完全不用绑定Python。FastAPI天然支持异步接收文件配CORS中间件就能让前端跨域调用。接口的核心逻辑很简单接收上传图片、存临时文件、调PaddleX识别、返回JSON。from fastapi import FastAPI, UploadFile, File from fastapi.middleware.cors import CORSMiddleware from paddlex import create_pipeline import uuid, os app FastAPI() app.add_middleware( CORSMiddleware, allow_origins[*], allow_methods[POST], allow_headers[*], ) pipeline create_pipeline(pipelineOCR) app.post(/api/ocr/recognize) async def recognize(file: UploadFile File(...)): tmp_path f/tmp/{uuid.uuid4().hex}.jpg with open(tmp_path, wb) as f: f.write(await file.read()) try: result pipeline.predict(tmp_path) rec_texts [] rec_scores [] for res in result: rec_texts res.get(rec_texts, []) rec_scores res.get(rec_scores, []) return {code: 0, texts: rec_texts, scores: rec_scores} finally: os.remove(tmp_path)这里要多说一句千万不要把临时文件堆在/tmp里不清理。我们第一版就吃过这个亏识别服务跑了半个月磁盘被临时图片塞满整个系统卡死。记住无论识别成功还是失败都要在finally里删掉临时文件。3. umeditor插件机制的底层拆解注册、命令与UI扩展3.1 registerUI和toolbars配置的关系umeditor已经很多年没有大版本更新了但它的插件机制在今天看来依然够用。它把“功能扩展”拆成了两层UI层和命令层。UI层负责在工具栏上放一个按钮命令层负责按钮点击之后真正做什么。想要新增一个按钮只需要做两件事在配置文件里的toolbars数组中加上按钮名然后用UE.registerUI注册这个按钮对应的UI对象。以我这个截图OCR按钮为例UE.registerUI(mechOcr, function(editor, uiName) { var btn new UE.ui.Button({ name: uiName, title: 截图OCR, onclick: function() { editor.execCommand(mechOcr); } }); return btn; });这段代码的含义是告诉umeditor工具栏里如果出现mechOcr这个名字就用这个工厂函数生成按钮。按钮点击后执行screencapture命令。这里有个很多人第一次接触umeditor会懵的点registerUI注册的UI名必须和toolbars配置里的名字完全一致差一个字符都加载不出来。我们曾经在配置里写了一下午都没出来按钮最后检查发现工具栏名称写成了mechOCR而UI注册名是mechOcr大小写不匹配。3.2 注册命令execCommand之后发生了什么按钮的onclick里执行了editor.execCommand(mechOcr)这一句会把控制权交给命令注册表。命令注册用UE.commands对象UE.commands[mechOcr] { execCommand: function(cmdName) { openOcrDialog(this); } };this就是当前编辑器实例在命令回调里可以通过this拿到选中内容、插入内容、设置内容。我实现的openOcrDialog没有直接用umeditor自带的Dialog控件——那套东西年代久远样式和现在的后台UI搭不起来。我选用的是项目里已经有的Bootstrap模态框这样点击按钮后弹窗风格和其他页面保持一致。这个取舍很关键不是所有功能都必须嵌进umeditor的Dialog机制里业务系统已有的弹窗组件完全可以直接复用。命令注册完毕之后还要在umeditor.config.js的toolbars数组里加按钮名。我放在“附件类”按钮后面这样用户在录入材料时视觉动线比较顺。4. 前端插件实现从截图/粘贴到按钮回填的完整流程4.1 粘贴图片事件的完整实现机械工程师用电脑的习惯是手机拍图纸、微信传电脑、Snipaste或QQ截图复制下来然后到系统里粘贴。所以插件最核心的入口不是点工具栏按钮而是“粘贴”。umeditor默认在粘贴时会做格式清理很多情况下会把图片粘贴事件过滤掉。为了让外部截图能进来必须在ready事件里提前给编辑器的文档对象挂上粘贴监听editor.addListener(ready, function() { var doc editor.document; if (!doc) return; $(doc).on(paste, function(e) { var clipboardData e.originalEvent.clipboardData || window.clipboardData; if (!clipboardData) return; var items clipboardData.items || []; var imageFile null; for (var i 0; i items.length; i) { if (items[i].type items[i].type.indexOf(image) ! -1) { imageFile items[i].getAsFile(); break; } } if (!imageFile) return; e.preventDefault(); processImageFile(imageFile, editor); }); });这里坑比较多一个一个说。第一umeditor内部自己也绑定了paste事件来做格式清理如果你的监听写在它后面可能还没轮到你的代码执行编辑器已经把剪贴板内容处理掉了。实测下来我在ready事件里用jQuery绑定能拿到图片粘贴但特别老的umeditor版本需要在beforepaste事件里拦截否则还是会被过滤。第二e.preventDefault()必须在找到图片文件之后立即调用否则图片会被浏览器当作默认行为粘贴进去。第三剪贴板里可能同时有文本和图片只取第一个图片即可。4.2 图片压缩与上传为什么不能盲目压小拿到图片文件之后不能直接丢给OCR接口。剪贴板里的截图尤其是微信截取的高分屏图往往是一张4000x2000px、体积6-8MB的PNG。这么大的图直接上传一是请求耗时二是OCR服务处理慢。但机械图纸不是普通照片缩小过度会导致小字号字符模糊OCR识别率断崖式下跌。我的做法是长边超过2600px才等比缩放否则保留原尺寸PNG带透明通道时用白色画布垫底再转JPEG质量参数固定在0.88实测肉眼几乎看不出区别。function processImageFile(file, editor) { var reader new FileReader(); reader.onload function(e) { var img new Image(); img.onload function() { var maxSide 2600; var scale Math.min(1, maxSide / Math.max(img.width, img.height)); var canvas document.createElement(canvas); canvas.width Math.round(img.width * scale); canvas.height Math.round(img.height * scale); var ctx canvas.getContext(2d); ctx.fillStyle #ffffff; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.drawImage(img, 0, 0, canvas.width, canvas.height); canvas.toBlob(function(blob) { var fd new FormData(); fd.append(file, blob, snapshot.jpg); // 直接调OCR接口 uploadAndRecognize(fd, editor); }, image/jpeg, 0.88); }; img.src e.target.result; }; reader.readAsDataURL(file); }toBlob的回调是异步的前端新手特别容易在这里写成同步逻辑导致拿不到blob。另外还要提一句canvas.toBlob在部分老旧浏览器里不支持机械行业后台用户用的浏览器版本可能很旧保险起见补一个兼容降级直接用canvas.toDataURL(image/jpeg, 0.88)转base64再上传。4.3 OCR结果回填的策略先确认再插入OCR识别的结果直接插入编辑器是危险的。机械行业的字符识别有它固定的易错点0和O、1和l、Z和2在工程字体里长得非常接近纯靠模型很难做到100%正确。如果直接插进去后面审单的人很难发现这个隐蔽错误。所以我设计了一个“识别确认弹窗”图片上传识别之后弹窗里左侧显示原图预览右侧是可编辑的文本框里面是OCR识别出的文本。用户看着原图把识别结果修改确认后点“确认插入”才会有图片和文本一起进编辑器。插入图片用umeditor的原生命令editor.execCommand(insertimage, { src: data.url, _src: data.url, style: max-width:100%; });插入文本用insertHtml注意要对文本做HTML转义否则识别结果里万一混入尖括号直接插入会破坏页面结构function safeHtml(text) { return text.replace(//g, amp;) .replace(//g, lt;) .replace(//g, gt;); } editor.execCommand(insertHtml, divb识别结果/b/div div stylemargin:6px 0;padding:8px;border-left:3px solid #bbb; safeHtml(recognizedText) /div);图片我建议不要走base64内嵌量大之后编辑器内容会非常臃肿数据库存起来也痛苦。后台上传接口把图片保存到服务器数据库里存URL这才是常规做法。5. 机械行业识别场景的特殊处理图纸、铭牌与低质扫描件5.1 图像预处理先让机器“看”清楚再识别机械图纸和普通照片的像素特征差别很大。图纸上的文字笔画细、字号小背景是淡黄色或灰白色旁边还有各种粗细线、剖面线。直接扔给OCR模型会把很多线条当成文字框。我在这里加了一层OpenCV预处理核心步骤是转灰度、降噪、对比度增强、自适应二值化。但要特别提醒二值化不能一刀切机械图纸一旦阈值选得太大细笔画字符会被直接抹掉整张图变成一堆噪声。我实际跑下来比较好用的预处理组合是先做一次高斯模糊去扫描噪点然后用cv2.createCLAHE做对比度受限自适应增强最后用自适应阈值而不是全局阈值做二值化。这个组合对低亮度扫描图纸尤其有效比直接全局阈值稳定得多。import cv2 def preprocess(image_path): img cv2.imread(image_path, cv2.IMREAD_GRAYSCALE) img cv2.GaussianBlur(img, (3, 3), 0) clahe cv2.createCLAHE(clipLimit2.0, tileGridSize(8, 8)) img clahe.apply(img) thresh cv2.adaptiveThreshold( img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 15, 10) return thresh还有一类特殊问题图纸里有很多和文字粘在一起的线条边框比如标题栏的表格线。文字压线之后OCR识别会非常吃力。我用的方法是在识别前做一些形态学操作检测并淡化长直线段。具体做法是用cv2.getStructuringElement(cv2.MORPH_RECT, (40,1))和(1,40)分别提取水平和垂直线然后在原图上把检测到的线区域涂白。这个办法不能完全解决表格线干扰但能把识别率从六成提升到八成以上。5.2 用正则和词库把“识别结果”变成“业务数据”模型输出的文本往往是碎片化的“DX-6001-E”可能被识别成“DX-6001-E”带个多余的空格也可能被识别成“DX-6001E”少了横杠。机械行业的数据最终是要落到物料编码、规格型号这些数据库字段里的。所以在交给编辑器之前我加了一层规则后处理。后处理的思路不是去“猜”文字而是基于已知的业务格式做约束。比如我们系统的物料编码规则是“2位字母-4位数字-可选的1位字母后缀”那我就可以这样写function normalizeCode(text) { return text .replace(/\s/g, ) .replace(/([A-Z]{1,4})-?([0-9O]{3,5})-?([A-Z0-9]{0,2})/g, function(all, p1, p2, p3) { var digits p2.replace(/O/g, 0); return p1 - digits (p3 ? - p3 : ); }) .replace(/[:]\s*/g, ); }这里有个重要原则只对符合已知模式的片段做纠正不要全局把字母O替换成数字0。因为图纸上可能出现“LOCATION”这样的完整英文单词全局替换O会闹出“L0CAT10N”的笑话。很多OCR后处理工具就死在这上面规则写得太激进反而把原本正确的文本改坏了。5.3 方向分类与多区域识别的取舍机械行业的图片来源很杂有的是手机拍的有的是扫描仪扫的甚至有的是传真过来的。图片方向不统一是常态。PaddleOCR内置方向分类器开启后能把倒置或旋转90度的图片自动转正。我实测下来对手机拍摄的图纸效果很显著但对本来就是正放的扫描件影响不大。开启方向分类会增加一定的推理耗时如果你的系统里图片基本是正放的可以选择关闭来换速度。多个图纸区域识别的问题上我的建议是克制。一开始我做过“自动检测图框内所有文字块然后按位置排序”的功能貌似很智能实际用起来反而添乱。真实车间场景里用户往往只需要某一栏的信息比方说现在就要录“材质”这一个字段你把标题栏、明细表、技术要求全部识别出来用户还得在一堆结果里翻找目标。截图识别最大的价值就是“用户主动框选目标区域”这个主动性不要用自动化去破坏。6. 部署上线中的实战坑位与性能优化6.1 跨域接口与服务架构前面说过OCR服务是独立部署的跟业务后台分离。这时候第一个要处理的就是跨域。我们的FastAPI服务跑在8888端口业务后台跑在8080端口前端ajax直接调OCR接口必然触发CORS限制。解决方法是给FastAPI加CORSMiddleware这在上面的服务端代码里已经有了。但生产环境建议不要把allow_origins设置成[*]至少限定为后台系统的域名否则任何网页都能往你的OCR接口上传图片等于把内网计算资源暴露了出去。6.2 并发与CPU瓶颈一张图识别要多久PaddleOCR识别一张压缩后的截图在普通4核CPU服务器上大概是0.5到2秒具体取决于图片文字密度。这个响应速度对单个用户来说能接受但如果系统里有几十个人同时上传截图就会挤爆CPU。我第一版部署时直接用uvicorn main:app --host 0.0.0.0 --port 8888单进程单worker并发一上来请求队列直接堆到十几秒。改为gunicorn多worker后问题就缓解多了但要注意OCR初始化会加载模型到内存每个worker都要加载一份内存占用大约每份1GB左右。启动命令长这样gunicorn -w 4 -k uvicorn.workers.UvicornWorker main:app -b 0.0.0.0:8888用多少个worker参考CPU核心数一个核心开1到2个worker就差不多了。开多了不仅不会变快反而会因为频繁切换CPU上下文把性能拖垮。如果预算允许OCR服务加点显存跑GPU识别时间能从1秒压到300毫秒以内体验完全是两个级别。6.3 低置信度结果的人工复核与日志留痕无论预处理做得再精细机械图纸里总有一些识别结果是不该被信任的。我的做法是OCR接口返回每个文本块的同时返回置信度rec_scores前端拿到分数后做颜色标识。置信度低于0.85的文本块在确认弹窗里用红色下划线标出来提醒用户重点核对。这个机制看起来不起眼实际使用价值很大比单纯把识别结果插进去让用户事后发现错误要高效得多。另外我还给识别请求加了一份结构化日志记录原图文件名、图片MD5、识别出的完整文本、用户确认后最终插入的文本。日志存在数据库独立表里。之所以做这一步是因为机械行业对数据溯源要求很严万一之后发现某个物料编码录错了能翻出当初的识别日志判断到底是OCR错了还是用户改错了这个责任界定在实际工作中太重要了。6.4 关于浏览器兼容的一个提醒umeditor本身面向的是老系统环境很多机械企业的办公电脑还是Windows 7配老浏览器。canvas.toBlob、FormData这些新API在老浏览器里不一定能用。我们部门用的前台管理机有相当一部分还是老版本360安全浏览器兼容模式下FileReader和canvas都能跑但toBlob表现不稳定。最后我干脆做了特性检测if (canvas.toBlob) { ... } else { canvas.toDataURL(...) }两种路径都验证过才能上线。这类“小问题”在技术社区里没人会专门写但真实项目里它是最大的阻力来源之一。按我个人的使用体会这个插件的开发并没有用到什么高深算法真正的难点全在贴合场景的细节上图片怎么压缩才不影响识别、识别文本怎么让用户高效复核、机械编码的易混字符怎么约束、老浏览器怎么兼容。把这几点都抠完插件才算真正能用。后续如果还想扩展可以在OCR服务端加一个“模板保存”功能让用户把常见的标题栏截图命名保存下次识别时直接按模板提取对应字段方向上值得试试但前提仍然是先把基础的截图识别链路跑稳。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

OpenClaw个人智能体部署与A2A协同实战:从单机养虾到多智能体组队 2026/10/2 4:57:54

OpenClaw个人智能体部署与A2A协同实战:从单机养虾到多智能体组队

1. 从“你养龙虾了?”说起:个人智能体到底在养什么第一次听到“你养龙虾了?”这个说法,我愣了三秒。后来才反应过来,这是圈子里对部署 OpenClaw 这类个人智能体的一种戏称——就像养宠物一样,你得给它搭窝&…

阅读更多 →
AI Agent并发实战与智能体工程化落地指南 2026/10/2 4:57:54

AI Agent并发实战与智能体工程化落地指南

1. 今日头版:AI Agent进入“并发实战”考察期1.1 热搜最密集的词不是模型,而是Agent今天后台的搜索热词里,单条“ai agent”的检索热度冲得很高,紧随其后的还有“多ai协作”“ai agent 怎么扛并发”。这个现象挺有意思&#xff1a…

阅读更多 →
DeepSeek Harness桌面端实操:从安装配置到技能库工作流全解析 2026/10/2 4:57:54

DeepSeek Harness桌面端实操:从安装配置到技能库工作流全解析

最近DeepSeek Harness悄悄出了桌面端,这事在AI工具圈里传得挺快。我第一时间下下来扒了一遍,从安装包结构到工作流配置,从API对接到底层skill机制,基本都摸了一遍。这篇文章不讲虚的,直接把我踩过的坑、看懂的源码目录…

阅读更多 →
AI浏览器与AI手机智能体越权风险:端侧Agent安全边界与防护实践 2026/10/2 4:57:54

AI浏览器与AI手机智能体越权风险:端侧Agent安全边界与防护实践

1. 当手机和浏览器开始“自作主张”:一个被忽视的风险切面过去一年我一直在折腾端侧智能体和AI浏览器的自动化链路,从最早的简单脚本到后来的多智能体协作框架,踩过的坑比写过的代码还多。最开始我的关注点全在“怎么让Agent更聪明”上——怎…

阅读更多 →
群晖NAS搭建Git Server:SSH权限与ACL实战指南 2026/10/2 4:57:54

群晖NAS搭建Git Server:SSH权限与ACL实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
从张量到NPU:端侧AI部署的底层执行逻辑全解析 2026/10/2 4:57:47

从张量到NPU:端侧AI部署的底层执行逻辑全解析

我干端侧 AI 部署也有几年了,从早期在手机上调 DSP,到后来在 NPU 上跑检测模型,再到这两年把大模型塞进 AI PC,一个感受特别强烈:很多人对 NPU 的理解停留在“TOPS 越大越快”或者“NPU 就是硬件加速器”这种层面&…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉