提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
提示:以下是本篇文章正文内容,下面案例可供参考
一、准备工作
1.新建一个文件夹yolov8(可以自定义命名),在文件夹下再创一个dataset文件夹
2.在dataset下新建两个文件夹 labels 以及 images
images:用于存放要标注的图片(jpg格式)
labels :用于存放标注图片后产生的内容(这里采用YOLO格式)
dataset(文件名可自定义)
–images
----train(训练集)
----val(验证集)
–labels
----train(训练集标签文件)
----val(存验证集标签文件)
标签文件和训练用的图片名称一一对应。
3.准备一个视频chezi.mp4
1)图片类型数据:可以直接使用
2)视频类型数据:利用opencv进行视频抽帧
import cv2
import os
#此删除文件夹内容的函数来源于网上
def del_file(filepath):
"""
删除某一目录下的所有文件或文件夹
:param filepath: 路径
:return:
"""
del_list = os.listdir(filepath)
for f in del_list:
file_path = os.path.join(filepath, f)
if os.path.isfile(file_path):
os.remove(file_path)
def video_to_images(fps,path):
cv = cv2.VideoCapture(path)
if(not cv.isOpened()):
print("\n打开视频失败!请检查视频路径是否正确\n")
exit(0)
if not os.path.exists("images2/"):
os.mkdir("images2/") # 创建文件夹
else:
del_file('images2/') # 清空文件夹
order = 0 #序号
h = 0
while True:
h=h+1
rval, frame = cv.read()
if h == fps:
h = 0
order = order + 1
if rval:
cv2.imwrite('./images2/' + str(order) + '.jpg', frame) #图片保存位置以及命名方式
cv2.waitKey(1)
else:
break
cv.release()
print('\nsave success!\n')
# 参数设置
fps = 6 # 隔多少帧取一张图 1表示全部取
path="chezi.mp4" # 视频路径
if __name__ == '__main__':
video_to_images(fps,path)
代码可以直接复制但是要修改目录地址
运行完是这样的,我们可以修改代码的fps的值来修改多少帧取一张图,这样可以决定张数,我这边是两百多张,全部复制粘贴到dataset/images/train目录下方便我们标注
二、使用labelImg标注图片
1. 安装labellmg
labelImg下载:在终端pip install labelimg下载就行
也可以去网上下载别人打包好的,网上有人打包好了此软件,可以自行去下载。
2. 使用labellmg
打开labelimg直接在终端输入labelimg然后回车
下载了别人打包好的软件也可以去labelimg的目录栏进入cmd输入
python labelImg.py #运行软件
进入labelimg之后点击左上角的View勾选 auto save mode 自动保存
打开的目录选择我们刚刚用代码截取的图片位置images/train
存放目录选择labels/train
格式选择YOLO格式
三、训练模型
1.准备工作
打开我们创建的yolov5文件夹
在文件夹下再创一个data文件夹,并且在文件夹下创一个coco128.yaml文件
打开文件输入
train: C:\Users\XDL\Desktop\yolov5\dataset\images\train
val: C:\Users\XDL\Desktop\yolov5\dataset\images\val
# Classes
nc: 17
names: ['dog', 'person', 'cat', 'tv', 'car', 'meatballs', 'marinara sauce', 'tomato soup', 'chicken noodle soup', 'french onion soup', 'chicken breast', 'ribs', 'pulled pork', 'hamburger', 'cavity', 'car', 'people']
看一下图片具体怎么操作的。
2.训练模型代码
在yolov5目录下创建demo.py文件具体代码如下:
import os
from ultralytics import YOLO
def list_files(directory, file_extension):
return [f for f in os.listdir(directory) if f.endswith(file_extension)]
def check_empty_labels(path):
empty_files = []
for file in os.listdir(path):
if file.endswith('.txt'):
file_path = os.path.join(path, file)
if os.path.getsize(file_path) == 0:
empty_files.append(file)
return empty_files
# 删除缓存文件
cache_train = r'C:\Users\XDL\Desktop\yolov5\dataset\labels\train.cache'
cache_val = r'C:\Users\XDL\Desktop\yolov5\dataset\labels\val.cache'
if os.path.exists(cache_train):
os.remove(cache_train)
if os.path.exists(cache_val):
os.remove(cache_val)
# 打印数据集配置以确认加载
data_config = r'C:/Users/XDL/Desktop/yolov5/data/coco128.yaml'
print(f"使用的数据集配置文件: {data_config}")
# 确认数据集文件存在
train_images_path = r'C:\Users\XDL\Desktop\yolov5\dataset\images\train'
val_images_path = r'C:\Users\XDL\Desktop\yolov5\dataset\images\val'
train_labels_path = r'C:\Users\XDL\Desktop\yolov5\dataset\labels\train'
val_labels_path = r'C:\Users\XDL\Desktop\yolov5\dataset\labels\val'
print(f"训练图像目录内容: {list_files(train_images_path, '.jpg')[:5]} ...")
print(f"验证图像目录内容: {list_files(val_images_path, '.jpg')[:5]} ...")
print(f"训练标签目录内容: {list_files(train_labels_path, '.txt')[:5]} ...")
print(f"验证标签目录内容: {list_files(val_labels_path, '.txt')[:5]} ...")
# 检查是否存在空标签文件
train_empty_labels = check_empty_labels(train_labels_path)
val_empty_labels = check_empty_labels(val_labels_path)
if train_empty_labels:
print(f"训练集中的空标签文件: {train_empty_labels}")
if val_empty_labels:
print(f"验证集中的空标签文件: {val_empty_labels}")
# 进一步检查标签文件内容
def check_label_file_content(path):
for file in os.listdir(path):
if file.endswith('.txt'):
file_path = os.path.join(path, file)
with open(file_path, 'r') as f:
content = f.read().strip()
if content:
lines = content.split('\n')
for line in lines:
parts = line.split()
if len(parts) != 5:
print(f"{file} 格式不正确: {line}")
print("检查训练集标签文件内容...")
check_label_file_content(train_labels_path)
print("检查验证集标签文件内容...")
check_label_file_content(val_labels_path)
# 确认数据集配置文件内容
with open(data_config, 'r') as f:
config_content = f.read()
print(f"数据集配置文件内容:\n{config_content}")
# 加载模型
model = YOLO('yolov5su.pt')
# 训练模型
print("开始训练模型...")
try:
model.train(data=data_config, epochs=100)
print("模型训练完成。")
except Exception as e:
print(f"模型训练过程中出错: {e}")
注意文件夹的目录位置改成自己的目录位置不然报错。
经过了高达十个小时的训练时间也是终于训练好了属于自己的模型!
训练得到了一个runs的文件我们的训练模型在runs/detect/train/weights/best.pt
四、YOLOv8 目标追踪
1、代码解析
第一步
导入模块部分:
- 导入了与
PySide6
相关的模块用于界面开发,以及cv2
用于图像处理,time
等其他相关模块。
from PySide6 import QtWidgets, QtCore, QtGui
import cv2, os, time
from threading import Thread
from PySide6.QtWidgets import QMainWindow, QFileDialog
# 不然每次运行都会打印一些信息
os.environ['YOLO_VERBOSE'] = 'False'
from ultralytics import YOLO
定义 MWindow
类部分:
__init__
方法:进行界面的初始化设置,包括创建各种控件、布局,以及加载模型、设置定时器等操作。
- 定义了界面的整体布局,包括图像展示区域和按钮区域。
- 启动了处理视频帧的独立线程。
- 定义了与摄像头和视频文件相关的操作和定时器。
代码加载YOLO模型的部分就是用到了我们自己训练的训练模型!!
def __init__(self):
super().__init__()
# 设置界面
self.UI()
# 视频按钮的点击
self.sx.clicked.connect(self.sx_clicked)
self.tz.clicked.connect(self.stop)
self.sp.clicked.connect(self.sp_button)
# 定义定时器,用于控制显示视频的帧率
self.timer_camera = QtCore.QTimer()
# 定时到了,回调self.show_camera()函数
self.timer_camera.timeout.connect(self.show_camera)
#加载 YOLO nano 模型
self.model = YOLO('best.pt')
# 要处理的视频帧图片队列,目前就放1帧图片
self.frameToAnalyze = []
#启动处理视频帧独立线程
Thread(target=self.frameAnalyeThreadFunc,daemon=True).start()
# 定义定时器,用于控制显示视频文件的帧率
self.timer_videoFile = QtCore.QTimer()
# 定时到了,回调 self.show_camera
self.timer_videoFile.timeout.connect(self.show_videoFile)
# 当前要播放的视频帧号
self.vframeIdx = 0
# cv2.VideoCapture 实例
self.cap = None
self.stopFlag = False
UI
方法:具体构建界面的布局和控件。
def UI(self):
self.resize(1000, 800)
self.setWindowTitle('yolov8 目标追踪')
# central Widget
centralWidget = QtWidgets.QWidget(self)
self.setCentralWidget(centralWidget)
# central Widget 里面的 主 layout
mainLayout = QtWidgets.QVBoxLayout(centralWidget)
# 界面的上半部分 : 图像展示部分
topLayout = QtWidgets.QHBoxLayout()# QHBoxLayout:水平布局 QVBoxLayout:垂直布局
self.label_ori_video = QtWidgets.QLabel(self)
self.label_treated = QtWidgets.QLabel(self)
self.label_ori_video.setMinimumSize(700, 700)
self.label_treated.setMinimumSize(700, 700)
self.label_ori_video.setStyleSheet('border:1px solid #1d649c')
self.label_treated.setStyleSheet('border:1px solid #1d649c')
# 保持原始分辨率
self.label_ori_video.setSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Ignored)
self.label_ori_video.setScaledContents(True)
self.label_treated.setSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Ignored)
self.label_treated.setScaledContents(True)
topLayout.addWidget(self.label_ori_video)
topLayout.addWidget(self.label_treated)
mainLayout.addLayout(topLayout)
# 界面下半部分:按钮
groupBox = QtWidgets.QGroupBox('按钮')
bottomLayout = QtWidgets.QHBoxLayout(groupBox)
mainLayout.addWidget(groupBox)
btnLayout = QtWidgets.QHBoxLayout()# 按钮垂直布局
self.sp = QtWidgets.QPushButton('🎞️视频文件')
self.sx = QtWidgets.QPushButton('📹摄像头')
self.tz = QtWidgets.QPushButton('🛑停止')
btnLayout.addWidget(self.sp)
btnLayout.addWidget(self.sx)
btnLayout.addWidget(self.tz)
bottomLayout.addLayout(btnLayout)
sx_clicked
方法:处理摄像头按钮的点击事件,打开摄像头并启动相关定时器。
# 摄像头按钮
def sx_clicked(self):
self.cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
if not self.cap.isOpened():
print('1号摄像头打开失败')
return
if self.timer_camera.isActive() == False:# 如果定时器未启动
self.timer_camera.start(30) # 启动定时器,计时时间是16ms,约为60帧每秒
self.stopFlag = False
show_camera
方法:显示从摄像头读取的视频帧。
def show_camera(self):
ret, frame = self.cap.read() # 从视频中读取
if not ret:
return
self.setFrameToOriLabel(frame)
setFrameToOriLabel
方法:将视频帧转换为合适的格式并显示在相应的标签上。
def setFrameToOriLabel(self,frame):
# 视频色彩转换回RGB,OpenCV images as BGR
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
qImage = QtGui.QImage(frame.data, frame.shape[1], frame.shape[0],
QtGui.QImage.Format_RGB888)# 变成QImage格式
# 往显示视频的Label里显示QImage
self.label_ori_video.setPixmap(QtGui.QPixmap.fromImage(qImage))# 显示视频
# 如果当前没有处理任务
if not self.frameToAnalyze:
self.frameToAnalyze.append(frame)
frameAnalyeThreadFunc
方法:在独立线程中处理视频帧的分析任务。
def frameAnalyeThreadFunc(self):
while True:
if not self.frameToAnalyze:
continue
frame = self.frameToAnalyze.pop(0)
results = self.model(frame)[0]
img = results.plot(line_width=1)
# img = results.plot(line_width=1, font='')
qImage = QtGui.QImage(img.data, img.shape[1], img.shape[0],
QtGui.QImage.Format_RGB888)# 变成QImage格式
self.label_treated.setPixmap(QtGui.QPixmap.fromImage(qImage))# 往显示Label里显示QImage
stop
方法:停止相关操作,释放资源。
def stop(self,):
self.stopFlag = True # 让 frameAnalyzeThreadFunc 不要再设置 label_treated
self.timer_camera.stop() # 关闭定时器
self.timer_videoFile.stop() # 关闭定时器
if self.cap:
self.cap.release() # 释放视频流
# 清空视频显示区域
self.label_ori_video.clear()
self.label_treated.clear()
# # 延时500ms清除,有的定时器处理任务可能会在当前时间点后处理完最后一帧
# QtCore.QTimer.singleShot(500, clearLabels)
sp_button
方法:处理选择视频文件的按钮点击事件。
def sp_button(self):
# 先关闭原来打开的
self.stop()
videoPath, _ = QtWidgets.QFileDialog.getOpenFileName(
self, # 父窗口对象
"选择视频文件", # 标题
"../../作业/图片标注/图片标注6", # 起始目录
"视频文件 (*.mp4 *.avi)" # 选择类型过滤项,过滤内容在括号中
)
print('videoPath is', videoPath)
if not videoPath:
return
self.cap = cv2.VideoCapture(videoPath)
if not self.cap.isOpened():
print("打开文件失败")
return
self.timer_videoFile.start(30) # 设置时间间隔为16ms,约为60帧每秒
self.stopFlag = False
print("ok")
show_videoFile
方法:显示视频文件中的视频帧。
def show_videoFile(self):
# 选取视频帧位置,
self.cap.set(cv2.CAP_PROP_POS_FRAMES, self.vframeIdx)
self.vframeIdx += 1
ret, frame = self.cap.read() # 从视频流中读取
# 读取失败,应该是视频播放结束了
if not ret:
self.stop()
return
self.setFrameToOriLabel(frame)
主程序部分:
- 创建应用程序实例,创建
MWindow
对象并显示窗口,最后执行应用程序的主循环。
app = QtWidgets.QApplication()
window = MWindow()
window.show()
app.exec()
2、全部代码
from PySide6 import QtWidgets, QtCore, QtGui
import cv2, os, time
from threading import Thread
from PySide6.QtWidgets import QMainWindow, QFileDialog
# 不然每次运行都会打印一些信息
os.environ['YOLO_VERBOSE'] = 'False'
from ultralytics import YOLO
class MWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
# 设置界面
self.UI()
# 视频按钮的点击
self.sx.clicked.connect(self.sx_clicked)
self.tz.clicked.connect(self.stop)
self.sp.clicked.connect(self.sp_button)
# 定义定时器,用于控制显示视频的帧率
self.timer_camera = QtCore.QTimer()
# 定时到了,回调self.show_camera()函数
self.timer_camera.timeout.connect(self.show_camera)
#加载 YOLO nano 模型
self.model = YOLO('best.pt')
# 要处理的视频帧图片队列,目前就放1帧图片
self.frameToAnalyze = []
#启动处理视频帧独立线程
Thread(target=self.frameAnalyeThreadFunc,daemon=True).start()
# 定义定时器,用于控制显示视频文件的帧率
self.timer_videoFile = QtCore.QTimer()
# 定时到了,回调 self.show_camera
self.timer_videoFile.timeout.connect(self.show_videoFile)
# 当前要播放的视频帧号
self.vframeIdx = 0
# cv2.VideoCapture 实例
self.cap = None
self.stopFlag = False
def UI(self):
self.resize(1000, 800)
self.setWindowTitle('yolov8 目标追踪')
# central Widget
centralWidget = QtWidgets.QWidget(self)
self.setCentralWidget(centralWidget)
# central Widget 里面的 主 layout
mainLayout = QtWidgets.QVBoxLayout(centralWidget)
# 界面的上半部分 : 图像展示部分
topLayout = QtWidgets.QHBoxLayout()# QHBoxLayout:水平布局 QVBoxLayout:垂直布局
self.label_ori_video = QtWidgets.QLabel(self)
self.label_treated = QtWidgets.QLabel(self)
self.label_ori_video.setMinimumSize(700, 700)
self.label_treated.setMinimumSize(700, 700)
self.label_ori_video.setStyleSheet('border:1px solid #1d649c')
self.label_treated.setStyleSheet('border:1px solid #1d649c')
# 保持原始分辨率
self.label_ori_video.setSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Ignored)
self.label_ori_video.setScaledContents(True)
self.label_treated.setSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Ignored)
self.label_treated.setScaledContents(True)
topLayout.addWidget(self.label_ori_video)
topLayout.addWidget(self.label_treated)
mainLayout.addLayout(topLayout)
# 界面下半部分:按钮
groupBox = QtWidgets.QGroupBox('按钮')
bottomLayout = QtWidgets.QHBoxLayout(groupBox)
mainLayout.addWidget(groupBox)
btnLayout = QtWidgets.QHBoxLayout()# 按钮垂直布局
self.sp = QtWidgets.QPushButton('🎞️视频文件')
self.sx = QtWidgets.QPushButton('📹摄像头')
self.tz = QtWidgets.QPushButton('🛑停止')
btnLayout.addWidget(self.sp)
btnLayout.addWidget(self.sx)
btnLayout.addWidget(self.tz)
bottomLayout.addLayout(btnLayout)
# 摄像头按钮
def sx_clicked(self):
self.cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
if not self.cap.isOpened():
print('1号摄像头打开失败')
return
if self.timer_camera.isActive() == False:# 如果定时器未启动
self.timer_camera.start(30) # 启动定时器,计时时间是16ms,约为60帧每秒
self.stopFlag = False
def show_camera(self):
ret, frame = self.cap.read() # 从视频中读取
if not ret:
return
self.setFrameToOriLabel(frame)
def setFrameToOriLabel(self,frame):
# 视频色彩转换回RGB,OpenCV images as BGR
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
qImage = QtGui.QImage(frame.data, frame.shape[1], frame.shape[0],
QtGui.QImage.Format_RGB888)# 变成QImage格式
# 往显示视频的Label里显示QImage
self.label_ori_video.setPixmap(QtGui.QPixmap.fromImage(qImage))# 显示视频
# 如果当前没有处理任务
if not self.frameToAnalyze:
self.frameToAnalyze.append(frame)
def frameAnalyeThreadFunc(self):
while True:
if not self.frameToAnalyze:
continue
frame = self.frameToAnalyze.pop(0)
results = self.model(frame)[0]
img = results.plot(line_width=1)
# img = results.plot(line_width=1, font='')
qImage = QtGui.QImage(img.data, img.shape[1], img.shape[0],
QtGui.QImage.Format_RGB888)# 变成QImage格式
self.label_treated.setPixmap(QtGui.QPixmap.fromImage(qImage))# 往显示Label里显示QImage
def stop(self,):
self.stopFlag = True # 让 frameAnalyzeThreadFunc 不要再设置 label_treated
self.timer_camera.stop() # 关闭定时器
self.timer_videoFile.stop() # 关闭定时器
if self.cap:
self.cap.release() # 释放视频流
# 清空视频显示区域
self.label_ori_video.clear()
self.label_treated.clear()
# # 延时500ms清除,有的定时器处理任务可能会在当前时间点后处理完最后一帧
# QtCore.QTimer.singleShot(500, clearLabels)
def sp_button(self):
# 先关闭原来打开的
self.stop()
videoPath, _ = QtWidgets.QFileDialog.getOpenFileName(
self, # 父窗口对象
"选择视频文件", # 标题
"../../作业/图片标注/图片标注6", # 起始目录
"视频文件 (*.mp4 *.avi)" # 选择类型过滤项,过滤内容在括号中
)
print('videoPath is', videoPath)
if not videoPath:
return
self.cap = cv2.VideoCapture(videoPath)
if not self.cap.isOpened():
print("打开文件失败")
return
self.timer_videoFile.start(30) # 设置时间间隔为16ms,约为60帧每秒
self.stopFlag = False
print("ok")
def show_videoFile(self):
# 选取视频帧位置,
self.cap.set(cv2.CAP_PROP_POS_FRAMES, self.vframeIdx)
self.vframeIdx += 1
ret, frame = self.cap.read() # 从视频流中读取
# 读取失败,应该是视频播放结束了
if not ret:
self.stop()
return
self.setFrameToOriLabel(frame)
app = QtWidgets.QApplication()
window = MWindow()
window.show()
app.exec()
3、结果呈现
运行代码后可以看到我们的前端界面是这样的
试着打开一个视频试试看