这是一份阐述如何通过第三方程序调用 PaddleOCR-json.exe
的文档。
v1.4.0
版功能调整:
- 为避免服务器模式下的隐私安全问题,默认禁用剪贴板识图功能。如有需要,可参考 构建指南 自行构建程序并启用该功能。
调用方与引擎进程的交互方式有三种:单次识图模式,匿名管道模式,TCP套接字服务器模式。
在启动参数中指定 image_path=图片路径
即可。程序会识别该图片,输出识别内容,然后结束进程。
注意,此时最好传入纯英文路径。
剪贴板相关接口已弃用,不建议使用
支持剪贴板识别,指令为 image_path=clipboard
示例:
PaddleOCR-json.exe -image_path="D:/test/test 1.jpg"
交互遵循对话原则,对每一行(以\n结尾)输入,必然会产生有且仅有一行输出。调用步骤如下:
- Window下有三个标准io管道:标准输入stdin,标准输出stdout,标准错误输出stderr。调用方需要重定向本程序stdin和stdout。不建议重定向stderr,这可能收到不必要的日志信息。此外,还要指定工作目录cwd(即PaddleOCR-json.exe所在的目录)。
- 如果要指定参数(如配置文件路径或者OCR参数),必须在这个阶段传入,参数详见后面的说明。
示例:(以python为例)
ret = subprocess.Popen(
"程序目录/PaddleOCR-json.exe", # 引擎位置
cwd="程序目录", # 引擎工作目录
stdout=subprocess.PIPE, # 重定向标准输出
stdin=subprocess.PIPE # 重定向标准输入
)
- 本程序启动时,第三方链接库会打印大量日志信息。不过绝大多数日志输出在
stderr
流,可以不去管它。 - 程序在
stdout
输出OCR init completed.
标志着初始化完成。调用方应该先循环读取直到读到完成标志,再进入正式工作。
示例:
while True: # 循环,直到检测到启动完成或失败
strOut = str(ret.stdout.readline()) # 读取一行输入
if "OCR init completed." in strOut: # 若检测到启动成功
break # 退出循环
if not self.ret.poll() == None: # 若检测到子进程不在运行
raise Exception("OCR初始化失败") # 初始化失败,抛出异常
print('初始化完成!')
所有传入指令必须为json格式的字符串。强烈建议为传入的json启用ascii转义,以避免任何编码问题。
示例:
原:
{"image_path": "测试图片\\test 1.jpg"}
转义后:
{"image_path": "\u6D4B\u8BD5\u56FE\u7247\\test 1.jpg"}
json传入值支持以下参数(每次任务仅可传入其中一项):
键名称 | 值说明 |
---|---|
image_path | 图片路径。 |
image_base64 | 图片经过base64编码的字符串。 |
说明:
- image_base64传入的base64字符串请不要带有类似
data:image/jpg;base64,
的前缀。只需传入数据部分。引擎会自动分析图片格式。
- 传入指令字典转换为字符串后,必须在结尾添加换行符
\n
。 - 发送字符串到引擎进程的
stdin
。 - 从引擎进程的
stdout
接收返回值。
单次任务示例:
imgObj = {"image_path": "test.png"} # 待发送指令
imgStr = json.dumps(imgObj, ensure_ascii=True, indent=None) # 转为json字符串,启用ascii转义,关闭自动换行
imgStr += "\n" # 在结尾添加换行符
ret.stdin.write(imgStr.encode()) # 写入引擎进程的stdin流
ret.stdin.flush() # 发送
getBytes = ret.stdout.readline() # 获取一行结果
getStr = getBytes.decode('ascii', errors='ignore') # 解码为ascii字符串
getObj = json.loads(getStr) # 反序列化json
print("识别结果为:", getObj)
完成所有识图工作后,可以关闭引擎进程以释放被占用的系统资源。
如果需要更换识别语言,也需要先关闭旧的引擎进程,再开启一个新的进程。
关闭进程有两种方法:
方法一:直接kill掉。
示例:
self.ret.kill() # 关闭子进程
方法二:传入exit指令。可以包含在json内,或直接发送字符exit\n
。
示例:
{"exit": ""}
套接字模式的指令格式跟管道模式完全一致,只是启动参数有所区别,交互方式换成TCP。
TCP通信的性能比管道略低。单纯考虑传输延时,TCP本地环回比管道慢一个数量级,非本地环回要走网卡的话差距更大。但是,这几毫秒的差距在OCR任务本身的耗时面前不值一提。实测进行1000次任务,本地环回只比管道慢3s左右。
对于本地通信,TCP的优势在于它是可靠传输,而且持续读取时不容易丢包,理论上对于超大数据包(单个100MB量级)会比管道更安全。
当然,TCP套接字模式也支持本机调用部署在局域网内另一台设备上的引擎进程。
启动参数不传入image_path
时,将启动循环多次任务模式,可重复接受调用方指令。默认为交互方式为管道模式。如果需要启用套接字模式,必须指定port
为0~65535的值。可选指定addr
参数。
键名称 | 默认值 | 值说明 |
---|---|---|
addr | loopback | 指定开展服务的ip地址。可选值:loopback , localhost 仅在本地环回(127.0.0.1)开展服务。any 在本机任何可用ip地址开展服务。或者其他IPv4地址。 |
port | -1 | 指定开展服务的端口。传入0时随机端口,传入1~65535则设为该端口。 |
示例:
PaddleOCR-json.exe -port=0
输出:
如果OCR初始化成功,程序在 stdout
输出 OCR init completed.
标志着初始化完成。
接下来会初始化套接字,如果套接字初始化成功,则会在 stdout
输出 Socket init completed. IP地址:端口号
。
如果套接字初始化失败,会在 stderr
输出报错信息,然后结束进程。
成功示例:
stdin: PaddleOCR-json.exe -port=8888
stdout: …………
stdout: OCR init completed.
stdout: Socket init completed. 127.0.0.1:8888
(开始任务循环)
失败示例:
stdin: PaddleOCR-json.exe -port=8888
stdout: …………
stdout: OCR init completed.
stderr: Failed to bind address.
(进程结束)
如果你希望让调用方程序获取开启的端口(尤其是设置了-port=0
随机端口时),可以在收到 OCR init completed.
后,再监听一行输出,提取冒号:
后面的数字。
注意,在成功启动服务器后(收到Socket init completed.
),最好释放掉strout重定向,让引擎进程的stdout重新输出到默认控制台流中。可以防止 stdout
的缓冲区被意外填满,导致堵塞。
示例:
# 假设已经检测到OCR初始化完成,下面检测套接字是否初始化完成
strOut = str(ret.stdout.readline()) # 读取一行输入
if not self.ret.poll() == None: # 若检测到子进程不在运行
raise Exception("套接字初始化失败") # 初始化失败,抛出异常
if "Socket init completed. " in strOut: # 初始化成功
splits = strOut.split(":")
self.ip = splits[0].split("Socket init completed. ")[1] # 提取ip地址
self.port = int(splits[1]) # 提取端口号
self.ret.stdout.close() # 关闭管道重定向,防止缓冲区填满导致堵塞
print(f"套接字服务器初始化成功。{self.ip}:{self.port}")
else:
raise Exception("套接字初始化失败")
对于一次OCR任务,客户端应遵循以下步骤:
- 连接到服务端的ip:端口
- 发送指令(格式与管道模式相同)
- 循环接收回送
- 断开TCP连接
单次任务示例:
# 连接
clientSocket.connect(("127.0.0.1", "8888"))
# 发送
clientSocket.sendall(发送数据)
# 循环接收回传(因为单次接收的缓冲区大小有限)
resData = b''
while True:
res = clientSocket.recv(1024)
if not res: # 接收完毕
break
resData += res
# 关闭连接
clientSocket.close()
# TODO:处理数据resData。要先从bytes转字符串,再解析json转字典……
与管道模式一致,建议传入exit
或{"exit":""}
来结束进程,引擎会释放被占用的网络资源。
引擎虽然支持网络连接,但是不支持并发和多客户端连接。如果需要并发,建议由调用方来中转。
比如用python写个中转器,客户端先用网络连接到中转器,中转器再通过TCP或管道的方式与引擎进程交互。当多个客户端发起请求,则排队顺序执行任务。中转器可以给客户端先返回排队和预计耗时等信息。
由于PPOCR引擎本身调教策略非常激进,繁重任务时通常可以吃满CPU全核,所以顺序调用的效率是最优的。多开引擎进程反而可能由于CPU调度策略,而减低总效率。
管道模式和套接字模式,输入输出值的格式是完全相同的,仅仅是交互方式不同。编写API时,建议仅重载进程交互的函数来适配这两种模式,其他参数解析等部分的代码可以复用。
配置参数规定OCR的各项属性和识别模型库的路径。通过加载不同模型库,引擎能识别不同语言的文字。
PaddleOCR跟语言和模型库相关的属性有以下:
键名称 | 默认值 | 值说明 |
---|---|---|
det_model_dir | "models/ch_PP-OCRv3_det_infer" | det目标检测模型库目录 |
cls_model_dir | "models/ch_ppocr_mobile_v2.0_cls_infer" | cls方向分类模型库目录 |
rec_model_dir | "models/ch_PP-OCRv3_rec_infer" | rec文本识别模型库目录 |
rec_char_dict_path | "models/dict_chinese.txt" | rec文本识别字典路径 |
rec_img_h | 48 | V2模型库要改为32 |
对于不同语言,det和cls模型是通用的,只需更改rec模型、rec字典即可。PPOCR-V3模型使用默认的rec_img_h
,但V2模型要改为32
。
建议语言相关的参数采用配置文件的形式导入引擎,因为每种语言的模型参数是固定的,配置文件的方式更易于调用。
配置文件的格式是:(键和值之间可以用=或空格来分割)
# 单行注释
键=值
键 值
文件示例: 假设要配置一组德文的识别库参数,rec模型是V2版的german_mobile_v2.0_rec_infer
,字典文件是german_dict.txt
。
将上述两个文件放入models
目录,然后创建一个config_german_v2.txt
,内容如下:
# 德文识别库(PPOCR-V2)
# rec识别库目录
rec_model_dir models/german_mobile_v2.0_rec_infer
# rec字典路径
rec_char_dict_models/path german_dict.txt
# 因为是V2模型,所以要设为32。V3模型不用写这个参数。
rec_img_h 32
注意,相对路径是相对于引擎工作目录的路径!
使用示例:
PaddleOCR-json.exe -config_path="models/config_german_v2.txt"
你可以在 PP-OCR系列 V3多语言识别模型列表 和 字典列表 寻找需要的模型库。
PaddleOCR跟OCR相关的常用属性有以下:
键名称 | 默认值 | 值说明 |
---|---|---|
enable_mkldnn | true | 启用CPU推理加速,关掉可以减少内存占用,但会降低速度。 |
limit_side_len | 960 | 若图片长边长度大于该值,会被缩小到该值,以提高速度。 |
cls | false | 启用cls方向分类,识别方向不是正朝上的图片。 |
use_angle_cls | false | 启用方向分类,必须与cls值相同。 |
还有更多参数,如预测精度、滤波阈值、批大小等。正常情况下,这些参数已经是最优解。不过如果你熟悉OCR工作原理并且希望调节这些参数来适应自己的任务需求,可以参考 args.cpp 中的注释。
对于OCR相关的参数,不建议写在配置文件中,而是通过启动参数来传入。
示例: 开启方向矫正,并调高缩放阈值以便提高大分辨率图片的识别率:
PaddleOCR-json.exe -limit_side_len=4096 -cls=1 -use_angle_cls=1
欢迎各位开发者向本项目贡献新语言的API,或完善现有API!
为保证PR合并顺利及格式统一,建议:
- 将新API的主要文件放在api目录中的新文件夹下。
- 至少应该含有接口
PPOCR_api.xx
、调用示例demo.xx
、使用说明README.md
。请不要在项目中添加无关的文件。 - 将API名称、目录链接、简短调用示例代码块写进根目录的
README.md
,格式如下:
### 1. Python API
[资源目录](api/python)
<details>
<summary>使用示例</summary>
`` `python
示例代码
`` `
</details>
👆当你需要修改本项目代码时欢迎参考。