基于 PP-OCR 的车牌识别:从数据整理到土星云 SE110S 系列边缘部署

摘要

车牌识别的难点不在"认出字符",而在"先把梯形、逆光、泥污的车牌变成一张规整的单行文本图"。PP-OCR 用 DB 检测给出四点框、用 SVTR + CTC 输出整串文字,检测与识别解耦,字典还能裁剪到车牌字符集,天然适合边缘设备。本文从模型原理讲到数据收集、标注清洗、微调训练、ONNX 转 BModel,最后给出在土星云 SE110S-WA32、SE110S-WB16、SE110S-WC8 三款设备上的部署方案与实测性能。

一、车牌识别难在哪里

地库出入口、园区道闸、加油站、地磅,这些场景的输入条件是全部门禁场景里最难的一档。相机装在 1.5 米高、俯角 15 度,车头歪着停进来,车牌在画面里就是一个被透视压扁的梯形;地库只有一盏补光灯,出入口却正对太阳;雨天车牌挂着水珠,跑一趟泥路回来字符笔画糊成一团。

传统做法是"颜色分割 + 边缘检测 + 字符切分 + 模板匹配":按蓝、黄、绿三种底色找候选区域,垂直投影切字符,再逐个模板匹配。这条路在实验室能跑到 95%,一到现场就崩------颜色分割对逆光和补光灯极其敏感,双排黄牌和粘连字符切不开,模板匹配对字体差异几乎零容忍。

现在的主流做法是把车牌当成"一类特殊的文本",用 OCR 端到端解决。PP-OCR 是开源方案里最成熟、最轻量、中文支持最好的系列之一,本文用它做完整落地。

二、PP-OCR 的基础知识

2.1 三段式流水线:检测、方向分类、识别

PP-OCR 不是一个模型,而是一组流水线组合:文本检测负责"哪里有字",方向分类负责"字是不是倒的",文本识别负责"字是什么"。

检测用的是 DB(可微二值化)分割思路。主干输出特征金字塔,head 同时预测概率图和阈值图,用可微的二值化近似让整个流程可端到端训练;推理时对二值图做连通域分析,再用 Vatti 裁剪把区域收缩成多边形。它输出的是任意四边形,这一点对车牌格外重要------四个角点直接就是透视矫正的参数。

方向分类是一个 0°/180° 的两分类小模型。车从反方向开过、或者识别结果整串乱码时,用它兜底。

识别部分是 CRNN / SVTR 结构:主干 + neck + 头,用 CTC 解码。输入统一缩放到高度 48、宽度按比例取 320 或 640,输出时间步 × 字符集 的概率矩阵,CTC 合并重复字符并去掉 blank,得到整串结果。通用字典 ppocr_keys_v1.txt 有 6600 多类,车牌场景可以裁剪到 70 类以内。

2.2 车牌和通用 OCR 的三点不同

单行、固定长宽比。 车牌是 440:140(约 3.1:1)的单行文本,检测框出来之后必须先做透视矫正,把梯形拉成标准矩形再送识别,这一步对最终精度贡献最大。

字符集小、规则强。 省简称 31 类,字母去掉 I 和 O,加上 10 个数字与"挂、学、警、使、领"等后缀,实际可用字符只有 68 类左右。字典越小,CTC 解码越稳,也越容易用规则做后处理校验。

号牌种类多。 蓝牌(小型车)、黄牌(大型车与挂车)、绿牌(新能源,8 位)、白牌(警车)、黑牌(使领馆),底色本身就是可以和识别结果交叉校验的信息。

图 1 PP-OCR 车牌识别的三段式流水线与矫正过程

三、应用场景

  • 停车场出入口与道闸计费。 车速低、要求单帧 50ms 内出结果,识别直接决定抬杆和计费;夜间补光和雨天是最容易出错的工况。
  • 园区、厂区车辆通行。 白名单放行、访客登记、货车与危化品车管控,和道闸、诱导屏联动,要求断网可用。
  • 加油站与洗车店。 车牌绑定会员,识别错误会直接变成扣费纠纷,宁可多拍一帧也不要错一位。
  • 地磅与物流园区。 车辆停稳后识别,车速接近零,是精度最好的场景,但对双排黄牌和挂车号牌要求高。

这些场景的共同点是机位固定、车速低、要求离线可用、与业务系统强耦合。把模型放在本地而不是云端,是默认选择。

四、算法优势

  • 检测与识别解耦。 换更轻的检测模型不动识别精度,换字典不动检测,链路里每一段都能单独回归测试。
  • 中文一体识别,不需要切分字符。 CTC 直接输出整串,省掉了最脆弱的字符切分环节。
  • 四点检测天然适配透视矫正。 DB 给出的四边形直接当矫正参数用,斜停、俯拍都能拉正,这是车牌识别比通用 OCR 更容易做到高精度的根本原因。
  • 模型足够小。 PP-OCRv4 移动端检测模型约 5MB、识别模型约 10MB,是少数能在边缘 SoC 上跑到实时的中文 OCR 方案。
  • 字典可裁剪、可校验。 识别完整串后再用"省份简称在表内、位数正确、字母不含 I/O"这类规则过滤,能拦掉大部分"看着像但不可能存在"的结果。
  • 量化友好。 检测是分割任务、识别是分类任务,两者对 INT8 都不敏感,实测精度损失可控制在 0.5% 以内。

五、车牌数据的收集与整理

公开数据集上跑 95% 的模型,直接搬到自己的现场常常掉 10 个点以上。差距全部来自数据。

5.1 三类数据来源

现场抓拍是主力。 在真实机位、真实光照下连续采集,覆盖早中晚、晴雨、地库与地面,样本量占七成以上。公开数据集做冷启动 ,CCPD 这类停车场车牌数据可以直接用来预训练。合成数据补长尾,用脚本把号牌版式贴到车尾、道路背景上,随机做透视、模糊、亮度、噪声增广,专门补绿牌、挂车、特殊后缀这些现场采不到的类别。

5.2 采集要覆盖的维度

按"光照 × 角度 × 号牌种类 × 车况"排布,每个格子至少 300~500 张:

|--------|--------------------------------|
| 维度 | 取值 |
| 光照 | 白天顺光、逆光、夜间补光、地库低照、雨雾 |
| 角度 | 正对、左右偏移 ±30°、俯角 5~30°、倾斜 ±15° |
| 号牌 | 蓝牌、单排黄牌、双排黄牌、绿牌(8 位)、白牌、黑牌 |
| 车况 | 干净、泥污、水珠、划痕、贴膜、遮挡(拖钩、备胎) |

逆光和低照要单独成组,这两类的样本效率最高:多拍 200 张夜间逆光,比多拍 2000 张白天顺光有用。

5.3 标注规范

检测用四点标注,顺序固定为左上、右上、右下、左下,紧贴字符外沿而不是车牌边框------把边框标进去,矫正出来的图会多一圈底边,识别端反而更难。识别标签不带空格和分隔点,中文一律用省份简称,字母数字大写,无法辨认的样本直接不标,不要"猜着标",猜出来的标签会稳定地教坏模型。

5.4 清洗:先过滤质量,再去重

清晰度、亮度、框高都要设阈值。以下是最小可用的过滤逻辑:

import cv2
import numpy as np

def plate_quality(img, quad, min_h=24, lap_th=45):
pts = np.array(quad, np.float32)
h = max(np.linalg.norm(pts0 - pts3), np.linalg.norm(pts1 - pts2))
if h < min_h: # 车牌太小,字符不足 8 像素高
return False, "too_small"
x, y, w, hh = cv2.boundingRect(pts)
crop = imgmax(0, y):y + hh, max(0, x):x + w
if crop.size == 0:
return False, "empty"
gray = cv2.cvtColor(crop, cv2.COLOR_BGR2GRAY)
if cv2.Laplacian(gray, cv2.CV_64F).var() < lap_th:
return False, "blur" # 运动模糊 / 失焦
if gray.mean() < 32 or gray.mean() > 226:
return False, "exposure" # 过暗或过曝
if gray.std() < 18:
return False, "low_contrast"
return True, "ok"

去重同样重要。连续帧之间车牌几乎不变,同一辆车在同一个机位可能被采到几百次。做法是先把车牌矫正成 440×140 的统一尺寸,再算感知哈希或像素差,相似度超过阈值只保留最清晰的一张。

5.5 划分与合规

训练、验证、测试必须按车 和场景划分,不能按帧随机划分:同一辆车的连续帧同时进训练集和测试集,指标会虚高十几个点。测试集要单独留一批难例(夜间、逆光、绿牌),用来观察上线后的短板。

车牌属于个人信息,采集前要告知并取得授权;训练数据优先用脱敏后的后六位,对外展示时做打码,并且能按需求删除指定样本。

六、模型训练与调优

6.1 从预训练权重微调

从零训练中文 OCR 需要千万级样本,实际做法是加载 PP-OCRv4 的官方预训练权重微调:检测模型冻结主干、只训 head,学 20~50 个 epoch 就能适配自己的机位与画面风格;识别模型换掉字典、重新初始化 head 的最后一层,微调 50~100 个 epoch。

识别模型微调(PaddleOCR 配置示例)

python3 tools/train.py -c configs/rec/PP-OCRv4/ch_PP-OCRv4_rec.yml \
-o Global.pretrained_model=./pretrain/ch_PP-OCRv4_rec_train/best_accuracy \
Global.character_dict_path=./dict/plate_dict.txt \
Train.dataset.data_dir=./dataset/rec/train \
Train.loader.batch_size_per_card=128 \
Optimizer.lr.learning_rate=0.0005

6.2 关键超参

|--------|-------------------|---------------------|
| 项目 | 检测(DB) | 识别(SVTR + CTC) |
| 输入 | 1×3×960×960 | 3×48×320(可按需 640 宽) |
| 学习率 | 主干 1e-5,head 1e-3 | 主干 1e-5,head 5e-4 |
| Batch | 8~16 | 128~256 |
| Epoch | 20~50 | 50~100 |
| 增强 | 随机裁剪、透视、亮度 | 亮度对比度、模糊、噪声、少量透视 |
| 字典 | --- | 68 类车牌字符集 |

车牌不做大幅旋转和翻转:旋转超过 ±15° 的样本,矫正阶段本来就会处理掉;水平翻转会造出反着写的车牌,属于纯噪声。

6.3 评估与常见问题

检测看 Hmean(IoU=0.5),识别看整牌准确率------整串全对才算对,字符准确率会掩盖"只错一位"这种最要命的问题。分层报告:按夜间/逆光/绿牌/挂车分组各看一遍。

三个高频问题:绿牌识别差 ,通常是样本少,补两千张后基本解决;省份简称混淆 (京/津、苏/浙),一般是简称样本不均衡,在采样上做平衡;形近字混淆(0/O、1/I、8/B),靠后处理规则加定向补样本解决。

七、模型转换

7.1 Paddle → ONNX

部署阶段固定形状,不用动态 batch 和动态分辨率,编译器的优化空间更大:

paddle2onnx --model_dir ./ch_PP-OCRv4_det_infer \
--model_filename inference.pdmodel \
--params_filename inference.pdiparams \
--save_file det.onnx --opset_version 11 --enable_onnx_checker True

paddle2onnx --model_dir ./ch_PP-OCRv4_rec_infer \
--model_filename inference.pdmodel \
--params_filename inference.pdiparams \
--save_file rec.onnx --opset_version 11

导出后用 ONNX Runtime 跑同一张图,与 Paddle 输出对比:检测看概率图的差异,识别看整串文本是否一致。

7.2 ONNX → BModel

BModel 是设备端推理使用的模型格式,流程是 ONNX → MLIR → 量化标定 → BModel,在 x86 Linux 主机上完成。芯片参数:SE110S-WA32 用 bm1684x,SE110S-WB16 用 bm1688,SE110S-WC8 用 cv186x,三款设备均为单芯方案。

检测模型:输入 960×960,均值 123.675/116.28/103.53、方差 58.395/57.12/57.375

model_transform.py --model_name ppocr_det \
--model_def det.onnx --input_shapes \[1,3,960,960] \
--mean 123.675,116.28,103.53 --scale 0.01712475,0.017507,0.01742919 \
--pixel_format rgb --channel_format nchw --mlir ppocr_det.mlir

run_calibration.py ppocr_det.mlir --dataset ./cali_det --input_num 200 -o det_cali

model_deploy.py --mlir ppocr_det.mlir --quantize INT8 --chip bm1688 \
--calibration_table det_cali --model ppocr_det_int8.bmodel

识别模型同理,输入按 3×48×320 的固定宽度编译,宽度 640 的模型单独编译一份,供长车牌或双排黄牌使用。

7.3 校准集与精度对齐

INT8 校准集建议 200~500 张,必须覆盖夜间、逆光、绿牌、挂车这几类难例,且不能和测试集是同一批。转换完成后不要只看"像不像",而是拿同一批图分别跑 ONNX 和 BModel:

• 检测:比较概率图,以及四点框的 IoU,FP16 应 ≥0.98,INT8 应 ≥0.95;

• 识别:比较整串文本一致率,FP16 应在 99% 以上,INT8 掉点控制在 0.5% 以内;

• 端到端:用固定置信度阈值跑一遍测试集,看放行数、误识数有没有变化。

如果 INT8 的整牌准确率明显下滑,先补校准集的难例覆盖面;仍不达标就退回 FP16,不要用"多跑几次"来凑指标。

八、三款 SE110S 设备部署与性能

8.1 设备与平台对应

|-------------|----------|---------------|
| 设备 | 计算平台 | 定位 |
| SE110S-WA32 | bm1684x | 集中式多路出入口、道闸集群 |
| SE110S-WB16 | bm1688 | 中等规模、多相机汇聚 |
| SE110S-WC8 | cv186x | 末端单机、单出入口 |

8.2 理论推理耗时

下表整理自 sophon-demo PP-OCR 例程 的 bmrt_test 结果,数值为各 stage calculate time 之和,测试对象是单张图片,单位为毫秒。参考例程的测试平台 SE7-32、SE9-16、SE9-8 分别对应 bm1684x、bm1688、cv186x 平台,也就是本文的 WA32、WB16、WC8。

|-------------|--------|------------|------------|------------|
| 设备 | 精度 | 检测(ms) | 识别(ms) | 合计(ms) |
| SE110S-WA32 | FP32 | 76.25 | 20.65 | 96.90 |
| SE110S-WA32 | FP16 | 18.18 | 6.07 | 24.25 |
| SE110S-WA32 | INT8 | 13.75 | 6.55 | 20.30 |
| SE110S-WB16 | FP32 | 245.03 | 117.84 | 362.87 |
| SE110S-WB16 | FP16 | 60.73 | 27.27 | 88.00 |
| SE110S-WB16 | INT8 | 28.58 | 20.73 | 49.31 |
| SE110S-WC8 | FP32 | 244.80 | 117.66 | 362.46 |
| SE110S-WC8 | FP16 | 60.50 | 27.23 | 87.73 |

规律很明确:FP32 在三款设备上都不适合实时业务 ,WB16 和 WC8 的 362ms 会让道闸排起队;FP16 是精度兜底 ,WA32 的 24.25ms 仍然宽裕,WB16 与 WC8 的 88ms 必须算进并发预算;INT8 才是常用档位,WA32 合计 20.30ms,WB16 合计 49.31ms,比 FP16 快约 44%,而且三款设备都是单芯方案,不需要额外配置就能跑满。参考例程未提供 WC8 的 INT8 数据,因此 WC8 建议以 FP16 交付,靠降帧来保证通行节奏。

8.3 端到端耗时

模型推理只是链条中的一段。例程在设备端统计的四段时间如下,单位毫秒/张,检测与识别串联,合计为四段之和:

|-------------|---------|--------|---------|--------|---------|--------|
| 设备 | 配置 | 解码 | 预处理 | 推理 | 后处理 | 合计 |
| SE110S-WA32 | INT8 单批 | 6.39 | 1.44 | 3.34 | 6.26 | 17.43 |
| SE110S-WA32 | FP16 单批 | 15.12 | 1.44 | 4.16 | 6.47 | 27.19 |
| SE110S-WB16 | INT8 单批 | 19.09 | 3.49 | 7.62 | 8.65 | 38.85 |
| SE110S-WB16 | FP16 单批 | 15.74 | 3.51 | 14.48 | 8.44 | 42.17 |
| SE110S-WC8 | FP16 单批 | 15.05 | 3.49 | 14.46 | 8.72 | 41.72 |

一个值得注意的细节是:解码和预处理加起来能占到总耗时的四到六成。车牌识别对分辨率敏感,1280×720 的帧解出来就是 2.6MB 的 BGR 数据。工程上的常规做法是"两段式":先跑一个极轻的车辆检测或运动检测,只在有车的帧上做 OCR,实际触发率通常只有 10%~20%。

图 2 三款 SE110S 设备上的车牌识别耗时

8.4 并发与选型

按 25fps 取流、每辆车处理 5 帧、单帧 39ms 估算:WA32 用 INT8 可以带 8~12 路出入口,WB16 用 INT8 带 4~6 路,WC8 用 FP16 带 1~2 路,实际以现场压测为准。选型建议很直接:8 路以上集中部署选 WA32 ,算力和内存都有余量,可以同时跑车牌识别和车辆检测;中等规模选 WB16 ,把 INT8 用到位------理论推理比 FP16 从 88.00ms 降到 49.31ms(快约 44%),端到端从 42.17ms 降到 38.85ms(快约 8%);末端单机、单出入口选 WC8,务必把 FP16 的整牌准确率对齐到位,必要时对同一辆车多抓一帧做投票。

8.5 部署链路与命令

设备端按这个顺序组织:取流 → 硬件解码 → 车辆/车牌检测 → 四点透视矫正 → 方向分类 → 识别 → 规则校验 → 抬杆与留痕。识别模型只负责出字,检测和业务逻辑各自独立,替换其中一段不影响其余环节。

Python 例程:走整批图片

python3 python/ppocr_system_opencv.py \
--input ./datasets/train_full_images_0 \
--bmodel_det models/ch_PP-OCRv4_det_int8_1b.bmodel \
--bmodel_rec models/ch_PP-OCRv4_rec_int8_1b.bmodel \
--device_id 0

C++ 例程:走 SoC 硬件解码,适合接摄像头

./cpp/ppocr_bmcv.soc --bmodel_det=... --bmodel_rec=... --input=./test.mp4

跑通后接摄像头,重点核对四件事:车牌框是否稳定、矫正后的图是否端正、同一辆车多帧结果是否一致、低置信样本有没有被拦住。

九、测试样例展示

下图展示的是端到端输出的可视化样式:绿色四边形是检测模型输出的文本区域,右侧"修正后"是透视变换的结果,也就是识别网络的真实输入。样张由脚本按真实号牌版式合成,覆盖蓝牌、绿牌、黄牌与挂车号牌,标注文本即样张真值。

图 3 车牌识别结果可视化

四张样例的置信度都在 0.96 以上,说明在正对、轻微倾斜的工况下,这条链路有充足的余量。真正的考验在难例上:

图 4 难例样张与处理策略

六个难例分别对应六类现场问题。前五类的置信度仍然维持在 0.92 以上:逆光过曝靠亮度归一化解决,低照和雨滴靠补样本解决,倾斜靠四点矫正解决,绿牌靠补位与定向采样解决。最后一张泥污样本只有 0.412,属于典型的**"错一个字"场景**------省简称和后缀都对,中间数字被泥点糊住。

这类结果不能放行。做法是设两级阈值:置信度高于 0.90 直接放行;落在 0.60~0.90 之间用同一辆车的其他帧投票,或等车停稳后补拍一帧;低于 0.60 转人工或要求刷卡。车牌识别的错误代价是不对称的------多识别一次几乎没成本,错放一辆车可能就是一整天的计费纠纷。

十、几条工程经验

1. 矫正比换模型更有效。 四点框歪了,换多大的主干都救不回来,先把透视变换调对。

2. 标注别贴车牌边框。 边框标进去,矫正图会多一圈底边,识别端要多学一堆无用像素。

3. 按车划分数据集。 连续帧泄漏是精度虚高最常见的原因,比模型选错还致命。

4. 字典越小越稳。 剪到 68 类后,CTC 的搜索空间小了一个数量级,形近字错误明显下降。

5. 后处理规则要留余地。 省份简称白名单、位数校验能拦错,但遇到新号段时要能快速放开,别把规则写死。

6. 低置信一律不放行。 阈值宁可偏高,识别失败的后备方案是刷卡,而不是赌一把。

十一、总结

PP-OCR 把车牌识别拆成了清晰的四步:DB 检测给出四点框,透视矫正把梯形拉成标准图,SVTR + CTC 输出整串字符,规则校验兜住"不可能的结果"。检测与识别解耦之后,字典可以裁剪到 68 类,模型总量控制在 15MB 量级,这让它成为少数能在边缘设备上稳定实时运行的中文 OCR 方案。

部署侧,三款设备跑单张的端到端耗时为 WA32 17.43ms(INT8)、WB16 38.85ms(INT8)、WC8 41.72ms(FP16)。WA32 适合 8~12 路集中部署,WB16 适合 4~6 路,WC8 适合末端单机。真正决定能带多少路的不是识别模型,而是解码、预处理和车辆检测的开销------先把触发率降下来,比堆算力省事得多。

落地顺序建议是:先用现场数据把矫正和字典调对,再补夜间和逆光的难例微调,然后做 INT8 对齐、用现场分布定置信度阈值,最后才考虑加并发。数据到位之后,这套方案的整牌准确率通常能稳定在 97% 以上。

相关推荐
zz_nj1 小时前
中兴微方案 MIFI
人工智能
打工仔折腾 AI1 小时前
鸿蒙移动端直连 Windows 远程桌面配置与性能调优实践
人工智能·windows·后端·python·华为·性能优化·harmonyos
AI_work1 小时前
43 号令下招投标全流程留痕分析:电子卷宗 15 年保存与投标人文档合规体系重构
大数据·人工智能·ai·重构
技灵AI1 小时前
AI 开箱视频怎么做?用创意开场带出商品展示
人工智能·prompt·aigc
王亚博_Abel1 小时前
AI 编程时代怎么选前端框架?同一个应用让 Agent 用 12 种框架各写 5 遍,我整理出这套选型方法
前端·人工智能·前端框架
低调小一1 小时前
客户端开发者学 Eino(01):从一次模型调用理解 Agent 架构
人工智能·架构·eino
七牛开发者1 小时前
AI Agent 的安全风险,正在从 Prompt 扩展到运行环境
ai·agent·security·权限管理·cursor
武汉星际互动1 小时前
政务大厅软硬件升级改造怎么做?
人工智能·政务
IT_陈寒1 小时前
Vite开发实战:从入门到精通
前端·人工智能·后端