简短结论: 这不是「rec_only 算法不对」,而是 Paddle 2.x 在 Apple Silicon 上的 CPU 推理栈不成熟 ;在你们场景里 ocr(det=False) 恰好是最常用、又最走「纯 rec 推理」那条路径 ,所以表现为「一用车牌整牌识别就挂死/无输出」。项目里记录的是实测现象 + 工程规避,仓库里没有 Paddle 官方的单一根因说明。
1. 项目里实际观察到什么
文档和代码里的依据主要是:
| 现象 | 来源 |
|---|---|
Mac 上 ocr(det=False) 易挂死或长时间无输出 |
requirements.txt、测试报告 §2 |
因此 macOS 曾把 predict/det_rec 放在 rec_only 前面(现已统一 rec_only) |
历史 _plate_roi_infer_attempts |
加了 rec_only 预热 (_warmup_plate_roi_infer) |
注释:避免 Mac 上 det=False 首次挂死 |
| Mac 上 限制 OpenMP 线程 | OMP_NUM_THREADS=1、MKL_NUM_THREADS=1 |
也就是说:在 2.6 + 2.8 + Apple Silicon 上,纯识别路径比「先 det 再 rec」更不稳定;和「车牌必须用 rec_only」直接冲突。
2. 根本原因可以分成几层(由底到表)
(1)底层:Paddle 2.x 在 Apple Silicon 上的推理引擎问题(主因)
社区里大量反馈(M1/M2/M3):
- 调用
ocr()或predictor.run()后 进程卡死、CPU 打满、无返回 - 问题常在 Paddle 推理内核,而不在业务代码
- Linux + NVIDIA 正常,Mac ARM 复现 → 典型 ARM64 CPU 推理适配/算子 问题
Paddle 侧也有反馈:升到 3.x beta/正式版后 Mac 卡死缓解或消失(与你们升 3.3.1 的方向一致)。
所以根因大类是:2.x 时代 Mac ARM 的 Paddle CPU 推理不够可靠,不是 YOLO ROI 或车牌逻辑写错。
(2)运行时:多线程 / OpenMP 在 ARM Mac 上易死锁或「像挂死」
你们代码里已有注释:
19:22:yolo_infer/utils/ocr_processor.py
# Mac/ARM 上 Paddle 多线程首次推理易长时间无输出,默认限制 OpenMP 线程数
if sys.platform == "darwin":
os.environ.setdefault("OMP_NUM_THREADS", "1")
os.environ.setdefault("MKL_NUM_THREADS", "1")
在 2.x + 默认多线程 下,首次推理 常出现:
- 真死锁(永久无返回)
- 或极慢(几分钟像挂死)
det=False 的 第一次 rec 推理 就会触发这套初始化,所以车牌场景(几乎每次都 rec_only)特别容易踩中。
(3)路径差异:为什么偏偏说 det=False / rec_only?
在 PaddleOCR 2.x 里,调用形态不同,加载和执行的子图不同:
| 调用 | 实际做什么 |
|---|---|
ocr(det=True) / det+rec |
先 TextDetector ,再 TextRecognizer |
ocr(det=False) |
只跑识别模型,跳过检测 |
「挂死」并不 只 发生在 det=False(Mac 上完整 ocr() 也有大量卡死报告),但在你们项目里:
- 车牌链路依赖 rec_only(YOLO 已框好牌,不需要 OCR 再 det)
- 历史上 det+rec 有时还能跑通 ,rec_only 更容易挂/无输出
- 所以工程上记成:rec_only 与 Mac 2.x 冲突
可能机制包括(多为经验归纳,非单一官方结论):
- 纯 rec 路径 对输入宽高比更敏感(小 ROI、扩框后尺寸不规则),在 2.x ARM 上触发边界 bug
- lazy load :只初始化 rec 时,首次
run()在 ARM 上更容易踩线程/内存问题 - det+rec 误打误撞:先跑 det 换了一套算子/尺寸,有时能绕过 rec-only 上的 bug(所以曾用 det_rec 当回退)
(4)环境层:会放大成「挂死」(叠加因素)
这些不唯一指向 det=False,但 Mac 上很常见:
| 因素 | 影响 |
|---|---|
| Python 是 x86(Rosetta)而非 arm64 | Paddle 在 M 芯片上极易异常/卡死 |
| OpenCV 版本与 paddleocr 2.7 不匹配 | 有用户报告 CPU 99% 卡死 |
| MKL/OneDNN | 2.x Mac CPU 路径依赖这些库,ARM 支持不完善 |
3. 和后来 3.x 的 ShapeError 不是同一回事
| 问题 | 时代 | 性质 |
|---|---|---|
| Mac 2.x 挂死 / 无输出 | Paddle 2.6 + paddleocr 2.8 | 运行时 + ARM 适配 + 线程 |
| ShapeError scale 60 vs 16 | Paddle 3.x | 模型缓存/格式与 3.x 不兼容 |
升 3.x 是为解决 Mac 稳定性并与生产对齐;ShapeError 是 models/paddlex 混装旧模型 的另一层问题。
4. 为何 3.x 上仍保留预热和单线程
3.x 改善了很多 Mac 卡死,但你们仍保留:
- darwin 上单线程
_warmup_plate_roi_infer(rec_only 预热)- 关 UVDoc、关 mkldnn
说明 ARM + 小 ROI + 首次推理 在工程上仍需谨慎,不完全等于 2.x 的「必挂」。
一句话
根本原因: Paddle 2.x 在 Apple Silicon 上的 CPU 推理(含线程/OpenMP、算子/预测器实现)不成熟 ,社区与你们实测表现为调用 OCR 后 卡死或长时间无输出 。
为何和 rec_only 冲突: 车牌场景 必须走 det=False ,这条路径在 2.x Mac 上 最不稳定 ;而 det+rec 曾作为不稳定环境下的绕行,所以被记成「rec_only 易挂死」------不是整牌识别思路错了,是 2.x Mac 跑不动这条 Paddle 路径。
升 3.3.1 + paddleocr 3.6.0 、native arm64 Python、限制线程并预热,是针对这类根因的工程解法;若仍遇 3.x ShapeError,再单独修 模型缓存,那是下一层问题。