詳細過程帶你用Python做車牌自動識別系統(tǒng)
前言
前段時間,用PyQt5寫了兩篇文章,自己用python做的一款超炫酷音樂播放器、用Python做個個性的動畫掛件讓桌面不單調(diào)。有粉絲問我,為什么要用PyQt5?之前沒接觸過PyQt5,能不能多分享一些這方面的開發(fā)案例?
今天就繼續(xù)給大家分享一個實戰(zhàn)案例,帶大家一起用Python的PyQt5開發(fā)一個車牌自動識別系統(tǒng)!
首先一起來看看最終實現(xiàn)的車牌識別系統(tǒng)效果圖:
下面,我們就開始介紹如何實現(xiàn)這款自動車牌識別系統(tǒng)。
一、核心功能設(shè)計
總體來說,我們首先要進行UI界面構(gòu)建設(shè)計,根據(jù)車牌識別系統(tǒng)功能進行畫面排版布局;其次我們的這款車牌識別系統(tǒng)的主要功能車輛圖片讀取識別顯示、圖片中車牌ROI區(qū)域獲取、車牌識別結(jié)果輸出顯示。
對于結(jié)果輸出顯示,我們主要包含了讀取圖片名稱、讀取錄入時間、識別車牌號碼、識別車牌顏色、識別車牌所屬地。最后我們還可以將車牌識別系統(tǒng)的數(shù)據(jù)信息導(dǎo)出本地存儲。
拆解需求,大致可以整理出核心功能如下:
UI設(shè)計排版布局
- 左側(cè)區(qū)域進行識別信息顯示,包含圖片名稱、讀取錄入時間、識別車牌號碼、識別車牌顏色、識別車牌所屬地信息
- 右側(cè)可以分成3個區(qū)域,頂部區(qū)域包含窗體最小化,最大化,關(guān)閉功能;中間區(qū)域顯示讀取車輛圖片;底部區(qū)域包含車牌顯示區(qū)域、圖片讀取、車牌信息存儲功能
車牌識別
- 通過讀取圖片進行車牌區(qū)域提取輸出
- 車牌自動識別結(jié)果輸出
車牌信息顯示存儲
- 根據(jù)自動識別結(jié)果對車牌各類信息顯示
- 對錄入識別的車輛車牌識別信息存儲
二、實現(xiàn)步驟
1. UI設(shè)計排版布局
根據(jù)車牌識別需要的功能,首先進行UI布局設(shè)計,我們這次還是使用的pyqt5。核心設(shè)計代碼如下:
# author:CSDN-Dragon少年 def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(1213, 670) MainWindow.setFixedSize(1213, 670) # 設(shè)置窗體固定大小 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) # 設(shè)置1列的寬度 self.tableWidget.setColumnWidth(1, 130) # 設(shè)置2列的寬度 self.tableWidget.setColumnWidth(2, 110) # 設(shè)置3列的寬度 self.tableWidget.setColumnWidth(3, 90) # 設(shè)置4列的寬度 self.tableWidget.setColumnWidth(4, 181) # 設(shè)置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) # 設(shè)置點擊事件 self.pushButton.setStyleSheet('''QPushButton{background:#222225;border-radius:5px;}QPushButton:hover{background:#2B2B2B;}''') self.pushButton_2.clicked.connect(self.__writeFiles) # 設(shè)置點擊事件 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() # 創(chuàng)建左側(cè)部件的網(wǎng)格布局層 self.close_widget.setLayout(self.close_layout) # 設(shè)置左側(cè)部件布局為網(wǎng)格 self.left_close = QPushButton("") # 關(guān)閉按鈕 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) # 設(shè)置關(guān)閉按鈕的大小 self.left_visit.setFixedSize(15, 15) # 設(shè)置按鈕大小 self.left_mini.setFixedSize(15, 15) # 設(shè)置最小化按鈕大小 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() # 獲取當(dāng)前工程文件位置 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) # 設(shè)置窗口透明度 MainWindow.setAttribute(Qt.WA_TranslucentBackground) MainWindow.setWindowFlag(Qt.FramelessWindowHint) # 隱藏邊框 # author:CSDN-Dragon少年 def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "車牌識別系統(tǒng)")) self.label_0.setText(_translate("MainWindow", "原始圖片:")) self.label.setText(_translate("MainWindow", "")) self.label_1.setText(_translate("MainWindow", "識別結(jié)果:")) self.label_2.setText(_translate("MainWindow", "車牌區(qū)域:")) self.label_3.setText(_translate("MainWindow", "")) self.pushButton.setText(_translate("MainWindow", "打開文件")) self.pushButton_2.setText(_translate("MainWindow", "導(dǎo)出數(shù)據(jù)")) self.label_4.setText(_translate("MainWindow", "事件:")) self.scrollAreaWidgetContents_1.show()
UI實現(xiàn)效果如下:
2. 車牌識別
接下來我們需要實現(xiàn)兩個核心功能,包括獲取車牌ROI區(qū)域和車牌自動識別功能。
車牌ROI區(qū)域提取:
根據(jù)讀取的車輛圖片,預(yù)處理進行車牌ROI區(qū)域提取,主要通過Opencv的圖像處理相關(guān)知識點來完成。主要包括對圖像去噪、二值化、邊緣輪廓提取、矩形區(qū)域矯正、藍綠黃車牌顏色定位識別。核心代碼如下:
# author:CSDN-Dragon少年 # 預(yù)處理 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) # 圖片分辨率調(diào)整 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); # 與上一次開運算結(jié)果融合 # 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) # 查找圖像邊緣整體形成的矩形區(qū)域,可能有很多,車牌就在其中一個矩形區(qū)域中 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] # 逐個排除不是車牌的矩形區(qū)域 car_contours = [] for cnt in contours: # 框選 生成最小外接矩形 返回值(中心(x,y), (寬,高), 旋轉(zhuǎn)角度) rect = cv2.minAreaRect(cnt) # print('寬高:',rect[1]) area_width, area_height = rect[1] # 選擇寬大于高的區(qū)域 if area_width < area_height: area_width, area_height = area_height, area_width wh_ratio = area_width / area_height # print('寬高比:',wh_ratio) # 要求矩形區(qū)域長寬比在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) # 矩形區(qū)域可能是傾斜的矩形,需要矯正,以便使用顏色定位 card_imgs = [] for rect in car_contours: if rect[2] > -1 and rect[2] < 1: # 創(chuàng)造角度,使得左、高、右、低拿到正確的值 angle = 1 else: angle = rect[2] rect = (rect[0], (rect[1][0] + 5, rect[1][1] + 5), angle) # 擴大范圍,避免車牌邊緣被排除 box = cv2.boxPoints(rect) heigth_point = right_point = [0, 0] left_point = low_point = [pic_width, pic_hight] for point in box: if left_point[0] > point[0]: left_point = point if low_point[1] > point[1]: low_point = point if heigth_point[1] < point[1]: heigth_point = point if right_point[0] < point[0]: right_point = point if left_point[1] <= right_point[1]: # 正角度 new_right_point = [right_point[0], heigth_point[1]] pts2 = np.float32([left_point, heigth_point, new_right_point]) # 字符只是高度需要改變 pts1 = np.float32([left_point, heigth_point, right_point]) M = cv2.getAffineTransform(pts1, pts2) dst = cv2.warpAffine(oldimg, M, (pic_width, pic_hight)) self.__point_limit(new_right_point) self.__point_limit(heigth_point) self.__point_limit(left_point) card_img = dst[int(left_point[1]):int(heigth_point[1]), int(left_point[0]):int(new_right_point[0])] card_imgs.append(card_img) elif left_point[1] > right_point[1]: # 負角度 new_left_point = [left_point[0], heigth_point[1]] pts2 = np.float32([new_left_point, heigth_point, right_point]) # 字符只是高度需要改變 pts1 = np.float32([left_point, heigth_point, right_point]) M = cv2.getAffineTransform(pts1, pts2) dst = cv2.warpAffine(oldimg, M, (pic_width, pic_hight)) self.__point_limit(right_point) self.__point_limit(heigth_point) self.__point_limit(new_left_point) card_img = dst[int(right_point[1]):int(heigth_point[1]), int(new_left_point[0]):int(right_point[0])] card_imgs.append(card_img) #使用顏色定位,排除不是車牌的矩形,目前只識別藍、綠、黃車牌 colors = [] for card_index, card_img in enumerate(card_imgs): green = yellow = blue = black = white = 0 try: # 有轉(zhuǎn)換失敗的可能,原因來自于上面矯正矩形出錯 card_img_hsv = cv2.cvtColor(card_img, cv2.COLOR_BGR2HSV) except: print('BGR轉(zhuǎn)HSV失敗') card_imgs = colors = None return card_imgs, colors if card_img_hsv is None: continue row_num, col_num = card_img_hsv.shape[:2] card_img_count = row_num * col_num # 確定車牌顏色 for i in range(row_num): for j in range(col_num): H = card_img_hsv.item(i, j, 0) S = card_img_hsv.item(i, j, 1) V = card_img_hsv.item(i, j, 2) if 11 < H <= 34 and S > 34: # 圖片分辨率調(diào)整 yellow += 1 elif 35 < H <= 99 and S > 34: # 圖片分辨率調(diào)整 green += 1 elif 99 < H <= 124 and S > 34: # 圖片分辨率調(diào)整 blue += 1 if 0 < H < 180 and 0 < S < 255 and 0 < V < 46: black += 1 elif 0 < H < 180 and 0 < S < 43 and 221 < V < 225: white += 1 color = "no" # print('黃:{:<6}綠:{:<6}藍:{:<6}'.format(yellow,green,blue)) limit1 = limit2 = 0 if yellow * 2 >= card_img_count: color = "yellow" limit1 = 11 limit2 = 34 # 有的圖片有色偏偏綠 elif green * 2 >= card_img_count: color = "green" limit1 = 35 limit2 = 99 elif blue * 2 >= card_img_count: color = "blue" limit1 = 100 limit2 = 124 # 有的圖片有色偏偏紫 elif black + white >= card_img_count * 0.7: color = "bw" # print(color) colors.append(color) # print(blue, green, yellow, black, white, card_img_count) if limit1 == 0: continue # 根據(jù)車牌顏色再定位,縮小邊緣非車牌邊界 xl, xr, yh, yl = self.accurate_place(card_img_hsv, limit1, limit2, color) if yl == yh and xl == xr: continue need_accurate = False if yl >= yh: yl = 0 yh = row_num need_accurate = True if xl >= xr: xl = 0 xr = col_num need_accurate = True card_imgs[card_index] = card_img[yl:yh, xl:xr] \ if color != "green" or yl < (yh - yl) // 4 else card_img[yl - (yh - yl) // 4:yh, xl:xr] if need_accurate: # 可能x或y方向未縮小,需要再試一次 card_img = card_imgs[card_index] card_img_hsv = cv2.cvtColor(card_img, cv2.COLOR_BGR2HSV) xl, xr, yh, yl = self.accurate_place(card_img_hsv, limit1, limit2, color) if yl == yh and xl == xr: continue if yl >= yh: yl = 0 yh = row_num if xl >= xr: xl = 0 xr = col_num card_imgs[card_index] = card_img[yl:yh, xl:xr] \ if color != "green" or yl < (yh - yl) // 4 else card_img[yl - (yh - yl) // 4:yh, xl:xr] # cv2.imshow("result", card_imgs[0]) # cv2.imwrite('1.jpg', card_imgs[0]) # print('顏色識別結(jié)果:' + colors[0]) return card_imgs, colors
至此我們就可以輸出車牌ROI區(qū)域和車牌顏色了,效果如下:
車牌自動識別:
本篇介紹調(diào)用百度AI提供的車牌識別接口 – 百度AI開放平臺鏈接,識別效果非常不錯。
這里面我們可以創(chuàng)建一個車牌識別的應(yīng)用,其中的API Key及Secret Key后面我們調(diào)用車牌識別檢測接口時會用到。
我們可以看到官方提供的幫助文檔,介紹了如何調(diào)用請求URL數(shù)據(jù)格式,向API服務(wù)地址使用POST發(fā)送請求,必須在URL中帶上參數(shù)access_token,可通過后臺的API Key和Secret Key生成。這里面的API Key和Secret Key就是我們上面提到的。
接下來我們看看調(diào)用車牌識別接口代碼示例。
那我們?nèi)绾潍@取識別的車牌號碼呢?API文檔可以看到里面有個words_result字典 ,其中的color代表車牌顏色 ,number代表車牌號碼 。這樣我就可以知道識別的車牌顏色和車牌號了。
車牌識別的接口調(diào)用流程基本已經(jīng)清楚了,下面就可以進行代碼實現(xiàn)了。
# author:CSDN-Dragon少年 def get_token(self): host = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=' + self.client_id + '&client_secret=' + self.client_secret response = requests.get(host) if response: token_info = response.json() token_key = token_info['access_token'] return token_key # author:CSDN-Dragon少年 def get_license_plate(self, car_pic): result = {} card_imgs, colors = self.pretreatment(car_pic) request_url = "https://aip.baidubce.com/rest/2.0/ocr/v1/license_plate" # 二進制方式打開圖片文件 f = open(car_pic, 'rb') img = base64.b64encode(f.read()) params = {"image": img} access_token = self.get_token() request_url = request_url + "?access_token=" + access_token headers = {'content-type': 'application/x-www-form-urlencoded'} response = requests.post(request_url, data=params, headers=headers) if response: print(response.json()) license_result = response.json()['words_result']['number'] card_color = response.json()['words_result']['color'] if license_result != []: result['InputTime'] = time.strftime("%Y-%m-%d %H:%M:%S") result['Type'] = self.cardtype[card_color] result['Picture'] = card_imgs[0] result['Number'] = ''.join(license_result[:2]) + '·' + ''.join(license_result[2:]) try: result['From'] = ''.join(self.Prefecture[license_result[0]][license_result[1]]) except: result['From'] = '未知' return result else: return None
這樣我們就可以拿到車牌顏色和車牌號碼了,效果如下:
3. 車牌信息顯示存儲
3.1 車牌信息顯示:
# author:CSDN-Dragon少年 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(result['Number'])) self.tableWidget.setItem(self.RowLength - 1, 3, QTableWidgetItem(result['Type'])) if result['Type'] == '藍色牌照': self.tableWidget.item(self.RowLength - 1, 3).setBackground(QBrush(QColor(3, 128, 255))) elif result['Type'] == '綠色牌照': self.tableWidget.item(self.RowLength - 1, 3).setBackground(QBrush(QColor(98, 198, 148))) elif result['Type'] == '黃色牌照': self.tableWidget.item(self.RowLength - 1, 3).setBackground(QBrush(QColor(242, 202, 9))) self.tableWidget.setItem(self.RowLength - 1, 4, QTableWidgetItem(result['From'])) self.tableWidget.item(self.RowLength - 1, 4).setBackground(QBrush(QColor(255, 255, 255))) # 顯示識別到的車牌位置 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))
效果如下:
3.2 信息導(dǎo)出存儲:
# author:CSDN-Dragon少年 def __writexls(self, DATA, path): wb = xlwt.Workbook(); ws = wb.add_sheet('Data'); # DATA.insert(0, ['文件名稱','錄入時間', '車牌號碼', '車牌類型', '車牌信息']) for i, Data in enumerate(DATA): for j, data in enumerate(Data): ws.write(i, j, data) wb.save(path) QMessageBox.information(None, "成功", "數(shù)據(jù)已保存!", QMessageBox.Yes) def __writecsv(self, DATA, path): f = open(path, 'w') # DATA.insert(0, ['文件名稱','錄入時間', '車牌號碼', '車牌類型', '車牌信息']) for data in DATA: f.write((',').join(data) + '\n') f.close() QMessageBox.information(None, "成功", "數(shù)據(jù)已保存!", QMessageBox.Yes) def __writeFiles(self): path, filetype = QFileDialog.getSaveFileName(None, "另存為", self.ProjectPath, "Excel 工作簿(*.xls);;CSV (逗號分隔)(*.csv)") if path == "": # 未選擇 return if filetype == 'Excel 工作簿(*.xls)': self.__writexls(self.Data, path) elif filetype == 'CSV (逗號分隔)(*.csv)': self.__writecsv(self.Data, path)
效果如下:
導(dǎo)出車牌信息數(shù)據(jù)如下:
至此,整個車牌自動識別系統(tǒng)就完成了~今天我們就到這里,明天繼續(xù)努力!
如果本篇博客有任何錯誤,請批評指教,不勝感激 !
相關(guān)文章
Pyinstaller 打包發(fā)布經(jīng)驗總結(jié)
這篇文章主要介紹了Pyinstaller 打包發(fā)布經(jīng)驗總結(jié),使用Pyinstaller打包Python項目包含了大量的坑,感興趣的可以一起來了解一下2020-06-06Python中使用正則表達式及正則表達式匹配規(guī)則詳解
這篇文章主要介紹了Python中使用正則表達式以及正則表達式匹配規(guī)則,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-03-03Python用于學(xué)習(xí)重要算法的模塊pygorithm實例淺析
這篇文章主要介紹了Python用于學(xué)習(xí)重要算法的模塊pygorithm,結(jié)合實例形式簡單分析了pygorithm模塊的功能、算法調(diào)用、源碼獲取、時間復(fù)雜度計算等相關(guān)操作技巧,需要的朋友可以參考下2018-08-08pandas學(xué)習(xí)之df.fillna的具體使用
本文主要介紹了pandas學(xué)習(xí)之df.fillna的具體使用,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08Python中schedule模塊定時任務(wù)的使用方法(2)
這篇文章主要介紹了Python中schedule模塊定時任務(wù)的使用方法,文章基于上一篇文章的內(nèi)容展開的后續(xù),需要的朋友可以參考一下2022-05-05python開發(fā)之字符串string操作方法實例詳解
這篇文章主要介紹了python開發(fā)之字符串string操作方法,以實例形式較為詳細的分析了Python針對字符串的轉(zhuǎn)義、連接、換行、輸出等操作技巧,需要的朋友可以參考下2015-11-11