我(项目原作者)由于能力有限,只提供Windows10/11的版本。但是,PaddleOCR库是支持多平台的。因此,我在V1.3.0的重构过程中,着重考虑了可移植性,将所有平台绑定的代码都分离到单独的函数和文件中。移植工作者理论上只需重写一部分平台绑定的函数,即可移植到目标平台上。
本项目的代码主要分为“OCR推理”和“OCR任务流程控制”两个类型。推理部分的代码已经适配多平台,只需重写任务流程中的部分代码即可。
构建项目时,需要下载导入对应平台(Linux/macOS)的PaddlePaddle核心库与OpenCV组件。构建指南 中会介绍。
开发过程中,可以参考 task.h
和 task_win32.cpp
。
任务流程通过类Task
来控制,其中绑定平台、需要重写的是以下成员函数:
重要性:高。必须重写。
代替OpenCV的imread,从一个utf-8编码的路径字符串中读取图片,并返回cv::Mat。失败时通过set_state
设置错误码,然后返回空Mat。注意,如果传入的路径字符串是clipboard
,那么要执行剪贴板识图。
示例:
// 代替cv imread,接收utf-8字符串传入,返回Mat。
// flag:与cv::imread(path, flag) 的flag作用相同,指定图片读取的格式,默认值为cv::IMREAD_COLOR
cv::Mat Task::imread_u8(std::string pathU8, int flag) {
if (pathU8 == u8"clipboard") { // 若为剪贴板任务
return imread_clipboard(flag); // 返回剪贴板识别
}
// TODO:尝试从路径pathU8中读取图片
if(成功) {
return 图片Mat;
}
else {
// TODO: 根据错误类型,设置不同的错误码和错误信息,如下↓
set_state(CODE_ERR_PATH_READ, MSG_ERR_PATH_READ(pathU8)); // 报告状态:无法读取
return cv::Mat(); // 返回空Mat
}
}
重要性:低。如果暂时不想写,可以直接 return cv::Mat();
。
尝试从当前剪贴板中读取图片,并返回cv::Mat。失败时通过set_state
设置错误码,然后返回空Mat。
示例:
// 从剪贴板读入一张图片,返回Mat。
cv::Mat Task::imread_clipboard(int flag)
{
// TODO: 读取剪贴板
if(成功) {
return 图片Mat
}
else {
// TODO: 根据错误类型,设置不同的错误码和错误信息,如下↓
set_state(CODE_ERR_CLIP_OPEN, MSG_ERR_CLIP_OPEN); // 报告状态:剪贴板打开失败
return cv::Mat(); // 返回空Mat
}
}
重要性:低。如果暂时不想写,可以直接 return 0;
。
套接字服务器模式工作流程。大致分为这些步骤:
- 初始化套接字
- 绑定地址和端口号
- 输出实际地址和端口号:如
Socket init completed. 127.0.0.1:8888
- 开启任务循环:
- 接受客户端连接
- 接受客户端指令
- 解析指令
- 若类属性
is_exit
被设为true,则退出循环,结束进程 - 调用OCR
- 向客户端回传识别结果
- 关闭本次连接
注意,服务器初始化成功后必须cout输出 Socket init completed. IP地址:端口
,以便客户端可以知道并提取端口号。
请将以上重写的成员函数,写在一个单独的.cpp中,如 task_linux.cpp
。然后为整个文件加上条件编译。
示例: task_linux.cpp
// Linux 平台的任务处理代码
#ifdef _LINUX
#include "include/paddleocr.h"
#include "include/args.h"
#include "include/task.h"
#include <平台相关的库>
namespace PaddleOCR
{
cv::Mat Task::imread_u8(std::string pathU8, int flag){
…………
}
cv::Mat Task::imread_clipboard(int flag){
…………
}
int Task::socket_mode(){
…………
}
}
#endif
如果需要使用一些辅助函数,请一并写在该文件中。如果需要为类添加绑定平台的类函数(如Windows需要用到Task::imread_wstr
来处理宽字节码),那么将成员函数定义在 task.h
中,并加上条件编译。
如果需要新增错误码和错误信息宏定义,请写在 task.h
的头部。
总之,请参考 task.h
和 task_win32.cpp
。
Umi-OCR v1 需要调用 imread_u8
和 imread_clipboard
。
Umi-OCR v2 仅需使用 imread_u8
,无需调用 imread_clipboard
和 socket_mode
。
我很乐意并感谢你为这个项目贡献代码!如果可以的话,请尽量遵守以下约定:
关于编码:
- 尽量写注释。
- 请尽量考虑到并区分不同的异常情况,用错误码和错误信息宏定义向调用方反馈。
- 代码文件使用utf-8编码。
- 如果需要输出调试信息,并且不希望调用方接收到(以免干扰正常的OCR流程),用cerr而不是cout。
- 尽量复用项目中已有的函数而不是造轮子。