Yolov5环境搭建(详细过程)

本文最后更新于 2024年9月7日 下午

这是我在用Yolov5做目标检测时的环境搭建过程以及训练的模型过程。

先把参考连接放这: Yolov5训练自己的数据集(详细完整版)

然后环境的话就用Anaconda安装pyhton3.9就行

1. 安装Anaconda

Anaconda是一个用于科学计算的Python发行版,包含了众多科学计算相关的库。Anaconda提供了包管理器和环境管理器,可以方便地安装、更新和卸载Python包, 并且可以创建多个独立的环境,每个环境可以包含不同版本的Python和不同的包,方便进行多项目的管理和隔离。详细的安装步骤可以参考这个教程
Anaconda超详细安装教程(Windows环境下)


2. 创建虚拟环境

在Anaconda中,可以使用conda命令创建虚拟环境。虚拟环境是一种隔离的环境,可以在其中安装和管理Python包,而不会影响系统级别的Python环境。

1
conda create -n yolov5 python=3.9  #在终端中运行这段代码

也可以在Anaconda的GUI界面中创建虚拟环境:

1

2

然后在终端中激活虚拟环境,后面在终端中进行的操作都是在你虚拟环境中进行的

1
conda activate yolov5

3. 下载yolov5

用Git下载yolov5代码,我这里也放个百度网盘链接(里边还有我收集好的数据集)提取码:6666

1
git clone https://github.com/ultralytics/yolov5

4. 在yolov5目录下和对应虚拟环境中安装依赖

在文件资源管理器进入yolov5目录,打开终端(输入cmd),输入以下命令安装依赖:

3

激活对应的虚拟环境后输入下面的命令安装依赖:

1
pip install -r requirements.txt #安装依赖,这个可能比较慢,换清华源很快
1
pip install -r requirements.txt -i  https://pypi.tuna.tsinghua.edu.cn/simple #换清华源

5. 训练自己模型前的准备工作

5.1 准备数据集

具体参考我收集好的数据集(MyData文件夹):

4

5.1.1 创建文件夹:Annotations和images

先创建MyData文件夹(也可以起其他名字),然后在里面创建两个文件夹: Annotations文件夹用来存放标注文件,images文件夹用来存放图片(这两个文件夹的名字一定是这俩) 创建标注文件需要我们安装一个python脚本,labelImg 下载地址,这里我们直接在刚创建的虚拟环境中安装就行: 首先激活我们的虚拟环境:

然后安装labelImg:

1
pip install labelImg

安装好以后我们运行它:

1
labelImg

5

运行后会弹出一个窗口,我们点击Change save directory选择我们刚才创建的Annotations文件夹,然后点击Open Dir选择我们刚才创建的images文件夹,然后点击Create RectBox按钮,就可以开始标注了。 这里有个tip:按w标注,a上一张,d下一张。为了方便,记得打开自动保存

6

当然几百张照片,一张一张下载挺麻烦的,我这里有一个脚本Video2imags.py能够实现视频转图片:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#coding=utf8
import cv2 #这是opencv库,如果你的虚拟环境中没有安装,可以先安装一下
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("images/"):
os.mkdir("images/") # 创建文件夹
else:
del_file('images/') # 清空文件夹
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('./images/' + str(order) + '.jpg', frame) #图片保存位置以及命名方式
cv2.waitKey(1)
else:
break
cv.release()
print('\nsave success!\n')

# 参数设置
fps = 1 # 隔多少帧取一张图 1表示全部取
path="" # 视频路径 比如 D:\\images\\tram_result.mp4 或者 D:/images/tram_result.mp4,注意斜杠的方向(在vscode中一定要注意路径的斜杠方向)

if __name__ == '__main__':
video_to_images(fps,path)
# 会在代码的当前文件夹下 生成images文件夹 用于保存图片
# 如果有images文件夹,会清空文件夹!

1
python video2image.py #在终端中运行这个脚本

要安装opencv库的同学看这里:

1
pip install opencv-python

有了图片,就可以开始我们的标注了,注意尽量贴合目标进行标注。

5.1.2 准备训练集和验证集

训练集用来训练模型,验证集用来评估模型的效果。我们需要把数据集分成训练集和验证集。这里我们运行split_train_val.py就可以自动帮我们生成训练集和验证集, 注意脚本里的一些代码需要根据你自己的需求来改(如果打开代码发现中文注释乱码就切换一下编码方式重新打开):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# coding=utf-8
#这个文件基本不用改动,主要是用来划分训练集和验证集的,可以根据自己的需要进行修改
import os
import random
import argparse

parser = argparse.ArgumentParser()
#xml文件的地址,根据自己的数据进行修改 xml一般存放在Annotations下
parser.add_argument('--xml_path', default='Annotations', type=str, help='input xml label path')
#数据集的划分,地址选择自己数据下的ImageSets/Main
parser.add_argument('--txt_path', default='ImageSets/Main', type=str, help='output txt label path')
opt = parser.parse_args()

trainval_percent = 1.0 # 训练集和验证集所占比例。 这里没有划分测试集
train_percent = 0.9 # 训练集所占比例,可自己进行调整
xmlfilepath = opt.xml_path
txtsavepath = opt.txt_path
total_xml = os.listdir(xmlfilepath)
if not os.path.exists(txtsavepath):
os.makedirs(txtsavepath)

num = len(total_xml)
list_index = range(num)
tv = int(num * trainval_percent)
tr = int(tv * train_percent)
trainval = random.sample(list_index, tv)
train = random.sample(trainval, tr)

file_trainval = open(txtsavepath + '/trainval.txt', 'w')
file_test = open(txtsavepath + '/test.txt', 'w')
file_train = open(txtsavepath + '/train.txt', 'w')
file_val = open(txtsavepath + '/val.txt', 'w')

for i in list_index:
name = total_xml[i][:-4] + '\n'
if i in trainval:
file_trainval.write(name)
if i in train:
file_train.write(name)
else:
file_val.write(name)
else:
file_test.write(name)

file_trainval.close()
file_train.close()
file_val.close()
file_test.close()

1
python split_train_val.py #在终端中运行这个脚本

运行这个脚本会生成下面几个文件夹:dataSet_path,ImagesImages下的Main, Main里边有对应的txt文件

7

5.1.3 准备标签文件

标签文件是用来告诉模型我们要检测什么,我们需要一个txt文件。这里我们运行xml_to_yolo.py就可以自动帮咱生成,注意这个代码要把路径改成自己的数据集路径,还有你的类别要改成自己的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# -*- coding: utf-8 -*-
import xml.etree.ElementTree as ET
import os
from os import getcwd

sets = ['train', 'val', 'test']
classes = [ "shark","turtle","red","octopus","blue"] # 改成自己的类别
abs_path = os.getcwd()
print(abs_path)

def convert(size, box):
dw = 1. / (size[0])
dh = 1. / (size[1])
x = (box[0] + box[1]) / 2.0 - 1
y = (box[2] + box[3]) / 2.0 - 1
w = box[1] - box[0]
h = box[3] - box[2]
x = x * dw
w = w * dw
y = y * dh
h = h * dh
return x, y, w, h

def convert_annotation(image_id):
in_file = open('D:/fileForWhile/yolov5/MyData/Annotations/%s.xml' % (image_id), encoding='UTF-8') # 改成自己的路径
out_file = open('D:/fileForWhile/yolov5/MyData/labels/%s.txt' % (image_id), 'w') # 改成自己的路径
tree = ET.parse(in_file)
root = tree.getroot()
size = root.find('size')
w = int(size.find('width').text)
h = int(size.find('height').text)
for obj in root.iter('object'):
difficult = obj.find('difficult').text
#difficult = obj.find('Difficult').text
cls = obj.find('name').text
if cls not in classes or int(difficult) == 1:
continue
cls_id = classes.index(cls)
xmlbox = obj.find('bndbox')
b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text),
float(xmlbox.find('ymax').text))
b1, b2, b3, b4 = b
# 标注越界修正
if b2 > w:
b2 = w
if b4 > h:
b4 = h
b = (b1, b2, b3, b4)
bb = convert((w, h), b)
out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')

wd = getcwd()
for image_set in sets:
if not os.path.exists('D:/fileForWhile/yolov5/MyData/labels/'): # 改成自己的路径
os.makedirs('D:/fileForWhile/yolov5/MyData/labels/') # 改成自己的路径
image_ids = open('D:/fileForWhile/yolov5/MyData/ImageSets/Main/%s.txt' % (image_set)).read().strip().split() # 改成自己的路径

if not os.path.exists('D:/fileForWhile/yolov5/MyData/dataSet_path/'): # 改成自己的路径
os.makedirs('D:/fileForWhile/yolov5/MyData/dataSet_path/') # 改成自己的路径

list_file = open('dataSet_path/%s.txt' % (image_set), 'w')
# 这行路径不需更改,这是相对路径
for image_id in image_ids:
list_file.write('D:/fileForWhile/yolov5/MyData/images/%s.jpg\n' % (image_id)) # 改成自己的路径
convert_annotation(image_id)
list_file.close()

1
python xml_to_yolo.py #在终端中运行这个脚本

运行这个脚本会生成labels文件夹,里面有我们需要的txt文件。

1.打开Anaconda Navigator

5.1.4 准备配置文件

创建一个txt文件,名字叫myvoc.txt,将后缀修改为yaml,在记事本中进行编辑,内容如下:

1
2
3
4
5
6
7
8
9
# 训练自己的数据集
train: train.txt # 改成自己的train.txt路径,注意前边的空格
val: val.txt # 改成自己的val.txt路径,注意前边的空格

# number of classes
nc: 5 # 改成自己的类别数量

# class names
names: ["shark","turtle","red","octopus","blue"] # 改成自己的类别

6. 训练模型

6.1 训练自己的数据集

我们需要训练自己的模型,这里我们运行train.py就可以训练我们的模型:

1
python train.py --weights weights/yolov5s.pt  --cfg models/yolov5s.yaml  --data data/myvoc.yaml --epoch 200 --batch-size 8 --img 640   --device cpu

参数解释

  • --weights:指定预训练权重,这里我们使用的是yolov5s.pt,如果没有下载,你运行后它会自己下载的
  • --cfg:指定模型配置文件,这里我们使用的是yolov5s.yaml
  • --data:指定数据集配置文件,这里我们使用的是myvoc.yaml。这个要改成你自己的配置文件路径。
  • --epoch:指定训练的轮数,这里我们设置为200。训练200次
  • --batch-size:指定训练的batch size,这里我们设置为8。训练8张图片后进行权重更新
  • --img:指定训练的图片大小,这里我们设置为640。
  • --device:指定训练的设备,这里我们设置为cpu。当然cpu会比较慢而且如果你cpu不咋行会报一些错,如果可以建议用GPU训练。

6.2 使用GPU训练

使用GPU训练需要你安装pytorch,这边直接放参考链接吧: 安装PyTorch详细过程 注意还是在我们的虚拟环境yolov5中安装的,装好以后运行train.py的参数中将--device cpu 改成--device 0即可。

6.3 训练结果检验

训练结束以后我们在runs\train\exp\weights下可以看到两个文件best.ptlast.ptbest.pt是我们训练的最佳权重,last.pt是我们训练的最后一次权重。

我们可以用

1
python detect.py --weights runs/train/exp6/weights/best.pt --source MyData/output.MP4

来测试一下我们的模型效果。

参数解释

  • --weights:指定训练好的权重,这里我们使用的是runs/train/exp6/weights/best.pt
  • --source:指定测试的视频或者图片路径,这里我们使用的是MyData/output.MP4

7. 总结

至此,我们已经完成了yolov5的环境搭建、数据集的准备、训练模型、测试模型等一系列操作。 yolov5是一个非常强大的目标检测模型,它可以检测出很多种类别的目标,而且它的速度也非常快,可以应用于各种场景。 而yolov5移植到树莓派上实现使用摄像头对目标的检测,我刚开始是这样想的,在树莓派上安装minianaconda,然后搭建一个和windows一样的虚拟环境,装好需要的 依赖,然后在树莓派上运行detect.py文件就好了,但实际上树莓派是linux操作系统,直接运行Windows系统上跑出来的模型是不行的好像。后边我才知道,原来yolov5 还有一个export.py脚本,可以将训练好的模型导出为ONNX格式,这样就可以在树莓派上运行了。并且在树莓派上安装4.5以上的opencv就可以用实现了。

要用export.py导出ONNX格式的模型,我们需要先安装onnx库,然后运行export.py脚本,命令如下:

1
pip install onnx
1
python export.py --weights runs/train/exp6/weights/best.pt --img 640 --batch 1

更具体可以去看b站这个视频:树莓派:YOLOV5目标检测-模型训练与移植,获取这个视频里的代码YuHong-LDU/Python-RaspberryPI - Gitee.com

然后下面是树莓派上运行的代码,是上边那个视频里的,但视频里只演示了对单张图片的代码,下边这个是对视频或者摄像头的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import cv2
import numpy as np
import time
def plot_one_box(x, img, color=None, label=None, line_thickness=None):
"""
description: Plots one bounding box on image img,
this function comes from YoLov5 project.
param:
x: a box likes [x1,y1,x2,y2]
img: a opencv image object
color: color to draw rectangle, such as (0,255,0)
label: str
line_thickness: int
return:
no return
"""
tl = (
line_thickness or round(0.002 * (img.shape[0] + img.shape[1]) / 2) + 1
) # line/font thickness
color = color or [random.randint(0, 255) for _ in range(3)]
c1, c2 = (int(x[0]), int(x[1])), (int(x[2]), int(x[3]))
cv2.rectangle(img, c1, c2, color, thickness=tl, lineType=cv2.LINE_AA)
if label:
tf = max(tl - 1, 1) # font thickness
t_size = cv2.getTextSize(label, 0, fontScale=tl / 3, thickness=tf)[0]
c2 = c1[0] + t_size[0], c1[1] - t_size[1] - 3
cv2.rectangle(img, c1, c2, color, -1, cv2.LINE_AA) # filled
cv2.putText(
img,
label,
(c1[0], c1[1] - 2),
0,
tl / 3,
[225, 255, 255],
thickness=tf,
lineType=cv2.LINE_AA,
)

def post_process_opencv(outputs,model_h,model_w,img_h,img_w,thred_nms,thred_cond):

conf = outputs[:,4].tolist()
c_x = outputs[:,0]/model_w*img_w
c_y = outputs[:,1]/model_h*img_h
w = outputs[:,2]/model_w*img_w
h = outputs[:,3]/model_h*img_h
p_cls = outputs[:,5:]
if len(p_cls.shape)==1:
p_cls = np.expand_dims(p_cls,1)
cls_id = np.argmax(p_cls,axis=1)

p_x1 = np.expand_dims(c_x-w/2,-1)
p_y1 = np.expand_dims(c_y-h/2,-1)
p_x2 = np.expand_dims(c_x+w/2,-1)
p_y2 = np.expand_dims(c_y+h/2,-1)
areas = np.concatenate((p_x1,p_y1,p_x2,p_y2),axis=-1)
# print(areas.shape)
areas = areas.tolist()
ids = cv2.dnn.NMSBoxes(areas,conf,thred_cond,thred_nms)
if len(ids)>0:
return np.array(areas)[ids],np.array(conf)[ids],cls_id[ids]
else:
return [],[],[]

def infer_image(net,img0,model_h,model_w,thred_nms=0.4,thred_cond=0.5):

img = img0.copy()
img = cv2.resize(img,[model_h,model_w])
blob = cv2.dnn.blobFromImage(img, scalefactor=1/255.0, swapRB=True)
net.setInput(blob)
outs = net.forward()[0]
print(outs[0])
det_boxes,scores,ids = post_process_opencv(outs,model_h,model_w,img0.shape[0],img0.shape[1],thred_nms,thred_cond)
return det_boxes,scores,ids


if __name__=="__main__":
dic_labels= {0:'led',
1:'buzzer',
2:'teeth'}

model_h = 640
model_w = 640
file_model = 'best-led-640.onnx'
net = cv2.dnn.readNet(file_model)

video = 0
cap = cv2.VideoCapture(video)
flag_det = False
while True:
success, img0 = cap.read()
if success:

if flag_det:
t1 = time.time()
det_boxes,scores,ids = infer_image(net,img0,model_h,model_w,thred_nms=0.4,thred_cond=0.2)
t2 = time.time()

for box,score,id in zip(det_boxes,scores,ids):
label = '%s:%.2f'%(dic_labels[id],score)

plot_one_box(box.astype(np.int16), img0, color=(255,0,0), label=label, line_thickness=None)

str_FPS = "FPS: %.2f"%(1./(t2-t1))

cv2.putText(img0,str_FPS,(50,50),cv2.FONT_HERSHEY_COMPLEX,1,(0,255,0),3)


cv2.imshow("video",img0)
m_key = cv2.waitKey(1) & 0xFF

if m_key == ord('s'):
flag_det = not flag_det
if flag_det == True:
print("start detecting")
else:
print("stop detecting")
elif m_key == ord('q'):
break

cap.release()

Yolov5环境搭建(详细过程)
https://qingyun0118.github.io/2024/08/27/yolov5Env/
作者
Wei Jicheng
发布于
2024年8月27日
许可协议