ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

SpringBoot集成OCR实战:Tesseract选型、配置、调参与报错排查

SpringBoot集成OCR实战:Tesseract选型、配置、调参与报错排查 简介这是一份面向Spring Boot开发者的OCR集成示例工程聚焦于如何在Web项目中快速接入光学字符识别功能适用于需要实现图片文字提取、终端票据识别或证件信息录入等场景的Java工程师也适合作为理解第三方SDK与云端API调用的入门样例。压缩包整体仅9KB共7个文件由三部分构成3个Java类负责核心识别逻辑与REST接口定义pom.xml和properties文件完成依赖管理及关键参数配置mvnw/cmd脚本则提供Maven包装器支持目录结构清晰导入IDE后即可对照学习。目前已有409人学习下载。通过该demo可掌握Spring Boot集成Tesseract本地引擎或阿里云、腾讯云等OCR服务的完整思路包括依赖添加、参数配置、控制器接口设计、图片上传处理、结果解析与返回并了解工程化基础。整个示例以最小化代码展示了从图片输入到文本输出的完整链路适合作为自动化办公、信息录入系统的基础模块更可在此基础上扩展异步识别、缓存等优化手段。1. SpringBoot集成OCR功能demo别把它当成一个Controller那么接SpringBoot集成OCR功能demo表面上是“前端传一张图片后端返回识别文字”但真正动手才会发现坑全藏在引擎选型、语言包和图片预处理这些看似与SpringBoot无关的地方。我在几个项目里先后接过本地Tesseract、云OCR和PaddleOCR结论很直接demo能不能在半天内跑通取决于你先把哪根桩打对。这篇文章写给“想接OCR但不知从哪下手”的Java后端沿着引擎选型、最小接口、参数调优、高频报错排查的顺序把一条能直接照做的路走完。2. 先选OCR引擎demo也要做对本地、云还是深度学习的三选一“用SpringBoot集成的OCR”这句话背后藏着一个假设你已经把OCR当成黑匣子只想对外暴露一个HTTP接口。这个假设恰恰是第一个坑。验证码、合同、票据、随手拍这些场景对OCR的要求差异极大选错引擎的demo往往在换第二张图片时翻车——第一张截图识别得好好的换一张手机拍的合同就开始输出乱码。所以动手写代码之前先花半小时把引擎选型定下来。2.1 本地Tesseract、云端API与PaddleOCR的取舍目前SpringBoot里能接的OCR引擎主要分三类本地离线引擎、云端HTTP API、本地深度学习推理。我给它们列了一张对比表后面讲选型都围绕这张表展开。维度Tesseracttess4j云端OCR百度/腾讯/阿里PaddleOCR部署成本低打一个jar包加语言包即可最低只需接口鉴权配置高Python环境或ONNX Runtime建议独立部署开机成本免费离线可用按调用量计费有免费额度免费但模型文件大、首次启动慢中文识别率干净图片可用复杂背景明显下降高尤其常用字段场景最强梯队支持版面分析、表格结构结构化输出只有纯文本字段提取要自己写车牌、合同、身份证有现成模板需额外做信息抽取或配合PaddleX pipelineSpringBoot集成直接调tess4j最简单走HTTPClient注意签名和超时一般起sidecar服务Java再调HTTP适合场景截图、扫描件、受控清晰图片合同、票据等需字段级结果批量文档、复杂版面、数据敏感场景我自己的判断标准很简单想验证“SpringBoot里能不能把图片变成文字”选Tesseract因为它不挑环境、不产生外部依赖如果业务目标是“上传合同后自动读收入、单位、时间等字段”直接先调云API跑MVP省去自己分词和字段匹配的功夫如果数据量上来了且不能出内网再迁移到PaddleOCR独立服务。2.2 识别场景决定选型验证码、合同字段和通用文字场景是选型的筛子。第一刀砍下去先看你要识别的是什么文字。纯数字或字母验证码属于受控图片Tesseract就能胜任配合横排单行模式和人字符白名单准确率能到95%以上。这类需求网上也常见比如现学现做的验证码识别模块效果通常取决于预处理而不是引擎选型。第二类是中英文通用场景比如拍一张书页、聊天截图Tesseract的chi_sim语言包能跑但遇到手机摄像头的透视畸变识别质量就变得很玄学这时候PaddleOCR的检测识别两阶段模型优势明显。第三类是合同、发票这类需要字段级结果的场景如果坚持用Tesseract拿到的是一整段文本还需要自己写一整套正则和字段映射而主流云OCR已经内置字段抽取直接返回Json对demo来说省的不是一点半点。所以标题里只说“OCR功能”是不够的我一般会先问一句你要识别的图片长什么样、最终要的是整段文字还是几个字段回答完这两问引擎选型反而比写Controller更快。2.3 选型落地20张样本图跑一轮再签字不是拍板拍得快而是先跑一轮再拍板。这里说的“跑”是指用真实样本而不是网上找的测试图。具体流程我习惯拆成四步收集目标场景的真实图片20到30张统一转成JPG或PNG并记录每张图里期望识别的关键字段。用Tesseract默认配置跑一遍自己数错字把错误分成三类乱码、掉字、格式错。如果有云API免费额度同批图片再跑一遍对比字段级结果。只需要把识别结果放到Excel里谁好谁坏一目了然。在对比结果之外把部署成本、离线要求、并发预算三个维度也加入决策表。只要你把“demo”定位成一个月后可能变成正式服务的东西这一步就值得做。如果只是临时验证接口连通性那直接跳过选型跳到下一章的Tesseract方案也无妨但我建议至少跑一遍对照否则你在第5章遇到的大概率是识别率翻车而不是接口报错。3. 用SpringBoot跑通最小OCR服务依赖、配置和三层Java代码选型定了Tesseract之后最小可行服务其实只需要三层配置层、Service层、Controller层。下面每一个环节都给出可直接照抄的写法再解释参数含义和常见误区。3.1 pom.xmltess4j依赖和版本匹配先用Maven引入tess4j这是Java对Tesseract的封装底层通过JNA加载本地库。版本号建议直接看Maven Central的最新5.x版本尽量别用4.x老版本因为老版本对新版SpringBoot的依赖协调不太友好。dependency groupIdnet.sourceforge.tess4j/groupId artifactIdtess4j/artifactId version5.14.0/version /dependency这段依赖引入后会连带解析JNA等传递依赖。注意SpringBoot本身会做依赖版本管理如果tess4j的传递依赖与SpringBoot的BOM发生冲突启动时可能报NoClassDefFoundError或UnsatisfiedLinkError解决办法放在第5章专门讲。还有一个容易忽略的细节tess4j只会把它依赖的tesseract原生库带到特定平台Windows下跑通不代表Linux下也能跑部署环境这一节放后面。3.2 application.yml先把上传大小和语言包目录设好不设默认配置就启动demo最容易撞上两个问题SpringBoot默认上传大小上限1MB图片稍大就413tess4j找不到语言包识别中文直接报错。所以先把application.yml里两块配置写清楚。spring: servlet: multipart: max-file-size: 10MB max-request-size: 12MB ocr: language: chi_simeng psm: 3 datapath: ./tessdatamultipart的max-file-size是单个文件上限max-request-size是整个HTTP请求体上限如果前端还有额外字段两者要留出余量。ocr.language用加号连接多个语言chi_sim是简体中文eng是英文顺序一般把主要语言放前面Tesseract会按语言包列表依次执行识别。datapath指的是traineddata语言包所在目录我习惯在项目根目录放一个tessdata目录提交到代码仓库或者用resources目录下打包方式。两种方式各有取舍外部目录方便换语言包不用重新打包resources目录适合Docker化部署路径不依赖工作目录。3.3 OcrService把Tesseract调用包成一个可测试的服务Service层是核心。Tesseract实例在这里创建识别逻辑在这里封装Controller不直接接触原生库这样就算以后换引擎也只需要改这一层。Service public class OcrService { private static final Logger log LoggerFactory.getLogger(OcrService.class); private final Tesseract tesseract; private final String datapath; private final String language; private final int psm; public OcrService(Value(${ocr.datapath:./tessdata}) String datapath, Value(${ocr.language:chi_simeng}) String language, Value(${ocr.psm:3}) int psm) { this.datapath datapath; this.language language; this.psm psm; this.tesseract new Tesseract(); this.tesseract.setDatapath(datapath); this.tesseract.setLanguage(language); this.tesseract.setOcrEngineMode(TessOcrEngineMode.OEM_DEFAULT); this.tesseract.setPageSegMode(psm); } public String recognize(MultipartFile file) throws OcrException { long start System.currentTimeMillis(); try (InputStream in file.getInputStream()) { BufferedImage image ImageIO.read(in); if (image null) { throw new OcrException(无法解码图片文件可能不是合法图片); } String text tesseract.doOCR(image); log.info(OCR识别完成耗时 {} ms识别长度 {}, System.currentTimeMillis() - start, text.length()); return text.trim(); } catch (IOException e) { throw new OcrException(读取上传文件失败: e.getMessage(), e); } catch (TesseractException e) { throw new OcrException(Tesseract识别失败: e.getMessage(), e); } } }逻辑说明构造方法从配置读取语言包目录、语言组合和psm模式避免把参数写死在代码里。识别方法用try-with-resources确保InputStream关闭ImageIO.read返回null说明原始数据无法被解码常见于传了webp这类Tesseract原生不支持的格式。doOCR接收的是BufferedImage而不是文件路径这样既能处理上传流也不必把临时文件写到磁盘。参数说明setOcrEngineMode(OEM_DEFAULT)让Tesseract自己选择LSTM或传统引擎一般不用手动指定。setPageSegMode(psm)控制版面分析策略默认3是自动分页但遇到单行验证码或整页合同这个默认值不一定最优第4章专门讲。注意Tesseract实例本身是线程安全的我用的是单例高并发下也建议共享实例而不是每次new一个因为加载语言包是重操作。3.4 OcrController接收上传并返回结构化响应Controller层只做三件事接收MultipartFile、调用OcrService、把结果包成JSON返回。这里不要把字节流转成String再交给Service二进制数据一旦经过String重编码就容易出乱码。RestController RequestMapping(/api/ocr) public class OcrController { private static final Logger log LoggerFactory.getLogger(OcrController.class); private final OcrService ocrService; public OcrController(OcrService ocrService) { this.ocrService ocrService; } PostMapping(/recognize) public ResponseEntityMapString, Object recognize(RequestParam(file) MultipartFile file) { long start System.currentTimeMillis(); if (file null || file.isEmpty()) { return ResponseEntity.badRequest().body(Map.of( success, false, message, 文件为空)); } try { String text ocrService.recognize(file); return ResponseEntity.ok(Map.of( success, true, text, text, costMs, System.currentTimeMillis() - start )); } catch (OcrException e) { log.warn(OCR业务异常: {}, e.getMessage()); return ResponseEntity.badRequest().body(Map.of( success, false, message, e.getMessage())); } catch (Exception e) { log.error(OCR服务异常, e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(Map.of(success, false, message, 识别服务暂不可用)); } } }这里返回的是HashMap包装的JSON字段直观前端直接取text和success就行。用Map.of要求JDK9以上如果你还在SpringBoot 2.x配JDK8改成HashMap手动put或者定义OcrResult类。逻辑说明很关键异常被分成业务异常和系统异常。业务异常返回400系统异常返回500且不把堆栈暴露给前端这是后端接口的基本卫生习惯。识别耗时costMs也在JSON里返回为第6章的准确率评估保留最原始的数据。到这里SpringBoot集成OCR的demo已经能跑了。启动项目后用Postman或浏览器工具向/api/ocr/recognize发一个multipart POST请求字段名叫file就能拿到识别文本。但demo跑通只是开始识别质量、并发性能、部署移植这些才是真考验后面三章逐个拆。4. 识别率上不去的四个调参位语言包、psm模式、预处理和并发很多第一次接OCR的人以为识别率不行就是引擎不行其实在换引擎之前还有四个参数位值得先动一遍。它们都是Tesseract体系里的常规手段成本低见效快也是新手最容易忽略的“黑匣子”开关。4.1 语言包不只影响乱码还决定能识别哪种文字Tesseract的语言支持不像查字典那么轻量它靠的是traineddata语言包。默认的eng.traineddata只覆盖英文和欧洲字符直接识别中文会输出一串由拉丁字母和数字拼起来的乱码。解决方案是补上简体中文语言包chi_sim.traineddata如果涉及繁体或韩文还要分别放chi_tra和kor。注意下载语言包要看Tesseract主版本不同大版本的训练数据不能混用tess4j里对应的Tesseract版本通常会标明支持的data兼容范围。语言包位置由datapath指定。如果你把tessdata放在项目根目录启动时用的是相对路径这在IDE里和用java -jar启动时的当前工作目录不一样容易找不到语言包。我的习惯是把tessdata放进resources目录然后用ClassPathResource把它在运行期解压到临时目录或者直接用绝对路径配置到外部挂载卷。这样无论是本地还是服务器语言包路径都可控。识别之前还要确认setLanguage的参数和文件名一致比如文件名是chi_sim.traineddatasetLanguage里就是chi_sim不带扩展名。4.2 psm模式一行验证码和整页合同用同一套默认参数最亏pageSegMode是Tesseract最容易出效果也最容易被人忽略的参数。默认的PSM 3表示“全自动页面分割但没有特定的版面限制”它对大多数图片都有效但它不是最优解。比如验证码这类只有一行文字的图片用PSM 3会让Tesseract先去寻找块级版面结构反而干扰单行识别而整页合同用单行模式又会让段落顺序乱掉。下面这张表是我常用的对应关系psm值模式名使用场景3全自动页面分割默认通用整页文字6假设为统一文本块段落清晰的扫描件7单行文本验证码、小票一行、商品标签11稀疏文本无特定顺序货架标签、随机粘贴的标签13稀疏文本OSD包含旋转内容的杂乱图片在代码里直接用setPageSegMode(psm)切换把这几个值在预处理后各跑一遍记录识别耗时和错字率。我的经验是验证码类场景psm 7配合白名单能明显提升带横线、表格线的扫描件用psm 6比psm 3更稳如果图片本身歪斜psm 11配合自动纠偏有时效果更好。调psm不用重载语言包改完立即生效这是最廉价的去黑匣子手段。4.3 图片预处理二值化、放大、纠偏的Java实现与阈值玄学Tesseract输入质量决定输出质量这句话适用于所有OCR引擎。常见的预处理路线是灰度化、放大、二值化、纠偏。Java里用ImageIO就能做前三个下面是我在项目里沉淀下来的一段工具方法虽然不像OpenCV那么完备但胜在不引入额外依赖。public static BufferedImage preprocess(BufferedImage src) { // 先放大小图直接识别字号小容易掉字 int targetWidth 1200; if (src.getWidth() targetWidth) { double scale targetWidth * 1.0 / src.getWidth(); BufferedImage scaled new BufferedImage( (int)(src.getWidth() * scale), (int)(src.getHeight() * scale), src.getType()); Graphics2D g scaled.createGraphics(); g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); g.drawImage(src, 0, 0, scaled.getWidth(), scaled.getHeight(), null); g.dispose(); src scaled; } // 灰度化 BufferedImage gray new BufferedImage(src.getWidth(), src.getHeight(), BufferedImage.TYPE_BYTE_GRAY); Graphics2D g2 gray.createGraphics(); g2.drawImage(src, 0, 0, null); g2.dispose(); // 取中值做阈值二值化背景与前景分离 int w gray.getWidth(), h gray.getHeight(); int total w * h; int[] hist new int[256]; for (int y 0; y h; y) { for (int x 0; x w; x) { hist[gray.getRaster().getSample(x, y, 0) 0xFF]; } } int sum 0, threshold 128; for (int i 0; i 256; i) { sum hist[i]; if (sum total / 2) { threshold i; break; } } BufferedImage bin new BufferedImage(w, h, BufferedImage.TYPE_BYTE_GRAY); for (int y 0; y h; y) { for (int x 0; x w; x) { int v gray.getRaster().getSample(x, y, 0) 0xFF; bin.getRaster().setSample(x, y, 0, v threshold ? 0 : 255); } } return bin; }逻辑说明第一段用双三次插值放大把低于1200px宽的小图放大避免小字号导致的掉字。放大这个操作常常能把合同盖章体的识别率拉高几个点成本低、效果明显。第二段灰度化是二值化的前一步因为阈值判断只需要一个通道。第三段通过直方图中值找阈值比写死128更适应不同明暗背景但还是属于简化版。真正生产环境我会用Otsu大津法Java实现起来稍麻烦如果项目里引了OpenCV直接cv::threshold加THRESH_OTSU即可。参数说明scale倍率、目标宽度、阈值选取都会影响结果。放大过大会引入锯齿一般控制在2倍以内阈值选择玄学对同一张图片可以先保存二值化结果检查一遍再决定用中值还是固定阈值。这一步的坑在于setRGB性能极差所以我用getRaster和setSample直接操作数据要说明的是这套代码在超大图上逐像素处理会有性能压力需要限制图片尺寸比如超过2000px先等比缩小否则耗时和内存都可能失控。4.4 并发一高就超时给OCR单独开线程池最后一个参数位不在Tesseract内部而在SpringBoot的线程策略。OCR是CPU密集型操作一张高清图可能占用几百毫秒甚至几秒的CPU时间。如果直接在Controller里同步调用高并发下Tomcat工作线程会被全部占满后续所有请求排队最终拖垮整个应用。我惯用的做法是给OCR单独准备一个线程池限制并发数同时把识别任务异步化、可超时。Configuration public class OcrThreadPoolConfig { Bean(ocrExecutor) public Executor ocrExecutor() { ThreadPoolTaskExecutor executor new ThreadPoolTaskExecutor(); executor.setCorePoolSize(2); executor.setMaxPoolSize(4); executor.setQueueCapacity(200); executor.setThreadNamePrefix(ocr-); executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.initialize(); return executor; } }逻辑说明核心线程数2、最大线程数4是保守值因为OCR任务占用CPU时间过长线程多了反而在上下文切换上浪费资源。队列容量200让短时间突发任务排队等待而不是立刻触发拒绝策略。RejectedExecutionHandler选CallerRunsPolicy意思是队列满了就让提交任务的线程自己执行这样不会丢任务代价是调用线程会被脱住一会儿。参数说明这些数字不是拍脑袋定的需要根据机器的CPU核数和单张图片平均耗时来算。比如4核机器单张识别200ms期望QPS是10那需要的并发线程数大约是QPS乘单张耗时也就是2到3个线程就够。不要盲目调大maxPoolSize否则你会在监控里看到CPU打满、接口全面变慢。Controller里注入这个Executor把识别调用包成CompletableFuture再配合Future.get的超时时间能在单张异常图片卡死时兜底。这一层的调优是demo和正式服务的分水岭。5. OCR demo高频报错排查从file format error到韩文乱码这一章直接从真实报错入手。以下五个问题是我和同行在处理SpringBoot集成OCR时反复遇到的同款坑每一条都按“现象、原因、解决”来拆其中的关键词如果正好是你搜索时遇到的报错原文照着做基本能解。5.1 云端API报“file format error”问题往往不在图片格式现象用百度OCR或其他云OCR时Postman里传图正常但代码里传MultipartFile就收到类似error_msg: file format error的响应。原因这是二进制转码翻车。MultipartFile里的数据是字节流很多人会习惯性先转成String再当参数传比如new String(file.getBytes())这样底层会把原始图片字节按平台默认字符集重编码图片的魔法数字被损坏云端收到的已经不是一张合法图片。还有一个常见原因是上传的文件本身就是webp、heic或bmp格式云API的入口不接收必须在客户端或服务端先转成JPG/PNG。解决在Service层直接拿字节数组或InputStream保持二进制一路到底。调用云API时用Base64编码字节数组不要经过String中转。如果图片格式可疑先在服务端用ImageIO读取并重新写出为标准PNG再交给云服务。检测文件头还有个细节PNG以89 50 4E 47开头JPG以FF D8开头写个小工具在入口校验比等云端报错更快。5.2 韩文识别不了别只盯着SpringBoot代码现象Tesseract识别韩文返回空字符串或夹杂乱码类似的报错还有直接用PaddleX的pipeline跑韩文完全识别不出。原因Tesseract默认只加载了语言包列表里声明的语言。如果你setLanguage(kor)但tessdata目录里没有kor.traineddata它会报加载失败如果语言包放对了还要确认版本兼容。而PaddleX这种深度学习pipeline默认初始化可能只加载了中英文模型韩文不在模型覆盖范围内这属于模型选型问题不是代码问题。解决Tesseract侧把kor.traineddata放入tessdata目录代码里setLanguage(kor)或“koreng”。如果图片里韩文不在干净背景上还要考虑把语言包换成Tesseract针对韩文优化的版本。深度学习侧要么改初始化参数加载多语言模型要么换用支持韩文的OCR服务。遇到韩文这类CJK组合字Tesseract的准确率本来就不如专用模型业务上对准确率有硬指标的话建议直接切PaddleOCR或云API不要本地硬扛。5.3 SpringBoot版本太高导致tess4j启动崩溃现象SpringBoot 3.x项目集成tess4j 5.14.0启动时报java.lang.UnsatisfiedLinkError或NoClassDefFoundError: javax/xml/bind/...而且本地和服务器表现还不一样。原因SpringBoot 3基于Jakarta EE部分老依赖还指向javax包同时tess4j底层依赖JNAJNA版本与glibc、平台架构不匹配就会出现UnsatisfiedLinkError。搜索热词里有不少人问“springboot版本太高”就是这个场景实际上不是它太高而是依赖协调出了岔子。解决先升级tess4j到支持新版JNA的版本确保JNA版本高于5.13再看项目里SpringBoot管理的JNA版本是否被覆盖排除dependencyManagement里的低版本JNA。其次SpringBoot 3要求JDK17如果部署环境还是JDK8直接用2.7.x版本更稳。还有一个容易漏的jna依赖在Windows和Linux下需要不同的native库不要只拷了一份本地库里用。5.4 上传大图被拒或识别空白multipart限制与超时是一对现象上传3MB图片接口返回413 Request Entity Too Large或者图片传上去能通但识别结果永远是空白字符串。原因是两个坑撞到一起了。SpringBoot默认multipart的max-file-size是1MB超过直接拒绝返回413而识别空白的原因要么是图片过大导致Tesseract处理超时或内存溢出要么是图片本身分辨率够但没有做预处理文字区域像素过浅被当成背景丢掉。解决配置文件里放行上传大小前面第3章的配置就是基础版。同时给图片尺寸设一个上限超过2500px的图片先等比缩小既控制耗时也避免内存压力。识别空白的处理不能只靠调阈值先保存预处理后的中间图片看一眼是哪里丢信息如果是过曝导致字和背景同为浅色做对比度拉伸如果是图太小先放大再做二值化。5.5 Windows本地正常、Linux部署挂掉原生库和字体问题现象Windows的IDE里测试都通过部署到CentOS或Ubuntu后启动时报找不到libtesseract或者识别中文时大量乱码。原因Tess4J是JNA封装它依赖系统里的tesseract原生动态库。Windows上安装的是tesseract的dllLinux上是libtesseract.so两者完全不通用。如果服务器上根本没装tesseract引擎或者系统缺少其依赖的leptonica库JNA自然加载失败。中文乱码则指向语言包路径问题Windows路径分隔符、Linux权限或当前工作目录不一致导致加载了默认的eng语言包。解决Docker部署是最可控的方式镜像里用apt-get安装tesseract-ocr和对应语言包再把tessdata挂载到容器内绝对路径。比如apt-get install -y tesseract-ocr tesseract-ocr-chi-sim然后把application.yml的datapath指向/usr/share/tesseract-ocr/5/tessdata这种系统路径。如果坚持不用Docker就要在启动脚本里设置LD_LIBRARY_PATH并确认tesseract命令本身能跑通。判定标准很简单在服务器上直接运行tesseract --version如果这步都不通过Java代码怎么看都白搭。6. 从demo到可靠服务准确率验证的实操方法demo最后拼的不是能跑而是能稳定复现效果。我迭代OCR功能时习惯做一套极简回归脚本避免每改一个参数就靠肉眼重新看一遍几十张图。准备20张真实样本文件名按“编号_期望结果”命名比如case_01_合同编号SD2024001.jpg这样期望值就从文件名里解析出来不需要额外管理标注文件。然后跑一个批量识别把输出的每个字符与期望做编辑距离计算得到字符级准确率。这一步能将玄学参数调整变成一次可量化的回归。脚本里还要同时记录单张耗时因为准确率和耗时往往此消彼长放大图、开纠偏都会让耗时上升需要对照着看。验证通过之后进阶方向优先级我一般这样排识别率上不去先拿预处理中间态做样本复盘而不是换引擎重来结构字段提取需要稳定再考虑云API或PaddleOCR并预留交换引擎的接口接口并发扛不住回到第4章的线程池按真实QPS重新调参。我吃过一次亏项目上线后才发现某类发票的底色让Tesseract总是多识别出几个空格后来把预处理阶段单独抽出来每次改动都先跑回归再发布才没再被这类问题突袭。希望帮到你。本文还有配套的精品资源点击获取
返回列表