基于opencv-python的车牌识别



记一篇数字图像课程的实训

基于opencv-python的车牌识别,代码主要参考CSDN上几个版主的代码,对代码进行了一定的优化,一定程度上提高了识别的准确率。并重写了一个GUI界面,添加数据导出功能。

先放一张运行截图:
在这里插入图片描述

基本实现流程:

  1. 读取图像
    使用cv2.imdecode()函数将图片文件转换成流数据,赋值到内存缓存中,便于后续图像操作。使用cv2.resize()函数对读取的图像进行缩放,以免图像过大导致识别耗时过长。

  2. 降噪
    使用cv2.GaussianBlur()进行高斯去噪。使用cv2.morphologyEx()函数进行开运算,再使用cv2.addWeighted()函数将运算结果与原图像做一次融合,从而去掉孤立的小点,毛刺等噪声。

# 高斯去噪 
if blur > 0:  
    img = cv2.GaussianBlur(img, (blur, blur), 0)  
oldimg = img  
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  
# cv2.imshow('GaussianBlur', img) 
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) 

在这里插入图片描述
在这里插入图片描述
3. 二值化
使用cv2.threshold()函数进行二值化处理,再使用cv2.Canny()函数找到各区域边缘。

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) 

在这里插入图片描述

  1. 将图像边缘连接为一个整体
    使用cv2.morphologyEx()cv2.morphologyEx()两个函数分别进行一次开运算(先腐蚀运算,再膨胀运算)和一个闭运算(先膨胀运算,再腐蚀运算),去掉较小区域,同时填平小孔,弥合小裂缝。将车牌位置凸显出来。

    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) 
    

    在这里插入图片描述

  2. 查找车牌(矩形区域)
    查找图像边缘整体形成的矩形区域,可能有很多,车牌就在其中一个矩形区域中,逐个排除不是车牌的矩形区域。车牌形成的矩形区域长宽比在2到5.5之间,因此使用cv2.minAreaRect()函数框选矩形区域计算长宽比,长宽比在2到5.5之间的可能是车牌,其余的矩形排除。最后使用cv2.drawContours()函数将可能是车牌的区域在原图中框选出来。(此处处理结果可能得到多个符合要求的矩形,而未必直接得到车牌位置,因此还需后续处理。)在这里插入图片描述

  3. 图形修正
    矩形区域可能是倾斜的矩形,需要矫正,以便使用颜色定位,从而进一步确认是否是车牌。类似下两图(仅列举出两个,可能有很多)。
    在这里插入图片描述 在这里插入图片描述

  4. 颜色识别
    使用颜色定位,排除不是车牌的矩形,目前只识别车牌的颜色主要为蓝、绿、黄三种颜色车牌。根据矩形的颜色不同从而选出最可能是车牌的矩形。同时匹配出车牌的类型(颜色类型)。使用参数为*cv2.COLOR_BGR2HSV*的cv2.cvtColor()函数将原始的RGB图像转换成HSV图像,以便定位颜色。
    基于HSV颜色模型可知色调H的取值范围为0°~360°,从红色开始按逆时针方向计算,红色为0°,绿色为120°,蓝色为240°。它们的补色是:黄色为60°,青色为180°,品红为300°;查阅相关资料确定出下表:

    黄色 绿色 蓝色
    H 14-34 34-99 99-124

    根据上表计算出每个矩形中各颜色的占有量,比较每个矩形三个颜色的占有量,即可确定最可能是车牌的矩形以及车牌颜色。
    在这里插入图片描述 在这里插入图片描述

  5. 车牌部分二值化
    利用参数为*cv2.COLOR_BGR2GRAY*的cv2.cvtColor()函数将定位到的车牌部分RGB图像转化为灰度图像,再利用cv2. threshold() 函数将灰度图像二值化。需要注意的是,黄、绿色车牌字符比背景暗、与蓝的车牌刚好相反,所以黄、绿车牌在二值化前需要利用cv2.bitwise_not( )函数取反向。

# 做一次锐化处理 
kernel = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]], np.float32)  # 锐化 
card_img = cv2.filter2D(card_img, -1, kernel=kernel)  
# cv2.imshow("custom_blur", card_img) 
  
# RGB转GARY 
gray_img = cv2.cvtColor(card_img, cv2.COLOR_BGR2GRAY)  
# cv2.imshow('gray_img', gray_img) 
  
# 黄、绿车牌字符比背景暗、与蓝车牌刚好相反,所以黄、绿车牌需要反向 
if color == "green" or color == "yellow":  
    gray_img = cv2.bitwise_not(gray_img)  
# 二值化 
ret, gray_img = cv2.threshold(gray_img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)  
# cv2.imshow('gray_img', gray_img)
  1. 字符分割(投影法)
    根据设定的阈值和图片直方图,找出波峰,利用找出的波峰,分隔图片。因为车牌中“ • ”也会产生一组波峰,因此将八组波峰中的第三组去除掉,即可得到每个字符的波峰,再根据每组波峰的宽度分割牌照图像得到每个字符的图像.
    在这里插入图片描述
    在这里插入图片描述

  2. 匹配模板
    将分割后的每个图像逐个与已训练好的模板进行匹配,得到识别结果。
    在这里插入图片描述

GUI 部分

在这里插入图片描述

  • 打开文件
def __openimage(self):  
    path, filetype = QFileDialog.getOpenFileName(None, "选择文件", self.ProjectPath,  
                               "JPEG Image (*.jpg);;PNG Image (*.png);;JFIF Image (*.jfif)")  # ;;All Files (*) 
    if path == "":  # 未选择文件 
        return  
    filename = path.split('/')[-1]  
  
    # 尺寸适配 
    size = cv2.cv2.imdecode(np.fromfile(path, dtype=np.uint8), cv2.IMREAD_COLOR).shape  
    if size[0] / size[1] > 1.0907:  
        w = size[1] * self.label.height() / size[0]  
        h = self.label.height()  
        jpg = QtGui.QPixmap(path).scaled(w, h)  
    elif size[0] / size[1] < 1.0907:  
        w = self.label.width()  
        h = size[0] * self.label.width() / size[1]  
        jpg = QtGui.QPixmap(path).scaled(w, h)  
    else:  
        jpg = QtGui.QPixmap(path).scaled(self.label.width(), self.label.height())  
  
    self.label.setPixmap(jpg)  
    result = self.__vlpr(path)  
    if result is not None:  
        self.Data.append(  
            [filename, result['InputTime'], result['Number'], result['Type'], str(result['UseTime']) + '秒',  
             result['From']])  
        self.__show(result, filename)  
    else:  
        QMessageBox.warning(None, "Error", "无法识别此图像!", QMessageBox.Yes)  
  • 界面显示
def __show(self, result, FileName):  
    # 显示表格 
    self.RowLength = self.RowLength + 1  
    if self.RowLength > 18:  
        self.tableWidget.setColumnWidth(5, 157)  
    self.tableWidget.setRowCount(self.RowLength)  
    self.tableWidget.setItem(self.RowLength - 1, 0, QTableWidgetItem(FileName))  
    self.tableWidget.setItem(self.RowLength - 1, 1, QTableWidgetItem(result['InputTime']))  
    self.tableWidget.setItem(self.RowLength - 1, 2, QTableWidgetItem(str(result['UseTime']) + '秒'))  
    self.tableWidget.setItem(self.RowLength - 1, 3, QTableWidgetItem(result['Number']))  
    self.tableWidget.setItem(self.RowLength - 1, 4, QTableWidgetItem(result['Type']))  
    if result['Type'] == '蓝色牌照':  
        self.tableWidget.item(self.RowLength - 1, 4).setBackground(QBrush(QColor(3, 128, 255)))  
    elif result['Type'] == '绿色牌照':  
        self.tableWidget.item(self.RowLength - 1, 4).setBackground(QBrush(QColor(98, 198, 148)))  
    elif result['Type'] == '黄色牌照':  
        self.tableWidget.item(self.RowLength - 1, 4).setBackground(QBrush(QColor(242, 202, 9)))  
    self.tableWidget.setItem(self.RowLength - 1, 5, QTableWidgetItem(result['From']))  
  
    # 显示识别到的车牌位置 
    size = (int(self.label_3.width()), int(self.label_3.height()))  
    shrink = cv2.resize(result['Picture'], size, interpolation=cv2.INTER_AREA)  
    shrink = cv2.cvtColor(shrink, cv2.COLOR_BGR2RGB)  
    self.QtImg = QtGui.QImage(shrink[:], shrink.shape[1], shrink.shape[0], shrink.shape[1] * 3,  
                              QtGui.QImage.Format_RGB888)  
    self.label_3.setPixmap(QtGui.QPixmap.fromImage(self.QtImg))  
  • 依赖文件检测
if os.path.exists('provinces.json'):  
    if os.path.exists('cardtype.json'):  
        if os.path.exists('Prefecture.json'):  
            if os.path.exists('config.js'):  
                app = QtWidgets.QApplication(sys.argv)