YOLO 训练车牌定位模型 + OpenCV C++ 部署完整步骤

YOLO 训练车牌定位模型 + OpenCV C++ 部署完整步骤

一、前期准备(核心工具 / 环境)

  1. 硬件:GTX 1060 及以上显卡(显存≥6G),避免训练卡顿
  2. 软件:
    • 训练端:Windows/Linux + Python 3.8~3.10 + PyTorch 1.10~2.0 + YOLOv8(ultralytics 库,适配性最优)
    • 部署端:Windows + VS2019/2022 + OpenCV 4.5~4.8(需带 DNN 模块,默认编译已包含)
  3. 数据集:标注好的车牌数据集(格式为 YOLO txt,含 train/val 集,比例 8:2)

二、步骤 1:车牌数据集制作 / 整理(关键:标注精准 + 场景全覆盖)

1. 数据集收集

  • 来源:网络公开车牌数据集(如 CCPD、CALTech)+ 实拍图(多角度、逆光、雨雾、遮挡、不同车牌颜色),总量≥1000 张(越多精度越高,建议 2000+)
  • 规格:统一缩放至 640×640(适配 YOLO 默认输入,减少训练失真)

2. 标注(用 LabelImg,简单易操作)

  1. 安装 LabelImg:pip install labelImg,终端输入labelImg启动
  2. 标注设置:
    • 点击「Change Save Dir」选标注文件保存路径
    • 顶部「View」勾选「Auto Save mode」「YOLO」,切换为 YOLO 标注格式
  3. 标注流程:
    • 打开图片文件夹,框选车牌区域,标签名统一为「plate」(仅 1 类,简化模型)
    • 标注后自动生成 txt 文件(与图片同名),txt 内容格式:0 x1 y1 x2 y2(0 为 plate 类别 ID,坐标为归一化后值,无需手动换算)

3. 数据集目录结构(严格按 YOLO 要求)

plaintext

复制代码
plate_dataset/
├─ images/       # 所有图片
│  ├─ train/     # 训练集图片(80%)
│  └─ val/       # 验证集图片(20%)
└─ labels/       # 所有标注txt
   ├─ train/     # 训练集标注(对应images/train)
   └─ val/       # 验证集标注(对应images/val)

4. 生成数据集配置文件(plate.yaml)

新建 txt 改名为plate.yaml,内容如下(路径填自己的数据集绝对路径):

yaml

复制代码
# 数据集路径
path: D:/plate_dataset  # 数据集根目录
train: images/train     # 训练集图片路径(相对path)
val: images/val         # 验证集图片路径(相对path)

# 类别
names:
  0: plate  # 仅车牌1类,ID=0

三、步骤 2:YOLOv8 训练车牌定位模型

1. 安装 YOLOv8 库

终端执行:pip install ultralytics(自动适配 PyTorch 环境)

2. 启动训练(单类定位,参数精简高效)

新建train_plate.py,代码如下(直接运行,无需改太多参数):

python

运行

复制代码
from ultralytics import YOLO

# 1. 加载预训练模型(用yolov8n.pt,轻量化,适配C++部署)
model = YOLO('yolov8n.pt')

# 2. 训练配置(核心参数,按需微调)
results = model.train(
    data='D:/plate_dataset/plate.yaml',  # 数据集配置文件路径
    epochs=50,                           # 训练轮数(1000张图50轮足够,多了易过拟合)
    batch=8,                             # 批次大小(显卡显存6G设8,8G设16)
    imgsz=640,                           # 输入图片尺寸
    lr0=0.01,                            # 初始学习率
    classes=[0],                         # 仅训练第0类(plate)
    save=True,                           # 保存模型
    device=0,                            # 用GPU训练(0为GPU编号,CPU填cpu)
    patience=10                          # 10轮精度无提升停止训练,防过拟合
)

3. 训练完成后取模型

训练结束后,在runs/detect/train/weights/目录下,取best.pt(最优精度模型),后续转 ONNX 格式供 C++ 调用。

四、步骤 3:将 YOLOv8.pt 模型转 ONNX 格式(OpenCV DNN 支持)

YOLO 原生 pt 模型 OpenCV 不兼容,需转 ONNX(通用格式,DNN 模块可直接加载)

1. 模型转换

新建pt2onnx.py,代码如下:

python

运行

复制代码
from ultralytics import YOLO

# 加载最优模型
model = YOLO('runs/detect/train/weights/best.pt')
# 转ONNX,简化模型(opset=12适配OpenCV 4.5+)
model.export(format='onnx', opset=12, simplify=True)

运行后,在best.pt同目录下生成best.onnx,即部署用模型。

2. 提取类别名(可选,用于显示标签)

新建class_names.txt,仅写 1 行:plate(与训练时标签一致)

五、步骤 4:OpenCV C++ 部署 YOLOv8 ONNX 模型(核心代码,可直接跑)

1. VS 项目配置(关键:关联 OpenCV)

  1. 新建 VS 控制台项目(空项目,x64 Debug/Release)
  2. 配置属性(右键项目→属性→配置属性):
    • 包含目录:添加 OpenCV 安装目录下includeinclude/opencv2(如D:/opencv4.6/build/include
    • 库目录:添加 OpenCVx64/vc16/lib(如D:/opencv4.6/build/x64/vc16/lib
    • 链接器→输入→附加依赖项:
      • Debug 模式:opencv_world460d.lib(460 对应 OpenCV 版本,改自己的)
      • Release 模式:opencv_world460.lib
  3. 复制 OpenCV 动态库到项目 exe 目录:
    • opencv/build/x64/vc16/bin下的opencv_world460d.dll(Debug)/opencv_world460.dll(Release),粘贴到 VS 项目x64/Debugx64/Release目录(与 exe 同路径)

2. 核心 C++ 代码(完整可运行,含预处理 + 推理 + 后处理)

新建main.cpp,代码如下(注释详细,直接替换模型 / 图片路径即可):

cpp

运行

复制代码
#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>
#include <iostream>
#include <vector>
#include <string>

using namespace cv;
using namespace cv::dnn;
using namespace std;

// YOLOv8配置参数(需根据自己模型微调)
const float CONF_THRESH = 0.5f;    // 置信度阈值(低于0.5过滤)
const float NMS_THRESH = 0.4f;     // NMS阈值(去重重叠框)
const int INPUT_W = 640;           // 输入宽
const int INPUT_H = 640;           // 输入高
const string MODEL_PATH = "D:/best.onnx";  // ONNX模型路径
const string CLASS_PATH = "D:/class_names.txt";  // 类别名路径

// 读取类别名
vector<string> readClassNames(const string& path) {
    vector<string> classes;
    ifstream file(path);
    string line;
    while (getline(file, line)) {
        classes.push_back(line);
    }
    return classes;
}

// YOLOv8推理+后处理(解析结果,画框)
void yoloDetect(Mat& img, Net& net, vector<string>& classes) {
    int img_w = img.cols;
    int img_h = img.rows;

    // 1. 图像预处理(归一化+转Blob,YOLOv8输入格式:RGB、0-1归一化)
    Mat blob;
    blobFromImage(img, blob, 1.0 / 255.0, Size(INPUT_W, INPUT_H), Scalar(0, 0, 0), true, false);
    net.setInput(blob);

    // 2. 模型推理(YOLOv8输出层名固定为"output0")
    Mat outputs = net.forward("output0");  // outputs尺寸:1×8400×6(8400个预测框,6=xywh+conf+class_id)

    // 3. 结果解析(过滤低置信度,提取有效框)
    vector<int> class_ids;    // 类别ID
    vector<float> confs;      // 置信度
    vector<Rect> boxes;       // 检测框(像素坐标)

    float* data = (float*)outputs.data;
    int num_boxes = outputs.size[1];  // 8400个预测框
    for (int i = 0; i < num_boxes; i++) {
        float conf = data[4];  // 置信度(当前框是目标的概率)
        if (conf < CONF_THRESH) continue;

        // 提取类别ID(取最大概率对应的类别)
        float max_class_conf = 0;
        int class_id = 0;
        for (int j = 5; j < 6; j++) {  // 仅1类(plate),j从5到5(6-1)
            float class_conf = data[j];
            if (class_conf > max_class_conf) {
                max_class_conf = class_conf;
                class_id = j - 5;  // 类别ID=0
            }
        }

        // 计算检测框像素坐标(YOLO输出为归一化xywh,转像素xyxy)
        float cx = data[0] * img_w;  // 框中心x
        float cy = data[1] * img_h;  // 框中心y
        float w = data[2] * img_w;   // 框宽
        float h = data[3] * img_h;   // 框高
        int x1 = max(0, int(cx - w / 2));  // 框左上角x
        int y1 = max(0, int(cy - h / 2));  // 框左上角y
        int x2 = min(img_w - 1, int(cx + w / 2));  // 框右下角x
        int y2 = min(img_h - 1, int(cy + h / 2));  // 框右下角y

        // 保存结果
        class_ids.push_back(class_id);
        confs.push_back(conf * max_class_conf);  // 最终置信度=目标置信度×类别置信度
        boxes.push_back(Rect(x1, y1, x2 - x1, y2 - y1));

        // 移动到下一个预测框(每框6个参数)
        data += 6;
    }

    // 4. NMS非极大值抑制(去重重叠框)
    vector<int> indices;
    NMSBoxes(boxes, confs, CONF_THRESH, NMS_THRESH, indices);

    // 5. 画框+显示结果
    Scalar color(0, 255, 0);  // 绿色框
    for (int idx : indices) {
        Rect box = boxes[idx];
        int class_id = class_ids[idx];
        float conf = confs[idx];

        // 画检测框
        rectangle(img, box, color, 2, 8);
        // 显示标签+置信度
        string label = classes[class_id] + ":" + format("%.2f", conf);
        int label_h = 25;
        Rect label_rect(box.x, box.y - label_h, box.width, label_h);
        rectangle(img, label_rect, color, -1);  // 标签背景
        putText(img, label, Point(box.x + 5, box.y - 5), FONT_HERSHEY_SIMPLEX, 0.6, Scalar(255, 255, 255), 1);
    }
}

int main() {
    // 1. 加载ONNX模型和类别名
    Net net = readNetFromONNX(MODEL_PATH);
    vector<string> classes = readClassNames(CLASS_PATH);
    if (net.empty() || classes.empty()) {
        cout << "模型或类别名加载失败!" << endl;
        return -1;
    }
    // 启用GPU加速(有GPU必开,速度提升10倍+)
    net.setPreferableBackend(DNN_BACKEND_CUDA);
    net.setPreferableTarget(DNN_TARGET_CUDA);

    // 2. 读取图片/视频(二选一,按需切换)
    // 方式1:图片检测
    Mat img = imread("D:/test_plate.jpg");  // 测试图片路径
    if (img.empty()) {
        cout << "图片读取失败!" << endl;
        return -1;
    }
    yoloDetect(img, net, classes);
    imshow("Plate Detection", img);
    waitKey(0);  // 按任意键关闭窗口

    // 方式2:视频/摄像头检测(注释图片检测,打开下方代码)
    // VideoCapture cap(0);  // 0=电脑摄像头,也可填视频路径(如"D:/test.mp4")
    // if (!cap.isOpened()) {
    //     cout << "摄像头/视频打开失败!" << endl;
    //     return -1;
    // }
    // Mat frame;
    // while (cap.read(frame)) {
    //     yoloDetect(frame, net, classes);
    //     imshow("Plate Detection", frame);
    //     if (waitKey(1) == 27) break;  // ESC键退出
    // }
    // cap.release();

    destroyAllWindows();
    return 0;
}

3. 运行测试

  1. 替换代码中MODEL_PATH(best.onnx 路径)、CLASS_PATH(class_names.txt 路径)、test_plate.jpg(测试图路径)
  2. 选择 x64 Debug/Release 模式,点击运行,即可看到车牌定位结果(绿色框 + plate 标签 + 置信度)

六、常见问题排查

  1. 模型加载失败:检查 ONNX 路径是否正确,OpenCV 版本是否≥4.5,opset 是否为 12
  2. 无检测结果:置信度阈值设太低(可降为 0.3),数据集场景与测试图差异大,训练轮数不足
  3. 速度慢:未启用 GPU 加速(确认 VS 配置 CUDA,代码中已加setPreferableBackend/Target
  4. 框不准:标注框未紧贴车牌,数据集不足,可增加训练轮数或扩充数据
相关推荐
一勺汤1 小时前
YOLO11 改进、魔改| 空间与通道协同注意力模块SCSA,通过空间与通道注意力的协同作用,提升视觉任务的特征提取能力与泛化性能。
yolo·注意力机制·遮挡·yolo11·yolo11改进·小目标·scsa
18你磊哥2 小时前
chromedriver.exe的使用和python基本处理
开发语言·python
闲人编程2 小时前
Python的抽象基类(ABC):定义接口契约的艺术
开发语言·python·接口·抽象类·基类·abc·codecapsule
vx_dmxq2112 小时前
【微信小程序学习交流平台】(免费领源码+演示录像)|可做计算机毕设Java、Python、PHP、小程序APP、C#、爬虫大数据、单片机、文案
java·spring boot·python·mysql·微信小程序·小程序·idea
无垠的广袤3 小时前
【工业树莓派 CM0 NANO 单板计算机】本地部署 EMQX
linux·python·嵌入式硬件·物联网·树莓派·emqx·工业物联网
雾岛听蓝3 小时前
C++ 类和对象(一):从概念到实践,吃透类的核心基础
开发语言·c++·经验分享·笔记
艾莉丝努力练剑4 小时前
【Python基础:语法第一课】Python 基础语法详解:变量、类型、动态特性与运算符实战,构建完整的编程基础认知体系
大数据·人工智能·爬虫·python·pycharm·编辑器
Dream it possible!4 小时前
LeetCode 面试经典 150_图_克隆图(90_133_C++_中等)(深度优先:DFS)
c++·leetcode·面试·
gCode Teacher 格码致知4 小时前
Python基础教学:如何拼接字符串?-由Deepseek产生
python