一起用Python做个车牌自动识别系统,好玩又实用!

Posted Dragon少年

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一起用Python做个车牌自动识别系统,好玩又实用!相关的知识,希望对你有一定的参考价值。

前言

前段时间,用PyQt5写了两篇文章,关于Python自制一款炫酷音乐播放器自定义桌面动画挂件。有粉丝问我,为什么要用PyQt5?之前没接触过PyQt5,能不能多分享一些这方面的开发案例?

今天就继续给大家分享一个实战案例,带大家一起用Python的PyQt5开发一个车牌自动识别系统!

首先一起来看看最终实现的车牌识别系统效果图:
下面,我们就开始介绍如何实现这款自动车牌识别系统。

一、核心功能设计

总体来说,我们首先要进行UI界面构建设计,根据车牌识别系统功能进行画面排版布局;其次我们的这款车牌识别系统的主要功能车辆图片读取识别显示、图片中车牌ROI区域获取、车牌识别结果输出显示。

对于结果输出显示,我们主要包含了读取图片名称、读取录入时间、识别车牌号码、识别车牌颜色、识别车牌所属地。最后我们还可以将车牌识别系统的数据信息导出本地存储。

拆解需求,大致可以整理出核心功能如下:

  • UI设计排版布局

    • 左侧区域进行识别信息显示,包含图片名称、读取录入时间、识别车牌号码、识别车牌颜色、识别车牌所属地信息
    • 右侧可以分成3个区域,顶部区域包含窗体最小化,最大化,关闭功能;中间区域显示读取车辆图片;底部区域包含车牌显示区域、图片读取、车牌信息存储功能
  • 车牌识别

    • 通过读取图片进行车牌区域提取输出
    • 车牌自动识别结果输出
  • 车牌信息显示存储

    • 根据自动识别结果对车牌各类信息显示
    • 对录入识别的车辆车牌识别信息存储

二、实现步骤

1. UI设计排版布局

根据车牌识别需要的功能,首先进行UI布局设计,我们这次还是使用的pyqt5。核心设计代码如下:

# author:CSDN-Dragon少年
def setupUi(self, MainWindow):
    MainWindow.setObjectName("MainWindow")
    MainWindow.resize(1213, 670)
    MainWindow.setFixedSize(1213, 670)  # 设置窗体固定大小
    MainWindow.setToolButtonStyle(QtCore.Qt.ToolButtonIconOnly)
    self.centralwidget = QtWidgets.QWidget(MainWindow)
    self.centralwidget.setObjectName("centralwidget")
    self.scrollArea = QtWidgets.QScrollArea(self.centralwidget)
    self.scrollArea.setGeometry(QtCore.QRect(690, 40, 511, 460))
    self.scrollArea.setWidgetResizable(True)
    self.scrollArea.setObjectName("scrollArea")
    self.scrollAreaWidgetContents = QtWidgets.QWidget()
    self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 500, 489))
    self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
    self.label_0 = QtWidgets.QLabel(self.scrollAreaWidgetContents)
    self.label_0.setGeometry(QtCore.QRect(10, 10, 111, 20))
    font = QtGui.QFont()
    font.setPointSize(11)
    self.label_0.setFont(font)
    self.label_0.setObjectName("label_0")
    self.label = QtWidgets.QLabel(self.scrollAreaWidgetContents)
    self.label.setGeometry(QtCore.QRect(10, 40, 481, 420))
    self.label.setObjectName("label")
    self.label.setAlignment(Qt.AlignCenter)
    self.scrollArea.setWidget(self.scrollAreaWidgetContents)
    self.scrollArea_2 = QtWidgets.QScrollArea(self.centralwidget)
    self.scrollArea_2.setGeometry(QtCore.QRect(10, 10, 671, 631))
    self.scrollArea_2.setWidgetResizable(True)
    self.scrollArea_2.setObjectName("scrollArea_2")
    self.scrollAreaWidgetContents_1 = QtWidgets.QWidget()
    self.scrollAreaWidgetContents_1.setGeometry(QtCore.QRect(0, 0, 669, 629))
    self.scrollAreaWidgetContents_1.setObjectName("scrollAreaWidgetContents_1")
    self.label_1 = QtWidgets.QLabel(self.scrollAreaWidgetContents_1)
    self.label_1.setGeometry(QtCore.QRect(10, 10, 111, 20))
    font = QtGui.QFont()
    font.setPointSize(11)
    self.label_1.setFont(font)
    self.label_1.setObjectName("label_1")
    self.tableWidget = QtWidgets.QTableWidget(self.scrollAreaWidgetContents_1)
    self.tableWidget.setGeometry(QtCore.QRect(10, 40, 651, 581))  # 581))
    self.tableWidget.setObjectName("tableWidget")
    self.tableWidget.setColumnCount(5)
    self.tableWidget.setColumnWidth(0, 140)  # 设置1列的宽度
    self.tableWidget.setColumnWidth(1, 130)  # 设置2列的宽度
    self.tableWidget.setColumnWidth(2, 110)  # 设置3列的宽度
    self.tableWidget.setColumnWidth(3, 90)  # 设置4列的宽度
    self.tableWidget.setColumnWidth(4, 181)  # 设置5列的宽度
    self.tableWidget.setHorizontalHeaderLabels(["图片名称", "录入时间", "车牌号码", "车牌类型", "车牌信息"])
    self.tableWidget.setRowCount(self.RowLength)
    self.tableWidget.verticalHeader().setVisible(False)  # 隐藏垂直表头)
    self.tableWidget.setEditTriggers(QAbstractItemView.NoEditTriggers)
    self.tableWidget.raise_()
    self.scrollArea_2.setWidget(self.scrollAreaWidgetContents_1)
    self.scrollArea_3 = QtWidgets.QScrollArea(self.centralwidget)
    self.scrollArea_3.setGeometry(QtCore.QRect(690, 510, 341, 131))
    self.scrollArea_3.setWidgetResizable(True)
    self.scrollArea_3.setObjectName("scrollArea_3")
    self.scrollAreaWidgetContents_3 = QtWidgets.QWidget()
    self.scrollAreaWidgetContents_3.setGeometry(QtCore.QRect(0, 0, 339, 129))
    self.scrollAreaWidgetContents_3.setObjectName("scrollAreaWidgetContents_3")
    self.label_2 = QtWidgets.QLabel(self.scrollAreaWidgetContents_3)
    self.label_2.setGeometry(QtCore.QRect(10, 10, 111, 20))
    font = QtGui.QFont()
    font.setPointSize(11)
    self.label_2.setFont(font)
    self.label_2.setObjectName("label_2")
    self.label_3 = QtWidgets.QLabel(self.scrollAreaWidgetContents_3)
    self.label_3.setGeometry(QtCore.QRect(10, 40, 321, 81))
    self.label_3.setObjectName("label_3")
    self.scrollArea_3.setWidget(self.scrollAreaWidgetContents_3)
    self.scrollArea_4 = QtWidgets.QScrollArea(self.centralwidget)
    self.scrollArea_4.setGeometry(QtCore.QRect(1040, 510, 161, 131))
    self.scrollArea_4.setWidgetResizable(True)
    self.scrollArea_4.setObjectName("scrollArea_4")
    self.scrollAreaWidgetContents_4 = QtWidgets.QWidget()
    self.scrollAreaWidgetContents_4.setGeometry(QtCore.QRect(0, 0, 159, 129))
    self.scrollAreaWidgetContents_4.setObjectName("scrollAreaWidgetContents_4")
    self.pushButton_2 = QtWidgets.QPushButton(self.scrollAreaWidgetContents_4)
    self.pushButton_2.setGeometry(QtCore.QRect(20, 50, 121, 31))
    self.pushButton_2.setObjectName("pushButton_2")
    self.pushButton = QtWidgets.QPushButton(self.scrollAreaWidgetContents_4)
    self.pushButton.setGeometry(QtCore.QRect(20, 90, 121, 31))
    self.pushButton.setObjectName("pushButton")
    self.label_4 = QtWidgets.QLabel(self.scrollAreaWidgetContents_4)
    self.label_4.setGeometry(QtCore.QRect(10, 10, 111, 20))
    font = QtGui.QFont()
    font.setPointSize(11)
    self.label_4.setFont(font)
    self.label_4.setObjectName("label_4")
    self.scrollArea_4.setWidget(self.scrollAreaWidgetContents_4)
    MainWindow.setCentralWidget(self.centralwidget)
    self.statusbar = QtWidgets.QStatusBar(MainWindow)
    self.statusbar.setObjectName("statusbar")
    MainWindow.setStatusBar(self.statusbar)
    self.retranslateUi(MainWindow)
    QtCore.QMetaObject.connectSlotsByName(MainWindow)
    self.retranslateUi(MainWindow)
    QtCore.QMetaObject.connectSlotsByName(MainWindow)
    self.pushButton.clicked.connect(self.__openimage)  # 设置点击事件
    self.pushButton.setStyleSheet('''QPushButton{background:#222225;border-radius:5px;}QPushButton:hover{background:#2B2B2B;}''')
    self.pushButton_2.clicked.connect(self.__writeFiles)  # 设置点击事件
    self.pushButton_2.setStyleSheet('''QPushButton{background:#222225;border-radius:5px;}QPushButton:hover{background:#2B2B2B;}''')
    self.retranslateUi(MainWindow)
    self.close_widget = QtWidgets.QWidget(self.centralwidget)
    self.close_widget.setGeometry(QtCore.QRect(1130, 0, 90, 50))
    self.close_widget.setObjectName("close_widget")
    self.close_layout = QGridLayout()  # 创建左侧部件的网格布局层
    self.close_widget.setLayout(self.close_layout)  # 设置左侧部件布局为网格
    self.left_close = QPushButton("")  # 关闭按钮
    self.left_close.clicked.connect(self.close)
    self.left_visit = QPushButton("")  # 空白按钮
    self.left_visit.clicked.connect(MainWindow.big)
    self.left_mini = QPushButton("")  # 最小化按钮
    self.left_mini.clicked.connect(MainWindow.mini)
    self.close_layout.addWidget(self.left_mini, 0, 0, 1, 1)
    self.close_layout.addWidget(self.left_close, 0, 2, 1, 1)
    self.close_layout.addWidget(self.left_visit, 0, 1, 1, 1)
    self.left_close.setFixedSize(15, 15)  # 设置关闭按钮的大小
    self.left_visit.setFixedSize(15, 15)  # 设置按钮大小
    self.left_mini.setFixedSize(15, 15)  # 设置最小化按钮大小
    self.left_close.setStyleSheet(
        '''QPushButton{background:#F76677;border-radius:5px;}QPushButton:hover{background:red;}''')
    self.left_visit.setStyleSheet(
        '''QPushButton{background:#F7D674;border-radius:5px;}QPushButton:hover{background:yellow;}''')
    self.left_mini.setStyleSheet(
        '''QPushButton{background:#6DDF6D;border-radius:5px;}QPushButton:hover{background:green;}''')

    QtCore.QMetaObject.connectSlotsByName(MainWindow)
    self.ProjectPath = os.getcwd()  # 获取当前工程文件位置
    self.scrollAreaWidgetContents.setStyleSheet(sc)
    self.scrollAreaWidgetContents_3.setStyleSheet(sc)
    self.scrollAreaWidgetContents_4.setStyleSheet(sc)
    b =             '''
         color:white;
         background:#2B2B2B;
        '''
    self.label_0.setStyleSheet(b)
    self.label_1.setStyleSheet(b)
    self.label_2.setStyleSheet(b)
    self.label_3.setStyleSheet(b)
    MainWindow.setWindowOpacity(0.95)  # 设置窗口透明度
    MainWindow.setAttribute(Qt.WA_TranslucentBackground)
    MainWindow.setWindowFlag(Qt.FramelessWindowHint)  # 隐藏边框

# author:CSDN-Dragon少年
def retranslateUi(self, MainWindow):
    _translate = QtCore.QCoreApplication.translate
    MainWindow.setWindowTitle(_translate("MainWindow", "车牌识别系统"))
    self.label_0.setText(_translate("MainWindow", "原始图片:"))
    self.label.setText(_translate("MainWindow", ""))
    self.label_1.setText(_translate("MainWindow", "识别结果:"))
    self.label_2.setText(_translate("MainWindow", "车牌区域:"))
    self.label_3.setText(_translate("MainWindow", ""))
    self.pushButton.setText(_translate("MainWindow", "打开文件"))
    self.pushButton_2.setText(_translate("MainWindow", "导出数据"))
    self.label_4.setText(_translate("MainWindow", "事件:"))
    self.scrollAreaWidgetContents_1.show()

UI实现效果如下:

2. 车牌识别

接下来我们需要实现两个核心功能,包括获取车牌ROI区域车牌自动识别功能。

车牌ROI区域提取:

根据读取的车辆图片,预处理进行车牌ROI区域提取,主要通过Opencv的图像处理相关知识点来完成。主要包括对图像去噪、二值化、边缘轮廓提取、矩形区域矫正、蓝绿黄车牌颜色定位识别。核心代码如下:

# author:CSDN-Dragon少年
# 预处理
def pretreatment(self, car_pic):
    if type(car_pic) == type(""):
        img = self.__imreadex(car_pic)
    else:
        img = car_pic
    pic_hight, pic_width = img.shape[:2]

    if pic_width > self.MAX_WIDTH:
        resize_rate = self.MAX_WIDTH / pic_width
        img = cv2.resize(img, (self.MAX_WIDTH, int(pic_hight * resize_rate)),
                         interpolation=cv2.INTER_AREA)  # 图片分辨率调整
    blur = self.cfg["blur"]
    # 高斯去噪
    if blur > 0:
        img = cv2.GaussianBlur(img, (blur, blur), 0)
    oldimg = img
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    kernel = np.ones((20, 20), np.uint8)
    img_opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)  # 开运算
    img_opening = cv2.addWeighted(img, 1, img_opening, -1, 0);  # 与上一次开运算结果融合
    # cv2.imshow('img_opening', img_opening)

    # 找到图像边缘
    ret, img_thresh = cv2.threshold(img_opening, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)  # 二值化
    img_edge = cv2.Canny(img_thresh, 100, 200)
    # cv2.imshow('img_edge', img_edge)

    # 使用开运算和闭运算让图像边缘成为一个整体
    kernel = np.ones((self.cfg["morphologyr"], self.cfg["morphologyc"]), np.uint8)
    img_edge1 = cv2.morphologyEx(img_edge, cv2.MORPH_CLOSE, kernel)  # 闭运算
    img_edge2 = cv2.morphologyEx(img_edge1, cv2.MORPH_OPEN, kernel)  # 开运算
    # cv2.imshow('img_edge2', img_edge2)
    # cv2.imwrite('./edge2.png', img_edge2)
    # 查找图像边缘整体形成的矩形区域,可能有很多,车牌就在其中一个矩形区域中
    image, contours, hierarchy = cv2.findContours(img_edge2, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    contours = [cnt for cnt in contours if cv2.contourArea(cnt) > self.Min_Area]
    # 逐个排除不是车牌的矩形区域
    car_contours = []
    for cnt in contours:
        # 框选 生成最小外接矩形 返回值(中心(x,y), (宽,高), 旋转角度)
        rect = cv2.minAreaRect(cnt)
        # print('宽高:',rect[1])
        area_width, area_height = rect[1]
        # 选择宽大于高的区域
        if area_width < area_height:
            area_width, area_height = area_height, area_width
        wh_ratio = area_width / area_height
        # print('宽高比:',wh_ratio)
        # 要求矩形区域长宽比在2到5.5之间,2到5.5是车牌的长宽比,其余的矩形排除
        if wh_ratio > 2 and wh_ratio < 5.5:
            car_contours.append(rect)
            box = cv2.boxPoints(rect)
            box = np.int0(box)
    # 矩形区域可能是倾斜的矩形,需要矫正,以便使用颜色定位
    card_imgs = []
    for rect in car_contours:
        if rect[2] > -1 and rect[2] < 1:  # 创造角度,使得左、高、右、低拿到正确的值
            angle = 1
        else:
            angle = rect[2]
        rect = (rect[0], (rect[1][0桌面太单调?一起用Python做个自定义动态壁纸,竟然还可以放视频!

桌面太单调?一起用Python做个自定义动画挂件,好玩又有趣!

java毕设项目车牌号码识别系统开源了,很好玩,建议尝试

java毕设项目车牌号码识别系统开源了,很好玩,建议尝试

用Python做一个人脸识别系统,简单操作又实用~

实战案例!1行Python代码识别车牌号码,轻松写一个停车场管理系统,YYDS