Skip to content

车牌识别

使用YOLO26 Pose模型直接检测车牌的四个角点坐标

YOLO26-Pose 模型,是 Ultralytics 推出的新一代实时姿态估计模型,提供关键点检测功能。

1.对比常用方案

方案目标检测YOLO26-Pose 关键点检测
车牌定位车牌锚框坐标车牌四个角点坐标
车牌矫正拉伸形变✅ 透视变换
OCR 识别较低✅ 较高
  • 目标检测 步骤
      1. 车牌检测 -> 车牌边界框 (bbox)
      1. 根据 bbox 裁剪车牌区域
      1. 拉伸车牌区域 到固定尺寸(提升识别率)
      1. OCR识别 -> 车牌识别结果
  • YOLO26-Pose 步骤
      1. 车牌检测 -> 车牌关键点坐标
      1. 将用4个角点计算,任意角度车牌变换为正向标准矩形
      1. OCR 识别(矫正后的车牌) -> 车牌识别结果

2.模型选择

模型参数量模型大小推理速度(ms)mAP
YOLO26n-pose3.3M6.3MB652.1
YOLO26s-pose11.6M22.6MB1262.5
YOLO26m-pose26.4M49.7MB2867.3
YOLO26l-pose44.8M84.0MB5069.8

选择建议:

  • 🚀 实时性要求高 → YOLO26n-pose
  • ⚖️ 平衡性能 → YOLO26s-pose (推荐)
  • 🎯 高精度要求 → YOLO26l-pose

3.数据集

数据集

3.1.中文车牌数据集(主流开源)

    1. CCPD(最常用,USTC,MIT 商用)
  • 全称:Chinese City Parking Dataset
  • 规模:50 万 +(CCPD2020+Green 新能源)
  • 场景:停车场 / 道路,蓝牌、绿牌、黄牌
  • 标注:车牌框、4 个角点、字符、倾斜 / 模糊 / 光照等属性
  • 特点:免费商用、难度高、最权威
  • 官网:https://github.com/detectRecog/CCPD
    1. CRPD(多目标 + 角点,补充 CCPD)
  • 全称:Chinese Road Plate Dataset
  • 规模:约 20 万,含多车牌同图
  • 标注:车牌框、4 角点、字符、类型
  • 特点:真实卡口场景、适合YOLO 检测 + 关键点
  • 官网:https://github.com/yxgong0/CRPD
    1. CBLPRD-330k(均衡 + 合成,GAN 增强)

3.2.关键点定义

对于车牌,我们定义4个关键点 + 置信度

  • 右下、左下、左上、右上
  • onfidence(置信度)

3.3.CCPD数据转换为YOLO格式

CCPD2020 转 YOLO 核心就两件事:从文件名解析出车牌框 → 转成归一化的 class x y w h 并按 YOLO 目录结构存放。下面给你完整流程和可直接跑的代码。

CCPD2020 标注规则(文件名即标签)
plaintext
025-95_113-154&383_386&473-386&473_177&454_154&383_363&402-0_0_22_27_27_33_16-37-15.jpg

每个名字可以被拆分为七个字段。这些场的解释如下。

  • 面积 :车牌面积与整个画面区域的面积比值。
  • 倾斜度 :水平倾斜度和垂直倾斜度。
  • 边界框坐标 :左上顶点和右下顶点的坐标。
  • 四个顶点位置 :整个图像中 LP 四个顶点的精确(x, y)坐标。这些坐标从右下顶点开始。
  • 车牌号

按 - 分割为多段,第 4 段 = 车牌四个角点 格式:p1_p2_p3_p4,每个点 x&y 顺序:右下、左下、左上、右上(车牌顺时针 / 逆时针四点)

输出 YOLOv8 Pose 关键点格式(外接框 + 四点)

标签格式:cls cx cy bw bh k1x k1y k2x k2y k3x k3y k4x k4y

解释:

  • cls:类别ID,车牌为0
  • cx cy bw bh:车牌边界框坐标
  • k1x k1y k2x k2y k3x k3y k4x k4y:车牌四个角点坐标, 右下、左下、左上、右上(车牌顺时针)
  • 安装
bash
pip install opencv-python tqdm
  • 转换 yolo_conversion.py
python
import os
import cv2
import shutil
from tqdm import tqdm

# ==================== 配置区 ====================
ROOT_SRC = "/Users/ly/Downloads/dataset/CCPD2020/ccpd_green"
ROOT_DST = "/Users/ly/Downloads/dataset/ccpd_green_pose"
CLASS_ID = 0
# =================================================

sub_sets = ["train", "val", "test"]

# 创建目录
for subset in sub_sets:
    os.makedirs(os.path.join(ROOT_DST, "images", subset), exist_ok=True)
    os.makedirs(os.path.join(ROOT_DST, "labels", subset), exist_ok=True)

def parse_bbox(file_name):
    """解析外接框 xyxy"""
    parts = file_name.split("-")
    bbox_str = parts[2]
    p1, p2 = bbox_str.split("_")
    x1, y1 = map(int, p1.split("&"))
    x2, y2 = map(int, p2.split("&"))
    return x1, y1, x2, y2

def parse_four_points(file_name):
    """解析四点原始坐标"""
    parts = file_name.split("-")
    pts_str = parts[3]
    pts = []
    for p in pts_str.split("_"):
        x, y = map(int, p.split("&"))
        pts.append(x)
        pts.append(y)
    return pts

# 遍历数据集
for subset in sub_sets:
    src_img_dir = os.path.join(ROOT_SRC, subset)
    dst_img_dir = os.path.join(ROOT_DST, "images", subset)
    dst_label_dir = os.path.join(ROOT_DST, "labels", subset)

    img_list = [f for f in os.listdir(src_img_dir) if f.lower().endswith(".jpg")]
    print(f"\n正在处理 {subset},共 {len(img_list)} 张图片")

    for img_name in tqdm(img_list):
        src_path = os.path.join(src_img_dir, img_name)
        img = cv2.imread(src_path)
        h, w = img.shape[:2]

        # 1. 外接框转YOLO格式
        x1, y1, x2, y2 = parse_bbox(img_name)
        cx = (x1 + x2) / 2.0 / w
        cy = (y1 + y2) / 2.0 / h
        bw = (x2 - x1) / w
        bh = (y2 - y1) / h

        # 2. 四点归一化
        raw_pts = parse_four_points(img_name)
        norm_pts = []
        for idx, val in enumerate(raw_pts):
            if idx % 2 == 0:
                norm_pts.append(f"{val / w:.6f}")
            else:
                norm_pts.append(f"{val / h:.6f}")

        # 复制图片
        shutil.copy(src_path, os.path.join(dst_img_dir, img_name))

        # 写入标签
        txt_name = os.path.splitext(img_name)[0] + ".txt"
        txt_path = os.path.join(dst_label_dir, txt_name)
        line = f"{CLASS_ID} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f} {' '.join(norm_pts)}\n"
        with open(txt_path, "w", encoding="utf-8") as f:
            f.write(line)

# 生成Pose专用yaml
yaml_content = f"""path: {ROOT_DST}
train: images/train
val: images/val
test: images/test

nc: 1
names: ["license_plate"]
kpt_shape: [4, 2]  # 4个关键点,每个点x/y
"""
yaml_path = os.path.join(ROOT_DST, "data.yaml")
with open(yaml_path, "w", encoding="utf-8") as f:
    f.write(yaml_content.strip())

print("\n✅ YOLOv8 Pose 四点关键点格式转换完成!")

4.模型训练代码

  • yolo_pose_plate.py
python
"""
车牌关键点检测训练脚本 - Pose任务
基于YOLO26 Pose模型
用于检测车牌的四个角点位置
"""

import os
from pathlib import Path
from ultralytics import YOLO

def train_model(data_yaml_path, task='pose'):
    """训练YOLO26 Pose模型"""

    print("="*50)
    print("开始加载模型...")

    # 加载预训练Pose模型
    model = YOLO("yolo26n-pose.pt")
    print("模型加载完成")

    # 开始训练
    print("="*50)
    print("开始训练...\n")

    results = model.train(
        data=data_yaml_path,   # 数据集配置
        task=task,             # 任务类型
        epochs=10,            # 训练轮数
        imgsz=640,             # 输入图像尺寸
        batch=-1,             # 批次大小
        device="mps",          # 设备:0(GPU)、cpu、mps(Mac)、0,1(多 GPU)
        workers=4,
        project="./results",
        name="plate_detection",
        exist_ok=True,
        amp=True,              # 混合精度训练 (推荐)
        patience=20,           # 早停机制
        save=True,
        save_period=10,
        cache=True,
        verbose=True,
        # 数据增强参数
        flipud=0.5,            # 纵向翻转
        fliplr=0.5,            # 横向翻转
        degrees=10,            # 旋转
        hsv_h=0.015,           # 色度调整
        hsv_s=0.7,             # 饱和度调整
        hsv_v=0.4,             # 亮度调整
        translate=0.1,         # 平移
        scale=0.5,             # 缩放
        mosaic=1.0,            # Mosaic增强
        mixup=0.0,             # Mixup增强
        close_mosaic=10,       # 最后10个epoch关闭Mosaic
        weight_decay=0.0005,   # 权重衰减
        lr0=0.001,             # 初始学习率
        lrf=0.01,              # 最终学习率系数
    )

    return results

def main():
    print("=" * 50)
    print("车牌关键点检测模型训练")
    print("任务: 检测4个角点 (Pose)")
    print("=" * 50)

    data_yaml_path = "/Users/ly/Downloads/dataset/ccpd_green_pose/data.yaml"

    if not Path(data_yaml_path).exists():
        print(f"\n错误: 配置文件不存在,请检查路径: {data_yaml_path}")
        return

    print(f"\n使用配置文件: {data_yaml_path}\n")

    # 开始训练
    results = train_model(data_yaml_path, task='pose')

    print("\n" + "=" * 50)
    print("训练完成!")
    print(f"最优模型: ./results/plate_detection/weights/best.pt")
    print(f"最后模型: ./results/plate_detection/weights/last.pt")
    print("=" * 50)

if __name__ == "__main__":
    main()

一次训练结果:

指标分类指标名称数值指标含义
基础统计验证图片数量1001验证集总图片数
基础统计目标实例数量1001验证集总车牌目标数
边界框检测精确率(P)0.976预测为正例中真实正例的比例
边界框检测召回率(R)0.967真实正例中被正确预测的比例
边界框检测mAP500.993IoU阈值0.5时的平均精度
边界框检测mAP50-950.817IoU从0.5到0.95区间的平均精度
关键点检测精确率(P)0.977关键点检测的精确率
关键点检测召回率(R)0.968关键点检测的召回率
关键点检测mAP500.993IoU阈值0.5时关键点的平均精度
关键点检测mAP50-950.988IoU从0.5到0.95区间关键点的平均精度

5.推理

5.1 单张图片推理

  • yolo_pose_plate_inference.py
python
from ultralytics import YOLO
import cv2
import numpy as np

def detect_plate_keypoints(image_path, model_path):
    """
    检测图像中的车牌和关键点

    输出格式‌:结果对象中包含:
    - boxes(边界框坐标、置信度)
    - keypoints(归一化/像素级的4个车牌角点坐标)、速度统计等字段,可直接用于后续车牌透视矫正等业务流程。


    Returns:
        results: YOLO26检测结果
        detections: 解析后的检测结果 [{
            'bbox': [x_min, y_min, x_max, y_max],
            'keypoints': [(x1,y1,conf1), (x2,y2,conf2), ...],
            'confidence': float
        }, ...]
    """

    # 加载模型
    model = YOLO(model_path)

    # 执行推理
    results = model(image_path)

    # 解析结果
    detections = []
    count = 0
    for result in results:

        result.show()   # 显示结果图片

        for box, kpts in zip(result.boxes, result.keypoints):
            count += 1


            # 边界框
            cls_id = int(box.cls[0])  # 类别ID
            confidence = float(box.conf[0]) # 置信度
            x1, y1, x2, y2 = box.xyxy[0].tolist()  # 边界框坐标
            bbox = [x1, y1, x2, y2]  # 边界框坐标

            # bbox = box.xyxy[0].cpu().numpy()
            # conf = box.conf[0].item()

            # 关键点
            keypoints = []
            
            detections.append({
                'bbox': bbox,
                'keypoints': kpts.xy[0],
                'confidence': confidence
            })


    print(f"检测到 {count} 个车牌")


    return results, detections

# 使用
if __name__ == "__main__":
    image_path = "./test_image2.jpeg"
    model_path = "./runs/pose/results/plate_detection/weights/best.pt"

    results, detections = detect_plate_keypoints(image_path, model_path)

    for i, det in enumerate(detections):
        print(f"车牌 {i+1}:")
        print(f"  置信度: {det['confidence']:.2f}")
        print(f"  关键点: {det['keypoints']}")

5.2 批量图片推理

6. 导出

7. OCR识别

8. 部署

京ICP备2024093538号-1