车牌识别之UI(Tkinter + OpenCV显示Picture和Video)

 

画一张总图:

图形界面开发

本篇只介绍图形界面开发。

遇到的第一个问题就是选择什么开发语言和技术。因为我之前用Python做过Tkinter的小东西,所以这次还是用Python Tkinter + OpenCV来搞吧。

这里面需要注意几个地方:

1. Tkinter 的布局

1.1 继承

我使用了class LPRSurface(Tk):类来继承Tk,如果是这样写需要在init函数中做做super().__init__()来实现对父类的初始化。

1.2 排版

我使用了界面定宽高的方式,所以把整个界面fix大小,并且规定好了

  • label:显示区域
  • Entry:输出区域
  • Button:按钮区域的大小和位置

使用了place而非pack。

1.3 更新

  • 图片的更新只需要重新config label的image即可,但是这里要注意image一定要用self修饰,否则就会出现资源被回收,图片无法正常加载的问题。 self.labelPic.configure(image = self.imgOri, bg="pink")
  • Video的更新则需要借助threading模块,重新创建线程了。

2. 路径记忆

使用了一个单独的类来记录打开过的图片路径,把这个路径存储在注册表中(这样做对Linux支持不好。。。).

需要借助winreg外部库

2.1 创建注册表

winreg.CreateKey(winreg.HKEY_CURRENT_USER, r"Software\{}\LPR".format(getpass.getuser()))

2.2 写入注册表

winreg.SetValueEx(key, "LPR", 0, winreg.REG_SZ, path)

2.3 读出注册表

winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Software\{}\LPR".format(getpass.getuser()))
winreg.QueryValueEx(key, "LPR")

3. OpenCV显示

3.1 格式转换

  • 还记得OpenCV是反人类的BGR排列吗?可以用cv.cvtColor把OpenCV read出来的图片转换成普通的RGB。
  • 然后再转换成Tkinter可以显示的数据格式,送给label中的image
imgCVRGB = cv.cvtColor(imgCV, cv.COLOR_BGR2RGB)
img = Image.fromarray(imgCVRGB)
imgTK = ImageTk.PhotoImage(image=img)

3.2 显示自适应

如果图片没有Tkinter的Lable大,则正常显示,否则就要等比例缩放,以保障整张图片可以在Lable显示完全。

widthScale = 1.0*self.labelPicWidth/picWidth
heightScale = 1.0*self.labelPicHeight/picHeight

scale = min(widthScale, heightScale)

resizeWidth = int(picWidth*scale)
resizeHeight = int(picHeight*scale)

img = img.resize((resizeWidth, resizeHeight), Image.ANTIALIAS)

3.3 Thread中的延迟

竟然可以用小数!

time.sleep(0.03)

from tkinter import *
from tkinter import filedialog
from tkinter import messagebox
from tkinter import ttk
import cv2 as cv
from PIL import Image, ImageTk
import numpy as np
import sys, random, datetime, os, winreg, getpass, time, threading

# PicPath: Save the last picture file path, this picture should be opened successful.
class LPRPath:
    def getPath(self):
        try:
            key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Software\{}\LPR".format(getpass.getuser()))
            self.path = winreg.QueryValueEx(key, "LPR")
        except:
            self.path = None
        return self.path

    def setPath(self, path):
        key = winreg.CreateKey(winreg.HKEY_CURRENT_USER, r"Software\{}\LPR".format(getpass.getuser()))
        winreg.SetValueEx(key, "LPR", 0, winreg.REG_SZ, path)
        self.path = path

# Main LPR surface
class LPRSurface(Tk):
	labelPicWidth  	= 700
	labelPicHeight 	= 700
	buttonWidth 	= 100
	buttonHeight 	= 50
	textWidth 		= 10
	textHeight 		= 50
	tkWidth 		= labelPicWidth
	tkHeigth 		= labelPicHeight + buttonHeight * 4
	isPicProcessing = False
	root 			= None
	videoThreadRun	= False

	def resizePicture(self, imgCV):
		if imgCV is None:
			print("Read Fail!")
			return None

		imgCVRGB = cv.cvtColor(imgCV, cv.COLOR_BGR2RGB)
		img = Image.fromarray(imgCVRGB)
		imgTK = ImageTk.PhotoImage(image=img)

		picWidth = imgTK.width()
		picHeight = imgTK.height()
		# print("Picture Size:", picWidth, picHeight)
		if picWidth <= self.labelPicWidth and picHeight <= self.labelPicHeight:
			return imgTK

		widthScale = 1.0*self.labelPicWidth/picWidth
		heightScale = 1.0*self.labelPicHeight/picHeight

		scale = min(widthScale, heightScale)

		resizeWidth = int(picWidth*scale)
		resizeHeight = int(picHeight*scale)

		img = img.resize((resizeWidth, resizeHeight), Image.ANTIALIAS)
		imgTK = ImageTk.PhotoImage(image=img)

		return imgTK

	# Load picture
	def loadPicture(self):
		# Get Picture Path
		if True == self.isPicProcessing:
			print("Please wait until previous picture process finish!!!")
			messagebox.showerror(title="PROCESSING", message="Please wait until previous picture process finish!!!")
			return
		self.videoThreadRun = False

		LPRPic = LPRPath()
		if None == LPRPic.getPath():
			initPath = ""
		else:
			initPath = LPRPic.path

		# fileName = None
		fileName = filedialog.askopenfilename(title='Load Picture', \
											  filetypes=[('Picture File', '*.jfif *.jpg *.png *.gif'), ('All Files', '*')], \
											  initialdir=initPath)

		print(fileName)
		if not os.path.isfile(fileName):
			print("Please input correct filename!")
			return False
		# Read Picture File.
		try:
			# self.imgOri = Image.open(fileName)
			# imgCV = cv.imdecode(np.fromfile(fileName, dtype=np.uint8), cv.IMREAD_COLOR)
			imgCV = cv.imread(fileName)
		except:
			print("Open file faile!")
			return False


		LPRPic.setPath(fileName)
		self.imgOri = self.resizePicture(imgCV)
		if self.imgOri is None:
			print("Load picture fail!")
			return False

		# self.imgOri = ImageTk.PhotoImage(self.imgOri)
		self.labelPic.configure(image = self.imgOri, bg="pink")

	# Video Thread
	def videoThread(self):
		self.videoThreadRun = True
		while self.videoThreadRun:
			ret, imgCV = self.camera.read()
			if ret is not True:
				print("Camera Read Fail!")
				return False
			self.imgOri = self.resizePicture(imgCV)
			self.labelPic.configure(image=self.imgOri, bg="pink")
			time.sleep(0.03)

		print("Video Thread Finish!")
		self.camera.release()

	# Load Video From Camera by OpenCV
	def loadVideo(self):
		if self.videoThreadRun == True:
			print("Video Is Opening!!!")
			messagebox.showerror(title="VIDEO ERROR", message="Camera Is Opening !!!")
			return False

		self.camera = cv.VideoCapture(0)
		if not self.camera.isOpened():
			print("Open Camera Fail!")
			messagebox.showerror(title="CAMERA ERROR", message="Open Camera Fail !!!")
			return False

		self.thread = threading.Thread(target=self.videoThread)
		self.thread.setDaemon(True)
		self.thread.start()
		self.videoThreadRun = True


	def __init__(self, *args, **kw):
		super().__init__()
		self.title("LPR Surface")
		self.geometry(str(self.tkWidth) + "x" + str(self.tkHeigth))
		self.resizable(0, 0)

		def labelInit():
			# Picture Label:
			self.labelPic = Label(self, text="Show Picture Area", font=("Arial", 24), bg="sky blue")
			self.labelPic.place(x=0, y=0, width=self.labelPicWidth, height=self.labelPicHeight)

			# Vehicle Plate Number Label:
			self.labelPlateNum = Label(self, text="Vehicle License Plate Number:", anchor=SW)
			self.labelPlateNum.place(x=0, y=self.labelPicHeight, \
									 width=self.textWidth * 20, height=self.textHeight)

			# Vehicle Colour Label:
			self.labelPlateCol = Label(self, text="Vehicle License Plate Color:", anchor=SW)
			self.labelPlateCol.place(x=0, y=self.labelPicHeight + self.textHeight * 2,
									 width=self.textWidth * 20, height=self.textHeight)

		def buttonInit():
			# Picture Button
			self.buttonPic = Button(self, text="Load Picture", command=self.loadPicture)
			self.buttonPic.place(x=self.tkWidth - 3 * self.buttonWidth / 2,
								 y=self.labelPicHeight + self.buttonHeight / 2, \
								 width=self.buttonWidth, height=self.buttonHeight)

			# Video Button
			self.buttonVideo = Button(self, text="Load Video", command=self.loadVideo)
			self.buttonVideo.place(x=self.tkWidth - 3 * self.buttonWidth / 2,
								   y=self.labelPicHeight + 5 * self.buttonHeight / 2, \
								   width=self.buttonWidth, height=self.buttonHeight)

		def entryInit():
			# Vehicle Plate Number Output
			self.entryPlateNumList = []
			for index in range(7):
				entryPlateNum = Entry(self)
				entryPlateNum.place(x=self.textWidth * index * 6, y=self.labelPicHeight + self.textHeight, \
									width=self.textWidth * 5, height=self.textHeight)
				self.entryPlateNumList.append(entryPlateNum)

			# Vehicle Plate Color Output
			self.entryPlateColor = Entry(self)
			self.entryPlateColor.place(x=0, y=self.labelPicHeight + self.textHeight * 3, \
									   width=self.textWidth * (42 - 1), height=self.textHeight)

		labelInit()
		buttonInit()
		entryInit()

		print("-------------init success-------------")
		self.mainloop()


if __name__ == '__main__':
	LS = LPRSurface()
	print("Finish")

 

 (四十九)车牌识别之界面(Tkinter + OpenCV显示Picture和Video) - 知乎

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/27194.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

冒泡排序、插入排序、希尔排序、选择排序

一、排序协议的定义 在博客的开头的&#xff0c;我们先给出排序协议的定义。因为我们本篇博客含有多种排序方式&#xff0c;为了使每种排序方法对外调用方式一致&#xff0c;我们需要定义一个排序的相关协议。所有排序的相关类都必须遵循该协议&#xff0c;让此协议来定义具体…

快速实现一个分布式定时器

定时器&#xff08;Timer&#xff09;是一种在业务开发中常用的组件&#xff0c;主要用在执行延时通知任务上。本文以笔者在工作中的实践作为基础&#xff0c;介绍如何使用平时部门最常用的组件快速实现一个业务常用的分布式定时器服务。同时介绍了过程中遇到问题的一些解决方案…

Android AIDL的使用(配源码)

零、完整源代码 链接: https://github.com/jx0260/TestGradle 一、创建AIDL文件 // IShopAidlInterface.aidl package com.example.testgradle;// Declare any non-default types here with import statementsinterface IShopAidlInterface {String getProductInfo(int prod…

【2023最新教程】一文3000字从0到1教你做app自动化测试(保姆级教程)

一、什么是App自动化&#xff1f;为什么要做App自动化&#xff1f; App自动化是指给 Android或iOS上的软件应用程序做的自动化测试。手工测试和自动化测试的对比如下&#xff1a; 手工测试优势&#xff1a;不可替代、发现更多bug、包含了人的想象力与理解力。 注意&#xff0c…

迅为iTOP-RK3588开发板Android12源码定制开发kernel开发

内核版本是 5.10.66 版本&#xff0c;内核默认的配置文件是 3588-android12/kernel-5.10/arch/arm64/configs/rockchip_defconfig 如果我们要使用图形化界面配置内核&#xff0c;操作方法如下所示&#xff1a; 方法一&#xff1a; 1 首先将默认的配置文件 rockchip_defconf…

博客系统测试用例设计之自动化测试

测试用例设计之自动化测试 &#x1f337; 一 测试用例设计&#x1f33a; 1 功能测试&#x1f338; &#xff08;1&#xff09;登录功能&#x1f338; &#xff08;2&#xff09;列表页功能&#x1f338; &#xff08;3&#xff09;编辑博客功能&#x1f338; &#xff08;4&…

DC LAB8SDC约束四种时序路径分析

DC LAB 1.启动DC2.读入设计3. 查看所有违例的约束报告3.1 report_constraint -all_violators (alias rc)3.2 view report_constraint -all_violators -verbose -significant_digits 4 (打印详细报告) 4.查看时序报告 report_timing -significant_digits 45. 约束组合逻辑(adr_i…

17 条件随机场

文章目录 17 条件随机场——CRF&#xff08;Condition Random Field&#xff09;17.1 背景介绍17.2 HMM与MEMM的区别17.3 MEMM与CRF的区别17.4 CRF模型17.4.1 CRF的概率密度函数17.4.2 CRF概率密度函数简化&#xff08;向量形式&#xff09; 17.5 CRF需要解决的问题17.6 边缘概…

测试者必知—如何做Web测试?常见测试点总结

目录 前言&#xff1a; 一、Web应用程序 二、功能测试 三、易用性测试&#xff08;界面测试&#xff09; 四、兼容性测试 五、安全性测试 六、性能测试 前言&#xff1a; Web测试是指对基于Web技术的应用程序进行测试&#xff0c;以测试其功能、性能、安全和稳定性等方面的表…

【图书推荐 | 12】前端系列

【赠书活动第十二期 】 图书推荐 本期书籍&#xff1a;前端系列 图书列表&#xff1a; Vue.js核心技术解析Nuxt.js实战Nuxt.js Web开发实战HTML5CSS 从入门到精通Flutter2 开发实例精解Electron项目开发实战 Vue.js核心技术解析 Nuxt.js实战 Nuxt.js Web开发实战 HTML5CSS 从入…

【业务功能篇20】Springboot java逻辑实现动态行转列需求

在此前&#xff0c;我也写过一个行转列的文章&#xff0c;是用存储过程sql处理的一个动态的逻辑 Mysql 存储过程\Mybatis框架call调用 实现动态行转列 那么后面我们同样又接收了业务的一个新需求&#xff0c;针对的是不同的业务数据&#xff0c;做的同样的一个展示数据报表&…

VueX使用简明笔记

1、作用&#xff1a; vuex是使用vue中必不可少的一部分&#xff0c;基于父子、兄弟组件&#xff0c;我们传值可能会很方便&#xff0c;但是如果是没有关联的组件之间要使用同一组数据&#xff0c;就显得很无能为力&#xff0c;那么vuex就很好的解决了我们这种问题&#xff0c;…

MySQL数据库 – node使用

1 MySQL查询对象 2 MySQL查询数组 3 mysql2库介绍使用 4 mysql2预处理语句 5 mysql2连接池使用 6 mysql2的Promi 这里仅说明如何使用服务器连接数据库并进行操作。 预处理语句就是可以输入变量的语句&#xff08;表现形式是有符号&#xff1a;&#xff1f;&#xff09;。需…

portraiture宿主插件最新v4中文版本下载及使用教程

自拍怎么可以不修图呢&#xff1f;如果要修图的话&#xff0c;磨皮就是其中非常重要的一环。皮肤看起来细腻光滑了&#xff0c;整个人的颜值都会瞬间拉高。下面就让我们介绍一下磨皮用什么软件好用&#xff0c;什么软件可以手动磨皮的相关内容。portraiture是ps人像修图中常用的…

为何唐宋诗词鼎盛,而到了明清变成了小说

我国是一个历史悠久的国家&#xff0c;在漫长的历史长河中&#xff0c;随着朝代的更替&#xff0c;很多事也发生了有趣的变化。 例如唐宋时期盛行的是诗词&#xff0c;而到了明清时代&#xff0c;小说又开始盛行了起来&#xff0c;那么造成这种文风改变的原因是什么呢&#xf…

Java版本spring cloud 工程管理系统软件 系统源代码 自主研发,工程行业适用

Java版工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离 功能清单如下&#xff1a; 首页 工作台&#xff1a;待办工作、消息通知、预警信息&#xff0c;点击可进入相应的列表 项目进度图表&#xff1a;选择&#xff08;总体或单个&#xff09;项目显示…

实战案例|黑灰产肆虐,腾讯ACE一键打造清朗游戏世界

随着游戏行业的快速发展&#xff0c;相关黑色产业链的问题日益严重&#xff0c;各种外挂、违规内容、非法交易现象的出现破坏着游戏的生态&#xff0c;为行业带来诸多安全挑战&#xff0c;也影响着玩家们的游戏体验。越来越多游戏厂商开始重视游戏安全问题&#xff0c;并探索全…

华为OD机试真题 JavaScript 实现【最远足迹】【2022Q4 100分】,附详细解题思路

一、题目描述 某探险队负责对地下洞穴进行探险。探险队成员在进行探险任务时&#xff0c;随身携带的记录器会不定期地记录自身的坐标&#xff0c;但在记录的间隙中也会记录其他数据。探索工作结束后&#xff0c;探险队需要获取到某成员在探险过程中相对于探险队总部的最远的足…

Mujoco210 Ubuntu 22.04配置安装(一)

目录 .1 下载 1.1 解压 1.2 许可问题 1.3 环境配置 1.4 测试mujoco .2 安装mujoco-py 2.1 conda激活虚拟环境\或新创建一个环境 2.2 下载mujoco-py ​编辑 2.3 配置环境变量 2.4 测试mujoco-py 2.5 测试时的一些报错处理 2.5.0 command /usr/bin/gcc failed with…

读写ini配置文件(C++)

文章目录 1、为什么要使用ini或者其它(例如xml,json)配置文件&#xff1f;2、ini文件基本介绍3、ini配置文件的格式4、C读写ini配置文件5、 代码示例6、 配置文件的解析库 文章转载于&#xff1a;https://blog.csdn.net/weixin_44517656/article/details/109014236 1、为什么要…