YOLOv8车牌识别系统 深度学习 LPRNet算法 pytorch 大数据 毕业设计(源码)✅
1、项目介绍
技术栈:
Python3.8 YOLOv8 深度学习 LPRNet算法 pytorch
2、项目界面
(1)上传图片进行车牌识别
(2)上传图片进行车牌识别2
(3)多车牌号码进行车牌识别
(4)上传视频进行车牌识别实时检测
(5)连接摄像头进行车牌识别
(6)连接摄像头进行车牌识别2
(7)车牌识别检测
3、项目说明
项目介绍:
基于YOLOv8+LPRNet进行车牌检测及识别,包括对车辆的车牌区域精确定位,利用校正探测器对定位的车牌进行边框校正处理,使用增强神经网络模型对车牌区域进行超分辨率技术处理和光学字符识别。经过多次试验测试,可以对视频中的车辆车牌实时识别以及图片中的车辆车牌进行准确定位和识别,识别速度快,准确率高,比那些传统车牌识别方法效果好很多。
LPRNet由轻量级的卷积神经网络组成,所以它可以采用端到端的方法来进行训练。据我们所知,LPRNet是第一个没有采用RNNs的实时车牌识别系统。因此,LPRNet算法可以为LPR创建嵌入式部署的解决方案,即便是在具有较高挑战性的中文车牌识别上。
4、核心代码
from ultralytics.yolo.engine.predictor import BasePredictor
from ultralytics.yolo.engine.results import Results
from ultralytics.yolo.utils import DEFAULT_CFG, LOGGER, SETTINGS, callbacks, ops
from ultralytics.yolo.utils.plotting import Annotator, colors, save_one_box
from ultralytics.yolo.utils.torch_utils import smart_inference_mode
from ultralytics.yolo.utils.files import increment_path
from ultralytics.yolo.utils.checks import check_imshow
from ultralytics.yolo.cfg import get_cfg
from PySide6.QtWidgets import QApplication, QMainWindow, QFileDialog, QMenu
from PySide6.QtGui import QImage, QPixmap, QColor
from PySide6.QtCore import QTimer, QThread, Signal, QObject, QPoint, Qt
from ui.CustomMessageBox import MessageBox
from ui.home import Ui_MainWindow
from UIFunctions import *
from collections import defaultdict
from pathlib import Path
from utils.capnums import Camera
from utils.rtsp_win import Window
import numpy as np
import time
import json
import torch
import sys
import cv2
import os
from yoloPre import YoloPredictor
class MainWindow(QMainWindow, Ui_MainWindow):
# 这是一个 PySide6 的信号(Signal)对象,
# 用于在应用程序中传递消息。在这个场景中,它被用于在主窗口和 YOLO 实例之间传递消息。
main2yolo_begin_sgl = Signal() # The main window sends an execution signal to the yolo instance
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
# basic interface 【UI初始化!】
self.setupUi(self)
self.setAttribute(Qt.WA_TranslucentBackground) # rounded transparent
self.setWindowFlags(Qt.FramelessWindowHint) # Set window flag: hide window borders
UIFuncitons.uiDefinitions(self)
# Show module shadows
UIFuncitons.shadow_style(self, self.Class_QF, QColor(162,129,247))
UIFuncitons.shadow_style(self, self.Target_QF, QColor(251, 157, 139))
UIFuncitons.shadow_style(self, self.Fps_QF, QColor(170, 128, 213))
UIFuncitons.shadow_style(self, self.Model_QF, QColor(64, 186, 193))
# read model folder 加载模型
self.pt_list = os.listdir('./models')
self.pt_list = [file for file in self.pt_list if file.endswith('.pt')]
self.pt_list.sort(key=lambda x: os.path.getsize('./models/' + x)) # sort by file size
self.model_box.clear()
self.model_box.addItems(self.pt_list)
self.Qtimer_ModelBox = QTimer(self) # Timer: Monitor model file changes every 2 seconds
self.Qtimer_ModelBox.timeout.connect(self.ModelBoxRefre)
self.Qtimer_ModelBox.start(2000)
# Yolo-v8 thread 初始化
self.yolo_predict = YoloPredictor() # Create a Yolo instance
self.select_model = self.model_box.currentText() # default model
self.yolo_predict.new_model_name = "./models/%s" % self.select_model
self.yolo_thread = QThread() # Create yolo thread
# 将 Yolo 类中的信号绑定到主线程的槽函数上
self.yolo_predict.yolo2main_pre_img.connect(lambda x: self.show_image(x, self.pre_video))
self.yolo_predict.yolo2main_res_img.connect(lambda x: self.show_image(x, self.res_video))
self.yolo_predict.yolo2main_status_msg.connect(lambda x: self.show_status(x))
self.yolo_predict.yolo2main_fps.connect(lambda x: self.fps_label.setText(x))
# self.yolo_predict.yolo2main_labels.connect(self.show_labels)
self.yolo_predict.yolo2main_class_num.connect(lambda x:self.Class_num.setText(str(x)))
self.yolo_predict.yolo2main_target_num.connect(lambda x:self.Target_num.setText(str(x)))
self.yolo_predict.yolo2main_progress.connect(lambda x: self.progress_bar.setValue(x))
# 将主线程的信号绑定到 Yolo 类的槽函数上,并启动 Yolo 线程
self.main2yolo_begin_sgl.connect(self.yolo_predict.run)
self.yolo_predict.moveToThread(self.yolo_thread)
# Model parameters
self.model_box.currentTextChanged.connect(self.change_model)
self.iou_spinbox.valueChanged.connect(lambda x:self.change_val(x, 'iou_spinbox')) # iou box
self.iou_slider.valueChanged.connect(lambda x:self.change_val(x, 'iou_slider')) # iou scroll bar
self.conf_spinbox.valueChanged.connect(lambda x:self.change_val(x, 'conf_spinbox')) # conf box
self.conf_slider.valueChanged.connect(lambda x:self.change_val(x, 'conf_slider')) # conf scroll bar
self.speed_spinbox.valueChanged.connect(lambda x:self.change_val(x, 'speed_spinbox'))# speed box
self.speed_slider.valueChanged.connect(lambda x:self.change_val(x, 'speed_slider')) # speed scroll bar
# Prompt window initialization
self.Class_num.setText('--')
self.Target_num.setText('--')
self.fps_label.setText('--')
self.Model_name.setText(self.select_model)
# Select detection source
self.src_file_button.clicked.connect(self.open_src_file) # select local file
self.src_cam_button.clicked.connect(self.chose_cam) #chose_camera
# self.src_rtsp_button.clicked.connect(self.show_status("The function has not yet been implemented."))#chose_rtsp
# start testing button
self.run_button.clicked.connect(self.run_or_continue) # pause/start
self.stop_button.clicked.connect(self.stop) # termination
# Other function buttons
self.save_res_button.toggled.connect(self.is_save_res) # save image option
self.save_txt_button.toggled.connect(self.is_save_txt) # Save label option
self.ToggleBotton.clicked.connect(lambda: UIFuncitons.toggleMenu(self, True)) # left navigation button
self.settings_button.clicked.connect(lambda: UIFuncitons.settingBox(self, True)) # top right settings button
# initialization
self.load_config()
# The main window displays the original image and detection results
@staticmethod
def show_image(img_src, label):
try:
ih, iw, _ = img_src.shape # 获取原始图像的高度和宽度
w = label.geometry().width() # 获取 QLabel 组件的当前宽度
h = label.geometry().height() # 获取 QLabel 组件的当前高度
# 根据原始图像和 QLabel 组件的大小,等比例缩放原始图像
if iw / w > ih / h: # 如果原始图像的宽度比高度大,按照宽度比例缩放
scal = w / iw
nw = w
nh = int(scal * ih)
img_src_ = cv2.resize(img_src, (nw, nh))
else: # 如果原始图像的高度比宽度大,按照高度比例缩放
scal = h / ih
nw = int(scal * iw)
nh = h
img_src_ = cv2.resize(img_src, (nw, nh))
# 将 OpenCV 图像转换为 QImage 对象,并将其显示在 QLabel 组件中
frame = cv2.cvtColor(img_src_, cv2.COLOR_BGR2RGB) # 将图像的颜色空间从 BGR 转换为 RGB
img = QImage(frame.data, frame.shape[1], frame.shape[0], frame.shape[2] * frame.shape[1],
QImage.Format_RGB888) # 创建 QImage 对象
label.setPixmap(QPixmap.fromImage(img)) # 将 QImage 对象转换为 QPixmap 对象,并将其显示在 QLabel 组件中
except Exception as e:
print(repr(e))
# Control start/pause
def run_or_continue(self):
if self.yolo_predict.source == '':
print('Please select a video source')
self.show_status('Please select a video source before starting detection...')
self.run_button.setChecked(False)
else:
self.yolo_predict.stop_dtc = False
print('start')
if self.run_button.isChecked():
self.run_button.setChecked(True) # start button
self.save_txt_button.setEnabled(False) # It is forbidden to check and save after starting the detection
self.save_res_button.setEnabled(False)
self.show_status('Detecting...')
self.yolo_predict.continue_dtc = True # Control whether Yolo is paused
if not self.yolo_thread.isRunning():
self.yolo_thread.start()
self.main2yolo_begin_sgl.emit()
else:
self.yolo_predict.continue_dtc = False
self.show_status("Pause...")
self.run_button.setChecked(False) # start button
# bottom status bar information
def show_status(self, msg):
self.status_bar.setText(msg)
print(msg)
if msg == 'Detection completed' or msg == '检测完成':
self.save_res_button.setEnabled(True)
self.save_txt_button.setEnabled(True)
self.run_button.setChecked(False)
self.progress_bar.setValue(0)
if self.yolo_thread.isRunning():
self.yolo_thread.quit() # end process
elif msg == 'Detection terminated!' or msg == '检测终止':
self.save_res_button.setEnabled(True)
self.save_txt_button.setEnabled(True)
self.run_button.setChecked(False)
self.progress_bar.setValue(0)
if self.yolo_thread.isRunning():
self.yolo_thread.quit() # end process
self.pre_video.clear() # clear image display
self.res_video.clear()
self.Class_num.setText('--')
self.Target_num.setText('--')
self.fps_label.setText('--')
# select local file
def open_src_file(self):
print('local file')
config_file = 'config/fold.json'
config = json.load(open(config_file, 'r', encoding='utf-8'))
open_fold = config['open_fold']
if not os.path.exists(open_fold):
open_fold = os.getcwd()
name, _ = QFileDialog.getOpenFileName(self, 'Video/image', open_fold, "Pic File(*.mp4 *.mkv *.avi *.flv *.jpg *.png)")
if name:
self.yolo_predict.source = name
self.show_status('Load File:{}'.format(os.path.basename(name)))
config['open_fold'] = os.path.dirname(name)
config_json = json.dumps(config, ensure_ascii=False, indent=2)
with open(config_file, 'w', encoding='utf-8') as f:
f.write(config_json)
self.stop()
# Select camera source---- have one bug
def chose_cam(self):
try:
print('open camera')
# self.stop()
# MessageBox(
# self.close_button, title='Note', text='loading camera...', time=2000, auto=True).exec()
# get the number of local cameras
# _, cams = Camera().get_cam_num()
# self.viewer = CameraViewer() # 创建新的 CameraViewer 类的对象
# self.viewer.show() # 显示 CameraViewer 类的 GUI 界面
self.yolo_predict.camera_run()
except Exception as e:
print(e)
self.show_status('%s' % e)
# select network source
def chose_rtsp(self):
self.rtsp_window = Window()
config_file = 'config/ip.json'
if not os.path.exists(config_file):
ip = "rtsp://admin:[email protected]:555"
new_config = {"ip": ip}
new_json = json.dumps(new_config, ensure_ascii=False, indent=2)
with open(config_file, 'w', encoding='utf-8') as f:
f.write(new_json)
else:
config = json.load(open(config_file, 'r', encoding='utf-8'))
ip = config['ip']
self.rtsp_window.rtspEdit.setText(ip)
self.rtsp_window.show()
self.rtsp_window.rtspButton.clicked.connect(lambda: self.load_rtsp(self.rtsp_window.rtspEdit.text()))
# load network sources
def load_rtsp(self, ip):
try:
self.stop()
MessageBox(
self.close_button, title='提示', text='加载 rtsp...', time=1000, auto=True).exec()
self.yolo_predict.source = ip
new_config = {"ip": ip}
new_json = json.dumps(new_config, ensure_ascii=False, indent=2)
with open('config/ip.json', 'w', encoding='utf-8') as f:
f.write(new_json)
self.show_status('Loading rtsp:{}'.format(ip))
self.rtsp_window.close()
except Exception as e:
self.show_status('%s' % e)
# Save test result button--picture/video
def is_save_res(self):
if self.save_res_button.checkState() == Qt.CheckState.Unchecked:
self.show_status('NOTE: Run image results are not saved.')
self.yolo_predict.save_res = False
elif self.save_res_button.checkState() == Qt.CheckState.Checked:
self.show_status('NOTE: Run image results will be saved.')
self.yolo_predict.save_res = True
# Save test result button -- label (txt)
def is_save_txt(self):
if self.save_txt_button.checkState() == Qt.CheckState.Unchecked:
self.show_status('NOTE: Labels results are not saved.')
self.yolo_predict.save_txt = False
elif self.save_txt_button.checkState() == Qt.CheckState.Checked:
self.show_status('NOTE: Labels results will be saved.')
self.yolo_predict.save_txt = True
# Configuration initialization ~~~wait to change~~~
def load_config(self):
config_file = 'config/setting.json'
if not os.path.exists(config_file):
iou = 0.26
conf = 0.33
rate = 10
save_res = 0
save_txt = 0
new_config = {"iou": iou,
"conf": conf,
"rate": rate,
"save_res": save_res,
"save_txt": save_txt
}
new_json = json.dumps(new_config, ensure_ascii=False, indent=2)
with open(config_file, 'w', encoding='utf-8') as f:
f.write(new_json)
else:
config = json.load(open(config_file, 'r', encoding='utf-8'))
if len(config) != 5:
iou = 0.26
conf = 0.33
rate = 10
save_res = 0
save_txt = 0
else:
iou = config['iou']
conf = config['conf']
rate = config['rate']
save_res = config['save_res']
save_txt = config['save_txt']
self.save_res_button.setCheckState(Qt.CheckState(save_res))
self.yolo_predict.save_res = (False if save_res==0 else True )
self.save_txt_button.setCheckState(Qt.CheckState(save_txt))
self.yolo_predict.save_txt = (False if save_txt==0 else True )
self.run_button.setChecked(False)
self.show_status("Welcome~")
# Terminate button and associated state
def stop(self):
print('stop')
if self.yolo_thread.isRunning():
self.yolo_thread.quit() # end thread
self.yolo_predict.stop_dtc = True
self.run_button.setChecked(False) # start key recovery
self.save_res_button.setEnabled(True) # Ability to use the save button
self.save_txt_button.setEnabled(True) # Ability to use the save button
self.pre_video.clear() # clear image display
self.res_video.clear() # clear image display
self.progress_bar.setValue(0)
self.Class_num.setText('--')
self.Target_num.setText('--')
self.fps_label.setText('--')
# Change detection parameters
def change_val(self, x, flag):
if flag == 'iou_spinbox':
self.iou_slider.setValue(int(x*100)) # The box value changes, changing the slider
elif flag == 'iou_slider':
self.iou_spinbox.setValue(x/100) # The slider value changes, changing the box
self.show_status('IOU Threshold: %s' % str(x/100))
self.yolo_predict.iou_thres = x/100
elif flag == 'conf_spinbox':
self.conf_slider.setValue(int(x*100))
elif flag == 'conf_slider':
self.conf_spinbox.setValue(x/100)
self.show_status('Conf Threshold: %s' % str(x/100))
self.yolo_predict.conf_thres = x/100
elif flag == 'speed_spinbox':
self.speed_slider.setValue(x)
elif flag == 'speed_slider':
self.speed_spinbox.setValue(x)
self.show_status('Delay: %s ms' % str(x))
self.yolo_predict.speed_thres = x # ms
# change model 【更换模型】
def change_model(self,x):
self.select_model = self.model_box.currentText()
self.yolo_predict.new_model_name = "./models/%s" % self.select_model
self.show_status('Change Model:%s' % self.select_model)
self.Model_name.setText(self.select_model)
# label result
# def show_labels(self, labels_dic):
# try:
# self.result_label.clear()
# labels_dic = sorted(labels_dic.items(), key=lambda x: x[1], reverse=True)
# labels_dic = [i for i in labels_dic if i[1]>0]
# result = [' '+str(i[0]) + ':' + str(i[1]) for i in labels_dic]
# self.result_label.addItems(result)
# except Exception as e:
# self.show_status(e)
# Cycle monitoring model file changes
def ModelBoxRefre(self):
pt_list = os.listdir('./models')
pt_list = [file for file in pt_list if file.endswith('.pt')]
pt_list.sort(key=lambda x: os.path.getsize('./models/' + x))
# It must be sorted before comparing, otherwise the list will be refreshed all the time
if pt_list != self.pt_list:
self.pt_list = pt_list
self.model_box.clear()
self.model_box.addItems(self.pt_list)
# Get the mouse position (used to hold down the title bar and drag the window)
def mousePressEvent(self, event):
p = event.globalPosition()
globalPos = p.toPoint()
self.dragPos = globalPos
# Optimize the adjustment when dragging the bottom and right edges of the window size
def resizeEvent(self, event):
# Update Size Grips
UIFuncitons.resize_grips(self)
# Exit Exit thread, save settings
def closeEvent(self, event):
config_file = 'config/setting.json'
config = dict()
config['iou'] = self.iou_spinbox.value()
config['conf'] = self.conf_spinbox.value()
config['rate'] = self.speed_spinbox.value()
config['save_res'] = (0 if self.save_res_button.checkState()==Qt.Unchecked else 2)
config['save_txt'] = (0 if self.save_txt_button.checkState()==Qt.Unchecked else 2)
config_json = json.dumps(config, ensure_ascii=False, indent=2)
with open(config_file, 'w', encoding='utf-8') as f:
f.write(config_json)
# Exit the process before closing
if self.yolo_thread.isRunning():
self.yolo_predict.stop_dtc = True
self.yolo_thread.quit()
MessageBox(
self.close_button, title='Note', text='Exiting, please wait...', time=3000, auto=True).exec()
sys.exit(0)
else:
sys.exit(0)
if __name__ == "__main__":
app = QApplication(sys.argv)
Home = MainWindow()
Home.show()
sys.exit(app.exec())
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
标签:predict,LPRNet,self,yolo,源码,毕业设计,save,config,button
From: https://blog.csdn.net/Jake_make/article/details/143961470