AI+交通应用入门:用YOLOv8搭建车牌识别服务
AI+交通应用听起来跨度很大,但落到具体场景,很多需求可以拆成“检测目标 + 识别内容 + 提供服务”三步。
这篇教程以车牌识别为例,带你在 Linux 服务器上跑通一个可调用的 HTTP 服务,适合想验证 AI 交通应用是否可行的零基础读者。
先确认你要解决的是检测还是识别
车牌识别通常包含两个子任务:先从图片或视频帧里找到车牌位置,再把车牌上的字符读出来。
只做检测没有业务价值,只做识别又容易把背景文字误读成车牌,所以实际项目里两者要串联。
本文的适用条件是:你有基本的 Linux 命令行操作能力,服务器有可用的 Python 环境或 Docker,图片质量不过度模糊。
如果只是想做离线批量处理,可以跳过 API 封装部分,直接看推理脚本。
环境准备上,推荐使用 Docker 隔离依赖,避免和服务器上已有 Python 项目冲突。
没有 Docker 的机器也可以用 venv,但需要自己处理 OpenCV 和 PyTorch 的版本兼容问题。
# 创建项目目录
mkdir -p /opt/plate-ocr && cd /opt/plate-ocr
# 拉取带 Python 3.10 的基础镜像
docker pull python:3.10-slim
安装依赖并准备模型文件
在项目目录下新建 requirements.txt,写入以下内容。
这里锁定主版本号,避免自动升级到不兼容的版本。
ultralytics==8.2.*
paddlepaddle==2.6.*
paddleocr==2.7.*
fastapi==0.111.*
uvicorn==0.30.*
opencv-python-headless==4.9.*
然后启动容器并安装依赖:
docker run -it --name plate-ocr -p 8000:8000 \
-v /opt/plate-ocr:/workspace \
python:3.10-slim bash
# 容器内执行
cd /workspace
pip install -r requirements.txt
模型文件方面,YOLOv8 需要车牌检测权重。
如果你没有训练好的权重,可以先用官方通用模型验证流程,但通用模型对车牌这类小目标效果有限,正式使用需要用自己的车牌数据集微调。
PaddleOCR 首次运行会自动下载中文识别模型,需要容器能访问外网。
编写检测与识别串联脚本
新建 plate_service.py,先实现单张图片的处理逻辑。
思路是:YOLO 检测出车牌框,按坐标裁剪,再交给 PaddleOCR 识别文字。
from ultralytics import YOLO
from paddleocr import PaddleOCR
import cv2
# 替换为你自己的车牌检测权重路径
detector = YOLO("weights/plate_det.pt")
ocr = PaddleOCR(use_angle_cls=True, lang="ch")
def recognize_plate(image_path):
img = cv2.imread(image_path)
results = detector(img, conf=0.4)
plates = []
for box in results[0].boxes.xyxy.cpu().numpy():
x1, y1, x2, y2 = map(int, box[:4])
crop = img[y1:y2, x1:x2]
ocr_result = ocr.ocr(crop, cls=True)
texts = [line[1][0] for line in ocr_result[0]] if ocr_result[0] else []
plates.append({"box": [x1, y1, x2, y2], "text": "".join(texts)})
return plates
这段代码的关键点是 conf=0.4,置信度阈值太低会引入误检,太高会漏掉模糊车牌。
建议先用一批测试图跑一遍,观察漏检和误检比例再调整。
用 FastAPI 暴露 HTTP 接口
在同一个文件里追加接口层,让外部系统可以通过上传图片调用识别能力。
from fastapi import FastAPI, UploadFile, File
import shutil, os
app = FastAPI()
@app.post("/recognize")
async def recognize(file: UploadFile = File(...)):
tmp_path = f"/tmp/{file.filename}"
with open(tmp_path, "wb") as f:
shutil.copyfileobj(file.file, f)
try:
result = recognize_plate(tmp_path)
finally:
os.remove(tmp_path)
return {"plates": result}
启动服务:
uvicorn plate_service:app --host 0.0.0.0 --port 8000
看到 Uvicorn running on http://0.0.0.0:8000 说明服务已经监听。
用 curl 上传一张测试图验证:
curl -X POST http://127.0.0.1:8000/recognize \
-F "file=@/workspace/test.jpg"
返回 JSON 中 plates 数组为空,说明检测阶段没有找到车牌,优先检查权重文件和置信度;
有框但 text 为空,说明裁剪区域或 OCR 语言配置有问题。
容易踩坑的地方
第一,PaddleOCR 默认下载模型需要网络,服务器无外网时会卡在下载阶段,需要提前把模型缓存目录挂载进容器。
第二,opencv-python-headless 和带 GUI 的版本不要同时安装,否则容易出现库冲突。
第三,车牌图片分辨率过低时,先做等比放大再送检测,比直接调低阈值更稳。
第四,生产环境不要把 --host 0.0.0.0 直接暴露到公网,应放在 Nginx 反向代理后面并加访问控制。
如果服务运行一段时间后内存持续上涨,优先检查是否在循环里重复创建 PaddleOCR 实例,OCR 对象应该全局初始化一次。
怎么判断这套流程是否可用
准备 20 到 50 张不同光照和角度的车牌图片,人工标注正确结果,跑一遍接口统计两个指标:检测召回率和字符准确率。
检测召回率低于预期时,先补充训练数据;
字符准确率偏低时,检查裁剪是否包含完整车牌边缘。
单张图片响应时间在 CPU 环境下通常明显慢于 GPU,如果业务要求实时视频流识别,需要评估帧率和硬件成本,不能直接套用图片接口的结论。
常见疑问方面,没有标注数据能不能直接用通用模型?
可以跑通流程,但车牌场景的准确率通常不理想,建议至少收集几百张实际场景图片做微调。
接口能不能直接处理视频?
当前示例只处理单张图片,视频需要抽帧后逐帧调用,并额外做去重逻辑。
到这里,一个最小可用的 AI+交通应用原型就完成了。
建议先按本文步骤在测试环境跑通,再根据实际图片质量调整阈值和模型,不要一开始就追求全场景覆盖。