ARTICLE DETAIL

资讯详情

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

Tess4J实战:Java中配置Tesseract实现中文OCR识别与调优

Tess4J实战:Java中配置Tesseract实现中文OCR识别与调优 说实话我最早接触Tesseract的时候心里是有点嫌弃的都什么年代了做个文字识别还要在本地装一个C写的OCR引擎再用Java去通过JNA调它怎么看都像在走老路。但后来真正做了几个和证件、票据、截图文字提取相关的需求我才意识到这种“笨办法”恰恰是最稳的办法——不依赖网络、不产生调用费用、还能在内部网络上跑图片只要不是过于离谱识别出来的文本就很能打。这篇博文我就用一次完整的项目经历把在IntelliJ IDEA 里配置和使用 Tess4J 这件事从头到尾讲清楚包括环境变量、语言包、JNA 加载 dll 的坑、中文识别调优以及我反复踩过的几个问题。适合什么样的人看两类一类是完全没用过 Tesseract 的Java新手需要一条能跑通的路径另一类是已经能跑通英文识别但一旦切到中文或部署到别的机器就各种诡异的报错想找排查思路的开发者。我会尽量少说概念多给能直接抄的配置和代码。1. 先把原理讲透Tesseract 和 Tess4J 到底是怎么协作的1.1 Tesseract 本身是什么Tesseract 是一个开源的 OCR 引擎最早由 HP 实验室开发后来由 Google 接手维护。它的核心是用 C 编写的所以它不是一个 Java 库更不是一个能直接new出来的对象。你把它装到系统里本质上是获得了一组本地二进制文件在 Windows 上就是tesseract.exe、tesseract.dll、libtesseract-5.dll这一堆文件再加上若干语言训练数据也就是traineddata文件。它能做的事很多人低估了不只是识别印刷体英文它支持的语言超过 100 种中文简体、中文繁体、日文、韩文都能识别而且它支持通过训练自定义字库把一些特殊字体、艺术字、甚至是你在业务里频繁出现的生僻词组输进去识别率还能继续往上拉。它最核心的几个特点一句话可以概括离线、免费、可训练、可折腾。也正因为它是 C 写的Java 开发者想用它就必须有一层“桥”。这层桥的做法大体有三种用ProcessBuilder去启动命令行进程、用 JNI 自己封装、用别人已经封装好的库。Tess4J 选的是第三条路也是成本最低的路。1.2 Tess4J 是 JNA 封装不是 Tesseract 的替代品Tess4J 的全称是 Tesseract for Java它基于 JNAJava Native Access直接加载 Tesseract 的动态链接库然后在 Java 里给你了一套相对友好的 API。你用Tesseract类的doOCR(File)方法底层其实就是在调用 C 引擎的函数把图片数据传进去再把识别出来的字符串返回来。这里有个很重要的点Tess4J 不是替代 Tesseract它只是包装。所以你的系统里必须有 Tesseract 的“本体”Tess4J 才有东西可调用。这就好比你想用 Java 操作数据库你只是加了 JDBC 驱动还不够总得先有一个数据库服务在跑。很多人第一次跑UnsatisfiedLinkError就是因为只加了 Maven 依赖但本地根本没装 Tesseract或者装了但 JNA 找不到 dll。我自己在早期还犯过一个错误以为不同的 Tess4J 版本会自带不同版本的 OCR 引擎后来才明白Tess4J 的发布版只是把 JNA 的接口和封装代码打成了一个 jar真正的识别能力完全取决于你本地装的 Tesseract 版本和语言包。这也是为什么你在网上搜教程会看到有人配好了之后识别效果不稳大概率不是代码写错了而是本地引擎和语言数据版本不一致。2. 环境准备先把“地基”搭稳2.1 下载和安装 TesseractWindows 首选 UB-Mannheim 版本Windows 下安装 Tesseract最省心的不是直接去官网下源码编译而是去 GitHub 上找 UB-Mannheim 的 Tesseract 构建版本。你在搜索引擎搜ub-mannheim tesseract一般第一条就是它的 release 页面里面有一个tesseract-ocr-w64-setup-5.3.0.20221222.exe之类的安装包直接下载即可。这个发行版是社区里各种教程的默认选择因为它把 Windows 下需要的 dll、exe、训练数据都打包好了安装完基本能直接用省掉了很多自己编译的痛苦。安装的时候有一步特别关键在Choose Components页面有一项是Additional language data下面会列出很多语言。你要是只做英文识别默认的 English 就够了但如果你要做中文识别建议在这里就把Chinese (Simplified)和Chinese (Traditional)勾上。这一步省得你后面再手动去下载语言包。安装路径建议不要放在带空格的路径下虽然C:\Program Files\Tesseract-OCR也没问题但为了后面配置省心我一般装在D:\Tesseract-OCR这种干净路径。装完之后建议先验证一次。打开命令行输入tesseract --version如果显示出版本信息说明安装成功。再执行tesseract --list-langs这一步能看到当前 tessdata 目录下默认装了哪些语言比如eng、chi_sim。如果chi_sim没出现在列表里说明安装时没勾选接下来需要手动补语言包。2.2 环境变量两个坑PATH 和 TESSDATA_PREFIX安装完 Tesseract 后第一件事是确认它的安装目录在不在系统PATH里。UB-Mannheim 的安装器一般会帮你把路径加到 PATH但有时候会因为杀毒软件、安装权限、或者你选择了“只给当前用户安装”导致没加进去。如果你在命令行里输入tesseract提示找不到命令就需要手动把D:\Tesseract-OCR加到系统环境变量的 PATH 中。第二个环境变量是TESSDATA_PREFIX它指向的是tessdata目录也就是存放eng.traineddata、chi_sim.traineddata这些语言数据的目录一般是D:\Tesseract-OCR\tessdata。这个变量不是必须的因为 Tess4J 的setDatapath()方法可以显式指定数据目录但如果你在别的地方写代码忘了设或者有些工具直接调用 Tesseract 命令行时依赖这个变量提前把它配上能少很多麻烦。我建议的做法是环境变量TESSDATA_PREFIX直接设为D:\Tesseract-OCR\tessdata然后重启一下 IDEA保证新的环境变量被加载。IDEA 不会自动感知系统环境变量的变化很多时候你改了环境变量发现程序还是报错就是因为没重启 IDEA。2.3 IntelliJ IDEA 的 JDK 与 Maven 准备打开 IntelliJ IDEA无论是社区版还是旗舰版这一步影响都不大。和 Tess4J 相关的 Java 环境要求其实不高JDK 8 以上基本都能跑。我目前用的项目是 JDK 11编译和运行都很顺。需要注意的一点是IDEA 默认编译级别和项目 SDK 要一致不要在 Project Structure 里把 SDK 设成 17结果模块的 Language level 还停留在 8那样有时会引发一些莫名其妙的编译报错。如果你是用 Maven 管理依赖建议先确认 IDEA 里的 Maven 设置能正常拉到中央仓库的依赖。Tess4J 本身依赖了 JNA在国内网络环境下首次拉包可能会有点慢可以给 Maven 配置一个镜像源或者提前把依赖下到本地仓库。这不是什么高深操作但能显著提升你跟着本文操作时的体验。3. 在 IntelliJ IDEA 中给项目接入 Tess4J3.1 Maven / Gradle 依赖写法与版本选型在pom.xml中加入 Tess4J 依赖dependency groupIdnet.sourceforge.tess4j/groupId artifactIdtess4j/artifactId version5.11.0/version /dependency如果你用的是 Gradle写法也一样implementation net.sourceforge.tess4j:tess4j:5.11.0关于版本我说两句。Tess4J 的版本迭代没有特别激进常用稳定版本已经足够。你并不需要追求最新只要确保和你本地的 Tesseract 版本能“大体匹配”就行。比如本地装的是 Tesseract 5.x那 Tess4J 5.x 系列是合适的如果你本地是老旧的 Tesseract 3.x强行用高版本 Tess4J 反而可能出现接口变化导致的兼容问题。依赖包会自动传递引入 JNA所以一般情况下你不需要手动再加 JNA 依赖。要是拉包之后发现缺了什么优先检查是不是本地 Maven 仓库里缓存了损坏的包删掉对应目录重新拉一次即可。3.2 第一段可运行的 OCR 代码不到 30 行依赖引入成功后先不要急着写业务代码先写一个最简的 Demo 验证环境通不通import net.sourceforge.tess4j.ITesseract; import net.sourceforge.tess4j.Tesseract; import net.sourceforge.tess4j.TesseractException; import java.io.File; public class OcrDemo { public static void main(String[] args) { ITesseract tesseract new Tesseract(); tesseract.setDatapath(D:/Tesseract-OCR/tessdata); tesseract.setLanguage(eng); File imageFile new File(D:/test.png); try { String result tesseract.doOCR(imageFile); System.out.println(识别结果:); System.out.println(result); } catch (TesseractException e) { e.printStackTrace(); } } }这段代码虽然短但每一行都很关键new Tesseract()创建 Tess4J 的核心对象底层会通过 JNA 加载本地库。setDatapath(D:/Tesseract-OCR/tessdata)告诉 Tess4J 去哪里找训练数据。这里填的是tessdata 目录本身,不是父目录不是 exe 所在目录。setLanguage(eng)指定用哪个语言包。你可以换成chi_sim但不能填一个不存在的语言包名。doOCR(File)执行识别返回字符串。我第一次跑这段代码的时候其实没有第一次就成功。报的是Unable to load library tesseract后来发现是我改了环境变量后没有重启 IDEAJNA 读不到新的 PATH。重启一次就好了。3.3 Tess4J 的默认库加载机制为什么老有人报找不到库Tess4J 之所以需要配置 PATH是因为 JNA 在加载本地库的时候会按一定顺序去搜索先看系统 PATH、再看jna.library.path、再看当前目录以及其他一些平台相关的路径。如果你在 Windows 下没把 Tesseract 安装目录加到 PATHJNA 就找不到tesseract.dll或者对应的 JNA 映射库于是抛UnsatisfiedLinkError。解决方式有几种最推荐把D:\Tesseract-OCR加入系统 PATH然后重启 IDEA。在某些不便改系统环境的场景下可以在代码最前面写一行System.setProperty(jna.library.path, D:/Tesseract-OCR);同样也可以在 IDEA 的运行配置里加一个 JVM 参数-Djna.library.pathD:/Tesseract-OCR。需要注意的是JNA 寻找的是动态链接库不是tesseract.exe。UB-Mannheim 安装目录下同时有 exe 和一堆 dll所以把这个目录加入 PATH 通常就能解决问题。还有一个容易忽略的点如果本机装了 32 位和 64 位两套 Java或者 JNA 相关包版本不一致也可能加载失败尽量保持 JRE 和 Tesseract 安装包位数一致优先 64 位。4. 中文识别与语言包的正确姿势4.1 chi_sim.traineddata 到底该放哪下载哪种英文识别不开任何语言包就能跑但中文识别必须要有中文训练数据也就是chi_sim.traineddata。这个文件在 Tesseract 的官方 tessdata 仓库里可以下载。网上也有很多人提到tessdata_best和tessdata_fast的区别tessdata_best识别精度更高但模型体积大、识别速度慢tessdata_fast速度快很多体积小大多数日常场景够用。UB-Mannheim 安装器默认拉取的一般是速度优先的版本你要是追求极致准确率可以单独去下载chi_sim.traineddata并替换到 tessdata 目录。下载完成后把文件放到D:\Tesseract-OCR\tessdata\chi_sim.traineddata。路径不能错文件名不能改否则会报找不到语言。然后在命令行里再执行一次tesseract --list-langs如果列表里出现了chi_sim说明语言包已经就位。这里我特别提醒一句如果你是用安装器后来补勾语言包或者手动下载替换了文件最好把 MATLAB 或别的程序占用的、未关闭的命令行全部关掉重开否则有些环境变量和文件锁会捣乱。4.2 datapath 和 langPath别把新老 API 搞混我见过很多老教程里写tesseract.setLangPath(D:/Tesseract-OCR/tessdata)在旧版 Tess4J 1.x 里确实这么用。但新版 Tess4J 已经把setLangPath废弃了统一改成了setDatapath。所以如果你用的是新版本照着老教程写会发现方法根本不存在或者提示已过时。setDatapath设置的路径是“数据父目录”它下面直接存放chi_sim.traineddata这类文件即可。setLanguage负责指定语言包的基底名称。举个例子文件叫chi_sim.traineddata那么setLanguage(chi_sim)文件叫eng.traineddata那么setLanguage(eng)。语言组合也可以尝试传chi_simeng底层会尝试同时加载两个语言包但这种混合模式在部分版本下表现不稳定。我自己的实践经验是如果图片是纯中文就用chi_sim如果是中英混排先尝试chi_simeng效果不好就切回纯中文然后对识别出来的英文代码、数字做正则后处理。4.3 影响中文识别效果的关键参数DPI 和 PageSegMode很多情况下代码没问题、语言包没问题但识别结果却一团糟问题往往出在 DPI 和分段模式上。Tesseract 在内部有一个user_defined_dpi参数如果它拿不到图片的 DPI 信息或图片 DPI 太低就会影响识别准确率。你可以在代码里显式给它一个合理的值tesseract.setTessVariable(user_defined_dpi, 300);另外Tesseract 的 PageSegMode页面分割模式决定了引擎怎么理解图像布局。默认模式下它会尝试自动分析页面结构这对整页文档很友好但对单行文本、单个文本块反而不一定最优。常用模式如下模式含义适用场景3自动版面分析整页文档、杂志扫描件6假定为单一文本块截图、段落文字7假定为单行文本验证码、标题行11稀疏文本散落在图片各处的短文本在 Tess4J 里可以这样设置tesseract.setPageSegMode(6);如果你用的是较老版本不支持这个方法也可以用tesseract.setTessVariable(tessedit_pageseg_mode, 6);我实际测试过对一个内容只有一段话的截图固定成6之后识别准确率明显高于自动模式而且识别耗时还下降了一些。5. 完整实操做一个可复用的 OCR 工具类5.1 项目结构规划这一步我给一个可以直接复用的工程结构用一个工具类封装大部分逻辑再用一个带界面的入口演示选图识别。项目结构如下ocr-demo ├── pom.xml └── src/main/java └── com/demo/ocr ├── OcrUtils.java └── MainFrame.javaOcrUtils负责创建 Tesseract 实例、执行识别、以及简单的图像预处理MainFrame是一个基于 Swing 的小窗口方便你选择图片并查看识别结果。这样你可以先通过命令行/单元测试验证再决定要不要套到自己的业务里。5.2 核心代码OcrUtils 实现package com.demo.ocr; import net.sourceforge.tess4j.ITesseract; import net.sourceforge.tess4j.Tesseract; import net.sourceforge.tess4j.TesseractException; import javax.imageio.ImageIO; import java.awt.*; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; public class OcrUtils { private static final String TESS_DATA_PATH D:/Tesseract-OCR/tessdata; public static String recognize(File imageFile, String lang) throws IOException, TesseractException { ITesseract tesseract new Tesseract(); tesseract.setDatapath(TESS_DATA_PATH); tesseract.setLanguage(lang); tesseract.setTessVariable(user_defined_dpi, 300); tesseract.setPageSegMode(6); BufferedImage image ImageIO.read(imageFile); BufferedImage processed preprocess(image); return tesseract.doOCR(processed); } /** * 简单的图像预处理灰度化 放大两倍。 * 灰度可以降低背景干扰放大能帮 Tesseract 更好地识别小字号文字。 */ private static BufferedImage preprocess(BufferedImage original) { int w original.getWidth(); int h original.getHeight(); BufferedImage gray new BufferedImage(w, h, BufferedImage.TYPE_BYTE_GRAY); Graphics2D g2d gray.createGraphics(); g2d.drawImage(original, 0, 0, null); g2d.dispose(); int newW w * 2; int newH h * 2; BufferedImage scaled new BufferedImage(newW, newH, BufferedImage.TYPE_BYTE_GRAY); Graphics2D g2d2 scaled.createGraphics(); g2d2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); g2d2.drawImage(gray, 0, 0, newW, newH, null); g2d2.dispose(); return scaled; } }这段代码里有几个地方值得展开preprocess方法做的是最基础、也是性价比最高的两步灰度和放大。很多截图在电脑上看很清晰但内部 DPI 很低Tesseract 对这类图片的识别率其实不高放大两倍后通常会有肉眼可见的改善。如果你的业务图片有倾斜、噪点、反光还需要上更重的预处理比如二值化、腐蚀膨胀、透视矫正这些可以后续单独讲。doOCR方法接收的是BufferedImage而不是File这也是一个很多人不知道的细节。Tess4J 的 API 既支持传文件也支持传图片对象。当你做预处理后直接把BufferedImage传进去即可没必要先写临时文件再读。5.3 带选图界面的入口 MainFramepackage com.demo.ocr; import javax.swing.*; import java.awt.*; import java.io.File; public class MainFrame extends JFrame { private JTextArea resultArea; public MainFrame() { setTitle(Tess4J OCR Demo); setSize(900, 600); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(new BorderLayout()); JButton openButton new JButton(选择图片并识别); resultArea new JTextArea(); resultArea.setFont(new Font(Microsoft YaHei, Font.PLAIN, 16)); JPanel topPanel new JPanel(); topPanel.add(openButton); add(topPanel, BorderLayout.NORTH); add(new JScrollPane(resultArea), BorderLayout.CENTER); openButton.addActionListener(e - chooseAndRecognize()); } private void chooseAndRecognize() { JFileChooser chooser new JFileChooser(); int result chooser.showOpenDialog(this); if (result JFileChooser.APPROVE_OPTION) { File selectedFile chooser.getSelectedFile(); try { String text OcrUtils.recognize(selectedFile, chi_sim); resultArea.setText(text); } catch (Exception ex) { resultArea.setText(识别失败 ex.getMessage()); ex.printStackTrace(); } } } public static void main(String[] args) { SwingUtilities.invokeLater(() - { MainFrame frame new MainFrame(); frame.setVisible(true); }); } }在 IDEA 里直接运行MainFrame点按钮选一张中文截图就能看到识别结果。第一次运行可能会稍微慢一点因为 JNA 要加载本地库、Tesseract 要初始化语言模型。如果控制台输出的中文是乱码这是因为 IDEA 控制台的默认编码和系统编码不一致在运行配置的 VM options 里加上-Dfile.encodingUTF-8即可。6. 常见问题与排查技巧实录6.1 UnsatisfiedLinkError一族的错误这是 Tess4J 新手遇到最多的错误报错信息可能长这样java.lang.UnsatisfiedLinkError: Unable to load library tesseract排查思路按顺序走本地是否安装了 Tesseract没有就去装。Tesseract 安装目录是否在 PATH 里没有就加然后重启 IDEA。PATH 加了但还是报错在代码里加System.setProperty(jna.library.path, D:/Tesseract-OCR);在第一次 new Tesseract 之前执行。还是不行检查 IDEA 进程是不是没重启直接重启 IDEA 再跑一次。有一种更隐蔽的情况你本机装了不只一个 Tesseract比如之前用过绿色版或者通过包管理器安装过导致 PATH 里同时存在多个版本JNA 加载了错误的 dll。这种情况下建议把所有相关路径理清楚只保留一个版本。6.2 中文语言包加载不了Failed loading language如果错误信息里带Failed loading language chi_sim说明 Tesseract 在setDatapath指定的目录里找不到对应语言包或者语言包本身损坏。检查步骤确认D:/Tesseract-OCR/tessdata/chi_sim.traineddata文件存在。确认文件名完全正确不能叫chinese_sim之类的。确认setDatapath指向的是 tessdata 目录本身不要多拼一层tessdata。在命令行执行tesseract --list-langs看能不能列出chi_sim。如果命令行能列出但 Java 里报错优先检查代码里 datapath 是否写错。还有一个小概率问题下载语言包时文件下载不完整导致损坏。重新下载一次或者换个源再试。6.3 识别结果乱码、漏字、错字严重这一类问题最让人头疼因为不是程序报错而是识别质量的问题。我总结下来常见原因就这几类图片分辨率太低。Tesseract 对低分辨率图片的鲁棒性一般把图片用程序放大两到三倍再识别往往比换任何模型都有效。图片有背景干扰。复杂背景、纹理、水印都会干扰识别尽量先用工具或代码把背景去掉转成清晰的黑白文字图。字体不是常规字体。Tesseract 对通用印刷体效果好对艺术字、手写体、特殊花纹字体会吃力这种情况建议做模板匹配或额外训练。PageSegMode 不合适。整页文档用自动模式单段文本用 6单行文字用 7不要一个模式通吃所有图。语言设置不对。中英文混排时只设置eng中文自然全部乱码只设置chi_sim纯英文数字也可能变成中文标点。可以在语言设置里尝试组合或者根据业务场景拆图分别处理。6.4 识别速度慢、CPU 占用高Tesseract 本身是纯 CPU 计算识别一张高分辨率图片耗时会比较明显。能做的优化有使用tessdata_fast语言包而不是tessdata_best速度差距可能有两到三倍。在代码里固定setPageSegMode(6)让它跳过复杂的版面分析。并行任务不要共享同一个Tesseract实例。我踩过这个坑让四个线程同时调用同一个实例的doOCR结果偶发崩溃。Tess4J 的实例底层不是完全线程安全的稳妥点用ThreadLocal或者每次识别时新建实例。新建实例的开销其实没那么大因为真正重的初始化是第一张识别时异步发生的。如果对高并发有硬性要求不建议在 Java 里无限堆线程更合理的方式是起一个 OCR 服务池内部用固定数量的工作线程串行调用 Tesseract。6.5 常见问题速查表错误/现象可能原因解决方法Unable to load library tesseractPATH 没配 / JNA 找不到 dll加 PATH、重启 IDEA、jna.library.pathFailed loading language chi_sim语言包不在 datapath 下检查路径和文件名中文全乱码没设置chi_simsetLanguage(chi_sim)英文数字乱码语言包只设了中文尝试chi_simeng控制台中文乱码IDEA 控制台编码加-Dfile.encodingUTF-8识别结果缺行页面分割模式不对设置setPageSegMode(6)速度特别慢用了 best 模型 / 自动版面分析换 fast 模型固定分段模式偶发崩溃多线程共享实例用 ThreadLocal 或每次新建实例7. 我在实际项目里的一些体会Tess4J 这个东西配置本身不难难的是“你以为你配好了但换一台机器、换一批图片就翻车”。我后来总结出一个可靠的工作流所有和 Tesseract 相关的路径都不写死在业务代码里而是放进配置文件TESSDATA_PREFIX 环境变量和 datapath 代码配置双保险每次部署新环境先跑一遍命令行验证再跑 Java 冒烟测试。预处理我通常也不会只做灰度放大而是把二值化、去噪、倾斜纠正都做成可开关的步骤。原因很简单Tesseract 对“干净图”的依赖比很多人想象中高同样的模型一张清晰的黑底白字图和一张手机拍的反光图识别率差距可以达到百分之三四十。先预处理一小时比换模型换半天更划算。最后再分享一个小技巧很多业务场景里OCR 输出的结果不能直接入库要用正则或词典做后处理。比如识别出的金额数字里可能混入字母 O 和 I这时我会把O转成0、把I转成1再套一层业务校验。把这些后处理规则沉淀下来之后整个识别链路才真正算“能上线”。如果你也在做类似的事建议从小批量样本开始先跑通流程再一步步打磨识别率和最终交付字段的稳定性。
返回列表