基于opencv的车牌识别系统(UI界面采用tkinter设计)

简介: 基于opencv的车牌识别系统(UI界面采用tkinter设计)

基于opencv的车牌识别系统(UI界面采用tkinter设计)

本系统采用python语言搭配opencv进行开发,在传统的车牌识别项目上进行改进,开发独特的GUI界面,方便使用者的使用。

需要源码的朋友点赞、关注我、再私信我获取源码,如果未能及时回复可以留下邮箱耐心等待奥

先上运行截图(下图分别为图片识别和摄像头识别结果)

项目结构

项目结构很简单主要由以下三种文件构成:

  1. predict.py
  2. surface.py
  3. svmchinese.dat(用于存放训练好的模型)

其余文件还包括用于训练和测试的图片数据集,这里就不一一列举了

项目实现的流程

利用tkinter设计UI界面包括主窗口、按钮(button)、摄像头界面,识别结果的可视化等控件。

class Surface(ttk.Frame):
  pic_path = ""
  viewhigh = 400   #摄像头
  viewwide = 400
  update_time = 0
  thread = None
  thread_run = False
  camera = None
  color_transform = {"green":("绿牌","#55FF55"), "yello":("黄牌","#FFFF00"), "blue":("蓝牌","#6666FF")}
    
  def __init__(self, win):
    ttk.Frame.__init__(self, win)
    frame_left = ttk.Frame(self)
    frame_right1 = ttk.Frame(self)
    frame_right2 = ttk.Frame(self)
    win.title("车牌识别系统")
    win.geometry('700x500')
    self.pack(fill=tk.BOTH, expand=tk.YES, padx="5", pady="5")
    frame_left.pack(side=LEFT,expand=1,fill=BOTH)
    frame_right1.pack(side=TOP,expand=1,fill=tk.Y)
    frame_right2.pack(side=RIGHT,expand=0.5)
    ttk.Label(frame_left, text='原图:').pack(anchor="nw") 
    ttk.Label(frame_right1, text='截取车牌:').grid(column=0, row=0, sticky=tk.W)
    
    from_pic_ctl = ttk.Button(frame_right2, text="图片识别", width=10, command=self.from_pic)
    from_vedio_ctl = ttk.Button(frame_right2, text="摄像头识别", width=10, command=self.from_vedio)
    self.image_ctl = ttk.Label(frame_left)
    self.image_ctl.pack(anchor="nw")
    
    self.roi_ctl = ttk.Label(frame_right1)
    self.roi_ctl.grid(column=0, row=1, sticky=tk.W)
    ttk.Label(frame_right1, text='获取结果:').grid(column=0, row=2, sticky=tk.W)
    self.r_ctl = ttk.Label(frame_right1, text="")
    self.r_ctl.grid(column=0, row=3, sticky=tk.W)
    self.color_ctl = ttk.Label(frame_right1, text="", width="20")
    self.color_ctl.grid(column=0, row=4, sticky=tk.W)
    from_vedio_ctl.pack(anchor="se", pady="5")
    from_pic_ctl.pack(anchor="se", pady="5")
    self.predictor = predict.CardPredictor()
    self.predictor.train_svm()

打开识别图片的按钮点击事件的设计

def get_imgtk(self, img_bgr):
    img = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
    im = Image.fromarray(img)
    imgtk = ImageTk.PhotoImage(image=im)
    wide = imgtk.width()
    high = imgtk.height()
    if wide > self.viewwide or high > self.viewhigh:
      wide_factor = self.viewwide / wide
      high_factor = self.viewhigh / high
      factor = min(wide_factor, high_factor)
      
      wide = int(wide * factor)
      if wide <= 0 : wide = 1
      high = int(high * factor)
      if high <= 0 : high = 1
      im=im.resize((wide, high), Image.ANTIALIAS)
      imgtk = ImageTk.PhotoImage(image=im)
    return imgtk

打开摄像头识别按钮点击事件的设计

def from_vedio(self):
    if self.thread_run:
      return
    if self.camera is None:
      self.camera = cv2.VideoCapture(0)
      if not self.camera.isOpened():
        mBox.showwarning('警告', '摄像头打开失败!')
        self.camera = None
        return
    self.thread = threading.Thread(target=self.vedio_thread, args=(self,))
    self.thread.setDaemon(True)
    self.thread.start()
    self.thread_run = True

打开摄像头或者图片识别,按识别的方式采用不同方法对捕捉到的画面结果进行识别:

def from_pic(self):
    self.thread_run = False
    self.pic_path = askopenfilename(title="选择识别图片", filetypes=[("jpg图片", "*.jpg")])
    if self.pic_path:
      img_bgr = predict.imreadex(self.pic_path)
      self.imgtk = self.get_imgtk(img_bgr)
      self.image_ctl.configure(image=self.imgtk)
      resize_rates = (1, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4)
      for resize_rate in resize_rates:
        print("resize_rate:", resize_rate)
        r, roi, color = self.predictor.predict(img_bgr, resize_rate)
        if r:
          break
      self.show_roi(r, roi, color)
  @staticmethod
  def vedio_thread(self):
    self.thread_run = True
    predict_time = time.time()
    while self.thread_run:
      _, img_bgr = self.camera.read()
      self.imgtk = self.get_imgtk(img_bgr)
      self.image_ctl.configure(image=self.imgtk)
      if time.time() - predict_time > 2:
        r, roi, color = self.predictor.predict(img_bgr)
        self.show_roi(r, roi, color)
        predict_time = time.time()
    print("run end")

最后设计关闭窗口的事件:

def close_window():
  print("destroy")
  if surface.thread_run :
    surface.thread_run = False
    surface.thread.join(2.0)
  win.destroy()

预测模型的设计

预测模型设计思路为:

1、训练设计所需图片数据的尺寸,并读取图片数据;

2、根据设定的阈值和图片直方图,找出波峰,用于分隔字符;

3、根据找出的波峰,分隔图片,从而得到逐个字符图片;

4、将来自opencv的sample,用于svm训练;

5、训练svm、字符识别;

6、高斯去噪、去掉图像中不会是车牌的区域、找到图像边缘、使用开运算和闭运算让图像边缘成为一个整体;

7、查找图像边缘整体形成的矩形区域,可能有很多,车牌就在其中一个矩形区域中、矩形区域可能是倾斜的矩形,需要矫正,以便使用颜色定位、识别到的字符、定位的车牌图像、车牌颜色;

class CardPredictor:
  def __init__(self):
    #车牌识别的部分参数保存在js中,便于根据图片分辨率做调整
    f = open('config.js')
    j = json.load(f)  #读取文件
    for c in j["config"]:
      if c["open"]:
        self.cfg = c.copy()
        break
    else:
      raise RuntimeError('没有设置有效配置参数')
  def __del__(self):
    self.save_traindata()
  def train_svm(self):
    self.model = SVM(C=1, gamma=0.5)   #识别英文字母和数字,c表示容忍度,c越高,说明越不能容忍出现误差
    self.modelchinese = SVM(C=1, gamma=0.5)  #识别中文
    if os.path.exists("svm.dat"):
      self.model.load("svm.dat")
    else:
      chars_train = []
      chars_label = []
      
      for root, dirs, files in os.walk("train\\chars2"):
        if len(os.path.basename(root)) > 1:
          continue
        root_int = ord(os.path.basename(root))
        for filename in files:
          filepath = os.path.join(root,filename)
          digit_img = cv2.imread(filepath)
          digit_img = cv2.cvtColor(digit_img, cv2.COLOR_BGR2GRAY)
          chars_train.append(digit_img)
          chars_label.append(root_int)
      
      chars_train = list(map(deskew, chars_train))
      chars_train = preprocess_hog(chars_train)
      #chars_train = chars_train.reshape(-1, 20, 20).astype(np.float32)
      chars_label = np.array(chars_label)
      self.model.train(chars_train, chars_label)
    if os.path.exists("svmchinese.dat"):
      self.modelchinese.load("svmchinese.dat")
    else:
      chars_train = []
      chars_label = []
      for root, dirs, files in os.walk("train\\charsChinese"):
        if not os.path.basename(root).startswith("zh_"):
          continue
        pinyin = os.path.basename(root)
        index = provinces.index(pinyin) + PROVINCE_START + 1 #1是拼音对应的汉字
        for filename in files:
          filepath = os.path.join(root,filename)
          digit_img = cv2.imread(filepath)
          digit_img = cv2.cvtColor(digit_img, cv2.COLOR_BGR2GRAY)
          chars_train.append(digit_img)
          #chars_label.append(1)
          chars_label.append(index)
      chars_train = list(map(deskew, chars_train))
      chars_train = preprocess_hog(chars_train)
      #chars_train = chars_train.reshape(-1, 20, 20).astype(np.float32)
      chars_label = np.array(chars_label)
      print(chars_train.shape)
      self.modelchinese.train(chars_train, chars_label)
  def save_traindata(self):
    if not os.path.exists("svm.dat"):
      self.model.save("svm.dat")
    if not os.path.exists("svmchinese.dat"):
      self.modelchinese.save("svmchinese.dat")
  def accurate_place(self, card_img_hsv, limit1, limit2, color):
    row_num, col_num = card_img_hsv.shape[:2]
    xl = col_num
    xr = 0
    yh = 0
    yl = row_num
    #col_num_limit = self.cfg["col_num_limit"]
    row_num_limit = self.cfg["row_num_limit"]
    col_num_limit = col_num * 0.8 if color != "green" else col_num * 0.5#绿色有渐变
    for i in range(row_num):
      count = 0
      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 limit1 < H <= limit2 and 34 < S and 46 < V:
          count += 1
      if count > col_num_limit:
        if yl > i:
          yl = i
        if yh < i:
          yh = i
    for j in range(col_num):
      count = 0
      for i in range(row_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 limit1 < H <= limit2 and 34 < S and 46 < V:
          count += 1
      if count > row_num - row_num_limit:
        if xl > j:
          xl = j
        if xr < j:
          xr = j
    return xl, xr, yh, yl
    
  def predict(self, car_pic, resize_rate=1):
    if type(car_pic) == type(""):
      img = imreadex(car_pic)
    else:
      img = car_pic
    pic_hight, pic_width = img.shape[:2]
    if pic_width > MAX_WIDTH:
      pic_rate = MAX_WIDTH / pic_width
      img = cv2.resize(img, (MAX_WIDTH, int(pic_hight*pic_rate)), interpolation=cv2.INTER_LANCZOS4)
    
    if resize_rate != 1:
      img = cv2.resize(img, (int(pic_width*resize_rate), int(pic_hight*resize_rate)), interpolation=cv2.INTER_LANCZOS4)
      pic_hight, pic_width = img.shape[:2]
      
    print("h,w:", pic_hight, pic_width)
    blur = self.cfg["blur"]
    #高斯去噪
    if blur > 0:
      img = cv2.GaussianBlur(img, (blur, blur), 0)#图片分辨率调整
    oldimg = img
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    #equ = cv2.equalizeHist(img)
    #img = np.hstack((img, equ))
    #去掉图像中不会是车牌的区域
    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);
    #找到图像边缘
    ret, img_thresh = cv2.threshold(img_opening, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
    img_edge = cv2.Canny(img_thresh, 100, 200)
    #使用开运算和闭运算让图像边缘成为一个整体
    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)
    #查找图像边缘整体形成的矩形区域,可能有很多,车牌就在其中一个矩形区域中
    try:
      contours, hierarchy = cv2.findContours(img_edge2, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    except ValueError:
      image, contours, hierarchy = cv2.findContours(img_edge2, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    contours = [cnt for cnt in contours if cv2.contourArea(cnt) > Min_Area]
    print('len(contours)', len(contours))
    #一一排除不是车牌的矩形区域
    car_contours = []
    for cnt in contours:
      rect = cv2.minAreaRect(cnt)
      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)
        #oldimg = cv2.drawContours(oldimg, [box], 0, (0, 0, 255), 2)
        #cv2.imshow("edge4", oldimg)
        #cv2.waitKey(0)
    print(len(car_contours))
    print("精确定位")
    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))
        point_limit(new_right_point)
        point_limit(heigth_point)
        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)
        #cv2.imshow("card", card_img)
        #cv2.waitKey(0)
      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))
        point_limit(right_point)
        point_limit(heigth_point)
        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)
        #cv2.imshow("card", card_img)
        #cv2.waitKey(0)

创作不易,走过路过不要错过,点个赞再走吧,需要源码的朋友可以点赞关注我,再私信我获取源码!!!!

目录
相关文章
|
2月前
|
计算机视觉 Python
基于Dlib的人脸识别客户端(UI界面)
基于Dlib的人脸识别客户端(UI界面)
77 2
|
2月前
|
机器学习/深度学习 监控 算法
基于计算机视觉(opencv)的运动计数(运动辅助)系统-源码+注释+报告
基于计算机视觉(opencv)的运动计数(运动辅助)系统-源码+注释+报告
68 3
|
3天前
|
移动开发 前端开发 Java
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
JavaFX是Java的下一代图形用户界面工具包。JavaFX是一组图形和媒体API,我们可以用它们来创建和部署富客户端应用程序。 JavaFX允许开发人员快速构建丰富的跨平台应用程序,允许开发人员在单个编程接口中组合图形,动画和UI控件。本文详细介绍了JavaFx的常见用法,相信读完本教程你一定有所收获!
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
|
3天前
|
人工智能 搜索推荐 算法
婚恋交友系统UI/UX设计优化 婚恋交友系统用户界面友好性提升 婚恋交友系统用户行为分析与优化 婚恋交友系统用户反馈收集与处理
针对婚恋交友系统的UI/UX设计优化,本文提出多项策略:简化用户界面、提升交互体验、个性化推荐算法;增强用户界面友好性,包括适应性、无障碍及情感化设计;通过数据收集与分析优化用户行为路径;建立多渠道反馈机制,分类处理并及时告知结果。这些措施旨在提高用户体验和满意度,促进平台健康发展。[点击查看完整演示和免费源码](https://gitee.com/duoke-official-open-source/hunlianjiaoyou)
19 4
|
23天前
|
前端开发 数据安全/隐私保护
全新紫色新UI数码盲盒系统源码/全开源无加密/附教程
全新紫色新UI数码盲盒系统源码/全开源无加密/附教程 前端uniapp+H5 后端FastAdmin框架 可打包成APP多端运行 亲测可用
46 13
|
1月前
|
开发框架 JavaScript 前端开发
HarmonyOS UI开发:掌握ArkUI(包括Java UI和JS UI)进行界面开发
【10月更文挑战第22天】随着科技发展,操作系统呈现多元化趋势。华为推出的HarmonyOS以其全场景、多设备特性备受关注。本文介绍HarmonyOS的UI开发框架ArkUI,探讨Java UI和JS UI两种开发方式。Java UI适合复杂界面开发,性能较高;JS UI适合快速开发简单界面,跨平台性好。掌握ArkUI可高效打造符合用户需求的界面。
117 8
|
2月前
|
Ubuntu 编译器 计算机视觉
Ubuntu系统编译OpenCV4.8源码
【10月更文挑战第17天】只要三步即可搞定,第一步是下载指定版本的源码包;第二步是安装OpenCV4.8编译需要的编译器与第三方库支持;第三步就是编译OpenCV源码包生成安装文件并安装。
|
2月前
|
机器学习/深度学习 API 计算机视觉
基于Python_opencv人脸录入、识别系统(应用dlib机器学习库)(下)
基于Python_opencv人脸录入、识别系统(应用dlib机器学习库)(下)
42 2
|
2月前
|
机器学习/深度学习 存储 算法
基于Python_opencv人脸录入、识别系统(应用dlib机器学习库)(上)
基于Python_opencv人脸录入、识别系统(应用dlib机器学习库)(上)
58 1
|
2月前
|
计算机视觉
Opencv学习笔记(三):图像二值化函数cv2.threshold函数详解
这篇文章详细介绍了OpenCV库中的图像二值化函数`cv2.threshold`,包括二值化的概念、常见的阈值类型、函数的参数说明以及通过代码实例展示了如何应用该函数进行图像二值化处理,并展示了运行结果。
583 0
Opencv学习笔记(三):图像二值化函数cv2.threshold函数详解

热门文章

最新文章