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_PATH 或 java.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其他示例哟
浙公网安备 33010602011771号