详细过程带你用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] + 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: # 有转换失败的可能,原因来自于上面矫正矩形出错 card_img_hsv = cv2.cvtcolor(card_img, cv2.color_bgr2hsv) except: print('bgr转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: # 图片分辨率调整 yellow += 1 elif 35 < h <= 99 and s > 34: # 图片分辨率调整 green += 1 elif 99 < h <= 124 and s > 34: # 图片分辨率调整 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 # 根据车牌颜色再定位,缩小边缘非车牌边界 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('颜色识别结果:' + colors[0]) return card_imgs, colors
至此我们就可以输出车牌roi区域和车牌颜色了,效果如下:
车牌自动识别:
本篇介绍调用百度ai提供的车牌识别接口 – 百度ai开放平台链接,识别效果非常不错。
这里面我们可以创建一个车牌识别的应用,其中的api key及secret key后面我们调用车牌识别检测接口时会用到。
我们可以看到官方提供的帮助文档,介绍了如何调用请求url数据格式,向api服务地址使用post发送请求,必须在url中带上参数access_token,可通过后台的api key和secret key生成。这里面的api key和secret key就是我们上面提到的。
接下来我们看看调用车牌识别接口代码示例。
那我们如何获取识别的车牌号码呢?api文档可以看到里面有个words_result字典 ,其中的color代表车牌颜色 ,number代表车牌号码 。这样我就可以知道识别的车牌颜色和车牌号了。
车牌识别的接口调用流程基本已经清楚了,下面就可以进行代码实现了。
# 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 信息导出存储:
# 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, "成功", "数据已保存!", 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, "成功", "数据已保存!", 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)
效果如下:
导出车牌信息数据如下:
至此,整个车牌自动识别系统就完成了~今天我们就到这里,明天继续努力!
如果本篇博客有任何错误,请批评指教,不胜感激 !