OCR(光学字符识别,Optical Character Recognition)是一种将图像中的文字转换为可编辑文本的技术。它广泛应用于文档数字化、信息提取和数据处理等领域。OCR 可以识别印刷文本、手写文本,甚至某些类型的字体和符号。
通用 OCR 产线用于解决文字识别任务,提取图片中的文字信息以文本形式输出,本产线集成了 PP-OCRv5 和 PP-OCRv4 的端到端 OCR 串联系统,支持超过 80 种语言的识别,并在此基础上,增加了对图像的方向矫正和扭曲矫正功能。基于本产线,可实现 CPU 上毫秒级的文本内容精准预测,使用场景覆盖通用、制造、金融、交通等各个领域。本产线同时提供了灵活的服务化部署方式,支持在多种硬件上使用多种编程语言调用。不仅如此,本产线也提供了二次开发的能力,您可以基于本产线在您自己的数据集上训练调优,训练后的模型也可以无缝集成。
通用OCR产线中包含必选的文本检测模块和文本识别模块,以及可选的文档图像方向分类模块、文本图像矫正模块和文本行方向分类模块。其中,文档图像方向分类模块和文本图像矫正模块作为文档预处理子产线被集成到通用OCR产线中。每个模块都包含多个模型,您可以根据下方的基准测试数据选择使用的模型。
如果您更注重模型的精度,请选择精度较高的模型;如果您更在意模型的推理速度,请选择推理速度较快的模型;如果您关注模型的存储大小,请选择存储体积较小的模型。
文档图像方向分类模块(可选):
模型 模型下载链接 Top-1 Acc(%) GPU推理耗时(ms)文本图像矫正模块(可选):
模型 模型下载链接 CER GPU推理耗时(ms)文本检测模块:
模型 模型下载链接 检测Hmean(%) GPU推理耗时(ms)文本识别模块:
模型 模型下载链接 识别 Avg Accuracy(%) GPU推理耗时(ms)👉模型列表详情 * PP-OCRv5 多场景模型 模型 模型下载链接 中文识别 Avg Accuracy(%) 英文识别 Avg Accuracy(%) 繁体中文识别 Avg Accuracy(%) 日文识别 Avg Accuracy(%) GPU推理耗时(ms)❗ 以上列出的是文本识别模块重点支持的4个核心模型,该模块总共支持18个全量模型,包含多个多语言文本识别模型,完整的模型列表如下:
文本行方向分类模块(可选):
模型 模型下载链接 Top-1 Acc(%) GPU推理耗时(ms)测试环境说明:
PaddleX 所提供的模型产线均可以快速体验效果,你可以在星河社区线体验通用 OCR 产线的效果,也可以在本地使用命令行或 Python 体验通用 OCR 产线的效果。
2.1 在线体验¶您可以在线体验通用 OCR 产线的效果,用官方提供的 Demo 图片进行识别,例如:
如果您对产线运行的效果满意,可以直接进行集成部署。您可以选择从云端下载部署包,也可以参考2.2节本地体验中的方法进行本地部署。如果对效果不满意,您可以利用私有数据对产线中的模型进行微调训练。如果您具备本地训练的硬件资源,可以直接在本地开展训练;如果没有,星河零代码平台提供了一键式训练服务,无需编写代码,只需上传数据后,即可一键启动训练任务。
2.2 本地体验¶❗ 在本地使用通用OCR产线前,请确保您已经按照PaddleX安装教程完成了PaddleX的wheel包安装。如果您希望选择性安装依赖,请参考安装教程中的相关说明。该产线对应的依赖分组为 ocr
。
--input
替换为本地路径,进行预测paddlex --pipeline OCR \
--input general_ocr_002.png \
--use_doc_orientation_classify False \
--use_doc_unwarping False \
--use_textline_orientation False \
--save_path ./output \
--device gpu:0
注:PaddleX 官方模型默认从 HuggingFace 获取,如运行环境访问 HuggingFace 不便,可通过环境变量修改模型源为 BOS:PADDLE_PDX_MODEL_SOURCE="BOS"
,未来将支持更多主流模型源;
相关的参数说明可以参考2.2.2 Python脚本方式集成中的参数说明。支持同时指定多个设备以进行并行推理,详情请参考 产线并行推理。
运行后,会将结果打印到终端上,结果如下:
{'res': {'input_path': './general_ocr_002.png', 'page_index': None, 'model_settings': {'use_doc_preprocessor': False, 'use_textline_orientation': False}, 'dt_polys': array([[[ 3, 10],
...,
[ 4, 30]],
...,
[[ 99, 456],
...,
[ 99, 479]]], dtype=int16), 'text_det_params': {'limit_side_len': 736, 'limit_type': 'min', 'thresh': 0.3, 'max_side_limit': 4000, 'box_thresh': 0.6, 'unclip_ratio': 1.5}, 'text_type': 'general', 'textline_orientation_angles': array([-1, ..., -1]), 'text_rec_score_thresh': 0.0, 'rec_texts': ['www.997700', '', 'Cm', '登机牌', 'BOARDING', 'PASS', 'CLASS', '序号SERIAL NO.', '座位号', 'SEAT NO.', '航班FLIGHT', '日期DATE', '舱位', '', 'W', '035', '12F', 'MU2379', '03DEc', '始发地', 'FROM', '登机口', 'GATE', '登机时间BDT', '目的地TO', '福州', 'TAIYUAN', 'G11', 'FUZHOU', '身份识别IDNO.', '姓名NAME', 'ZHANGQIWEI', '票号TKT NO.', '张祺伟', '票价FARE', 'ETKT7813699238489/1', '登机口于起飞前10分钟关闭 GATESCL0SE10MINUTESBEFOREDEPARTURETIME'], 'rec_scores': array([0.67634439, ..., 0.97416091]), 'rec_polys': array([[[ 3, 10],
...,
[ 4, 30]],
...,
[[ 99, 456],
...,
[ 99, 479]]], dtype=int16), 'rec_boxes': array([[ 3, ..., 30],
...,
[ 99, ..., 479]], dtype=int16)}}
运行结果参数说明可以参考2.2.2 Python脚本方式集成中的结果解释。
可视化结果保存在save_path
下,其中OCR的可视化结果如下:
from paddlex import create_pipeline
pipeline = create_pipeline(pipeline="OCR")
output = pipeline.predict(
input="./general_ocr_002.png",
use_doc_orientation_classify=False,
use_doc_unwarping=False,
use_textline_orientation=False,
)
for res in output:
res.print()
res.save_to_img(save_path="./output/")
res.save_to_json(save_path="./output/")
在上述 Python 脚本中,执行了如下几个步骤:
(1)通过 create_pipeline()
实例化 OCR 产线对象,具体参数说明如下:
pipeline
产线名称或是产线配置文件路径。如为产线名称,则必须为 PaddleX 所支持的产线。 str
None
config
产线具体的配置信息(如果和pipeline
同时设置,优先级高于pipeline
,且要求产线名和pipeline
一致)。 dict[str, Any]
None
device
产线推理设备。支持指定GPU具体卡号,如“gpu:0”,其他硬件具体卡号,如“npu:0”,CPU如“cpu”。支持同时指定多个设备以进行并行推理,详情请参考产线并行推理文档。 str
gpu:0
use_hpip
是否启用高性能推理插件。如果为 None
,则使用配置文件或 config
中的配置。 bool
| None
无 None
hpi_config
高性能推理配置 dict
| None
无 None
(2)调用 OCR 产线对象的 predict()
方法进行推理预测。该方法将返回一个 generator
。以下是 predict()
方法的参数及其说明:
input
待预测数据,支持多种输入类型,必填 Python Var|str|list
numpy.ndarray
表示的图像数据/root/data/img.jpg
;如URL链接,如图像文件或PDF文件的网络URL:示例;如本地目录,该目录下需包含待预测图像,如本地路径:/root/data/
(当前不支持目录中包含PDF文件的预测,PDF文件需要指定到具体文件路径)[numpy.ndarray, numpy.ndarray]
,[\"/root/data/img1.jpg\", \"/root/data/img2.jpg\"]
,[\"/root/data1\", \"/root/data2\"]
None
use_doc_orientation_classify
是否使用文档方向分类模块 bool|None
True
或者 False
;None
, 将默认使用产线初始化的该参数值,初始化为True
;None
use_doc_unwarping
是否使用文档扭曲矫正模块 bool|None
True
或者 False
;None
, 将默认使用产线初始化的该参数值,初始化为True
;None
use_textline_orientation
是否使用文本行方向分类模块 bool|None
True
或者 False
;None
, 将默认使用产线初始化的该参数值,初始化为True
;None
text_det_limit_side_len
文本检测的图像边长限制 int|None
0
的任意整数;None
, 将默认使用产线初始化的该参数值,初始化为 64
;None
text_det_limit_type
文本检测的图像边长限制类型 str|None
min
和 max
,min
表示保证图像最短边不小于 det_limit_side_len
,max
表示保证图像最长边不大于 limit_side_len
None
, 将默认使用产线初始化的该参数值,初始化为 min
;None
text_det_thresh
检测像素阈值,输出的概率图中,得分大于该阈值的像素点才会被认为是文字像素点 float|None
0
的任意浮点数None
, 将默认使用产线初始化的该参数值 0.3
None
text_det_box_thresh
检测框阈值,检测结果边框内,所有像素点的平均得分大于该阈值时,该结果会被认为是文字区域 float|None
0
的任意浮点数None
, 将默认使用产线初始化的该参数值 0.6
None
text_det_unclip_ratio
文本检测扩张系数,使用该方法对文字区域进行扩张,该值越大,扩张的面积越大 float|None
0
的任意浮点数None
, 将默认使用产线初始化的该参数值 2.0
None
text_rec_score_thresh
文本识别阈值,得分大于该阈值的文本结果会被保留 float|None
0
的任意浮点数None
, 将默认使用产线初始化的该参数值 0.0
。即不设阈值None
(3)对预测结果进行处理,每个样本的预测结果均为对应的Result对象,且支持打印、保存为图片、保存为json
文件的操作:
print()
打印结果到终端 format_json
bool
是否对输出内容进行使用 JSON
缩进格式化 True
indent
int
指定缩进级别,以美化输出的 JSON
数据,使其更具可读性,仅当 format_json
为 True
时有效 4 ensure_ascii
bool
控制是否将非 ASCII
字符转义为 Unicode
。设置为 True
时,所有非 ASCII
字符将被转义;False
则保留原始字符,仅当format_json
为True
时有效 False
save_to_json()
将结果保存为json格式的文件 save_path
str
保存的文件路径,当为目录时,保存文件命名与输入文件类型命名一致 无 indent
int
指定缩进级别,以美化输出的 JSON
数据,使其更具可读性,仅当 format_json
为 True
时有效 4 ensure_ascii
bool
控制是否将非 ASCII
字符转义为 Unicode
。设置为 True
时,所有非 ASCII
字符将被转义;False
则保留原始字符,仅当format_json
为True
时有效 False
save_to_img()
将结果保存为图像格式的文件 save_path
str
保存的文件路径,支持目录或文件路径 无
调用print()
方法会将结果打印到终端,打印到终端的内容解释如下:
input_path
: (str)
待预测图像的输入路径
page_index
: (Union[int, None])
如果输入是PDF文件,则表示当前是PDF的第几页,否则为 None
model_settings
: (Dict[str, bool])
配置产线所需的模型参数
use_doc_preprocessor
: (bool)
控制是否启用文档预处理子产线use_textline_orientation
: (bool)
控制是否启用文本行方向分类功能doc_preprocessor_res
: (Dict[str, Union[str, Dict[str, bool], int]])
文档预处理子产线的输出结果。仅当use_doc_preprocessor=True
时存在
input_path
: (Union[str, None])
图像预处理子产线接受的图像路径,当输入为numpy.ndarray
时,保存为None
model_settings
: (Dict)
预处理子产线的模型配置参数
use_doc_orientation_classify
: (bool)
控制是否启用文档方向分类use_doc_unwarping
: (bool)
控制是否启用文档扭曲矫正angle
: (int)
文档方向分类的预测结果。启用时取值为[0,1,2,3],分别对应[0°,90°,180°,270°];未启用时为-1dt_polys
: (List[numpy.ndarray])
文本检测的多边形框列表。每个检测框由4个顶点坐标构成的numpy数组表示,数组shape为(4, 2),数据类型为int16
dt_scores
: (List[float])
文本检测框的置信度列表
text_det_params
: (Dict[str, Dict[str, int, float]])
文本检测模块的配置参数
limit_side_len
: (int)
图像预处理时的边长限制值limit_type
: (str)
边长限制的处理方式thresh
: (float)
文本像素分类的置信度阈值box_thresh
: (float)
文本检测框的置信度阈值unclip_ratio
: (float)
文本检测框的膨胀系数text_type
: (str)
文本检测的类型,当前固定为"general"textline_orientation_angles
: (List[int])
文本行方向分类的预测结果。启用时返回实际角度值(如[0,0,1]),未启用时返回[-1,-1,-1]
text_rec_score_thresh
: (float)
文本识别结果的过滤阈值
rec_texts
: (List[str])
文本识别结果列表,仅包含置信度超过text_rec_score_thresh
的文本
rec_scores
: (List[float])
文本识别的置信度列表,已按text_rec_score_thresh
过滤
rec_polys
: (List[numpy.ndarray])
经过置信度过滤的文本检测框列表,格式同dt_polys
rec_boxes
: (numpy.ndarray)
检测框的矩形边界框数组,shape为(n, 4),dtype为int16。每一行表示一个矩形框的[x_min, y_min, x_max, y_max]坐标 ,其中(x_min, y_min)为左上角坐标,(x_max, y_max)为右下角坐标
调用save_to_json()
方法会将上述内容保存到指定的save_path
中,如果指定为目录,则保存的路径为save_path/{your_img_basename}_res.json
,如果指定为文件,则直接保存到该文件中。由于json文件不支持保存numpy数组,因此会将其中的numpy.array
类型转换为列表形式。
调用save_to_img()
方法会将可视化结果保存到指定的save_path
中,如果指定为目录,则保存的路径为save_path/{your_img_basename}_ocr_res_img.{your_img_extension}
,如果指定为文件,则直接保存到该文件中。(产线通常包含较多结果图片,不建议直接指定为具体的文件路径,否则多张图会被覆盖,仅保留最后一张图)
此外,也支持通过属性获取带结果的可视化图像和预测结果,具体如下:
json
获取预测的 json
格式的结果 img
获取格式为 dict
的可视化图像
json
属性获取的预测结果为dict类型的数据,相关内容与调用 save_to_json()
方法保存的内容一致。img
属性返回的预测结果是一个字典类型的数据。其中,键分别为 ocr_res_img
和 preprocessed_img
,对应的值是两个 Image.Image
对象:一个用于显示 OCR 结果的可视化图像,另一个用于展示图像预处理的可视化图像。如果没有使用图像预处理子模块,则字典中只包含 ocr_res_img
。此外,您可以获取OCR产线配置文件,并加载配置文件进行预测。可执行如下命令将结果保存在 my_path
中:
paddlex --get_pipeline_config OCR --save_path ./my_path
若您获取了配置文件,即可对OCR产线各项配置进行自定义,只需要修改 create_pipeline
方法中的 pipeline
参数值为产线配置文件路径即可。示例如下:
from paddlex import create_pipeline
pipeline = create_pipeline(pipeline="./my_path/OCR.yaml")
output = pipeline.predict(
input="./general_ocr_002.png",
use_doc_orientation_classify=False,
use_doc_unwarping=False,
use_textline_orientation=False,
)
for res in output:
res.print()
res.save_to_img("./output/")
res.save_to_json("./output/")
注: 配置文件中的参数为产线初始化参数,如果希望更改通用OCR产线初始化参数,可以直接修改配置文件中的参数,并加载配置文件进行预测。同时,CLI 预测也支持传入配置文件,--pipeline
指定配置文件的路径即可。
如果通用 OCR 产线可以达到您对产线推理速度和精度的要求,您可以直接进行开发集成/部署。
若您需要将通用 OCR 产线直接应用在您的Python项目中,可以参考 2.2.2 Python脚本方式中的示例代码。
此外,PaddleX 也提供了其他三种部署方式,详细说明如下:
🚀 高性能推理:在实际生产环境中,许多应用对部署策略的性能指标(尤其是响应速度)有着较严苛的标准,以确保系统的高效运行与用户体验的流畅性。为此,PaddleX 提供高性能推理插件,旨在对模型推理及前后处理进行深度性能优化,实现端到端流程的显著提速,详细的高性能推理流程请参考PaddleX高性能推理指南。
☁️ 服务化部署:服务化部署是实际生产环境中常见的一种部署形式。通过将推理功能封装为服务,客户端可以通过网络请求来访问这些服务,以获取推理结果。PaddleX 支持多种产线服务化部署方案,详细的产线服务化部署流程请参考PaddleX服务化部署指南。
以下是基础服务化部署的API参考与多语言服务调用示例:
API参考对于服务提供的主要操作:
200
,响应体的属性如下:logId
string
请求的UUID。 errorCode
integer
错误码。固定为0
。 errorMsg
string
错误说明。固定为"Success"
。 result
object
操作结果。
logId
string
请求的UUID。 errorCode
integer
错误码。与响应状态码相同。 errorMsg
string
错误说明。
服务提供的主要操作如下:
infer
获取图像OCR结果。
POST /ocr
file
string
服务器可访问的图像文件或PDF文件的URL,或上述类型文件内容的Base64编码结果。默认对于超过10页的PDF文件,只有前10页的内容会被处理。
Serving:
extra:
max_num_input_imgs: null
是 fileType
integer
| null
文件类型。0
表示PDF文件,1
表示图像文件。若请求体无此属性,则将根据URL推断文件类型。 否 useDocUnwarping
boolean
| null
请参阅产线对象中 predict
方法的 use_doc_unwarping
参数相关说明。 否 useTextlineOrientation
boolean
| null
请参阅产线对象中 predict
方法的 use_textline_orientation
参数相关说明。 否 textDetLimitSideLen
integer
| null
请参阅产线对象中 predict
方法的 text_det_limit_side_len
参数相关说明。 否 textDetLimitType
string
| null
请参阅产线对象中 predict
方法的 text_det_limit_type
参数相关说明。 否 textDetThresh
number
| null
请参阅产线对象中 predict
方法的 text_det_thresh
参数相关说明。 否 textDetBoxThresh
number
| null
请参阅产线对象中 predict
方法的 text_det_box_thresh
参数相关说明。 否 textDetUnclipRatio
number
| null
请参阅产线对象中 predict
方法的 text_det_unclip_ratio
参数相关说明。 否 textRecScoreThresh
number
| null
请参阅产线对象中 predict
方法的 text_rec_score_thresh
参数相关说明。 否 visualize
boolean
| null
是否返回可视化结果图以及处理过程中的中间图像等。
true
:返回图像。false
:不返回图像。null
:遵循产线配置文件Serving.visualize
的设置。Serving:
visualize: False
将默认不返回图像,通过请求体中的visualize
参数可以覆盖默认行为。如果请求体和配置文件中均未设置(或请求体传入null
、配置文件中未设置),则默认返回图像。 否 useDocOrientationClassify
boolean
| null
请参阅产线对象中 predict
方法的 use_doc_orientation_classify
参数相关说明。 否
result
具有如下属性:ocrResults
object
OCR结果。数组长度为1(对于图像输入)或实际处理的文档页数(对于PDF输入)。对于PDF输入,数组中的每个元素依次表示PDF文件中实际处理的每一页的结果。 dataInfo
object
输入数据信息。
ocrResults
中的每个元素为一个object
,具有如下属性:
prunedResult
object
产线对象的 predict
方法生成结果的 JSON 表示中 res
字段的简化版本,其中去除了 input_path
和 page_index
字段。 ocrImage
string
| null
OCR结果图,其中标注检测到的文本位置。图像为JPEG格式,使用Base64编码。 docPreprocessingImage
string
| null
可视化结果图像。图像为JPEG格式,使用Base64编码。 inputImage
string
| null
输入图像。图像为JPEG格式,使用Base64编码。 多语言调用服务示例 Python
import base64
import requests
API_URL = "http://localhost:8080/ocr"
file_path = "./demo.jpg"
with open(file_path, "rb") as file:
file_bytes = file.read()
file_data = base64.b64encode(file_bytes).decode("ascii")
payload = {"file": file_data, "fileType": 1}
response = requests.post(API_URL, json=payload)
assert response.status_code == 200
result = response.json()["result"]
for i, res in enumerate(result["ocrResults"]):
print(res["prunedResult"])
ocr_img_path = f"ocr_{i}.jpg"
with open(ocr_img_path, "wb") as f:
f.write(base64.b64decode(res["ocrImage"]))
print(f"Output image saved at {ocr_img_path}")
C++
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include "cpp-httplib/httplib.h" // https://github.com/Huiyicc/cpp-httplib
#include "nlohmann/json.hpp" // https://github.com/nlohmann/json
#include "base64.hpp" // https://github.com/tobiaslocker/base64
int main() {
httplib::Client client("localhost", 8080);
const std::string filePath = "./demo.jpg";
std::ifstream file(filePath, std::ios::binary | std::ios::ate);
if (!file) {
std::cerr << "Error opening file." << std::endl;
return 1;
}
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
std::vector buffer(size);
if (!file.read(buffer.data(), size)) {
std::cerr << "Error reading file." << std::endl;
return 1;
}
std::string bufferStr(buffer.data(), static_cast(size));
std::string encodedFile = base64::to_base64(bufferStr);
nlohmann::json jsonObj;
jsonObj["file"] = encodedFile;
jsonObj["fileType"] = 1;
auto response = client.Post("/ocr", jsonObj.dump(), "application/json");
if (response && response->status == 200) {
nlohmann::json jsonResponse = nlohmann::json::parse(response->body);
auto result = jsonResponse["result"];
if (!result.is_object() || !result["ocrResults"].is_array()) {
std::cerr << "Unexpected response structure." << std::endl;
return 1;
}
for (size_t i = 0; i < result["ocrResults"].size(); ++i) {
auto ocrResult = result["ocrResults"][i];
std::cout << ocrResult["prunedResult"] << std::endl;
std::string ocrImgPath = "ocr_" + std::to_string(i) + ".jpg";
std::string encodedImage = ocrResult["ocrImage"];
std::string decodedImage = base64::from_base64(encodedImage);
std::ofstream outputImage(ocrImgPath, std::ios::binary);
if (outputImage.is_open()) {
outputImage.write(decodedImage.c_str(), static_cast(decodedImage.size()));
outputImage.close();
std::cout << "Output image saved at " << ocrImgPath << std::endl;
} else {
std::cerr << "Unable to open file for writing: " << ocrImgPath << std::endl;
}
}
} else {
std::cerr << "Failed to send HTTP request." << std::endl;
if (response) {
std::cerr << "HTTP status code: " << response->status << std::endl;
std::cerr << "Response body: " << response->body << std::endl;
}
return 1;
}
return 0;
}
Java
import okhttp3.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Base64;
public class Main {
public static void main(String[] args) throws IOException {
String API_URL = "http://localhost:8080/ocr";
String imagePath = "./demo.jpg";
File file = new File(imagePath);
byte[] fileContent = java.nio.file.Files.readAllBytes(file.toPath());
String base64Image = Base64.getEncoder().encodeToString(fileContent);
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode payload = objectMapper.createObjectNode();
payload.put("file", base64Image);
payload.put("fileType", 1);
OkHttpClient client = new OkHttpClient();
MediaType JSON = MediaType.get("application/json; charset=utf-8");
RequestBody body = RequestBody.create(JSON, payload.toString());
Request request = new Request.Builder()
.url(API_URL)
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
if (response.isSuccessful()) {
String responseBody = response.body().string();
JsonNode root = objectMapper.readTree(responseBody);
JsonNode result = root.get("result");
JsonNode ocrResults = result.get("ocrResults");
for (int i = 0; i < ocrResults.size(); i++) {
JsonNode item = ocrResults.get(i);
JsonNode prunedResult = item.get("prunedResult");
System.out.println("Pruned Result [" + i + "]: " + prunedResult.toString());
String ocrImageBase64 = item.get("ocrImage").asText();
byte[] ocrImageBytes = Base64.getDecoder().decode(ocrImageBase64);
String ocrImgPath = "ocr_result_" + i + ".jpg";
try (FileOutputStream fos = new FileOutputStream(ocrImgPath)) {
fos.write(ocrImageBytes);
System.out.println("Saved OCR image to: " + ocrImgPath);
}
}
} else {
System.err.println("Request failed with HTTP code: " + response.code());
}
}
}
}
Go
package main
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
func main() {
API_URL := "http://localhost:8080/ocr"
filePath := "./demo.jpg"
fileBytes, err := ioutil.ReadFile(filePath)
if err != nil {
fmt.Printf("Error reading file: %v\n", err)
return
}
fileData := base64.StdEncoding.EncodeToString(fileBytes)
payload := map[string]interface{}{
"file": fileData,
"fileType": 1,
}
payloadBytes, err := json.Marshal(payload)
if err != nil {
fmt.Printf("Error marshaling payload: %v\n", err)
return
}
client := &http.Client{}
req, err := http.NewRequest("POST", API_URL, bytes.NewBuffer(payloadBytes))
if err != nil {
fmt.Printf("Error creating request: %v\n", err)
return
}
req.Header.Set("Content-Type", "application/json")
res, err := client.Do(req)
if err != nil {
fmt.Printf("Error sending request: %v\n", err)
return
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
fmt.Printf("Unexpected status code: %d\n", res.StatusCode)
return
}
body, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Printf("Error reading response body: %v\n", err)
return
}
type OcrResult struct {
PrunedResult map[string]interface{} `json:"prunedResult"`
OcrImage *string `json:"ocrImage"`
}
type Response struct {
Result struct {
OcrResults []OcrResult `json:"ocrResults"`
DataInfo interface{} `json:"dataInfo"`
} `json:"result"`
}
var respData Response
if err := json.Unmarshal(body, &respData); err != nil {
fmt.Printf("Error unmarshaling response: %v\n", err)
return
}
for i, res := range respData.Result.OcrResults {
if res.OcrImage != nil {
imgBytes, err := base64.StdEncoding.DecodeString(*res.OcrImage)
if err != nil {
fmt.Printf("Error decoding image %d: %v\n", i, err)
continue
}
filename := fmt.Sprintf("ocr_%d.jpg", i)
if err := ioutil.WriteFile(filename, imgBytes, 0644); err != nil {
fmt.Printf("Error saving image %s: %v\n", filename, err)
continue
}
fmt.Printf("Output image saved at %s\n", filename)
}
}
}
C#
using System;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
class Program
{
static readonly string API_URL = "http://localhost:8080/ocr";
static readonly string inputFilePath = "./demo.jpg";
static async Task Main(string[] args)
{
var httpClient = new HttpClient();
byte[] fileBytes = File.ReadAllBytes(inputFilePath);
string fileData = Convert.ToBase64String(fileBytes);
var payload = new JObject
{
{ "file", fileData },
{ "fileType", 1 }
};
var content = new StringContent(payload.ToString(), Encoding.UTF8, "application/json");
HttpResponseMessage response = await httpClient.PostAsync(API_URL, content);
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
JObject jsonResponse = JObject.Parse(responseBody);
JArray ocrResults = (JArray)jsonResponse["result"]["ocrResults"];
for (int i = 0; i < ocrResults.Count; i++)
{
var res = ocrResults[i];
Console.WriteLine($"[{i}] prunedResult:\n{res["prunedResult"]}");
string base64Image = res["ocrImage"]?.ToString();
if (!string.IsNullOrEmpty(base64Image))
{
string outputPath = $"ocr_{i}.jpg";
byte[] imageBytes = Convert.FromBase64String(base64Image);
File.WriteAllBytes(outputPath, imageBytes);
Console.WriteLine($"OCR image saved to {outputPath}");
}
else
{
Console.WriteLine($"OCR image at index {i} is null.");
}
}
}
}
Node.js
const axios = require('axios');
const fs = require('fs');
const path = require('path');
const API_URL = 'http://localhost:8080/layout-parsing';
const imagePath = './demo.jpg';
const fileType = 1;
function encodeImageToBase64(filePath) {
const bitmap = fs.readFileSync(filePath);
return Buffer.from(bitmap).toString('base64');
}
const payload = {
file: encodeImageToBase64(imagePath),
fileType: fileType
};
axios.post(API_URL, payload)
.then(response => {
const results = response.data.result.layoutParsingResults;
results.forEach((res, index) => {
console.log(`\n[${index}] prunedResult:`);
console.log(res.prunedResult);
const outputImages = res.outputImages;
if (outputImages) {
Object.entries(outputImages).forEach(([imgName, base64Img]) => {
const imgPath = `${imgName}_${index}.jpg`;
fs.writeFileSync(imgPath, Buffer.from(base64Img, 'base64'));
console.log(`Output image saved at ${imgPath}`);
});
} else {
console.log(`[${index}] No outputImages.`);
}
});
})
.catch(error => {
console.error('Error during API request:', error.message || error);
});
PHP
<?php
$API_URL = "http://localhost:8080/ocr";
$image_path = "./demo.jpg";
$image_data = base64_encode(file_get_contents($image_path));
$payload = array(
"file" => $image_data,
"fileType" => 1
);
$ch = curl_init($API_URL);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
$result = json_decode($response, true)["result"]["ocrResults"];
foreach ($result as $i => $item) {
echo "[$i] prunedResult:\n";
print_r($item["prunedResult"]);
if (!empty($item["ocrImage"])) {
$output_img_path = "ocr_{$i}.jpg";
file_put_contents($output_img_path, base64_decode($item["ocrImage"]));
echo "OCR image saved at $output_img_path\n";
} else {
echo "No ocrImage found for item $i\n";
}
}
?>
📱 端侧部署:端侧部署是一种将计算和数据处理功能放在用户设备本身上的方式,设备可以直接处理数据,而不需要依赖远程的服务器。PaddleX 支持将模型部署在 Android 等端侧设备上,详细的端侧部署流程请参考PaddleX端侧部署指南。 您可以根据需要选择合适的方式部署模型产线,进而进行后续的 AI 应用集成。
4. 二次开发¶如果通用 OCR 产线提供的默认模型权重在您的场景中,精度或速度不满意,您可以尝试利用您自己拥有的特定领域或应用场景的数据对现有模型进行进一步的微调,以提升通用 OCR 产线的在您的场景中的识别效果。
4.1 模型微调¶由于通用OCR产线包含若干模块,模型产线的效果如果不及预期,可能来自于其中任何一个模块。您可以对识别效果差的图片进行分析,进而确定是哪个模块存在问题,并参考以下表格中对应的微调教程链接进行模型微调。
情形 微调模块 微调参考链接 文本存在漏检 文本检测模块 链接 文本内容都不准 文本识别模块 链接 垂直或者旋转文本行矫正不准 文本行方向分类模块 链接 整图旋转矫正不准 文档图像方向分类模块 链接 图像扭曲矫正不准 文本图像矫正模块 暂不支持微调 4.2 模型应用¶当您使用私有数据集完成微调训练后,可获得本地模型权重文件。
若您需要使用微调后的模型权重,只需对产线配置文件做修改,将微调后模型权重的本地路径替换至产线配置文件中的对应位置即可:
SubPipelines:
DocPreprocessor:
...
SubModules:
DocOrientationClassify:
module_name: doc_text_orientation
model_name: PP-LCNet_x1_0_doc_ori
model_dir: null # 替换为微调后的文档图像方向分类模型权重路径
...
SubModules:
TextDetection:
module_name: text_detection
model_name: PP-OCRv5_server_det
model_dir: null # 替换为微调后的文本检测模型权重路径
...
TextLineOrientation:
module_name: textline_orientation
model_name: PP-LCNet_x0_25_textline_ori
model_dir: null # 替换为微调后的文本行方向分类模型权重路径
batch_size: 1
TextRecognition:
module_name: text_recognition
model_name: PP-OCRv5_server_rec
model_dir: null # 替换为微调后的文本识别模型权重路径
batch_size: 1
随后, 参考2.2 本地体验中的命令行方式或Python脚本方式,加载修改后的产线配置文件即可。 5. 多硬件支持¶
PaddleX 支持英伟达 GPU、昆仑芯 XPU、昇腾 NPU和寒武纪 MLU 等多种主流硬件设备,仅需修改 --device
参数即可完成不同硬件之间的无缝切换。
例如,您使用昇腾 NPU 进行 OCR 产线的推理,使用的 CLI 命令为:
paddlex --pipeline OCR \
--input general_ocr_002.png \
--use_doc_orientation_classify False \
--use_doc_unwarping False \
--use_textline_orientation False \
--save_path ./output \
--device npu:0
当然,您也可以在 Python 脚本中 create_pipeline()
时或者 predict()
时指定硬件设备。
若您想在更多种类的硬件上使用通用OCR产线,请参考PaddleX多硬件使用指南。
2025年8月14日 2025年8月14日 cuicheng01RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4