文章目录
- 一、视频演示
- 二、实现的功能
- 2.1、逻辑流程框架
- 三、Pyqt介绍
- 3.1、PyQt5软件安装
- 3.2PyQt5-tools软件安装
- 四、yolo目标识别算法介绍
- 4.1、YoloV8环境安装
- 五、环境搭建
- 六、运行跑一下
- 七、代码
一、视频演示
yolo目标识别算法实现车辆识别与行人识别
二、实现的功能
- 摄像头雾天画面捕捉与采集:采用雾天检测摄像头实时捕捉当前画面传给处理器。
- 对视频和图片信息进行算法识别与处理:将摄像头采集的数据或者本地的数据进行算法处理。
- 对车辆和行人标注并输出呈现:算法处理后的信息进行实时标注出车辆和行人并呈现到界面上。
- 导出已标注的采样视频功能:将已经标注了的视频可导出,方便反复查看,对交通事故的判定起到很好的记录作用。
2.1、逻辑流程框架
三、Pyqt介绍
python能做的事很多,使用起来很灵活,包括上位机软件大家熟知的是使用Qt开发,Py也可以的,本项目中用的是PyQt5。
选用PyQt5作为本次系统开发设计的原因有很多,其中是因为它的功能强大并且有着灵活易用的Python库,还提供了丰富的GUI控件、布局管理、事件处理、多线程处理、国际化支持、数据库集成以及与其他库的集成能力。这些功能使得PyQt5成为开发高质量跨平台图形用户界面应用程序的理想选择,所以我选择了使用PyQt5作为本次系统界面和主要逻辑框架的最佳选择。
3.1、PyQt5软件安装
打开CMD或者PowerShell,在命令窗中输入pip install PyQt5。
3.2PyQt5-tools软件安装
然后安装PyQt5-tools,同样的步骤,在命令窗口输入pip install PyQt5-tools。
四、yolo目标识别算法介绍
YOLOv8 是于2023年1月10日推出的,截至目前它是计算机视觉领域中用于分类、检测和分割任务的最先进模型。该模型在精度和执行时间方面都优于所有已知模型。YOLOv8是建立在Yolo系列的历史版本基础之上的,在此版本较之前版本在性能上进一步提升和增加了灵活性。YOLOv8具有如下特点,第一对用户友好的 API(命令行 + Python),第二模型更快更准确,第三模型能完成目标检测、实例分割和图像分类任务。YOLOv8是一种无锚(Anchor-Free)模型,这意味着它直接预测对象的中心,而不是通过预测锚框的偏移量来定位对象。相对于使用锚框的传统方法,无锚模型通过减少需要预测的边界框的数量,加速了复杂的推理步骤,如非极大值抑制(NMS)。根据以上特性在雾天的车辆行人识别系统选择此模型是不二选择。
4.1、YoloV8环境安装
YoloV8的安装同样也非常简单,只需要在电脑的管理界面中输入如下三行命令即可。管理界面是通过在Win+R的快捷键,输入cmd,即弹出黑色的对话框,依次输入如下命令即可安装。
详细的yolov8可通过查看官网资料了解
conda create -n yolov8 python= 3.8
activate yolov8
pip install ultralytics
五、环境搭建
在D盘根目录下新建一个文件夹,比如,我创建的文件夹是叫pyqt
ug文件夹里就是代码,打开此ug文件夹,然后在路径中输入cmd,就会弹出下图中的黑框框
然后依次在命令行中输入下面的指令,进行安装软件和环境
1、pip list
2、pip3 install torch torchvision torchaudio
如果2下载不成功就用3使用清华的源
3、pip3 install torch torchvision torchaudio -i https://pypi.tuna.tsinghua.edu.cn/simple
4、Pip3 install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
六、运行跑一下
在代码这个目录下的命令行对话框中输入python gui.py
稍等10s左右代码就启动运行了。
可以选择识别图片或者视频都可以,也可是使用电脑摄像头识别实时捕捉的画面同时支持导出当前识别后的结果。
七、代码
gui.py
import sys
from PyQt5.QtWidgets import (QWidget, QPushButton,
QHBoxLayout, QVBoxLayout, QApplication, QLabel,
QFileDialog, QMessageBox, QRadioButton, QFrame)
from PyQt5.QtGui import QPixmap, QImage
from PyQt5.QtCore import QTimer, Qt
import cv2
import os
from model import PredictModel
class MainGUI(QWidget):
def __init__(self):
super().__init__()
self.windows_title = "基于人工智能的机场车辆行人识别系统"
self.camera_stat = False # 摄像头默认关闭
self.cap = None # 视频或者摄像头
self.export_cap = None # 导出的视频
self.fname = None # 图片或者视频路径
self.init_ui() # 初始化gui
self.init_timer() # 初始化定时器
self.predictor = PredictModel() # 模型预测的类
def init_ui(self):
h_layout = QHBoxLayout() # 水平布局
# 第一列布局,显示窗口 选择图片/视频按钮 打开摄像头按钮
v1_layout = QVBoxLayout()
v1_h1_layout = QHBoxLayout()
self.display_label = QLabel() # 用于显示图片的label
self.display_label.setMinimumHeight(300)
self.display_label.setMinimumWidth(400)
self.display_label.setStyleSheet("border: 1px dashed")
v1_h1_layout.addWidget(self.display_label)
v1_layout.addLayout(v1_h1_layout)
v1_h2_layout = QHBoxLayout()
v1_h2_layout.addStretch(1)
self.image_btn = QPushButton("选择图片/视频") # 选择图片、视频按钮
self.image_btn.clicked.connect(self.select_file) # 选择图片视频按钮对应的事件
v1_h2_layout.addWidget(self.image_btn)
v1_h2_layout.addStretch(1)
self.camera_btn = QPushButton("打开/关闭摄像头") # 打开关闭摄像头按钮
self.camera_btn.clicked.connect(self.open_camera) # 打开关闭摄像头按钮对应的事件
v1_h2_layout.addWidget(self.camera_btn)
v1_h2_layout.addStretch(1)
v1_layout.addLayout(v1_h2_layout)
v1_frame = QFrame()
# v1_frame.setStyleSheet("border: 1px dashed; border-color: (100, 100, 100);")
v1_frame.setFrameStyle(QFrame.Panel|QFrame.Sunken)
v1_frame.setLayout(v1_layout)
# 第二列 显示检测结果
v2_layout = QVBoxLayout()
dis_frame = QFrame()
dis_frame.setFrameStyle(QFrame.Panel|QFrame.Sunken)
dis_frame.setFixedHeight(50)
dis_layout = QHBoxLayout()
self.display_detected_img_btn = QRadioButton("显示检测结果") # 是否展示检测结果的勾选按钮
self.display_detected_img_btn.setStyleSheet("border: 1px dashed")
self.display_detected_img_btn.setChecked(True)
dis_layout.addWidget(self.display_detected_img_btn)
dis_frame.setLayout(dis_layout)
v2_layout.addWidget(dis_frame)
export_frame = QFrame()
export_frame.setFixedHeight(120)
export_frame.setFrameStyle(QFrame.Panel|QFrame.Sunken)
export_layout = QVBoxLayout()
self.export_btn = QRadioButton("导出检测结果") # 是否保存检测结果的选择按钮
self.export_btn.setStyleSheet("border: 1px dashed")
self.export_btn.setChecked(True)
export_layout.addWidget(self.export_btn)
self.export_path_btn = QPushButton("选择导出路径") # 如果导出检测结果,选择对应的保存路径
self.export_path_btn.setChecked(True)
self.export_path_btn.clicked.connect(self.select_export_path)
export_layout.addWidget(self.export_path_btn)
default_export_path = os.path.join(os.getcwd(), 'outputs')
os.makedirs(default_export_path, exist_ok=True)
self.export_label = QLabel(default_export_path)
self.export_label.setFixedHeight(20)
self.export_label.setStyleSheet("border: 1px dashed; border-color: (100, 100, 100);")
export_layout.addWidget(self.export_label)
export_frame.setLayout(export_layout)
v2_layout.addWidget(export_frame)
self.info_label = QLabel() # 展示最终的检测结果
self.info_label.setStyleSheet("border: 1px dashed")
v2_layout.addWidget(self.info_label)
v2_frame = QFrame()
# v2_frame.setStyleSheet("border: 1px dashed; border-color: (100, 100, 100);")
v2_frame.setFrameStyle(QFrame.Panel|QFrame.Sunken)
v2_frame.setLayout(v2_layout)
h_layout.addWidget(v1_frame)
h_layout.addWidget(v2_frame)
self.setLayout(h_layout)
self.setWindowTitle(self.windows_title)
# self.setGeometry(300, 300, 500, 500)
self.show()
def select_export_path(self):
directory = QFileDialog.getExistingDirectory(None, "选择导出文件路径", "./") # 选择导出的文件路径
self.export_label.setText(directory)
return directory
def select_file(self):
# 仅允许选择一个文件
self.fname, _ =QFileDialog.getOpenFileName(self,'打开文件', "./", "Files(*.jpg *.bmp *.png *.jpeg *.mp4 *.avi)") # 选择文件仅允许图片和视频格式
if self.fname == '':
return
if self.fname.endswith(('png', 'bmp', 'jpg', 'jpeg')):
self.show_single_frame(self.fname) # 如果是图片,处理图片即可
self.fname = None # 清空路径
else:
self.cap = cv2.VideoCapture(self.fname) # 如果是视频,因为视频包含多个帧,逐帧处理,设定一个定时器,每间隔一定的时间就会处理一帧
if not self.cap.isOpened():
QMessageBox.information(self, "警告", "视频异常!", QMessageBox.Ok)
self.cap = None
return
self.display_label.setEnabled(True)
self.timer.start() # 清空路径放在定时器
print("beginning!") #
def open_camera(self):
if self.camera_stat: # 如果摄像头已经开启,那么再次点击就是关闭它
self.camera_stat = not self.camera_stat
self.close_camera()
return
self.cap = cv2.VideoCapture(0) # 开启摄像头
if not self.cap.isOpened():
QMessageBox.information(self, "警告", "摄像头开启失败!", QMessageBox.Ok)
self.cap = None
else:
self.camera_stat = True
# 幕布可以播放
self.display_label.setEnabled(True) # 摄像头开启,开启定时器,逐帧播放
self.timer.start()
print("beginning!")
def close_camera(self):
# 关闭摄像头,关闭定时器,清空导出路径
self.cap.release()
self.cap = None
self.timer.stop()
if self.export_cap != None: # 清空导出视频
self.export_cap.release()
self.export_cap = None
self.fname = None
# 播放视频画面
def init_timer(self):
# 初始化定时器,定时器的功能是按照间隔播放摄像头或者视频的内容
self.timer = QTimer(self)
self.timer.timeout.connect(self.show_pic)
# 显示视频图像
def show_pic(self):
# 如果是视频或者摄像头,逐帧检测结果
ret, img = self.cap.read()
if ret:
self.show_single_frame(img)
def show_single_frame(self, img):
# 检测图片,或者视频和摄像头的一个帧
image_path = None
if isinstance(img, str):
image_path = img
img = cv2.imread(img) # 如果是图片路径,则读取图片
##################################################det#####################################################
result, det_img, names, cls_info = self.predictor(img) # 对图片或者帧进行预测
txt = '' # 用于整合显示的检测结果
print(names, cls_info) # 打印相关信息
for k in names.keys():
name = names[k]
if k in cls_info.keys():
count = cls_info[k]
txt += name + ': ' + str(count) + '\n'
else:
txt += name + ': ' + '0' + '\n'
self.info_label.setText(txt) # 将检测结果显示到gui的右下角框内
if self.display_detected_img_btn.isChecked(): # 如果显示检测结果被选择,则显示检测结果
img = det_img
if self.export_btn.isChecked(): # 如果导出按钮被选择
if image_path is not None: # 检测的是图片
image_name = os.path.basename(image_path)
out_path = os.path.join(self.export_label.text(), image_name)
cv2.imwrite(out_path, det_img) # 保存检测结果
else: # 检测的是摄像头或者视频
if self.export_cap is None:
fourcc = cv2.VideoWriter_fourcc(*'XVID')
if self.fname is not None: # 如果是视频,保存其原有的名字,如果是摄像头,新建名字output.avi
video_name = os.path.basename(self.fname)
if 'mp4' in video_name:
video_name = video_name.replace('mp4', 'avi')
print(video_name)
else:
video_name = 'output.avi'
out_path = os.path.join(self.export_label.text(), video_name)
width = int(self.cap.get(3))
height = int(self.cap.get(4))
fps = self.cap.get(cv2.CAP_PROP_FPS)
self.export_cap = cv2.VideoWriter(out_path, fourcc, fps, (width, height), True)
self.export_cap.write(det_img) # 写入视频
##################################################det#####################################################
cur_frame = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 视频流的长和宽
height, width = cur_frame.shape[:2]
pixmap = QImage(cur_frame, width, height, QImage.Format_RGB888)
pixmap = QPixmap.fromImage(pixmap)
# 获取是视频流和label窗口的长宽比值的最大值,适应label窗口播放,不然显示不全
ratio = max(width / self.display_label.width(), height / self.display_label.height())
pixmap.setDevicePixelRatio(ratio)
# 视频流置于label中间部分播放
self.display_label.setAlignment(Qt.AlignCenter)
self.display_label.setPixmap(pixmap)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = MainGUI()
sys.exit(app.exec_())
model.py
from PIL import Image
from ultralytics import YOLO
import numpy as np
class PredictModel:
def __init__(self):
# 加载预训练的YOLOv8n模型
self.model = YOLO('train/weights/best.pt')
def __call__(self, img):
# bgr
results = self.model(img)
assert len(results) == 1, "The detected image number must equal 1, but got {}".format(len(results))
result = results[0]
names, cls_info = self.post_proccessing(result)
im_result = result.plot() # 绘制包含预测结果的BGR numpy数组
return results, im_result, names, cls_info
def post_proccessing(self, result):
names = result.names # 检测的类别名称
boxes = result.boxes # 检测的框
cls = boxes.cls.cpu().numpy().astype('int') # 检测的类别索引
keys, count = np.unique(cls, return_counts=True) # 检测的每个类别的个数
cls_info = {}
# 整合检测的结果
for key, c in zip(keys, count):
cls_info[key] = c
return names, cls_info