OCR开发实战:如何用PaddleOCR C++版实现本地文字识别(附模型文件处理技巧)
大概两个月前,我接到一个桌面端工具的需求:离线识别截图里的中文文字,识别结果要参与后续的自动化流程。技术栈限定在C++,不能套Python解释器,数据也不能出本机。当时我第一个想到的就是PaddleOCR——毕竟中文识别效果在开源方案里确实能打,而且官方有现成的C++推理示例。但真上手之后才发现,网上关于PaddleOCR C++版部署的资料零散得很,很多帖子还停留在旧版API,照着抄大概率编译不过。这篇就把我从环境搭建到推理跑通、再到模型文件处理和打包分发的完整过程写出来,给同样被C++版折磨过的朋友一个能直接抄的作业。
之所以强调"本地文字识别",是因为OCR这块的部署方案其实分两条路:一是调云端API,二是本地推理。前者省事但有网络依赖和隐私顾虑,后者要自己搞定模型、推理库和依赖环境,但胜在可控。PaddleOCR C++版的定位就是后者,它适合的读者画像是:已经有了C++项目(MFC/Qt/控制台皆可),想在不出网的情况下给程序加文字识别能力,并且愿意花点时间踩编译的坑。
1. 为什么我把OCR服务从Python换成了C++本地推理
先说清楚一个很多人纠结的问题:PaddleOCR官方主推的是Python版,pip install paddleocr一行就能用,识别效果和C++版完全一致,那为什么还要折腾C++?
1.1 部署现场的硬约束
我的实际场景是交付一个Windows桌面软件给客户,客户机器上不会有Python环境,也不可能为了一个OCR功能让客户去装Python解释器、装一堆pip依赖。如果强行用Python写识别模块,要么打包成exe(体积感人且容易被杀毒软件误报),要么在客户机器上搭Python环境(运维噩梦)。C++版直接把paddle_inference预测库和模型文件塞进程序目录,运行时就一个exe加几个dll,干净利落。
还有一个隐私问题:项目里处理的截图可能含客户的业务数据,明文出网走云端API在合规上过不去。本地推理意味着图像数据从头到尾不出机器,这一点在很多企业场景里是硬性要求。
1.2 C++版的真实能力边界
PaddleOCR C++版不是"阉割版",官方把PaddleOCR仓库里的deploy/cpp_infer目录维护得挺勤快,支持PP-OCR系列模型(v3、v4)的完整推理链路:文本检测(det)、方向分类(cls)、文字识别(rec)三段式pipeline全都包含,还支持CPU和GPU推理、TensorRT加速、批量识别。也就是说,Python版能跑的模型,C++版基本都能跑,识别精度和Python版没有差别,差别只在推理库接口的调用方式。
我实测下来,在i5-9500 CPU上,用PP-OCRv4的mobile系列模型,一张1920x1080的截图(大概20~30个文字框)完整走完检测+识别大约是150~250ms,完全够桌面工具用。如果用服务器级CPU或者N卡,速度还能再提一截。
1.3 哪些场景不建议用C++版
说实话,如果你的项目是内部工具、不要求交付给第三方,Python版是更快的选择。另外,如果识别量特别大(比如每天处理几十万张图)但机器上没有GPU,C++版带来的性能提升也很有限,因为瓶颈在CPU算力而非语言运行时。我个人的判断标准是:要不要用C++版,取决于"程序要不要离开你的机器"和"项目主语言是不是C/C++",而不是"哪个跑得更快"。
2. Windows下编译PaddleOCR C++推理库的版本搭配
这一节是全文最劝退的部分,也是踩坑最多的地方。PaddleOCR C++版依赖paddle_inference预测库,而预测库又依赖一堆第三方库,版本对不上就是无穷无尽的编译错误。我把自己验证过的版本组合直接抛出来,你照着配能少走至少半天弯路。
2.1 我验证过的编译环境组合
我的开发机是Windows 10 64位,用的VS2019(16.11版本),CMake 3.20,OpenCV 3.4.10,paddle_inference预测库版本是2.3.0。这套组合编译PaddleOCR的C++示例(deploy/cpp_infer)一次通过,没有报错。
各组件版本对照如下:
| 组件 | 推荐版本 | 备注 |
|---|---|---|
| Visual Studio | VS2019 16.8+ | VS2017也能用,但VS2022需要额外处理平台工具集 |
| CMake | 3.15以上 | 我用的3.20,3.30也没问题 |
| OpenCV | 3.4.x | 4.x也可以,但注意dll文件名不同 |
| paddle_inference | 2.3.0 | 官方预编译包,分CPU和GPU版 |
| PaddleOCR源码 | release/2.5 | 注意要用release分支,main分支接口可能不兼容 |
这里尤其要提醒:paddle_inference预测库的版本必须和PaddleOCR源码的tag对应。比如你下载的预测库是2.3.0,PaddleOCR源码最好用release/2.5(这个版本对应预测库2.3.x),否则头文件的接口对不上,编译时会出现一堆"找不到xxx成员"之类的错误。
2.2 triplet依赖:GFLAGS、GLOG和GOOGLETEST
PaddleOCR的CMakeLists里默认开启了编译选项WITH_GFLAGS、WITH_GLOG、WITH_GOOGLETEST,这三个库如果机器上没装,CMake会在configure阶段直接失败。这应该是新手最容易卡住的地方——报错信息通常是一长串"CMake Error at cmake/thirdparty.cmake",根本看不出是哪个库缺失。
我的做法是提前把这三个库的源码下载好:gflags、glog、googletest,并把它们的路径填进CMake的THIRD_PARTY_PATH变量。具体操作是在CMake GUI里设置THIRD_PARTY_PATH为本地目录(比如D:/paddle_third_party),PaddleOCR会自动下载并编译这三个库,前提是你网络通畅,能访问GitHub和Google的仓库。如果公司的网络拉不下来,就手动下载源码放到该目录下的src子目录里,CMake检测到已存在就不会再重新下载。
这里我额外说明一下为什么一定要这几个库:glog是日志库,PaddleOCR推理过程中的warning和error信息全靠它输出,没有glog你连"为什么识别失败"的日志都看不到;gflags是命令行参数解析库,PaddleOCR的demo程序用命令行参数控制模型路径、图像路径、是否使用GPU等;googletest是单元测试框架,编译示例时其实不是必需,但PaddleOCR的CMake默认开启,没法单独关掉,只能一起编译。
2.3 编译选项里那些一眼带过但很要命的开关
编译PaddleOCR C++示例时的几个CMake选项,我直接说结论:
CMAKE_BUILD_TYPE一定要设为Release,用Debug编译出来的程序推理速度会慢到怀疑人生,而且Debug模式下paddle_inference库的dll和Release模式下不兼容,链接时会报奇怪的LNK错误。PADDLE_LIB要指向paddle_inference预测库的解压目录,注意预测库压缩包解压后里面有个paddle文件夹,路径要写到这一层。CUDA_ARCH_NAME这个选项只在GPU版时需要指定,要根据你的显卡算力来填。比如GTX 1080是6.1,RTX 3060是8.6,填错了虽然能编译,但运行时kernel launcher会直接失败,报错信息是"no kernel image is available on the device"。
我在第一次编译时就是因为在PADDLE_LIB路径上少写了一层paddle,导致CMake一直提示找不到paddle_inference.h,来回折腾了快一个小时才发现是路径层级的问题。
编译成功后,会在build目录下生成ocr_system.exe,这就是识别程序本体。命令行执行方式大致是:
./ocr_system.exe --det_model_dir=./inference/det/ --rec_model_dir=./inference/rec/ --cls_model_dir=./inference/cls/ --image_dir=./test.png --use_gpu=false如果你能跑到这一步,恭喜,环境配通了。
3. 推理代码拆解:从cv::Mat到识别文本的完整链路
环境跑通只是第一步,真正要把OCR能力集成到自己的项目里,还是得读一遍PaddleOCR C++示例的源码,然后把核心推理逻辑抽出来。这一节用我实际改过的代码来拆解整个链路。
3.1 初始化:模型路径和推理配置
PaddleOCR C++示例的核心类是OCR和PPOCR,它们封装了检测、方向分类、识别三个模型。初始化时最重要的事情是告诉它三个模型分别在哪个目录、用CPU还是GPU、开几个线程。
#include "paddle_api.h" #include "ocr_det.h" #include "ocr_rec.h" #include "ocr_cls.h" #include "utils.h" paddleocr::DBDetector det( "inference/det", // 检测模型目录 false, // use_gpu 1, // gpu_id 0.3, // det_db_thresh 0.3, // det_db_box_thresh 0.5, // det_db_unclip_ratio false, // use_tensorrt 4, // cpu_threads true, // enable_mkldnn 960, // max_side_len 0.5, // det_db_score_mode 1.6, // det_db_box_thresh_ratio 0); // gpu_mem paddleocr::Classifier *cls = nullptr; // 方向分类模型可以为空,如果不需要识别旋转文字 cls = new paddleocr::Classifier("inference/cls", false, 1, 0.9, false, 4, true); paddleocr::CRNNRecognizer rec( "inference/rec", // 识别模型目录 false, // use_gpu 1, // gpu_id 0.5, // rec_score_thresh 0, // gpu_mem false, // use_tensorrt 4, // cpu_threads true); // enable_mkldnn这里每个参数的含义在demo代码里都有,但我特别想提示两个:det_db_unclip_ratio控制检测框的扩展幅度,默认0.5,如果你的图片文字间距特别大,识别出来的文本框会把两个字框进一个框里,导致识别结果黏在一起,这时候把它调大到0.8~1.0能解决;enable_mkldnn是指定用英特尔的MKL-DNN加速库,在CPU推理时强烈建议打开,我的实测数据是开启后单张图耗时降低约40%。
3.2 预处理:模型输入的数据细节
三个模型对输入图像的要求各不相同:
- 检测模型输入:shape为
[1, 3, h, w],h和w由max_side_len计算得出,默认最长边不超过960。 - 方向分类模型输入:
[1, 3, 48, 192],固定尺寸,识别图片是正的还是倒的。 - 识别模型输入:
[1, 3, 32, 320],如果识别长文本,宽会动态调整。
PaddleOCR示例里的Utility::ReadImage函数会统一处理:读取图片、做缩放、归一化到0~1、实现从BGR到RGB的转换。这个BGR和RGB的转换很容易被忽略,因为OpenCV默认读图是BGR,而Paddle模型是用RGB训练的,不转换会导致识别率急剧下降,而且你很难察觉是这里出了问题——程序不报错,但识别结果全错。我曾经用纯白底黑字的测试图都识别不出,排查了好久才发现是通道顺序的问题。
cv::Mat img = cv::imread(img_path, cv::IMREAD_COLOR); // 关键:OpenCV读出来是BGR,PaddleOCR用的是RGB cv::cvtColor(img, img, cv::COLOR_BGR2RGB);归一化这一步也值得多说一句:PaddleOCR模型的预处理是img / 255.0,然后减均值除方差,均值和方差都取0.5。也就是说最终像素值范围是[-1, 1]。很多从其他框架转过来的人习惯直接/255.0不做标准化,导致识别率低,就是这个原因。
3.3 推理:三段式pipeline的衔接逻辑
PaddleOCR的C++推理流程可以简化为以下几个步骤:
- 输入原图,跑检测模型,输出一组文本框坐标(四边形,四个点)。
- 对每个文本框,从原图上裁剪出对应区域,做仿射变换校正为水平矩形。
- 将裁剪后的图输入方向分类模型,判断是否需要旋转180度,如果需要就旋转。
- 把旋转后的图输入识别模型,逐个输出文字内容和置信度。
示例代码里把这个流程写成了一个循环:
std::vector<std::vector<std::vector<int>>> boxes; det.Run(img, &boxes); // 第一步:检测出所有文本框 for (int i = 0; i < boxes.size(); i++) { cv::Mat crop_img = GetRotateCropImage(img, boxes[i]); // 第二步:裁剪+校正 if (cls != nullptr) { int label = 0; cls->Run(crop_img, &label); // 第三步:判断是否旋转 if (label == 1) { // 1 表示需要旋转180度 cv::rotate(crop_img, crop_img, cv::ROTATE_180); } } std::string text; float score = 0.0; rec.Run(crop_img, &text, &score); // 第四步:识别文本 // 此时text就是识别出来的中文字符串 }这里有个值得注意的点:PaddleOCR把文本框用四个顶点坐标表示,顺序是左上的顺时针。如果你需要把识别结果和原图位置对应起来(比如做框选可视化),注意boxes存储的是经过缩放后的坐标,如果原图在预处理时被resize了,需要按比例映射回去。
3.4 后处理:识别结果的坐标和置信度
识别完成后,OCRResult里会带每个文本框的坐标、识别文本和置信度。实际开发中我通常会把"置信度低于0.6"的结果过滤掉,因为这类结果往往是检测框里的背景噪声或非文字区域。这个阈值可以根据你的业务场景调整:文档扫描件可以提到0.7,自然场景截图可以降到0.5。
另外,PaddleOCR会把所有文本框的识别结果按从上到下、从左到右的顺序输出,但并不意味着这个顺序一定符合阅读顺序。如果你的场景需要按段落顺序排列,可能需要额外做文本框的聚类排序,这属于后处理的高级话题,这里先提个醒。
4. 模型文件处理技巧:目录结构、格式和那个最坑的乱码问题
标题里特地写了"附模型文件处理技巧",这一节应该说是很多人的痛点。模型从下载到能跑,中间藏着好几个坑。
4.1 推理模型和训练模型不是一回事
PaddleOCR官方发布页面(GitHub Releases)提供了推理模型(inference model)和训练模型(trained model)两种下载。这两个名字看着像同一个东西,实际上格式完全不同,不能混用。
推理模型是经过PaddleSlim裁剪和导出工具转换后的模型,专门给paddle_inference预测库用的,文件后缀是.pdmodel和.pdiparams;训练模型是可以继续训练或微调的,通过model.ckpt保存。如果你下载了训练模型,C++版直接报错加载失败。这个坑我身边至少有三个同事踩过,因为官方发布页面把两种模型放在同一场合,一不留神就点错了。
判断下载的是不是推理模型的快速方法:解压后目录里应该有inference.pdmodel和inference.pdiparams两个文件,或者model.pdmodel和model.pdiparams,只看到model.ckpt.meta之类的,那就是训练模型,重新下载。
4.2 模型目录摆放的三个子模型
PaddleOCR的完整识别链路需要三个子模型:文本检测模型(det)、方向分类模型(cls)、文字识别模型(rec)。检测模型负责找文字在哪里,分类模型负责把倒着的字转正,识别模型负责把转正的字变成字符串。
我在项目里的目录结构是:
inference/ ├── det/ │ ├── inference.pdmodel │ ├── inference.pdiparams │ └── inference.pdiparams.info ├── cls/ │ ├── inference.pdmodel │ ├── inference.pdiparams │ └── inference.pdiparams.info └── rec/ ├── inference.pdmodel ├── inference.pdiparams └── inference.pdiparams.info注意.pdmodel是模型结构文件,.pdiparams是模型权重文件,.pdiparams.info是权重信息的元数据文件。有些精简版的预测库不读.info文件,但建议还是保留,因为有些版本的预测库会校验文件完整性。
4.3 模型加载失败和"no text detected"的排查链路
模型加载失败是最常见的报错,它通常长这样:PaddleOCR init failed: Cannot load model from infer_model directory或者could not create a primitive descriptor for: softmax。
我总结了一套排查链路,按顺序检查能覆盖90%的问题:
第一步,确认模型文件存在且路径正确。检查程序工作目录,如果是通过快捷方式启动的,工作目录可能和exe目录不一致,建议在代码里把模型路径写绝对路径或者用GetModuleFileName动态获取exe所在目录再拼接。
第二步,确认模型格式正确。打开目录看有没有.pdmodel和.pdiparams,如果只有__model__和__params__,说明下载的是旧版格式,需要重新下载新版推理模型。
第三步,确认预测库版本和模型版本兼容。PP-OCRv4的模型需要paddle_inference 2.4以上才能跑,用2.3的预测库去加载就会报"op version mismatch"之类的错误。解决办法是升级预测库版本,或者改用PP-OCRv3模型(2.3兼容)。
第四步,运行时的日志输出。PaddleOCR启动时会输出模型加载日志,注意看Load model from ...这一行,如果显示加载成功但后续识别不到任何文字,问题多半在图像预处理环节而不是模型环节。
"no text detected"这个报错非常误导人,它看起来像是模型没加载,实际上恰恰相反,模型加载成功了,但检测模型在原图上没找到任何文字区域。这种情况通常是:图像过大导致检测框被过滤,或者图像对比度太低、文字太小。解决办法是把det_db_box_thresh参数调低(比如从0.3调到0.1),或者提高图像分辨率后再跑一次。
4.4 识别结果乱码:UTF-8和GBK的编码战争
这里真是血泪教训。我第一次在Windows控制台跑通识别后,输出结果全是乱码,心想模型是不是坏了。后来才发现完全不是模型的问题,是编码问题。
PaddleOCR的识别结果默认是UTF-8编码的字符串,而Windows控制台默认用GBK(代码页936)显示,直接std::cout打印出来必然乱码。解决思路有两个:一是把控制台代码页切换为UTF-8,在程序开头调用SetConsoleOutputCP(CP_UTF8);二是把识别结果转成GBK再打印,用MultiByteToWideChar和WideCharToMultiByte配合实现。
如果你在用Qt开发界面,Qt的QString默认是UTF-16内部表示,从PaddleOCR拿到UTF-8字符串后直接用QString::fromUtf8(text.c_str())转换,显示就没问题。最怕的是拿到UTF-8字符串后直接转QString构造,那样也会乱码。
这里额外分享一个小技巧:在把识别结果写入文件(比如txt)时,尽量统一用UTF-8格式,带上BOM,这样Windows记事本打开能正常显示,Excel导入也没问题。如果写入的是GBK编码,在Linux环境下打开乱码。考虑到现在跨平台协作越来越多,UTF-8是更安全的选择。
4.5 如何挑选合适的模型
PaddleOCR官方提供多种推理模型,按体积和精度分为两大类:mobile版(轻量级)和server版(高精度)。mobile版模型体积小、推理快,适合部署在客户端,CPU上单张图识别大约100ms;server版模型体积大、精度略高,适合服务端部署。
| 模型系列 | 体积 | 精度 | 推荐场景 |
|---|---|---|---|
| PP-OCRv4 mobile | det约4.7M,rec约10M | 中高 | 桌面工具、嵌入式设备 |
| PP-OCRv4 server | det约80M,rec约70M | 高 | 服务端高精度识别 |
| PP-OCRv3 mobile | 与v4相近 | 中 | 兼容老版预测库(2.3)时使用 |
这里我特别说一句,PP-OCRv4的识别精度比v3提升了不少,尤其在中文长文本和复杂背景场景下。但如果你的paddle_inference预测库版本是2.3.0以下,还是老老实实用v3,因为v4模型需要2.4以上才能加载。另外,v4的mobile模型在很多CPU上都能跑出不错的性能,我建议你优先试试mobile,不够用再换server。
5. 性能调优与便携打包:让程序在其他机器上也能跑
模型能跑通、不乱码,这只是万里长征走完一半。真正要交付给别人用,性能优化和打包分发才是下半场。
5.1 CPU线程数和MKLDNN的取舍
PaddleOCR的C++接口提供set_cpu_math_library_num_threads方法来设置CPU线程数。线程数越多,推理越快,但不是线性的。我实测4线程到8线程提升明显,8线程到16线程几乎没什么变化,反而线程切换开销增加了。桌面应用建议设置4~6线程,服务端可以设置到8~12。
MKLDNN开启后,CPU推理速度能提升30%~50%,但有两类模型不适用:一是形状特别不固定的模型(比如识别模型输入宽度动态变化),MKLDNN需要重新构图,反而变慢;二是超低精度模型(INT8量化),MKLDNN的收益已经很有限。我的做法是在初始化时开MKLDNN,跑一组测试图对比速度和精度,如果精度下降明显就关掉。
5.2 GPU推理:什么时候值得用
如果你的目标机器有NVIDIA显卡(显存2GB以上),可以考虑GPU推理。PaddleOCR的GPU推理需要下载GPU版预测库,体积要比CPU版多几百MB,而且程序运行依赖CUDA和cuDNN的动态库,交付时要把这些一起带上,体积直奔1GB。所以说实话,桌面工具(尤其是面向普通用户的)用GPU的成本很高,收益却不明显。我只有在做服务端OCR的时候才会用GPU,桌面端统一用CPU+MKL-DNN。
5.3 便携打包:把dll带全是核心
C++程序交付最麻烦的就是dll依赖。PaddleOCR推理涉及到的dll包括但不限于:paddle_inference.dll(预测库核心)、paddle_fluid.dll(旧版叫这个)、mkldnn.dll、iomp5md.dll(Intel OpenMP运行时)、opencv_world340.dll(OpenCV库)。这些dll少一个,程序在客户机器上就会闪退或者报"无法定位程序输入点"。
我总结的打包清单如下:
- paddle_inference预测库解压目录下的
paddle/lib/*.dll - OpenCV的
bin目录下的opencv_world*.dll - 三个模型子目录
det/、cls/、rec/ - Visual C++运行库(VC_redist.x64.exe),如果客户机器没装过,需要先装一次
一个亲测有效的技巧:在Visual Studio里把工程设置为"Release + x64",然后生成的exe在同目录下放一个inference文件夹(模型目录),再把所有dll复制到exe同目录,用Dependencies工具扫描一下exe的依赖,看有没有标红的缺项。Dependencies是一个开源工具,比系统自带的dumpbin直观,一眼能看出缺哪些dll。
另外,如果你希望体积再小一点,可以尝试UPX压缩exe和dll,但注意UPX可能被杀毒软件误报,交付前要测试。
5.4 客户机器上验证的注意点
我通常会在交付前找一台干净Windows机器做验证:没有VS运行库、没有CUDA、没有Python。把打包好的文件夹整个拷过去,双击exe看能不能正常跑。如果提示VCRUNTIME140.dll丢失,说明VC++运行库没带上;如果提示paddle_fluid.dll找不到,说明路径不对,检查exe和dll是否同一目录。
这里还有一个容易被忽略的问题:PaddleOCR的预测库在首次运行时会在C:\Users\<用户名>\.paddle目录下创建缓存,如果客户机器是受限账户(没有写入权限),第一次会报错。解决办法是设置环境变量PADDLE_HOME指向程序目录下的一个可写文件夹,比如./.paddle,这样就不会依赖用户目录的写入权限了。
6. 往自己项目里集成时的一些额外提醒
如果你已经走通了demo,接下来要把OCR能力集成到现有项目里,这里有几个我吃了亏的经验,补救成本很高,提前避掉能省事很多。
6.1 避开项目里的符号冲突
PaddleOCR的C++代码用了大量的全局命名空间和宏定义,其中最典型的是它的CHECK和LOG宏,和Google的glog是同一个来源。如果你的项目里也有glog或者类似日志库,编译时会出现宏重定义的错误。前置解决办法是:把PaddleOCR的inference核心代码封装成一个独立的C++库(dll),只暴露一个统一的识别接口,不要直接把源码放进主工程编译。封装层用extern "C"导出几个函数,比如InitOcrEngine、OcrPredict、ReleaseOcrEngine,这样对主工程来说就是一个纯净的C接口,不会污染命名空间。
这个方法还有一个额外好处:以后如果换OCR引擎(比如换其他家的),只需要重写封装层,主工程代码完全不用动。
6.2 内存和线程模型
PaddleOCR的推理过程会占用较多内存,模型加载完成后基础占用大约300~500MB。如果频繁调用识别,建议复用同一个OCR对象,不要每次都重新初始化。重新加载模型的耗时大概是几百毫秒到一两秒,而且会重复申请内存,可能造成内存碎片。
另外,PaddleOCR的推理接口不是线程安全的。如果你的程序是多线程的(比如同时处理多个页面),每个线程要单独持有自己的OCR实例,不能共享同一个实例调识别接口。我曾经在这上面吃过亏:两个线程同时调识别,偶尔会崩溃,排查了半天发现是共享了OCR对象。
6.3 识别结果的工程化处理
识别后的字符串往往是原始文本,实际业务中还需要清洗。举例来说,识别发票上的金额时,PaddleOCR可能输出"¥1,234.56"也可能输出"1234.56",小数点、逗号的处理需要自己写规则。识别身份证号码时,字母O容易误识别为数字0,数字0也可能误识别为字母O,这些都需要业务层做校验纠正。
我在项目里专门写了一个后处理模块:合并空格、纠正常见OCR误识别(PaddleOCR训练模型自带的字典可能不认识特殊字符)、按置信度过滤后再落库。这一步看起来不复杂,但对最终用户的体感提升非常明显。
7. 结语:一个过来人的几点建议
写这篇的时候,我把当初踩坑的过程完整回忆了一遍,最有感触的一点是:PaddleOCR C++版的文档虽然没有Python版那么丰富,但源码质量和整体设计是经得起推敲的。遇到问题不要先怀疑框架,按编译环境、模型格式、预处理流程、编码转换的顺序排查,90%的问题都能定位。
如果让我给刚开始尝试的人一个建议,那就是不要卡在编译环境上太久。先按我上面给的版本组合把demo跑起来,哪怕识别效果不是最好的,至少证明整条链路是通的。之后再换模型、调参数、做集成,每一步优化都有清晰的参照。
最后说一个很小但很实用的技巧:PaddleOCR在Windows下运行时,日志里会输出大量调试信息,如果你觉得日志太占控制台空间,可以调用paddle::AnalysisConfig里的DisableGlogInfo()方法关闭日志输出。这个开关不影响识别结果,但能让你的程序在正式交付时输出干净简洁的信息,客户看起来也更专业。
这大概就是我在PaddleOCR C++版上从零到一的全过程。希望这篇文章能帮你少走几个弯,顺利把本地文字识别跑起来。