关注

手把手教你完成基于YOLOv11+CNN车牌识别系统,Opencv车牌矫正,基于深度学习的车牌识别系统

更新通知

最新版前台系统+后台管理系统整合了 YOLOv11/v8 算法 + PaddleOCR 算法完成车牌检测和车牌识别系统,由于 YOLOv11+CNN 车牌识别系统对倾斜角度较大和模糊的图片识别效果不佳、识别车牌单一、界面功能和样式单一 等问题,在本期的基础上进行升级 ,更多细节看点击下面的链接查看。

点击即可跳转: 手把手教你使用 YOLOv11/v8算法 + PaddleOCR算法完成车牌检测和车牌识别系统,AI智能体,毛玻璃系统,包括PaddlePaddle安装、数据集预处理、模型训练、AI大模型应用等

最新版系统演示效果如下:

模块界面演示
登录与注册在这里插入图片描述
界面演示在这里插入图片描述
AI 智能操作 演示在这里插入图片描述
新能源车牌识别在这里插入图片描述
蓝色车牌识别在这里插入图片描述
其他颜色车牌在这里插入图片描述
黄色车牌识别在这里插入图片描述
双层车牌识别在这里插入图片描述
官网模型开启双层车牌分割在这里插入图片描述
历史记录模块在这里插入图片描述
用户个人中心模块在这里插入图片描述
后台-首页在这里插入图片描述
后台-用户管理模块在这里插入图片描述
后台历史记录模块在这里插入图片描述

引言

车牌识别系统是智能交通、安防监控等领域的关键技术,结合深度学习方法可提升识别模型准确率。传统方法依赖手工特征提取,而OpenCV的车牌矫正技术进一步优化倾斜或变形车牌的预处理,为后续识别提供高质量输入,基于 YOLOv11的目标检测与 CNN 分类器的结合,能够实现端到端的车牌定位与字符识别。

一、相关技术

技术路线如下:

  1. 先利用YOLOv11算法定位车牌位置
  2. 由于有些车牌倾斜比较大,识别难度大大增加,那么就使用 cv2 进行边缘检测获得车牌区域坐标,并将车牌图形矫正
  3. 车牌经过矫正后,利用 CNN 网络进行车牌多标签端到端识别

实验结果,整体识别还好,对于倾斜角度和比较模糊的图片识别率不是很高,后续可以考虑将 CNN 算法更换为 LPRNet 算法,系统识别结果如下:
在这里插入图片描述

二、环境配置

1.YOLOv11环境配置

YOLO 系列环境配置讲过很多次了,其中YOLOv11/YOLOv10/YOLOv9/YOLOv8/YOLOv7/YOLOv5环境都是通用的,只需要安装一次就行,如果以前安装过,可以忽略本章的环境安装

🍀🍀 1.pytorch环境安装

手把手安装YOLO环境教程 1(非常推荐)链接参考链接: YOLO环境配置教程视频

手把手pytorch安装教程 2 链接参考链接: pytorch环境安装教程视频

🍀🍀2.其他依赖安装

安装 requirements.txt 文件的环境,需要注释掉下面两行,前面的步骤已经安装了,不注释的话会覆盖前面的会安装最新版本的 pytorch,所以注释掉

在这里插入图片描述
没有这个文件可以自己新建一个requirements.txt,然后把下面代码复制进去就好了
在这里插入图片描述

在这里插入图片描述

# Ultralytics requirements
# Example: pip install -r requirements.txt

# Base ----------------------------------------
matplotlib>=3.3.0
numpy==1.24.4 # pinned by Snyk to avoid a vulnerability
opencv-python>=4.6.0
pillow>=7.1.2
pyyaml>=5.3.1
requests>=2.23.0
scipy>=1.4.1
tqdm>=4.64.0

# Logging -------------------------------------
# tensorboard>=2.13.0
# dvclive>=2.12.0
# clearml
# comet

# Plotting ------------------------------------
pandas>=1.1.4
seaborn>=0.11.0

# Export --------------------------------------
# coremltools>=7.0  # CoreML export
# onnx>=1.12.0  # ONNX export
# onnxsim>=0.4.1  # ONNX simplifier
# nvidia-pyindex  # TensorRT export
# nvidia-tensorrt  # TensorRT export
# scikit-learn==0.19.2  # CoreML quantization
# tensorflow>=2.4.1  # TF exports (-cpu, -aarch64, -macos)
# tflite-support
# tensorflowjs>=3.9.0  # TF.js export
# openvino-dev>=2023.0  # OpenVINO export

# Extras --------------------------------------
psutil  # system utilization
py-cpuinfo  # display CPU info
thop>=0.1.1  # FLOPs computation
# ipython  # interactive notebook
# albumentations>=1.0.3  # training augmentations
# pycocotools>=2.0.6  # COCO mAP
# roboflow


2.CNN环境配置

训练CNN模型需要安装单独安装 tensorflow,要么安装 gpu 版本或者 cpu 版本,下面给出各自的安装教程

如果安装 gpu 版本,电脑必须有英伟达显卡,并且先安装对应版本的 cuda 和 cudnn,安装教程看这篇文章: cuda和cudnn的安装教程(全网最详细保姆级教程),我安装的 cuda 版本是11.3,cudnn 版本是 8.2,建议安装跟我一样,避免报错

安装完 cuda 和 cudnn 之后,输入如下命令来安装 tensorflow gpu 版本 :

pip install tensorflow_gpu==2.7.0

测试tensorflow gpu 是否能用,代码如下:

# -*- coding: utf-8 -*-
"""
@Auth : 挂科边缘
@File :Test.py
@IDE :PyCharm
@Motto:学习新思想,争做新青年
@Email :[email protected]
"""
import tensorflow as tf
a = tf.test.is_built_with_cuda()  # 判断CUDA是否可以用
b = tf.test.is_gpu_available(
    cuda_only=False,
    min_cuda_compute_capability=None
)                                  # 判断GPU是否可以用
print(a)
print(b)

输出两个True证明能用,如下图所示
在这里插入图片描述

如果安装 cpu 版本就简单了,不用安装cuda和cudnn,直接输入下面命令安装就行,命令如下:

pip install tensorflow-cpu==2.7.0

3.PySide6安装

运行系统还需要安装PySide6

pip install PySide6==6.4.2

三、数据集准备和预处理

1.数据集准备

由于车牌数据集比较少,将采用开源数据集,本次实验均采用CCPD数据集,数据集详情参考链接:CCPD数据集参考

2.数据预处理

YOLOv11算法训练前需要做的工作:
训练前需要对数据集进行预处理,训练 YOLOv11 模型需要数据集进行标注,由于CCPD 数据集的特殊性,人家已经标注好了,只需要处理成 YOLO 格式即可,可以参考这篇文章进行处理:CCPD数据集处理成YOLO格式

CNN算法训练前需要做的工作:
CCPD 数据集我拿了 30000 张,CNN算法训练前需要裁剪车牌图片,裁剪后我们发现车牌有些是倾斜的,那么我们需要进行矫正,为了方便模型输入,将裁剪后的车牌进行统一大小为宽240,高80,处理好的训练样本如下所示:
在这里插入图片描述
对数据集进行字符统计如下图所示:
在这里插入图片描述

CCPD裁剪+矫正代码如下:

# -*- coding: utf-8 -*-
"""
@Auth : 挂科边缘
@File :ccpd裁剪.py
@IDE :PyCharm
@Motto:学习新思想,争做新青年
@Email :[email protected]
"""
import shutil
import numpy as np
import cv2
import os

words_list = [
    "A", "B", "C", "D", "E",
    "F", "G", "H", "J", "K",
    "L", "M", "N", "P", "Q",
    "R", "S", "T", "U", "V",
    "W", "X", "Y", "Z", "0",
    "1", "2", "3", "4", "5",
    "6", "7", "8", "9"
]

con_list = [
    "皖", "沪", "津", "渝", "冀",
    "晋", "蒙", "辽", "吉", "黑",
    "苏", "浙", "京", "闽", "赣",
    "鲁", "豫", "鄂", "湘", "粤",
    "桂", "琼", "川", "贵", "云",
    "西", "陕", "甘", "青", "宁",
    "新"
]


def order_points(pts):
    rect = np.zeros((4, 2), dtype='float32')

    s = pts.sum(axis=1)  # 每行像素值进行相加;若axis=0,每列像素值相加
    rect[0] = pts[np.argmin(s)]  # top_left,返回s首个最小值索引,eg.[1,0,2,0],返回值为1
    rect[2] = pts[np.argmax(s)]  # bottom_left,返回s首个最大值索引,eg.[1,0,2,0],返回值为2

    # 分别计算左上角和右下角的离散差值
    diff = np.diff(pts, axis=1)  # 第i+1列减第i列
    rect[1] = pts[np.argmin(diff)]
    rect[3] = pts[np.argmax(diff)]

    return rect


def four_point_transform(image, pts):
    rect = order_points(pts)
    (tl, tr, br, bl) = rect

    # 计算新图片的宽度值,选取水平差值的最大值
    widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
    widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
    maxWidth = max(int(widthA), int(widthB))

    # 计算新图片的高度值,选取垂直差值的最大值
    heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
    heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
    maxHeight = max(int(heightA), int(heightB))

    # 构建新图片的4个坐标点,左上角为原点
    dst = np.array([
        [0, 0],
        [maxWidth - 1, 0],
        [maxWidth - 1, maxHeight - 1],
        [0, maxHeight - 1]], dtype="float32")

    M = cv2.getPerspectiveTransform(rect, dst)
    warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))
    return warped


if __name__ == "__main__":
    global points
    total = []
    input_dir = r'D:/Desktop/ccpd/ccpd/'      # CCPD数据集路径,英文路径
    output_dir = r'D:/Desktop/ccpd/new_ccpd'  # 保存路径,英文路径

    if os.path.exists(output_dir):
        for filename in os.listdir(output_dir):
            file_path = os.path.join(output_dir, filename)
            try:
                if os.path.isfile(file_path) or os.path.islink(file_path):
                    os.unlink(file_path)
                elif os.path.isdir(file_path):
                    shutil.rmtree(file_path)
            except Exception as e:
                print(f'删除 {file_path} 失败. 原因: {e}')
    else:
        os.makedirs(output_dir, exist_ok=True)


    for item in os.listdir(os.path.join(input_dir)):
        img_path = os.path.join(input_dir, item)
        img = cv2.imread(img_path)


        # 解析文件名部分
        parts = item.split('-')
        if len(parts) < 7:
            print(f"文件名格式错误: {item}")
            continue

        _, _, bbox, points_str, label_str, _, _ = parts

        # 解析点坐标
        points = points_str.split('_')
        tmp = points
        points = []
        for p in tmp:
            coords = p.split('&')
            if len(coords) >= 2:
                try:
                    points.append([int(coords[0]), int(coords[1])])
                except ValueError:
                    print(f"坐标解析错误: {p} in {item}")
                    continue

        # 解析车牌标签
        label_parts = label_str.split('_')
        if len(label_parts) < 7:
            print(f"标签格式错误: {label_str} in {item}")
            continue

        try:
            con = con_list[int(label_parts[0])]
            words = [words_list[int(num)] for num in label_parts[1:7]]
            plate_number = con + ''.join(words)
        except (IndexError, ValueError) as e:
            print(f"车牌解析错误: {e} in {item}")
            continue

        line = item + '\t' + plate_number + '\n'
        total.append(line)

        # 还原像素位置
        if len(points) < 4:
            print(f"点坐标不足: {item}")
            continue

        points_np = np.array(points, dtype=np.float32)
        warped = four_point_transform(img, points_np)

        warped = cv2.resize(warped, (240, 80), interpolation=cv2.INTER_CUBIC)


        safe_plate_number = plate_number.encode('utf-8').decode('utf-8')
        save_path = os.path.join(output_dir, safe_plate_number + '.jpg')


        # 检查文件是否已存在,避免覆盖
        counter = 1
        while os.path.exists(save_path):
            save_path = os.path.join(output_dir, f"{safe_plate_number}_{counter}.jpg")
            counter += 1

        success, encoded_image = cv2.imencode('.jpg', warped)
        if success:
            with open(save_path, 'wb') as f:
                f.write(encoded_image.tobytes())
        else:
            print(f"图像编码失败: {save_path}")

代码需要修改的地方如下,填一下输入数据集路径和保存的路径即可,需要注意的是路径不能有中文
在这里插入图片描述

CCPD原始数据集我只拿30000张,需要 训练好的模型+完整源码+原始数据集+处理好的数据集 在文章末尾发送 车牌识别 即可获取

四、模型训练

1.YOLOv11模型训练

先训练YOLOv11车牌定位模型,因为 YOLO 系列训练讲过很多了,这里训练的话参考这篇文章即可:YOLOv11来了,使用YOLOv11训练自己的数据集和推理(附YOLOv11网络结构图)

2.CNN车牌识别训练

在第三章我们已经处理好数据集了,直接拿来训练即可
修改自己数据集路径即可
在这里插入图片描述
训练结果如下:
我画图出现乱码,在训练代码加上下面代码即可

plt.rcParams["font.family"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False  # 正确显示负号

下面的图分别代表 模型损失、第一个字符准确率、第七个字符准确率、平均字符准确率
在这里插入图片描述

四、系统设计与实现

通过Qt Desiger设计好界面,如下图所示
在这里插入图片描述
之后实现后端代码即可,最终运行MainUI.py结果如下:

在这里插入图片描述

五、总结和源码获取

  • 当前系统的优势与不足:倾斜角度较大和模糊的图片识别效果不佳
  • 未来改进方向:更换更换的车牌识别模型,如 LprNet、PaddleOCR 等模型

CCPD原始数据集我只拿30000张,需要 训练好的模型+完整源码+原始数据集+处理好的数据集 在文章末尾发送 车牌识别 即可获取

转载自CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/weixin_44779079/article/details/149123571

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--