说明:后面的yolov5-3.1使用的是yolov5-6.1的环境,直接可以运行,向下兼容。
这里数据集的收集和标注具体过程省略,我直接用的labelImg软件进行标注。
labelImg标注软件一个是voc格式一个是yolo格式,最终生成xml文件或者txt文件。这里建议统一用voc格式去标,到时候要转换为yolo格式也方便,因为xml里面存储的信息更加详细,其次是yolo格式只有标号代表类别,标号有专门的classes文件。
不过这里如果你想要让多个人去标注,就得统一好class顺序 ,如果出现问题就可能类别重复,也挺麻烦的。所以用voc格式统一标注。
很不幸,这次我就遇到了没统一class顺序,几个人标注好了发现不能直接使用,所以我就采用先把txt转换为xml格式。
以下是参考脚本
# -*- coding: utf-8 -*-
"""
Created on Fri Mar 29 19:30:50 2019
@author: XIE-XUE-LI
"""
import os
import cv2
import os.path as osp
# 基本只需要修改这几个参数就行
src_img_dir = "G:\\1Marked\\Scared\\Scared" #原图.jpg文件的路径
src_txt_dir = "G:\\1Marked\\Scared\\Scaredlabels" #labels中.txt文件的路径
src_xml_dir = "G:\\1Marked\\Scared\\Scaredxml" #生成的xml文件需要保存的路径
VISDrone_CLASSES = ['Scared'] #标签的内容(我这里基本一类一个文件夹,,所以这里的class只有一类,你们可以更具自己的顺序添加)
folder = 'Scared' #文件夹命名
# 判断当前路径下是否存在Annotations这个文件夹,若不存在,自动创建一个
if not os.path.exists(src_xml_dir):
os.mkdir(src_xml_dir)
img_name = []
for id in os.listdir(src_img_dir):
img_name.append(id[:-4])
for img in img_name:
im = cv2.imread(osp.join(src_img_dir,img+'.jpg'))
im = cv2.cvtColor(im, cv2.COLOR_BGR2RGB)
height, width, channels = im.shape
xml_file = open((src_xml_dir + '\\' + img + '.xml'), 'w')
xml_file.write('\n' )
xml_file.write(' ' + folder + '\n')
xml_file.write(' ' + str(img) + '.jpg' + '\n')
xml_file.write(' \n' )
xml_file.write(' ' + str(width) + '\n')
xml_file.write(' ' + str(height) + '\n')
xml_file.write(' 3 \n')
xml_file.write(' \n')
with open(src_txt_dir + '\\' + img + '.txt') as f:
lines = f.readlines()
for line in lines:
box = line.split(' ')
x = int(float(box[1]) * width)
y = int(float(box[2]) * height)
w = int(float(box[3]) * width)
h = int(float(box[4]) * height)
xmin = x+1-w/2
ymin = y+1-h/2
xmax = x+1+w/2
ymax = y+1+h/2
xml_file.write(' )
name = VISDrone_CLASSES[0]
xml_file.write(' ' + str(name) + '\n')
xml_file.write(' Unspecified \n')
xml_file.write(' 0 \n')
difficult = 0
xml_file.write(' ' + str(difficult) + '\n')
xml_file.write(' \n' )
xml_file.write(' ' + str(xmin) + '\n')
xml_file.write(' ' + str(ymin) + '\n')
xml_file.write(' ' + str(xmax) + '\n')
xml_file.write(' ' + str(ymax) + '\n')
xml_file.write(' \n')
xml_file.write(' \n')
xml_file.write('')
这里转换完毕,接下来直接开始按照步骤进行操作!
这里我直接使用了这款软件,蛮好用的。
只需要修改路径和你的目标命名,脚本如下:
import numpy as np
import glob
import os
import xml.etree.ElementTree as ET
import xml.dom.minidom
'''
第一步,将xml文件和图片重新命名
'''
# 获取文件夹中bmp图片的数量
def getDirImageNum(path):
bmpDirImagesNum = 0
for bmpfile in os.listdir(path):
if os.path.splitext(bmpfile)[1] == '.jpg':
bmpDirImagesNum += 1
return bmpDirImagesNum
# 获取文件夹中xml文件的数量
def getDirXmlNum(path):
xmlDirXmlNum = 0
for xmlfile in os.listdir(path):
if os.path.splitext(xmlfile)[1] == '.xml':
xmlDirXmlNum += 1
return xmlDirXmlNum
inputpath1 = "E:\\Graduation_Project\\code\\yolov5\\yolov5-3.1\\7_emotion\\images1\\"
inputpath2 = "E:\\Graduation_Project\\code\\yolov5\\yolov5-3.1\\7_emotion\\annotations1\\"
outpath1 = "E:\\Graduation_Project\\code\\yolov5\\yolov5-3.1\\7_emotion\\images\\"
outpath2 = "E:\\Graduation_Project\\code\\yolov5\\yolov5-3.1\\7_emotion\\annotations\\"
file_name = os.listdir(inputpath2)
#只需要修改这个名字
name = '7_emotion'
error = []
for item in file_name:
print(item)
o_imap = inputpath1 + item.split('.')[0] + ".jpg"
o_xmlp = inputpath2 + item.split('.')[0] + ".xml"
i = getDirImageNum(outpath1) # 表示bmp文件的命名是从当前输出文件夹中的bmp文件数目开始的
if os.path.exists(o_imap) and os.path.exists(o_xmlp):
i = i + 1
new_name = name + format(str(i), '0>4s') + '.jpg'
dst1 = os.path.join(os.path.abspath(outpath1), new_name)
os.rename(o_imap, dst1)
dst2 = os.path.join(os.path.abspath(outpath2), name + format(str(i), '0>4s') + '.xml') # 为000000.xml形式,想要的格式
try:
dom = xml.dom.minidom.parse(o_xmlp)
root = dom.documentElement
# 获取标签对path之间的值并赋予新值j
# 文件夹赋值
# root.getElementsByTagName('folder')[0].firstChild.data = "VOC2007"
# 获取标签对filename之间的值并赋予新值j
root.getElementsByTagName('filename')[0].firstChild.data = new_name
# 将修改后的xml文件保存,xml文件修改前后的路径
# 打开并写入
with open(o_xmlp, 'w') as fh:
dom.writexml(fh)
os.rename(o_xmlp, dst2)
print('converting %s to %s ...' % (o_xmlp, dst2))
except:
error.append(new_name)
continue
# 如果有出错的文件,error++
print(len(error))
包括训练集,验证集和测试集,我这里没有设置测试集,trainval_percent和train_percent 两个代表占比,trainval_percent为train+val占全部图片的比例,若不需要test集则改为1。train_percent 代表拿来训练。
修改的话只需要修改图片和标签路径
import os
import random
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--xml_path', default='7_emotion/annotations', type=str, help='input xml label path')
parser.add_argument('--txt_path', default='7_emotion/imagesets', 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()
这里需要修改class
# -*- coding: utf-8 -*-
import xml.etree.ElementTree as ET
from tqdm import tqdm
import os
from os import getcwd
sets = ['train', 'test','val']
#这里使用要改
classes = ['Angry', 'Disgusted', 'Happy','Neutral','Sad',
'Scared','Surprised']
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
x = round(x,6)
w = round(w,6)
y = round(y,6)
h = round(h,6)
return x, y, w, h
#后面只用修改各个文件夹的位置
def convert_annotation(image_id):
#try:
in_file = open('8_emotion/annotations/%s.xml' % (image_id), encoding='utf-8')
out_file = open('8_emotion/labels/%s.txt' % (image_id), 'w', encoding='utf-8')
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
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')
#except Exception as e:
#print(e, image_id)
wd = getcwd()
for image_set in sets:
if not os.path.exists('8_emotion/labels/'):
os.makedirs('8_emotion/labels/')
image_ids = open('8_emotion/imagesets/%s.txt' %
(image_set)).read().strip().split()
list_file = open('8_emotion/%s.txt' % (image_set), 'w')
for image_id in tqdm(image_ids):
list_file.write('8_emotion/images/%s.jpg\n' % (image_id))
convert_annotation(image_id)
list_file.close()
在工程的data目录下创建mydata.yaml文件,修改三个地址(上面已经自动生成文件),修改类别数目和具体类别名字,顺序和上面转换时 一致。
train: 7_emotion/train.txt #训练集地址
val: 7_emotion/val.txt # 验证集地址
test: 7_emotion/test.txt #测试集地址
# number of classes
nc: 7
# class names
names: ['Angry', 'Disgusted', 'Happy','Neutral','Sad','Scared','Surprised']
需要设置这几个变量:
(1)–weights 就是权重文件的位置(官网可下载);
(2)–data 就是上面自己创建的mydata.yaml文件;
(3)–epochs 就是训练次数(三五千张图片,经过测试80-120左右基本就可以了)
(4)batch-size 和自己电脑配置有关,16大了就8,8大了就4,最小为1;
其它就暂时不用管!
然后直接值命令行,输入:
python train.py
可以看到,YOLOV5-3.1同样的数据集比YOLOV5-6.1快上很多,前者一轮1分30秒左右,后者一轮13分钟左右,没有具体去研究过原因,估计是网络更加复杂吧。
其次,对于图片的单独识别,我分别做了测试,准确率基本100%(选了三十多张简单测试,但是准确率确实不错),但是实时识别的效果6.1确实比3.1好一些,不过差不太多!
实时测试命令如下,只需要修改–weights 模型训练出来的位置,就可以实时识别!(实时会有一定误识别不过效果也还不错)
python .\detect.py --weights runs\exp1\weights\best.pt --source 0
其它测试命令
# 检测摄像头
python detect.py --weights runs/train/exp/weights/best.pt --source 0 # webcam
# 检测图片文件
python detect.py --weights runs/train/exp/weights/best.pt --source file.jpg # image
# 检测视频文件
python detect.py --weights runs/train/exp/weights/best.pt --source file.mp4 # video
# 检测一个目录下的文件
python detect.py --weights runs/train/exp/weights/best.pt path/ # directory
# 检测网络视频
python detect.py --weights runs/train/exp/weights/best.pt 'https://youtu.be/NUsoVlDFqZg' # YouTube video
# 检测流媒体
python detect.py --weights runs/train/exp_yolov5s/weights/best.pt 'rtsp://example.com/media.mp4' # RTSP, RTMP, HTTP stream
参考链接:
ERROR: No matching distribution found for numpy
报错如下:
说是位于这个镜像上的库不受信任或不安全的主机,将被忽略,所以我们需要让它受信任,所以将代码改为:
pip install numpy -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com,
成功。