目录
摘要
随着科技的不断发展,时代的不断更新和进步,各个行业都不断的走进了智能化,某停车场为了方便监测停车情况、计费统计等重要功能,实现停车场智能化,所以根据这个需求,将利用人工智能计算机视觉领域来实现此功能。本项目主要运用opencv知识模拟实现车牌识别、MYAQL存储数据、pygame界面显示三方面综合实现项目需求,其中opencv车牌识别为重点,MYSQL为数据存储的和数据提取的重要媒介,pygame界面显示主要用于显示停车场车牌识别,计费等功能的显示。此次项目综合性很强,具有一定的实践意义和挑战意义,通过此开发可以完成停车场的管理效率,同过此项目可以时时监控车辆进场、出场、停车时长等车辆流动的情况。
关键词:智能化、检测停车、车牌识别、MYAQL存储数据、pygame界面、管理效率
一、绪论
车牌识别是计算机视觉中的一个重要应用场景,旨在通过图像处理和模版匹配技术,识别出车辆的车牌并对其进行优化识别。OpenCV是一款非常流行的计算机视觉库,可用于实现车牌识别任务。本次车牌识别部分运用了opencv中的读图、显示图片、图像转换、图像去噪(中值滤波)、二值化、形态学腐蚀膨胀、字符分割、查找轮廓、模版匹配等操作。MYSQL主要用于存储车牌号,进场和出场的时间,以及临时车和VIP车的车辆存储,其中用到了停车信息表和计费统计表两张表,用MYSQL中的好处是具有高效性、可靠性、安全性、易于使用和可扩展性等,所以广泛运用于智能化等相关领域的数据存储。Pygame界面显示停车车牌,进场和出场信息,以及业务逻辑计费和可视化界面显示,pygame具有丰富的界面元素和交互功能,以及易用性、可扩展性和跨平台支持等。
重点是opencv车牌识别、MYAQL存储数据、pygame界面显示三者之间的连接和能够正常运维和运用。
二、系统设计
本章内容包括车牌识别、mysql存储数据]、pygame界面显示三个部分,其中mysql存储数据有建表语句和建表说明。 核心业务设计主要描述的是完成某项具体业务时,类之间的消息配合,用流程图进行表述。数据库设计主要描述在系统中需要持久化的对象,给出E-R]。
(一)总项目分析图
(二)车牌识别流程图
(三)连接数据库
1.msaql准备
Python中导入pymsql库,如果没有可在终端执行pip install pymsql下载,本地下载安装下图任意一个
2.python中连接数据库并封装
import pymysql
connect = pymysql.connect(
host="localhost",
port=3306,
user="root",
password="123456",
database="parkdb"
)
def Select(sql):
cursor = connect.cursor()
cursor.execute(sql)
res = cursor.fetchall()
return res
cursor.close()
connect.close()
def DDL(sql):
cursor = connect.cursor()
state = cursor.execute(sql)
connect.commit()
return state
cursor.close()
connect.close()
3.测试连接
import pymysql
# 1:创建连接对象
dbcon = pymysql.connect(host="localhost", user="root", password="123456", port=3306, database="parkdb")
# 2:创建命令执行对象(游标)
cursor = dbcon.cursor() # 类比为navigate 里面的查询
# 3:执行SQL语句返回执行结果[删除 修改 更新=>受影响的行数>0]
sql = "INSERT into parkInfo(carNumber,date,type,state) VALUES('贵C8787A','2023/06/08/15:58:00',1,0);"
# sql = "update parkInfo set state = 5 where carNumber='贵C8787A';"
#sql = "delete from parkInfo;"
res = cursor.execute(sql)
if res > 0:
dbcon.commit() # 提交
else:
dbcon.rollback() # 回滚[不做任何的改变 回到操作之前]
# 4:关闭释放资源???
cursor.close()
dbcon.close()
4.pygame界面设计流程图
三、 系统实现
(一)opencv车牌识别
1.实现代码
# 导入所需模块
import cv2
from matplotlib import pyplot as plt
import os
import numpy as np
# plt显示彩色图片
def plt_show0(img):
# cv2 中的通道顺序为 BGR,而 plt 中的通道顺序为 RGB,因此需要对通道进行调整。
# cv2与plt的图像通道不同:cv2为[b,g,r];plt为[r, g, b]
b, g, r = cv2.split(img)
img = cv2.merge([r, g, b])
plt.imshow(img)
plt.show()
# plt显示灰度图片
def plt_show(img):
plt.imshow(img, cmap='gray') #增强图像的可读性
plt.show()
# 图像去噪灰度处理
def gray_guss(image):
image = cv2.GaussianBlur(image, (3, 3), 0)
gray_image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
return gray_image
# 读取待检测图片
# origin_image = cv2.imread('./imge/img_1.png')
origin_image = cv2.imread('./imge/img_6.png')
#plt_show(origin_image)
# 复制一张图片,在复制图上进行图像操作,保留原图
image = origin_image.copy()
# 图像去噪灰度处理
gray_image = gray_guss(image) # 目的是为了保留原始图像,以便后续操作不会修改原始图像的像素值,
# plt_show(gray_image)
# x方向上的边缘检测(增强边缘信息)
Sobel_x = cv2.Sobel(gray_image, cv2.CV_16S, 1, 0) # 函数对输入的灰度图像进行 Sobel 算子边缘检测,在 x 方向进行检测。
absX = cv2.convertScaleAbs(Sobel_x) # 函数将幅值进行转换,使得结果像素值范围在 0 到 255 之间。方便后续显示和保存
image = absX
plt_show(image)
# 图像阈值化操作——获得二值化图
ret, image = cv2.threshold(image, 0, 255, cv2.THRESH_OTSU) #运用自适应阈值分割算法 OTSU方法进行阈值处理,(自动确定阈值,计算量小)
# 显示灰度图像
#plt_show(image)
# 形态学(从图像中提取对表达和描绘区域形状有意义的图像分量)——闭操作(先膨胀再腐蚀)
kernelX = cv2.getStructuringElement(cv2.MORPH_RECT, (30, 10)) #使用 cv2.getStructuringElement() 函数生成一个矩形内核,内核的大小为 (30, 10)。
image = cv2.morphologyEx(image, cv2.MORPH_CLOSE, kernelX,iterations = 1)
# 使用 cv2.morphologyEx() 函数对输入的图像进行形态学处理,采用闭运算操作。
#
#
# 显示灰度图像
# plt_show(image)
# 腐蚀(erode)和膨胀(dilate)
# cv2.MORPH_RECT 表示生成的结构元素为矩形形状,而 (50, 1) 和 (1, 30) 分别表示结构元素的宽和高。
kernelX = cv2.getStructuringElement(cv2.MORPH_RECT, (50, 1)) #
kernelY = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 30))
# 沿 X 轴方向进行扩张或收缩,从而去除二值化图像中的较小空洞或噪声,使得连通区域更加稳定;
# 沿 Y 轴方向进行的扩张或收缩,从而消除不必要的竖直连接或扩展,使得图像中更加的纵向连续
# x方向进行闭操作(抑制暗细节)
image = cv2.dilate(image, kernelX) #cv2.dilate 膨胀, 结构元素为 kernelX,从而使原来的物体轮廓变得更加粗壮、连续
image = cv2.erode(image, kernelX) #cv2.erode() 函数对形态学膨胀后的图像进行腐蚀,去掉不必要的细节,提高图像质量
# y方向的开操作
image = cv2.erode(image, kernelY)
image = cv2.dilate(image, kernelY)
# 中值滤波(去噪) 中值滤波被广泛应用于图像处理中,如图像去噪、图像增强、图像分割、运动目标检测等
# cv2.medianBlur() 函数对输入的图像进行中值滤波操作,其中的参数 9 是指滤波器的尺寸大小
image = cv2.medianBlur(image, 9) # 提高准确率和可信度
# 显示灰度图像
# plt_show(image)
# 获得轮廓
# #二值图 #cv2.RETR_EXTERNAL 表示只需要检测最外层轮廓
# cv2.CHAIN_APPROX_SIMPLE 则表示检测时只保存轮廓的顶点信息而不保留所有的点。
contours, hierarchy = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for item in contours:
rect = cv2.boundingRect(item) # cv2.boundingRect() 函数计算每个轮廓的外接矩形,item 是当前处理的轮廓
# rect 则是用于存储外接矩形的四个参数,分别表示外接矩形的左上角坐标(x, y),和宽度(weight),和高度(height)
x = rect[0]
y = rect[1]
weight = rect[2]
height = rect[3]
print(height)
# 根据轮廓的形状特点,确定车牌的轮廓位置并截取图像
if (weight > (height * 3)) and (weight < (height * 8)):
image = origin_image[y:y + height, x:x + weight]
plt_show0(image)
#车牌字符分割
# 图像去噪灰度处理
image= cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)#函数将RGB图像转化为灰度图像,灰度图像往往比RGB图像更容易处理和提取特征
# 高斯滤波 高斯滤波可以消除图像中的噪声和细节,从而使得后续的处理更加准确和可靠
# (3, 3) 表示滤波器的大小,而 0 表示高斯滤波的标准差,标准差越大,滤波器的效果越明显。
gray_image= cv2.GaussianBlur(image, (3, 3),0)
# 图像阈值化操作——获得二值化图
ret, image = cv2.threshold(gray_image, 0, 255, cv2.THRESH_OTSU)#OTSU 自适应二值化算法
plt_show(image)
#膨胀操作,使“苏”字膨胀为一个近似的整体,为分割做准备
# cv2.MORPH_RECT:表示使用矩形元素进行形态学处理
# (2, 2):表示形态学处理核的大小
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 2))
# cv2.dilate 函数是形态学处理中的一种操作,它可以扩张和增强二值图像中的白色区域,同时也会增加噪声、毛刺等问题
image = cv2.dilate(image, kernel)
plt_show(image)
# 查找轮廓
# cv2.RETR_EXTERNAL:表示只检索图像中最外围的轮廓。该参数通常用于检测非嵌套、相互独立的轮廓。
# cv2.CHAIN_APPROX_SIMPLE:表示使用简单的轮廓近似方法。该方法会过滤掉轮廓中的冗余点,从而减小轮廓大小和内存消耗。
# cv2.findContours 函数为轮廓检测
contours, hierarchy = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
words = []
word_images = []
#对所有轮廓逐一操作
for item in contours: #遍历轮廓
word = []
rect = cv2.boundingRect(item) #当前轮廓外接矩形
x = rect[0]
y = rect[1]
weight = rect[2]
height = rect[3]
word.append(x)
word.append(y)
word.append(weight)
word.append(height)
words.append(word)
# 排序,车牌号有顺序。words是一个嵌套列表
words = sorted(words,key=lambda s:s[0],reverse=False)
i = 0
#word中存放轮廓的起始点和宽高
for word in words:
# 筛选字符的轮廓
#判断当前单词是否符合一定的筛选条件,如单词的高度比宽度最小要大 1.5 倍、最大也不超过 3.5 倍,单词的宽度大于 10。
#把符合条案件的挑选出来
if (word[3] > (word[2] * 1.5)) and (word[3] < (word[2] * 3.5)) and (word[2] > 10):
i = i+1
splite_image = image[word[1]:word[1] + word[3], word[0]:word[0] + word[2]]
word_images.append(splite_image)
# print(i)
# print(words)
for i,j in enumerate(word_images):
plt.subplot(1,7,i+1)
plt.imshow(word_images[i],cmap='gray')
plt.show()
#模版匹配
# 准备模板(template[0-9]为数字模板;)
template = ['0','1','2','3','4','5','6','7','8','9',
'A','B','C','D','E','F','G','H','J','K','L','M','N','P','Q','R','S','T','U','V','W','X','Y','Z',
'藏','川','鄂','甘','赣','贵','桂','黑','沪','吉','冀','津','晋','京','辽','鲁','蒙','闽','宁',
'青','琼','陕','苏','皖','湘','新','渝','豫','粤','云','浙']
# 读取一个文件夹下的所有图片,输入参数是文件名,返回模板文件地址列表
def read_directory(directory_name):
referImg_list = []
for filename in os.listdir(directory_name):
referImg_list.append(directory_name + "/" + filename)
return referImg_list
# 获得中文模板列表(只匹配车牌的第一个字符)
def get_chinese_words_list():
chinese_words_list = []
for i in range(34,64):
#将模板存放在字典中
c_word = read_directory('F:/车牌数据集/annGray/'+ template[i])
chinese_words_list.append(c_word)
return chinese_words_list
chinese_words_list = get_chinese_words_list()
# 获得英文模板列表(只匹配车牌的第二个字符)
def get_eng_words_list():
eng_words_list = []
for i in range(10,34):
e_word = read_directory('F:/车牌数据集/annGray/'+ template[i])
eng_words_list.append(e_word)
return eng_words_list
eng_words_list = get_eng_words_list()
# 获得英文和数字模板列表(匹配车牌后面的字符)
def get_eng_num_words_list():
eng_num_words_list = []
for i in range(0,34):
word = read_directory('F:/车牌数据集/annGray/'+ template[i])
eng_num_words_list.append(word)
return eng_num_words_list
eng_num_words_list = get_eng_num_words_list()
# 读取一个模板地址与图片进行匹配,返回得分
plt_show(image)
def template_score(template,image):
#将模板进行格式转换
template_img=cv2.imdecode(np.fromfile(template,dtype=np.uint8),1)
template_img = cv2.cvtColor(template_img, cv2.COLOR_RGB2GRAY)
#模板图像阈值化处理——获得黑白图
ret, template_img = cv2.threshold(template_img, 0, 255, cv2.THRESH_OTSU)
# height, width = template_img.shape
# image_ = image.copy()
# image_ = cv2.resize(image_, (width, height))
image_ = image.copy()
#获得待检测图片的尺寸
height, width = image_.shape
# 将模板resize至与图像一样大小
template_img = cv2.resize(template_img, (width, height))
# 模板匹配,返回匹配得分
result = cv2.matchTemplate(image_, template_img, cv2.TM_CCOEFF)
return result[0][0]
# 对分割得到的字符逐一匹配
def template_matching(word_images):
results = []
for index,word_image in enumerate(word_images):
if index==0:
best_score = []
for chinese_words in chinese_words_list:
score = []
for chinese_word in chinese_words:
result = template_score(chinese_word,word_image)
score.append(result)
best_score.append(max(score))
i = best_score.index(max(best_score))
# print(template[34+i])
r = template[34+i]
results.append(r)
continue
if index==1:
best_score = []
for eng_word_list in eng_words_list:
score = []
for eng_word in eng_word_list:
result = template_score(eng_word,word_image)
score.append(result)
best_score.append(max(score))
i = best_score.index(max(best_score))
# print(template[10+i])
r = template[10+i]
results.append(r)
continue
else:
best_score = []
for eng_num_word_list in eng_num_words_list:
score = []
for eng_num_word in eng_num_word_list:
result = template_score(eng_num_word,word_image)
score.append(result)
best_score.append(max(score))
i = best_score.index(max(best_score))
# print(template[i])
r = template[i]
results.append(r)
continue
return results
word_images_ = word_images.copy()
# 调用函数获得结果
result = template_matching(word_images_)
print(result)
# "".join(result)函数将列表转换为拼接好的字符串,方便结果显示
# print( "".join(result))
results="".join(result)
print('车牌号为:'+ results)
# print('------------------------------------------')
# # 根据文件返回车牌号
# def getcn():
# results = "".join(result)
#
#
#
# return results
# if __name__ == '__main__':
# print(results)
2.最终结果显示
3.将车牌识别代码封装,封装省略。
4.车牌识别小结
优点:运用opencv进行车牌识别,可以学到计算机视觉的相关知识,温故而知新
缺点:opencv车牌识别每次只能识别一张,对于车牌图片不太正的识别不准确,需要调参,仿射变换等操作才能实现,而且每换一张图片就要重新多次调参,才能达到想要的效果
车牌识别推荐:1.申请百度key识别。优点是,识别速度快,缺点是学不到计算机视觉中opencv的相关知识及专业知识。
2.使用深度学习。优点是可以识别各种车牌,不需要调参,缺点是训练模型耗时场以及环境可能不支持深度学习所用到的框架。
3,.使用yolo5模型+OCR。优点是,识别较准确,缺点是,环境配置不一定能成功。
(二)数据库设计
1.数据需求分析
系统E-R图表明了实体与实体之间的关系,根据系统的功能需求数据库中主要有停车信息表和计费表如4.3.1图所示
2.数据库的实现
根据系统需求确定系统如下有2张表。
停车信息表:存储入场车牌信息。表格设计信息见表4.1所示。
字段名 | 字段描述 | 字段类型 | NULL |
cid | 编号 | Int | 否 |
carNumber | 车牌号 | Varchar(20) | 可空 |
date | 用户名密码 | datetime | 可空 |
type | 车类型[0:临时停车 1:长期停车] | Int | 可空 |
state | 车状态[0:入场 1:出场] | int | 可空 |
计费表:存储车牌的费用。表格设计信息见表4.2所示。
表 4.2计费表
字段名 | 字段描述 | 字段类型 | NULL |
cid | 编号 | Int | 否 |
carNumber | 车牌号 | Varchar(20) | 可空 |
chargeDate | 时间 | Int | 可空 |
chargeMoney | 费用 | Int | 可空 |
3.建表语句
3.1停车信息表
create table parkInfo ( pid int PRIMARY key auto_increment COMMENT '主键ID会自动增长', carNumber varchar(20) COMMENT '车牌号', date datetime COMMENT '入场时间', type int COMMENT '车类型[0:领停车 1:长期车]', state int COMMENT '车状态[0:入场 1:出场]' ); |
3.2计费表
create table parkCharge ( cid int PRIMARY key auto_increment COMMENT '主键ID会自动增长', carNumber varchar(20) COMMENT '车牌号', chargeDate int COMMENT '停车时长(分钟)', ChargeMoney int COMMENT '停车费用' ); |
(三) Pygame界面显示部分
1.基本窗体显示
import pygame
import pymysql
import pandas as pd
from pandas import DataFrame
import os
import cv2
import time
import btn
import btn1
#import ocrutil
import parkF
import payMoney
import MysqlUilt
from MysqlUilt import DDL, Select
size=1200,700 # 窗体大小
FPS=60 # 设置帧率(屏幕每秒的刷新次数)
# 设置背景颜色
DARKBLUE=(73,119,142)
BG=DARKBLUE
#定义颜色
BLAK=(0,0,0)
WHITE=(255,255,255)
GREEN=(0,255,0)
BLUE=(72,61,139)
GRAY=(96,96,96)
RED=(220,20,60)
YELLOW=(255,255,0)
Total =10 # 一共有多少车位
#carNumbe=parkF.getcn()
# carnumber = '贵DZM222' #手动设置车牌号,用于测试,节约时间
cartype = ''
CarNumber = ''
Time = ''
# 停车场车辆,获取所需列的值
sql = "select * from parkinfo "
df_sql = pd.read_sql_query(sql, MysqlUilt.connect)
#print(df_sql)
cars = df_sql[[ 'date', 'state']].values # 获取停车场车辆信息
#print(cars)
sql_4 = "SELECT carNumber FROM parkinfo where state =0;"
res_1 = Select(sql_4)
# print(res_1)
# res2=res1[0][0]
# print(res2)
sql_5 ="SELECT date FROM parkinfo where state =0;"
res_2 = Select(sql_5)
# print(res_2)
# for item in res_1:
# print(item[0])
#
#
# for item in res_2:
# print(item[0])
# 入场车的车辆数
sql_2 = "SELECT count(*) FROM parkinfo where state = 0;"
res = Select(sql_2)
carn =res[0][0]
print(carn)
# 自定义定义函数 获取车牌号
def place( screen, res_1):
if carn <10:
results = []
j = 20
for result in res_1:
results.append(result[0])
# print(results)
xtfont = pygame.font.SysFont('SimHei', 20)
screen.blit(xtfont.render(results[0], True, (238, 130, 238)), (680, 100))
for i in range(1, len(results)):
xtfont = pygame.font.SysFont('SimHei', 20)
screen.blit(xtfont.render(results[i], True, (238, 130, 238)), (680, 100 + j))
j += 20
else:
xtfont = pygame.font.SysFont('SimHei', 20)
screen.blit(xtfont.render('没有车位,不能停车', True, (238, 130, 238)), (680, 300))
# 自定义函数 获取时间
def place_time( screen, res_2):
if carn<10:
results = []
j = 20
for result in res_2:
results.append(result[0])
# print(results[0])
xtfont = pygame.font.SysFont('SimHei', 20)
screen.blit(xtfont.render(str(results[0]), True, (238, 130, 238)), (800, 100))
for i in range(1, len(results)):
xtfont = pygame.font.SysFont('SimHei', 20)
screen.blit(xtfont.render(str(results[i]), True, (238, 130, 238)), (800, 100 + j))
j += 20
else:
xtfont = pygame.font.SysFont('SimHei', 20)
screen.blit(xtfont.render('没有车位,不能停车', True, (238, 130, 238)), (680, 300))
def parkCount(res):
k = Total - res[0][0] # 剩余车位
if k <= 0:
return False
return True
t0 = ''
t1 = ''
t2 = ''
t3 = ''
t4 = ''
t5 = ''
# 1.初始化
pygame.init()
# 2.设置窗体名称
pygame.display.set_caption('智能停车场车牌识别计费系统')
# 3.加载图片,设置图标
ic_launcher=pygame.image.load('img/img.png')
pygame.display.set_icon(ic_launcher)
# 4.设置窗体大小、背景颜色
screen=pygame.display.set_mode(size)
screen.fill(BG)
#背景和信息文字
def text0(screen):
pygame.draw.rect(screen,BG,(650,2,350,640)) # 底色
pygame.draw.aaline(screen, GREEN, (662, 50), (1050, 50), 1) # 绘制横线
pygame.draw.rect(screen, GREEN, (650, 340, 400, 355), 2) # 绘制信息矩形框
xtfont=pygame.font.SysFont('SimHei',15) # 使用系统字体
textstart=xtfont.render('信息',True,GREEN) # 信息文字
text_rect=textstart.get_rect() # 获取文字图像位置
#设置文字图像中心点
text_rect.centerx=675
text_rect.centery=365
screen.blit(textstart,text_rect) # 绘制内容
#车位文字
def text1(screen):
k=Total-carn # 剩余车位
if k<10:
sk='0'+str(k)
if res[0][0] >=Total:
sk='无车位'
else:
sk=str(k)
xtfont = pygame.font.SysFont('SimHei', 20)
textstart=xtfont.render('共有车位:'+str(Total)+' 剩余车位:'+sk,True,WHITE) # 添加文字信息
text_rect=textstart.get_rect() # 获取文字图像位置
# 设置文字图像中心点
text_rect.centerx=820
text_rect.centery=30
# 绘制内容
screen.blit(textstart,text_rect)
#停车场信息表头
def text2(screen):
xtfont = pygame.font.SysFont('SimHei', 15)
textstart = xtfont.render('车号 时间' , True, WHITE) # 添加文字信息
text_rect = textstart.get_rect() # 获取文字图像位置
# 设置文字图像中心点
text_rect.centerx = 810
text_rect.centery = 70
screen.blit(textstart, text_rect) # 绘制内容
basicFont = pygame.font.SysFont('SimHei', 20)
# 游戏循环帧率设置(控制程序运行时间)
clock=pygame.time.Clock()
# 主线程
while True:
# 加载图像
image = pygame.image.load('img/img_1.png')
# 设置图片大小
image = pygame.transform.scale(image, (640, 480))
# 绘制视频画面
screen.blit(image, (2, 2))
text0(screen) # 背景和信息文字
text1(screen) # 停车位信息
text2(screen) # 停车场信息表头
#text3(screen)
place(screen, res_1)
place_time(screen, res_2)
# 创建识别按钮
btn.Button(screen, (640, 480), 150, 60,(186, 85, 211), WHITE, "入场识别", 25)
btn1.Button1(screen, (150, 480), 150, 60, (186, 85, 211), WHITE, "出场识别", 25) # 出场按钮的位置
for event in pygame.event.get():
# 关闭页面游戏退出
if event.type==pygame.QUIT:
pygame.quit()
exit()
elif event.type==pygame.MOUSEBUTTONDOWN:
print(str(event.pos[0])+':'+str(event.pos[1]))
if 492 <= event.pos[0] and event.pos[0] <= 642 and 422 <= event.pos[1] and event.pos[1] <= 482:
print('点击: 入场识别')
if carn<10:
try:
# carnumber=parkF.getcn() #获取识别的车牌号
CarNumber = carnumber
# print(1)
# 格式化当前时间
# localtime = time.strftime('%Y-%m-%d %H:%M', time.localtime())
Time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
# sql3 = "SELECT carNumber FROM parkinfo where carNumber ;"
print('识别成功')
if parkCount(res):
sql = "INSERT INTO parkinfo (carNumber, date, type, state) VALUES('%s', " \
"CURRENT_TIMESTAMP, 0, 0)" % (str(carnumber))
state = DDL(sql)
if state > 0 and state < 11:
print('操作成功')
else:
print('操作失败')
else:
print('没有停车位')
except:
print('识别失败')
continue
pass
else:
print('没有停车位无法识别')
s = "SELECT state FROM parkinfo WHERE carNumber = '%s' and type = 0 ORDER BY pid LIMIT 1;" % (
str(carnumber))
re = Select(s)
if re[0][0] == 0:
cartype = '临时车'
else:
cartype = 'VIP车'
if 0 <= event.pos[0] and event.pos[0] <= 150 and 422 <= event.pos[1] and event.pos[1] <= 482:
print('点击出场识别')
try:
CarNumber = carnumber
sql4 = "select TIMESTAMPDIFF(MINUTE, date, CURRENT_TIMESTAMP), state " \
"from parkinfo where carNumber = '%s' and type = 0 " \
"ORDER BY pid DESC LIMIT 1;" % (CarNumber)
res1 = Select(sql4)
if res1[0][1] == 1:
money = 0
else:
money = payMoney.payMoney(res1[0][0])
sql5 = "insert into parkCharge(carNumber, chargeDate, chargeMoney) VALUES" \
"('%s', '%s', '%s');" % (str(CarNumber), res1[0][0], money)
state = DDL(sql5)
if state > 0:
print('操作成功')
else:
print('操作失败')
sql6 = "select chargeDate, chargeMoney from parkCharge WHERE carNumber = '%s' " \
"ORDER BY cid DESC LIMIT 1;" % (str(CarNumber))
res2 = Select(sql6)
t0 = res2[0][0]
if res1[0][1] == 1:
t1 = 'VIP车'
t2 = 'VIP用户暂不收钱'
else:
t3 = '此车类型为:临时车'
if res2[0][1] <= 30:
t4 = '停车小于30分钟不收钱'
else:
t5 = res2[0][1]
sql7 = "UPDATE parkinfo set type = 1 where carNumber = '%s';" % (str(CarNumber))
s = DDL(sql7)
if s > 0:
print('修改成功')
else:
print('修改失败')
pygame.display.update()
except:
print('没有该车')
continue
pass
screen.blit(basicFont.render('此车牌号为:' + carnumber, True, (238, 130, 238), (73, 119, 142)),
(660, 380))
screen.blit(basicFont.render('此车类型为:' + cartype, True, (238, 130, 238), (73, 119, 142)),
(660, 410))
screen.blit(basicFont.render('进入时间为:' + Time, True, (238, 130, 238), (73, 119, 142)),
(660, 440))
screen.blit(basicFont.render('停车时间为: ' + str(t0) + '分钟', True, (238, 130, 238), (73, 119, 142)),
(660, 470))
screen.blit(basicFont.render('此车类型为:' + str(t1), True, (238, 130, 238), (73, 119, 142)), (660, 500))
screen.blit(basicFont.render('费用: ' + str(t2), True, (238, 130, 238), (73, 119, 142)), (660, 530))
screen.blit(basicFont.render(str(t3), True, (238, 130, 238), (73, 119, 142)), (660, 560))
screen.blit(basicFont.render(str(t4), True, (238, 130, 238), (73, 119, 142)), (730, 590))
screen.blit(basicFont.render('收取费用为:' + str(t5) + '元', True, (238, 130, 238), (73, 119, 142)), (660, 620))
pygame.display.flip() # 更新界面
clock.tick(FPS) # 控制游戏最大帧率为60
2.显示结果
(四)综合实现过程
1.准备模块
1.1封装opencv车牌识别的代码
# 导入所需模块
import cv2
from matplotlib import pyplot as plt
import os
import numpy as np
# 根据文件返回车牌号
def getcn():
# plt显示彩色图片
def plt_show0(img):
# cv2与plt的图像通道不同:cv2为[b,g,r];plt为[r, g, b]
b, g, r = cv2.split(img)
img = cv2.merge([r, g, b])
plt.imshow(img)
plt.show()
# plt显示灰度图片
def plt_show(img):
plt.imshow(img, cmap='gray')
plt.show()
# 图像去噪灰度处理
def gray_guss(image):
image = cv2.GaussianBlur(image, (3, 3), 0)
gray_image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
return gray_image
# 读取待检测图片
# origin_image = cv2.imread('./imge/img_1.png')
origin_image = cv2.imread('./imge/img_6.png')
# plt_show(origin_image)
# 复制一张图片,在复制图上进行图像操作,保留原图
image = origin_image.copy()
# 图像去噪灰度处理
gray_image = gray_guss(image)
# plt_show(gray_image)
# x方向上的边缘检测(增强边缘信息)
Sobel_x = cv2.Sobel(gray_image, cv2.CV_16S, 1, 0)
absX = cv2.convertScaleAbs(Sobel_x)
image = absX
# 图像阈值化操作——获得二值化图
ret, image = cv2.threshold(image, 0, 255, cv2.THRESH_OTSU)
# 显示灰度图像
# plt_show(image)
# 形态学(从图像中提取对表达和描绘区域形状有意义的图像分量)——闭操作
kernelX = cv2.getStructuringElement(cv2.MORPH_RECT, (30, 10))
image = cv2.morphologyEx(image, cv2.MORPH_CLOSE, kernelX, iterations=1)
# 显示灰度图像
# plt_show(image)
# 腐蚀(erode)和膨胀(dilate)
kernelX = cv2.getStructuringElement(cv2.MORPH_RECT, (50, 1))
kernelY = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 30))
# x方向进行闭操作(抑制暗细节)
image = cv2.dilate(image, kernelX)
image = cv2.erode(image, kernelX)
# y方向的开操作
image = cv2.erode(image, kernelY)
image = cv2.dilate(image, kernelY)
# 中值滤波(去噪)
image = cv2.medianBlur(image, 9)
# 显示灰度图像
# plt_show(image)
# 获得轮廓
contours, hierarchy = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
print('----------------------------------')
for item in contours:
rect = cv2.boundingRect(item)
x = rect[0]
y = rect[1]
weight = rect[2]
height = rect[3]
print(height)
# 根据轮廓的形状特点,确定车牌的轮廓位置并截取图像
if (weight > (height * 3)) and (weight < (height * 8)):
image = origin_image[y:y + height, x:x + weight]
# plt_show0(image)
# 车牌字符分割
# 图像去噪灰度处理
image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
gray_image = cv2.GaussianBlur(image, (3, 3), 0)
# 图像阈值化操作——获得二值化图
ret, image = cv2.threshold(gray_image, 0, 255, cv2.THRESH_OTSU)
# plt_show(image)
# 膨胀操作,使“苏”字膨胀为一个近似的整体,为分割做准备
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 2))
image = cv2.dilate(image, kernel)
# plt_show(image)
# 查找轮廓
contours, hierarchy = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
words = []
word_images = []
# 对所有轮廓逐一操作
for item in contours:
word = []
rect = cv2.boundingRect(item)
x = rect[0]
y = rect[1]
weight = rect[2]
height = rect[3]
word.append(x)
word.append(y)
word.append(weight)
word.append(height)
words.append(word)
# 排序,车牌号有顺序。words是一个嵌套列表
words = sorted(words, key=lambda s: s[0], reverse=False)
i = 0
# word中存放轮廓的起始点和宽高
for word in words:
# 筛选字符的轮廓
if (word[3] > (word[2] * 1.5)) and (word[3] < (word[2] * 3.5)) and (word[2] > 10):
i = i + 1
splite_image = image[word[1]:word[1] + word[3], word[0]:word[0] + word[2]]
word_images.append(splite_image)
# print(i)
# print(words)
for i, j in enumerate(word_images):
plt.subplot(1, 7, i + 1)
plt.imshow(word_images[i], cmap='gray')
# plt.show()
# 模版匹配
# 准备模板(template[0-9]为数字模板;)
template = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
'X', 'Y', 'Z',
'藏', '川', '鄂', '甘', '赣', '贵', '桂', '黑', '沪', '吉', '冀', '津', '晋', '京', '辽', '鲁', '蒙', '闽', '宁',
'青', '琼', '陕', '苏', '皖', '湘', '新', '渝', '豫', '粤', '云', '浙']
# 读取一个文件夹下的所有图片,输入参数是文件名,返回模板文件地址列表
def read_directory(directory_name):
referImg_list = []
for filename in os.listdir(directory_name):
referImg_list.append(directory_name + "/" + filename)
return referImg_list
# 获得中文模板列表(只匹配车牌的第一个字符)
def get_chinese_words_list():
chinese_words_list = []
for i in range(34, 64):
# 将模板存放在字典中
c_word = read_directory('F:/车牌数据集/annGray/' + template[i])
chinese_words_list.append(c_word)
return chinese_words_list
chinese_words_list = get_chinese_words_list()
# 获得英文模板列表(只匹配车牌的第二个字符)
def get_eng_words_list():
eng_words_list = []
for i in range(10, 34):
e_word = read_directory('F:/车牌数据集/annGray/' + template[i])
eng_words_list.append(e_word)
return eng_words_list
eng_words_list = get_eng_words_list()
# 获得英文和数字模板列表(匹配车牌后面的字符)
def get_eng_num_words_list():
eng_num_words_list = []
for i in range(0, 34):
word = read_directory('F:/车牌数据集/annGray/' + template[i])
eng_num_words_list.append(word)
return eng_num_words_list
eng_num_words_list = get_eng_num_words_list()
# 读取一个模板地址与图片进行匹配,返回得分
# plt_show(image)
def template_score(template, image):
# 将模板进行格式转换
template_img = cv2.imdecode(np.fromfile(template, dtype=np.uint8), 1)
template_img = cv2.cvtColor(template_img, cv2.COLOR_RGB2GRAY)
# 模板图像阈值化处理——获得黑白图
ret, template_img = cv2.threshold(template_img, 0, 255, cv2.THRESH_OTSU)
# height, width = template_img.shape
# image_ = image.copy()
# image_ = cv2.resize(image_, (width, height))
image_ = image.copy()
# 获得待检测图片的尺寸
height, width = image_.shape
# 将模板resize至与图像一样大小
template_img = cv2.resize(template_img, (width, height))
# 模板匹配,返回匹配得分
result = cv2.matchTemplate(image_, template_img, cv2.TM_CCOEFF)
return result[0][0]
# 对分割得到的字符逐一匹配
def template_matching(word_images):
results = []
for index, word_image in enumerate(word_images):
if index == 0:
best_score = []
for chinese_words in chinese_words_list:
score = []
for chinese_word in chinese_words:
result = template_score(chinese_word, word_image)
score.append(result)
best_score.append(max(score))
i = best_score.index(max(best_score))
# print(template[34+i])
r = template[34 + i]
results.append(r)
continue
if index == 1:
best_score = []
for eng_word_list in eng_words_list:
score = []
for eng_word in eng_word_list:
result = template_score(eng_word, word_image)
score.append(result)
best_score.append(max(score))
i = best_score.index(max(best_score))
# print(template[10+i])
r = template[10 + i]
results.append(r)
continue
else:
best_score = []
for eng_num_word_list in eng_num_words_list:
score = []
for eng_num_word in eng_num_word_list:
result = template_score(eng_num_word, word_image)
score.append(result)
best_score.append(max(score))
i = best_score.index(max(best_score))
# print(template[i])
r = template[i]
results.append(r)
continue
return results
word_images_ = word_images.copy()
# 调用函数获得结果
result = template_matching(word_images_)
print(result)
# "".join(result)函数将列表转换为拼接好的字符串,方便结果显示
print("".join(result))
results = "".join(result)
# print(results)
# print('------------------------------------------')
# results = "".join(result)
print('车牌号:' + results) # 输出车牌号
return results
# if __name__ == '__main__':
# getcn()
1.2.封装按钮模块
import pygame
#自定义按钮
class Button1():
#msg为要在按钮中显示的文本
def __init__(self,screen,centerxy,width,height,button_color,text_color,msg,size):
''' 初始化按钮的属性 '''
self.screen=screen
self.width,self.height=width,height # 设置按钮的宽和高
self.button_color=button_color # 设置按钮的rect对象颜色为深蓝
self.text_color=text_color # 设置文本的颜色为白色
# 1.设置文本字体与大小
self.font=pygame.font.SysFont('SimHei',size)
# 2.设置按钮大小
self.rect=pygame.Rect(0,0,self.width,self.height)
# 3.创建按钮的rect对象,并设置按钮的中心位置
self.rect.centerx=centerxy[0]-self.width/2+2
self.rect.centery=centerxy[1]-self.height/2+2
# 4.填充颜色
self.screen.fill(self.button_color, self.rect)
#渲染图像
self.deal_msg(msg)
def deal_msg(self,msg):
'''将msg渲染为图像,并将其在按钮上居中'''
# 5.将文本写到按钮上
self.msg_img=self.font.render(msg,True,self.text_color,self.button_color)
# 6.设置文本在按钮上的位置:文本的中心就是按钮的中心(即文本居中)
self.msg_img_rect=self.msg_img.get_rect()
self.msg_img_rect.center=self.rect.center
# 7.绘制到屏幕上
self.screen.blit(self.msg_img, self.msg_img_rect)
#自定义按钮
class Button2():
#msg为要在按钮中显示的文本
def __init__(self,screen,centerxy,width,height,button_color,text_color,msg,size):
''' 初始化按钮的属性 '''
self.screen=screen
self.width,self.height=width,height # 设置按钮的宽和高
self.button_color=button_color # 设置按钮的rect对象颜色为深蓝
self.text_color=text_color # 设置文本的颜色为白色
# 1.设置文本字体与大小
self.font=pygame.font.SysFont('SimHei',size)
# 2.设置按钮大小
self.rect=pygame.Rect(0,0,self.width,self.height)
# 3.创建按钮的rect对象,并设置按钮的中心位置
self.rect.centerx=centerxy[0]-self.width/2+2
self.rect.centery=centerxy[1]-self.height/2+2
# 4.填充颜色
self.screen.fill(self.button_color, self.rect)
#渲染图像
self.deal_msg(msg)
def deal_msg(self,msg):
'''将msg渲染为图像,并将其在按钮上居中'''
# 5.将文本写到按钮上
self.msg_img=self.font.render(msg,True,self.text_color,self.button_color)
# 6.设置文本在按钮上的位置:文本的中心就是按钮的中心(即文本居中)
self.msg_img_rect=self.msg_img.get_rect()
self.msg_img_rect.center=self.rect.center
# 7.绘制到屏幕上
self.screen.blit(self.msg_img, self.msg_img_rect)
1.3.封装连接数据库模块
import pymysql
connect = pymysql.connect(
host="localhost",
port=3306,
user="root",
password="123456",
database="parkdb"
)
def Select(sql):
cursor = connect.cursor()
cursor.execute(sql)
res = cursor.fetchall()
return res
cursor.close()
connect.close()
def DDL(sql):
cursor = connect.cursor()
state = cursor.execute(sql)
connect.commit()
return state
cursor.close()
connect.close()
1.4.封装共有车位剩余车位,车牌号和时间显示,信息和信息框等模块存于Obtain.txt文本中
import pygame
#总车位数
Total = 10
sk = ''
DARKBLUE=(73,119,142)
BG=DARKBLUE
GREEN=(0,255,0)
#WHITE = (255, 255, 255)
# 定义背景和信息文字
def text0(screen):
pygame.draw.rect(screen,BG,(650,2,350,640)) # 底色
pygame.draw.aaline(screen,(139,0,139),(662,50),(1050,50),3) # 绘制横线
pygame.draw.rect(screen,(139,0,139),(650,340,400,355),2) # 绘制信息矩形框
pygame.draw.rect(screen, (139, 0, 139), (650, 695, 400, 100), 2) # 绘制总收入矩形框
xtfont=pygame.font.SysFont('SimHei',15) # 使用系统字体
textstart=xtfont.render('信息',True,GREEN) # 信息文字
text_rect=textstart.get_rect() # 获取文字图像位置
#设置文字图像中心点
text_rect.centerx=675
text_rect.centery=355
screen.blit(textstart,text_rect) # 绘制内容
# 定义共有车位和剩余车位的位置
def text1(screen, res):
if res[0][0] == 0:
sk = Total
else:
k = Total - res[0][0]
if k <= 0:
sk=str(0)
else:
sk=str(k)
xtfont = pygame.font.SysFont('SimHei', 25)
textstart = xtfont.render('共有车位:' + str(Total) + ' 剩余车位:' + str(sk), True, (238, 130, 238)) # 添加文字信息
text_rect = textstart.get_rect() # 获取文字图像位置
# 设置文字图像中心点
text_rect.centerx = 830
text_rect.centery = 30
# 绘制内容
screen.blit(textstart, text_rect)
# 定义停车场信息表头(车号和时间)
def text2(screen):
xtfont = pygame.font.SysFont('SimHei', 15)
textstart = xtfont.render(' 车号 时间' , True, (238,130,238)) # 添加文字信息
text_rect = textstart.get_rect() # 获取文字图像位置
# 设置文字图像中心点
text_rect.centerx = 800
text_rect.centery = 70
screen.blit(textstart, text_rect) # 绘制内容
def text3(screen):
xtfont = pygame.font.SysFont('SimHei', 30)
textstart = xtfont.render(' 收入总计为:', True, (238, 130, 238)) # 添加文字信息
text_rect = textstart.get_rect() # 获取文字图像位置
# 设置文字图像中心点
text_rect.centerx = 740
text_rect.centery = 740
screen.blit(textstart, text_rect) # 绘制内容
def text4(screen):
xtfont = pygame.font.SysFont('SimHei', 35)
textstart = xtfont.render(' 欢迎光临青枫停车场', True, (238, 130, 238)) # 添加文字信息
text_rect = textstart.get_rect() # 获取文字图像位置
# 设置文字图像中心点
text_rect.centerx = 310
text_rect.centery = 20
screen.blit(textstart, text_rect) # 绘制内容
# 自定义定义函数 获取车牌号
def park_number( screen, res):
results = []
j = 20
for result in res:
results.append(result[0])
# print(results)
xtfont = pygame.font.SysFont('SimHei', 20)
screen.blit(xtfont.render(results[0], True, (238,130,238)), (680, 100))
for i in range(1, len(results)):
xtfont = pygame.font.SysFont('SimHei', 20)
screen.blit(xtfont.render(results[i], True, (238,130,238)), (680, 100 + j))
j += 20
# 自定义函数 获取时间
def place_time( screen, res):
results = []
j = 20
for result in res:
results.append(result[1])
# print(results[0])
xtfont = pygame.font.SysFont('SimHei', 20)
screen.blit(xtfont.render(str(results[0]), True, (238,130,238)), (800, 100))
for i in range(1, len(results)):
xtfont = pygame.font.SysFont('SimHei', 20)
screen.blit(xtfont.render(str(results[i]), True, (238,130,238)), (800, 100 + j))
j += 20
# 自定义函数数 用于判断是否有车位
def Parking_space(res):
k = Total - res[0][0] # 剩余车位
if k <= 0:
return False
return True
# if __name__ == '__main__':
# p = cahargeMoney(1500)
# print(p)
1.5.封装计费模块
def payMoney(time):
n = int(time/(60*24)) # 获得天数 时长转为天数
if time < 30: # 小于30分钟不收钱
f = 0
elif 600 <= time <= 1440: # 停车时长大于10小时小于24小时的收20块
f = 20
elif time > 24 * 60 * n: # 停车时长超过一天的按
f = int(20 * n + 0.05*(time - (24*60*n))) # 超过的部分3块一个小时
else:
f = 0.05 * time
return f
2.主函数部分主线程代码
import pygame
import time # 时间
import BtnF # 按钮
#import ocrutil
import parkF2
import parkF # 调用车牌号
from MysqlF import DDL, Select #连接数据库 查询
from location import text0,text1,text2,text3,text4, park_number, place_time,Parking_space
import Charging
import seaborn as sns
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']
# Matplotlib中设置字体-黑体,解决Matplotlib中文乱码问题
plt.rcParams['axes.unicode_minus'] = False
# 解决Matplotlib坐标轴负号'-'显示为方块的问题
sns.set(font='SimHei')
# Seaborn中设置字体-黑体,解决Seaborn中文乱码问题
#size=1500,484 # 窗体大小
size =1060,800 # 设定窗体大小
FPS=60 # 设置帧率(屏幕每秒的刷新次数)
# 设置背景颜色
DARKBLUE=(73,119,142)
# DARKBLUE = (75,0,130)
BG=DARKBLUE
#定义颜色
BLAK=(0,0,0)
WHITE = (255, 255, 255)
GREEN=(0,255,0)
BLUE=(72,61,139)
GRAY=(96,96,96)
RED=(220,20,60)
YELLOW=(255,255,0)
# 1.初始化
pygame.init()
# 2.设置窗体名称
pygame.display.set_caption('智能停车场车牌识别计费系统')
# 3.加载图片,设置图标
ic_launcher=pygame.image.load('img/img.png')
pygame.display.set_icon(ic_launcher)
# 4.设置窗体大小、背景颜色
screen=pygame.display.set_mode(size)
screen.fill(BG)
#获取进入停车场的车牌数,就是存入mysql中数据的条数(行数)
sql1 = "SELECT COUNT(*) FROM parkinfo WHERE type=0;"
res1 = Select(sql1)
#print(res1)
# 车位信息 根据type值查询车辆数 type为0表示车辆入场
sql2 = "SELECT count(*) FROM parkinfo where type = 0;"
res = Select(sql2)
print(res[0][0]) #打印入场车辆数 第一行第一列的值 type
# 车牌信息 所有车的车牌信息 (车牌号和入场时间)
sql3 = "SELECT carNumber, date FROM parkinfo where type = 0;"
select = Select(sql3) #用于调用
#print(select)
# sql4 = "SELECT carNumber FROM parkinfo where carNumber ;"
# print(sql4)
# carnumber = parkF.getcn() #获取识别的车牌号
# carnumber = '苏E05EV8'
# carnumber = parkF2.getcn() # 调用'泸KR9888'车牌号
carnumber = '泸KR9888' # 模拟车牌号,节约调用时间
# print(carnumber)
# carnumber = '苏ET07Z8'
# carnumber = '苏ET07Z8'
# carnumber = '苏EG08P9' #VIP车
# carnumber = '京Q58A77'
# carnumber = '鲁NE23AI' #手动设置车牌号,用于测试,节约时间
cartype = '' # 设定车的type初始值为空值
CarNumber = '' # 设定车牌号初始值为空值
Time = '' # 设定时间为空
# 初始化各类值
t0 = '' #停车时间
# t1 = '' #车的类型
# t2 = ''
t3 = ''
t4 = ''
t5 = ''
# type 车类型[0:入场 1:出场]
# state 车状态[0:临时车 1:长期车VIP]
# 设定字体显示大小
xFont = pygame.font.SysFont('SimHei', 20)
# 游戏循环帧率设(控制程序运行时间)
clock=pygame.time.Clock()
# 主线程
while True:
# 加载图像
image = pygame.image.load('imge/img_6.png')
# 设置图片大小
image = pygame.transform.scale(image, (640, 480))
# 绘制视频画面
screen.blit(image, (2, 2))
text0(screen) # 背景和信息文字的位置,以及矩形框的位置
text2(screen) # 停车信息表头的位置
text3(screen) # 收入总计的位置
text4(screen)
park_number(screen, select) # 入场车牌的位置
place_time(screen, select) # 入场时间的位置
# 创建识别按钮
BtnF.Button1(screen, (150, 480), 150, 60, (186,85,211), WHITE, "入场识别", 25) #入场按钮的位置
BtnF.Button2(screen, (640, 480), 150, 60, (186,85,211), WHITE, "计费统计", 25) #出场按钮的位置,同时是完成计费统计
for event in pygame.event.get():
# 关闭页面游戏退出
if event.type==pygame.QUIT:
pygame.quit()
exit()
elif event.type==pygame.MOUSEBUTTONDOWN: #事件判断鼠标是否进行了点击操作。
print(str(event.pos[0])+':'+str(event.pos[1]))#使用 event.pos 获取当前鼠标点击的位置坐标。
#入场识别按钮
# if 492<=event.pos[0] and event.pos[0]<=642 and 422<=event.pos[1] and event.pos[1]<=482:
if 0 <= event.pos[0] and event.pos[0] <= 150 and 422 <= event.pos[1] and event.pos[1] <= 482:
print('点击: 入场识别')
try:
#carnumber=parkF.getcn() #获取识别的车牌号
CarNumber=carnumber
#print(1)
# 格式化当前时间
# localtime = time.strftime('%Y-%m-%d %H:%M', time.localtime())
Time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
# sql3 = "SELECT carNumber FROM parkinfo where carNumber ;"
print('识别成功')
if Parking_space(res): #是否有车位
# 向数据库中插入时数据,同时插入车牌号,时间,类型,状态
# CURRENT_TIMESTAMP函数自动生成时间
sql = "INSERT INTO parkinfo (carNumber, date, type, state) VALUES('%s', " \
"CURRENT_TIMESTAMP, 0, 0)" % (str(carnumber))
state = DDL(sql)
if state > 0:
print('操作成功')
print("当前车牌号为:"+ CarNumber)
print('当前时间为:' + Time)
else:
print('操作失败')
else:
print('没有停车位')
except:
print('识别失败')
continue
pass
# 查询表中车牌号为carnumber,且车辆类型为0的最近一次停车记录的状态。
# 其中,ORDER BY子句按照记录的编号(pid)从小到大排序,LIMIT 1 子句只返回第一条查询结果。
s = "SELECT state FROM parkinfo WHERE carNumber = '%s' and type = 0 ORDER BY pid LIMIT 1;" % (str(carnumber))
re = Select(s)
# 查询车辆类型 查询的条件是车牌号和进入的车辆
# 判断车型 确定车是临时车还是VIP车
if re[0][0] == 0:
cartype = '临时车'
else:
cartype = 'VIP车'
#出场按钮识别 (用于计费统计各业务逻辑)
# if 0<=event.pos[0] and event.pos[0]<=150 and 422<=event.pos[1] and event.pos[1]<=482:
if 492<=event.pos[0] and event.pos[0]<=642 and 422<=event.pos[1] and event.pos[1]<=482:
print('点击出场识别')
try:
CarNumber = carnumber
# 车牌号为CarNumber,且车辆类型为0的最新一次停车记录的停车时间(单位: 分钟)和状态。
# TIMESTAMPDIFF函数用于计算两个时间戳之间的时间差
# ORDER BY子句按照记录的编号(pid)从大到小排序,LIMIT 1 子句只返回第一条查询结果
sql4 = "select TIMESTAMPDIFF(MINUTE, date, CURRENT_TIMESTAMP), state " \
"from parkinfo where carNumber = '%s' and type = 0 " \
"ORDER BY pid DESC LIMIT 1;" % (CarNumber)
res1 = Select(sql4)
#print(res1)
if res1[0][1] == 1: #得到state值
money = 0 # VIP账户不收钱
else: # 计费标准为
money = Charging.payMoney(res1[0][0])
sql5 = "insert into parkCharge(carNumber, chargeDate, chargeMoney) VALUES" \
"('%s', '%s', '%s');" % (str(CarNumber), res1[0][0], money)
state = DDL(sql5) # 数据条数
print(state)
if state > 0:
print('操作成功') # 插入数据成功
else:
print('操作失败')
# 它的作用是查询parkCharge表中符合条件carNumber = '%s'的记录,
# 并按照cid降序排序,只返回一条最新记录的chargeDate和chargeMoney。
sql6 = "select chargeDate, chargeMoney from parkCharge WHERE carNumber = '%s' " \
"ORDER BY cid DESC LIMIT 1;" % (str(CarNumber))
res2 = Select(sql6)
t0 = res2[0][0] #停车时长
# print(t0)
if res1[0][1] == 1: # state的值为1,则是VIP车
t1 = 'VIP车'
t2 = 'VIP用户不收钱,免费停车'
else:
t3 ='此车类型为:临时车' #否则为临时车
if res2[0][0] <= 30: #临时车有的情况
t4='停车小于30分钟不收钱'
else:
t5=Charging.payMoney(res2[0][0])
sql7 = "UPDATE parkinfo set type = 1 where carNumber = '%s';" % (str(CarNumber))
s = DDL(sql7) #更新信息表中的符合条件的车牌,让type值为1 表示该车已经出场
if s > 0:
print('修改成功')
else:
print('修改失败')
#算钱
#计算该车牌号对应的总费用,并按照总费用的升序进行排序。
sq1 = "SELECT carNumber,SUM(chargeMoney) FROM parkcharge where " \
"carNumber = '%s' GROUP BY carNumber ORDER BY SUM(chargeMoney);" % (str(carnumber))
re111 = Select(sq1) # 获取总收入金额
screen.blit(xFont .render( str(re111[0][1])+' 元', True, (238, 130, 238), (73, 119, 142)), (840, 730))
# 画出停车时长统计图
# sq1 = "SELECT carNumber,SUM(chargeDate) FROM parkcharge GROUP BY carNumber ORDER BY SUM(chargeDate);"
# re11 = Select(sq1)
# for i in re11:
# plt.bar(i[0], i[1], width=0.5, align='center')
# plt.title('停车时长')
# plt.savefig('./save_img/figure.jpg', bbox_inches='tight')
# plt.show()
# # 加载图像
# image1 = pygame.image.load('./save_img/figure.jpg')
# # 设置图片大小
# image1 = pygame.transform.scale(image1, (300, 300))
# # 绘制视频画面
# screen.blit(image1, (10, 490))
# 画出各车停车消费图
sq2 = "SELECT carNumber,SUM(chargeMoney) FROM parkcharge GROUP BY carNumber ORDER BY SUM(chargeMoney);"
re12 = Select(sq2)
for i in re12:
plt.bar(i[0], i[1], width=0.5, align='center')
plt.title('各车牌费用情况')
plt.savefig('./save_img/figure1.jpg', bbox_inches='tight')
# plt.show()
# 加载图像
image2 = pygame.image.load('./save_img/figure1.jpg')
# 设置图片大小
image2 = pygame.transform.scale(image2, (300, 300))
# 绘制视频画面
screen.blit(image2, (170, 490))
except:
print('没有该车')
continue
pass
screen.blit(xFont.render('此车牌号为:' + CarNumber, True, (238, 130, 238), (73, 119, 142)),
(660, 380))
text1(screen, res1) # 剩余车位和共有车位的位置
screen.blit(xFont.render('此入场车类型为:' + cartype, True, (238,130,238), (73, 119, 142)),
(660, 410))
screen.blit(xFont.render('进入时间为:' + Time, True, (238,130,238), (73, 119, 142)),
(660, 440))
screen.blit(xFont.render('停车时间为: ' + str(t0) + '分钟', True, (238,130,238), (73, 119, 142)),
(660, 470))
# screen.blit(xFont.render('此车类型为:'+ str(t1), True, (238,130,238), (73, 119, 142)), (660, 500))
# screen.blit(xFont.render('费用: ' + str(t2), True, (238,130,238), (73, 119, 142)),(660, 530))
screen.blit(xFont.render(str(t3), True, (238,130,238), (73, 119, 142)),(660, 560))
screen.blit(xFont.render(str(t4), True, (238,130,238), (73, 119, 142)),(730, 590))
screen.blit(xFont.render('收取费用为:' + str(t5) + ' 元', True, (238,130,238), (73, 119, 142)),(660, 620))
# XFont = pygame.font.SysFont('SimHei', 30)
# screen.blit(XFont.render('总计收入为:' , True, (238, 130, 238), (73, 119, 142)), (660, 730))
pygame.display.flip() # 更新界面
clock.tick(FPS) # 控制游戏最大帧率为60
3.项目演示
3.1 运行项目界面显示效果
3.2 车辆入场停车识别按钮测试
pygame界面显示效果
mysql中数据显示
发现该车停车成功
3.3出场识别按钮演示
出场的时候同时统计出每张车的费用,这里用可视化展示,并显示停车场总的收入
mysql中的数据显示结果
各车辆的停车费用情况
3.4项目最终显示结果
3.5项目优化显示
四、总结
基于人工智能计算机视觉实现的智能停车管理计费系统可以帮助停车场管理方快速高效地解决停车难管理的问题,提高了停车场的利用率和管理效率。此项目运用了opencv实现车牌识别,MYSQL存储数据,pygame显示车辆信息,综合性强,具有很强的实战意义,通过本次项目实现既能温故所学知识,又能激发自己的潜力,提升自己的综合分析能了和实战能力。其中opencv车牌识别部分最具有实战意义,综合了图像像基本处理、形态学、感兴趣区域、轮廓检测、模版匹配、车牌字符分割、字符识别等知识点。MYSQL部分主要是学会建表、查询、更改数据,以及学会python与mysql之间的交互,也就是连接数据库,在pyhton中操作mysql。Pygame部分主要是学会窗体的制作、车牌图片、信息、画图、按钮设置等操作。最后再将三者联系起来,形成一个完整的项目系统。但,opencv实现车牌识别部分存在很多问题,在本次项目中,识别图片只能一张一张的识别,而且每识别一张,就要重新更改参数阈值,寻找车牌的位置、轮廓等,而且车牌图片必须找比较正,清晰的图片,才能达到想要的效果。当然,随着时代的飞速运转,科技发展迅猛,对与opencv实现车牌识别也在不断的更新和替代,而对于一个学者来说,针对这个opencv存在的缺点,后面可以考虑用深度学习、yolo5+OCR识别,这样也可以学到很多东西,也能进行三者的效果比对,选取较好的方法进行车牌识别,若只是借用车牌识别的效果还可以申请百度key调用API,方便又快捷,但是学不到计算机视觉的相关知识。所以不论如何对于一个学者而言,都是在学习的路上,通过本次项目的实现可以说是极大的提升了自己的项目经历和能力,针对本次项目也有很大的缺陷和不足,对于opencv车牌识别部分后期可以考虑用深度学习Tensolrfow框架实现,pygame页面可以根据需求不断的改进和更新以及优化。