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

news2024/11/25 6:58:20

 

画一张总图:

图形界面开发

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

遇到的第一个问题就是选择什么开发语言和技术。因为我之前用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) - 知乎

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ADManager Plus:提升企业人员管理效率的全能工具

导言&#xff1a; 在现代企业中&#xff0c;高效的人员管理是取得成功的关键。随着企业规模的扩大和人员数量的增加&#xff0c;传统的人工管理方法已经无法满足快速变化的需求。ADManager Plus作为一款全能的人员管理工具&#xff0c;通过自动化和集成的方式&#xff0c;为企…

一文即可了解!自动化回归测试工具

目录 前言&#xff1a; 设计背景&#xff1a; 解决方案&#xff1a; 测试工具使用loadrunner脚本编写&#xff0c;这样的好处是 控制指令说明&#xff1a; 自定义检查方法说明&#xff1a; 变量说明&#xff1a; 附加动作说明&#xff1a; 前言&#xff1a; 自动化回归测试工…

Numpy---ndarray矩阵运算、广播机制、排序、文件操作

1. 矩阵运算 n 10 # 加 n - 10 # 减 n * 10 # 乘 n / 10 # 除 n // 2 # 整除 n ** 2 # 次方 n % 2 # 余数 n1 np.random.randint(0, 10, size(4, 5)) n2 np.random.randint(0, 10, size(4, 5)) display(n1, n2) array([[3, 6, 1, 9, 9],[8, 9, 2, 0, 4],[4, 8, 5, …

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 边缘概…

基于 AIGC,RocketMQ 学习社区探索开源软件学习新范式

作者&#xff1a;寒斜 AIGC 持续火爆全球&#xff0c;越来越多的场景开始接入并体现非凡的价值。其中应用广泛的场景之一就是智能知识问答&#xff0c;它改变了人们学习的方式&#xff0c;从阅读式到问答式&#xff0c;让知识的获取更加精准有效。开源软件拥有着广泛的求知群体…

ITSM 如何帮助制造业企业

ITSM在现代制造业中的作用 在过去的几年中&#xff0c;制造业已经看到了快速的数字化&#xff0c;以智能制造技术改进生产技术。在工业4.0和工业5.0的推动下&#xff0c;制造商正在摆脱陈旧 以及利用物联网、人工智能、机器学习和大数据等先进技术的互联智能制造系统&#xff…

【Protobuf速成指南】Any类型的使用

文章目录 2.2 Any类型的使用一、基本认识二、使用需知三、Any字段的使用①修改proto文件② Any相关函数③ 类型转换 四、Contact 2.2 改写 2.2 Any类型的使用 本系列文章将通过对通讯录项目的不断完善&#xff0c;带大家由浅入深的学习Protobuf的使用。这是Contacts的2.2版本&a…

基于脑电功率的疲劳驾驶检测研究_kaic

基于脑电功率的疲劳驾驶检测研究 摘 要 在道路交通安全领域&#xff0c;疲劳驾驶是一种常见的交通安全隐患。现有数据统计&#xff0c;全球每年有大约&#xff12;&#xff11;&#xff05;的重大交通事故与疲劳驾驶有关&#xff0c;疲劳驾驶成为了诱发交通事故的主要原因之一…

多模态深度学习:定义、示例、应用

人类使用五种感官来体验和解读周围的世界。我们的五种感官从五个不同的来源和五种不同的方式捕捉信息。模态是指某事发生、经历或捕捉的方式。 人脑由可以同时处理多种模式的神经网络组成。想象一下进行对话——您大脑的神经网络处理多模式输入&#xff08;音频、视觉、文本、…

《树莓派4B家庭服务器搭建指南》第十六期:安装Calibre Web建立公网可访问私人电子书库

最近在整理收藏的电子书&#xff0c;以便在公网可以随时访问自己的电子书&#xff0c;然而&#xff0c;Calibre桌面端虽然功能强大&#xff0c;并且可以开启本地http服务&#xff0c;但http的界面还是过于简陋 我发现了一个Calibre Web开源项目&#xff0c;界面也非常舒服&…

STM32F407实现1588v2(ptpd)

硬件&#xff1a; STM32F407ZGT6开发板 软件&#xff1a; VSCode arm-none-eabi-gcc openOCD st-link 在github搜到一个在NUCLEO-F429ZI开发板上移植ptpd的example&#xff0c;因为和F407差别很小&#xff0c;所以就打算用这个demo移植到手头的开发板上。因为目前只需要…

mysql中将字符123转变成1.2.3

具体业务需求&#xff1a;因为需求变更&#xff0c;之前存储数值型字符串&#xff0c;现需要将数值型转变为x.x.x update mpc_mp_package a join (select(selectGROUP_CONCAT(SUBSTRING(mp_ver, number, 1) separator .) as separated_stringfrom(selecti : i 1 as numberfro…

别再等了,这就是ping通上不了网的解决办法

ip能ping通&#xff0c;但是就是无法上网&#xff0c;应该大部分网工都遇到过这种情况吧。 能ping通&#xff0c;说明ip是能够和网络设备通信的&#xff0c;但是上不了网&#xff0c;就要具体问题具体分析了。 今天聊点基础的&#xff0c;ip能ping通但是上不了网&#xff0c;到…

百分点科技出席2023华为云TechWave中东中亚峰会

6月7日&#xff0c;2023华为云TechWave中东中亚峰会在哈萨克斯坦阿拉木图举办&#xff0c;百分点科技作为华为云生态合作伙伴出席活动&#xff0c;联合发布系列智能化解决方案&#xff0c;百分点科技CTO刘译璟进行主题分享。 刘译璟&#xff08;右二&#xff09;参与联合解决方…

RocketMQ集群部署

一、部署环境 两台服务器&#xff0c;每台一个nameserver构成集群&#xff0c;broker集群双主双从。 主机 容器名称 IP 与宿主机的端口映射 ODL控制器会同过vip通道10909去连接mq。主从通过10912端口同步这两个地址是通过-2 1 在配置文件端口10911基础上计算得出 两个宿主机…

高速信号的 pre-emphasis 预加重和 de-emphasis去加重

1 为何需要 emphasis &#xff1f; 预加重和去加重其实属于高速信号均衡技术的一种&#xff0c;均衡技术又叫信号补偿技术。 信号从发送端发出&#xff0c;经信道传输&#xff0c;到达接收端。在传输过程中&#xff0c;信号会发生失真&#xff0c;影响接收端对信号的正确判决。…

项目构建与部署

项目构建与部署 Jar部署War部署1&#xff0e;修改打包形式2&#xff0e;排除内置的Tomcat容器3&#xff0e;添加servlet-api依赖4&#xff0e;修改启动类 项目可以内嵌Servlet容器&#xff0c;因此部署极为方便&#xff0c;可直接打包成可执行Jar包部署在有Java运行环境的服务器…

BurpSuite2023测试越权漏洞

BurpSuite2023测试越权漏洞 BurpSuite安装创建项目 - 打开内置浏览器越权漏洞测试问题处理 BurpSuite安装 官网下载社区版并安装&#xff0c;下载地址&#xff1a;链接: https://portswigger.net/burp 安装成功后图标 创建项目 - 打开内置浏览器 打开BurpSuite&#xff0c…

uni-app打包ios的步骤

注意&#xff1a;下面的操作必须同时满足三个条件&#xff0c;且这三个条件都是必须得&#xff1a; 1.有一个苹果开发者账号(要收费) 2.有一台苹果笔记本(在笔记本上生成证书和文件) 3.有一部苹果手机(用于测试app的功能) 使用uniapp发布ios的应用的步骤如下&#xff1a; 点击发…

Lecture 14 Context-Free Grammar

目录 Context-Free GrammarBasics of Context-Free GrammarsCFG Parsing ConstituentsSyntactic ConstituentsConstituents and PhrasesExample: A Simple CFG for English and generating sentencesCFG Trees CYK AlgorithmCYK AlgorithmConvert to Chomsky Normal FormThe CY…