Java 开发者的福音!后端 OCR,可直接调 PaddleOCR的 C++ 高性能引擎

先说结论

如果你是一个 Java 后端开发,项目里要做 OCR,手里正好有(或能搞到)PaddleOCRSharp 编译出来的 C++ 动态库(Windows 下 PaddleOCR.dll,Linux 下 PaddleOCR.so),那JNA 直接加载这个库是一个很不错的的方案:

  • 不引入 Python 运行时
  • 不写 JNI
  • 不额外部署微服务
  • Spring Boot 里 @Autowired 就能用
  • 模型离线、数据不出机器
  • 识别和其他方案跑出来的结果一致

你们现在的做法大概率是这样的
ProcessBuilder pb = new ProcessBuilder(
    "python", "ocr_server.py", imagePath
);
Process p = pb.start();
String result = IOUtils.toString(p.getInputStream(), StandardCharsets.UTF_8);

  

或者

// 调一个内部 Python HTTP 服务
RestTemplate rest = new RestTemplate();
OCRResponse resp = rest.postForObject(
    "http://ocr-service:5000/detect", 
    new ImageRequest(base64), 
    OCRResponse.class
);

  


PaddleOCRSharp 的 C++ 内核PaddleOCR.dll,Java 一样能用。

PaddleOCRSharp 是一个基于百度飞桨PaddleOCR的开源代码修改并优化的.NET版本OCR可离线使用类库。项目核心组件PaddleOCR.dll由C++编写,根据百度飞桨PaddleOCR的C++代码修改并优化与编译。目前已经支持C\C++、.NET、Python、Golang、Rust、Java、LabVIEW、Delphi等众多开发语言的直接API接口调用。项目包含文本识别、文本检测、表格识别功能。在部分场景下,本项目经过针对性优化,识别率与推理性能得到提升。支持超轻量级中文OCR,单模型支持中、英、数字及PaddleOCR官方涵盖的多语种识别,同时支持竖排文本、长文本识别。

PaddleOCRSharp 封装极其简化,实际调用仅几行代码,极大方便了中下游开发者的使用,降低了PaddleOCR的使用门槛,同时适配多种.NET框架,方便各个行业应用开发与部署。NuGet包即装即用,支持离线部署,无需联网即可实现高精度中英文OCR识别。

Java 不需要 NuGet 包,不需要 C# 运行时。只要拿到中间那层 C++ 动态库 + 它的 C 风格导出头文件,JNA 直接上。

这里包含了完整的运行依赖和模型库,拿来即用。

https://gitee.com/raoyutian/PaddleOCRSharp/tree/master/Demo/win_runtime_x64

 JNA 接口也已经全部写好,太贴心了。以下是PaddleOCR.java的全部代码:

// Copyright (c) 2026 raoyutian. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import com.sun.jna.Pointer;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.PointerByReference;
public interface PaddleOCR extends Library {
    PaddleOCR INSTANCE = Native.load("PaddleOCR", PaddleOCR.class);
    void libaddLicense(String licfile);
    //OCR
    Pointer Initializejson( String modelPath_det_infer, String modelPath_cls_infer, String modelPath_rec_infer, String keys, String parameterjson);
    void EnableANSIResult(Pointer engine_p, boolean enable);
    void EnableJsonResult(Pointer engine_p, boolean enable);
    void libEnableDetUseRect(Pointer engine_p, boolean enable);
    String Detect(Pointer engine, String imagefile);
    String DetectByte(Pointer engine, byte[] imagebytedata,  Pointer  size);
    String DetectBase64(Pointer engine, String imagebase64);
    String DetectByteData(Pointer engine, byte[] img, int nWidth, int nHeight, int nChannel);
    void FreeEngine(Pointer engine);
    //table
    boolean StructureInitializejson( String modelPath_det_infer, String modelPath_rec_infer,String keys, String table_model_dir, String table_char_dict_path, String parameterjson);
    String GetStructureDetectFile(String imagefile);
    String GetStructureDetectByte(byte[] imagebytedata, Pointer size);
    String GetStructureDetectBase64(String imagebase64);
    void FreeStructureEngine();
    String GetError();
}

  


调用代码:
// Copyright (c) 2026 raoyutian. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import com.sun.jna.Pointer;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.PointerByReference;
public class PaddleOCRDemo {
    public static void main(String[] args) throws IOException {
        String root = System.getProperty("user.dir");
        String jsonConfig = new String(
                Files.readAllBytes(Paths.get(root + "/inference/PaddleOCR.config.json")),
                StandardCharsets.UTF_8
        );
        String  det_infer= root + "/inference/PP-OCRv6_small_det_infer";
        String  cls_infer=  root + "/inference/PP-OCRv5_mobile_cls_infer";
        String  rec_infer=  root + "/inference/PP-OCRv6_small_rec_infer";
        String  keys=   root + "/inference/keys.txt";
        Pointer ptr = PaddleOCR.INSTANCE.Initializejson( det_infer,cls_infer,rec_infer,keys, jsonConfig);
        if (ptr == null || ptr.equals(Pointer.NULL)) {
            System.err.println("PaddleOCR Initializejson failed!");
            return;
        }
        PaddleOCR.INSTANCE.EnableANSIResult(ptr, true);
        //返回纯文本
        PaddleOCR.INSTANCE.EnableJsonResult(ptr, false);
        //返回json字符串
        // PaddleOCR.INSTANCE.EnableJsonResult(ptr, true);
        File imageDir = new File(root + "/image");
        File[] files = imageDir.listFiles();
        if (files == null || files.length == 0) {
            System.err.println("image dir is empty");
            return;
        }
        double totalTimes = 0;
        int count = 0;
        for (File file : files) {
            String imagePath = file.getAbsolutePath();
            long start = System.nanoTime();
            String ocrResult = PaddleOCR.INSTANCE.Detect(ptr, imagePath);
            long end = System.nanoTime();
            double ms = (end - start) / 1_000_000.0;
            totalTimes += ms;
            System.out.printf( "--%d-----耗时:【 %.2f】ms,文件名【%s】-----%n",count, ms, file.getName()
            );
            System.out.println(ocrResult);
            count++;
        }
        System.out.printf(
                "--total times:%.2fms,平均:%.2fms-----------%n",
                totalTimes, totalTimes / count
        );
        // 防止程序直接退出
        System.out.println("Press Enter to exit...");
        System.in.read();
    }
}

  

 
就这些。没有 Python、没有额外进程、没有 HTTP 客户端、没有序列化胶水。

返回结果长什么样?

PaddleOCRSharp 的 Detect 系列接口默认返回 JSON(也可以通过 EnableJsonResult 控制),大致结构:

[
  {
    "Text": "发票代码:144001900111",
    "Score": 0.987,
    "BoxPoints": [[x1,y1], [x2,y2], [x3,y3], [x4,y4]]
  },
  {
    "Text": "开票日期:2024年03月15日",
    "Score": 0.972,
    "BoxPoints": [[x1,y1], [x2,y2], [x3,y3], [x4,y4]]
  }
]

  

 

Java 侧直接 JSON.parseArray(result, OcrItem.class) 就能用。太爱了,贼方便。

你们团队里常见的几个顾虑,可以直接回答:

"JNA 性能够吗?"

JNA 调用 native 方法的开销大概在 几十纳秒级,和 OCR 推理本身(几十到几百毫秒)相比可以忽略。真正吃时间的是模型推理,不在 JNA 这一层。

"和 PaddleOCRSharp 的 C# 版本效果一样吗?"

一模一样。 因为底层是同一个 PaddleOCR.dll / PaddleOCR.so,同一个模型文件,同一个后处理逻辑。C# 那边 P/Invoke 和 Java 这边 JNA 只是不同的"门面",进去之后走的是同一条路。

"Linux 服务器上能跑吗?"

能。PaddleOCRSharp 有 Linux x64 的 .so 构建(也有信创 ARM/龙芯版本)。Native.load("PaddleOCR", ...) 在 Linux 下自动找 PaddleOCR.so,只要 LD_LIBRARY_PATHjava.library.path 包含它所在目录即可。

"表格识别怎么做?"

StructureInitialize 初始化带表格模型的引擎,调 GetStructureDetectFile / GetStructureDetectByte,返回的是 HTML 格式的表格结构。JNA 接口映射方式和普通 OCR 完全对称。

"模型文件去哪拿?"

PaddleOCRSharp 的 Gitee仓库包含各种模型,

https://gitee.com/raoyutian/PaddleOCRSharp,也可以直接用 PaddleOCR 官方仓的 inference 模型,路径对上就行。

说一句实在的

Java 生态里做 OCR,最容易搜到的方案是 Tess4J(Tesseract 的 Java 封装)——但 Tesseract 对中文场景、复杂版面、表格的识别率,和 PaddleOCR 系列不在一个量级。

PaddleOCRSharp 的 C++ 内核 + JNA,本质上是给 Java 后端补上了"本地高精度 OCR 能力"这块短板,而且接入成本极低。

如果你手头已经有 PaddleOCRSharp 的 C++ 动态库,十几分钟就能跑通一个 Demo。如果还没有,去作者 仓库拿,里面 dll + 模型 + 头文件全齐,按我们上面这个骨架接进去,比你搭一个 Python 服务快多了。

最后放上仓库地址:https://gitee.com/raoyutian/PaddleOCRSharp

仓库内有java的可以直接编译运行的demo示例。

仓库还有 C\C++、.NET、Python、Golang其他示例哟

 
posted on 2026-08-27 17:38  英田科技-明月心  阅读(5)  评论(0)    收藏  举报