基于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界面)
73 2
|
2月前
|
机器学习/深度学习 监控 算法
基于计算机视觉(opencv)的运动计数(运动辅助)系统-源码+注释+报告
基于计算机视觉(opencv)的运动计数(运动辅助)系统-源码+注释+报告
64 3
|
1天前
|
移动开发 前端开发 Java
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
JavaFX是Java的下一代图形用户界面工具包。JavaFX是一组图形和媒体API,我们可以用它们来创建和部署富客户端应用程序。 JavaFX允许开发人员快速构建丰富的跨平台应用程序,允许开发人员在单个编程接口中组合图形,动画和UI控件。本文详细介绍了JavaFx的常见用法,相信读完本教程你一定有所收获!
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
|
1天前
|
人工智能 搜索推荐 算法
婚恋交友系统UI/UX设计优化 婚恋交友系统用户界面友好性提升 婚恋交友系统用户行为分析与优化 婚恋交友系统用户反馈收集与处理
针对婚恋交友系统的UI/UX设计优化,本文提出多项策略:简化用户界面、提升交互体验、个性化推荐算法;增强用户界面友好性,包括适应性、无障碍及情感化设计;通过数据收集与分析优化用户行为路径;建立多渠道反馈机制,分类处理并及时告知结果。这些措施旨在提高用户体验和满意度,促进平台健康发展。[点击查看完整演示和免费源码](https://gitee.com/duoke-official-open-source/hunlianjiaoyou)
14 4
|
22天前
|
前端开发 数据安全/隐私保护
全新紫色新UI数码盲盒系统源码/全开源无加密/附教程
全新紫色新UI数码盲盒系统源码/全开源无加密/附教程 前端uniapp+H5 后端FastAdmin框架 可打包成APP多端运行 亲测可用
45 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可高效打造符合用户需求的界面。
114 8
|
2月前
|
Ubuntu 编译器 计算机视觉
Ubuntu系统编译OpenCV4.8源码
【10月更文挑战第17天】只要三步即可搞定,第一步是下载指定版本的源码包;第二步是安装OpenCV4.8编译需要的编译器与第三方库支持;第三步就是编译OpenCV源码包生成安装文件并安装。
|
2月前
|
机器学习/深度学习 API 计算机视觉
基于Python_opencv人脸录入、识别系统(应用dlib机器学习库)(下)
基于Python_opencv人脸录入、识别系统(应用dlib机器学习库)(下)
38 2
|
2月前
|
机器学习/深度学习 存储 算法
基于Python_opencv人脸录入、识别系统(应用dlib机器学习库)(上)
基于Python_opencv人脸录入、识别系统(应用dlib机器学习库)(上)
55 1
|
1月前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义视图:打造个性化UI组件
【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。