一文多图搞定制作自己的VOC数据集+使用yolov4训练自己的数据集+封装video测试脚本(基于ubuntu)
制作VOC数据集
目前大多数的神经网络都是采用VOC的数据格式来进行训练的,yolo系列亦是如此。
标注自己的数据集
首先用labelImg生成的是xml格式标记文件
下载
方式一:labelimg下载网站:https://github.com/tzutalin/labelImg
:
下载后将labelImg-master.zip移动至home主文件夹下解压,得到LabelImg-master文件,打开后如下图所示。
方式二:使用git命令
git clone https://github.com/tzutalin/labelImg
下载后是自动在home目录下解压好的,文件名为LabelImg,里面的内容同上图。
安装方式:
$ sudo apt-get install pyqt4-dev-tools # 安装PyQt4
$ sudo pip install lxml # 安装lxml,如果报错,可以试试下面语句
$ sudo apt-get install python-lxml
然后打开终端,进入LabelImg目录后使用make编译
cd LabelImg
make all
使用:在labelImg目录下使用终端执行
python labelImg.py
快捷键:
Ctrl + u 加载目录中的所有图像,鼠标点击Open dir同功能
Ctrl + r 更改默认注释目标目录(xml文件保存的地址)
Ctrl + s 保存
Ctrl + d 复制当前标签和矩形框
space 将当前图像标记为已验证
w 创建一个矩形框
d 下一张图片
a 上一张图片
del 删除选定的矩形框
Ctrl++ 放大
Ctrl-- 缩小
↑→↓← 键盘箭头移动选定的矩形框
整理数据集路径格式
建立文件夹层次为 darknet / data/VOCdevkit / VOC2020,voc2020下面有三个文件夹:
先按照上面格式准备好数据
Annotations是存放标签xml文件
JPEGImage 存放图片
ImageSets 里面新建Main文件夹,在Main下新建3个txtImageSets/Main 里面txt按行存放着图片名字(只含文件名不带文件名后缀),如:
000001
000002
000003…
生成train.txt的教程:
自己制作数据集,一个类别的图片标记完了需要做成一个train.txt,包含所有的训练图片的名称,实现过程如下:
1、在包含训练图片的文件夹中新建TXT文件。
2、在TXT文件中输入 DIR ./B>train.txt (DIR命令后须有一空格)
3、保存后将后缀名改为BAT。
4、双击该文件即可生成一个train.txt。
5、打开再根据自己要求修改即可(将文件后缀等多余信息删除)。
训练数据集
环境
Ubuntu 18.04
Python3.7
cuda 10
首先把代码下载下来,可以在终端用下面命令下载,也可以直接点击上面代码链接下载。
git clone https://github.com/AlexeyAB/darknet.git
在Linux上编译
make在darknet目录中,由于底层是C++写的,所以要先通过Cmake编译,才能通过python运行。如果需要使用GPU加速,那么得先打开项目里面的makefile文件修改一些参数的值。修改完成之后再通过终端直接make。
GPU=1
CUDNN=1
CUDNN_HALF=1
OPENCV=1
OPENMP=1
LIBSO=1
DEBUG=1
make命令如下:
# cd到darknet-master目录下
make
或者 make -j8
测试一下源权重(所有测试命令可以在.sh文件里查看)
# 测试图片,结果保存在darknet-master/predictions.jpg
./darknet detect cfg/yolov4.cfg yolov4.weights data/dog.jpg
制作yolov4需要的label以及txt
在上文中VOCdevkit文件中进行以下操作:
这个时候只用voc数据集的格式是不满足我们这里需要的格式。首先打开路径下/home/alex/darknet/scripts/voc_label.py,修改voc_label.py里面的相应内容如下。
7 sets=[('2020', 'train'), ('2020', 'val'), ('2020', 'test')]#改成自己的年份的
8
9 classes = ["0", "1", "2", "3", "4"]#改成自己的类别
给每个路径前面加个data,如下图
修改完了之后,将voc_label.py剪切到在主目录darknet下,并且执行voc_label.py,否则哪些文件会生成在build/darknet//data下面,执行完成后你会看到主目录下的data/目录下会生成几个txt。主目录darknet下的data/VOCdevkit/VOC2020/下面会生成一个label文件夹。
修改配置文件
1.cfg/目录下复制coco.data,并且重命名为obj.data。然后使用修改下面以下内容
2.cfg/目录下复制coco.names,并且重命名为obj.names。改成自己类别的名称
3.复制cfg/yolov4-custom.cfg,并且重命名为yolo-obj.cfg,同时修改一下内容(batches可以根据自己的修改)
上图中修改width和height为416,修改最大batch迭代多少个数max_batches = 6000,修改steps多久学习率下降一次,一般设置为batch个数的80%和90%。
然后三个classes的地方要修改
还有三个filters=255的地方要修改成自己的
开始训练自己的数据集
./darknet detector train cfg/obj.data cfg/yolo-obj.cfg yolov4.conv.137
预测
训练完自己的数据集生成权重后,可以通过一些测试数据来检验权重模型的效果,如下命令通过test.jpg检验了效果:
./darknet detector test ./cfg/yolo-obj.data ./cfg/obj.cfg ./yolo-obj_final.weights data/test.jpg -i 0 -thresh 0.25
封装video测试脚本
将测试video的算法封装成PY文件,打开终端CD到darknet_sanhu,运行如下命令:
python3 darknet_video.py
from ctypes import *
import math
import random
import os
import cv2
import numpy as np
import time
import darknet
def convertBack(x, y, w, h):
xmin = int(round(x - (w / 2)))
xmax = int(round(x + (w / 2)))
ymin = int(round(y - (h / 2)))
ymax = int(round(y + (h / 2)))
return xmin, ymin, xmax, ymax
def cvDrawBoxes(detections, img):
color_dict = {
'bottle': [0, 255, 255], 'plastic_bag': [238, 123, 158], 'leaves_branches': [24, 245, 217], 'float_plants': [224, 119, 227],
'rubbish': [154, 52, 104]
}
count = 0
for detection in detections:
x, y, w, h = detection[2][0],\
detection[2][1],\
detection[2][2],\
detection[2][3]
# name_tag = str(detection[0].decode("ascii"))
name_tag = str(detection[0])
for name_key, color_val in color_dict.items():
if name_key == name_tag:
color = color_val
xmin, ymin, xmax, ymax = convertBack(
float(x), float(y), float(w), float(h))
pt1 = (xmin, ymin)
pt2 = (xmax, ymax)
cv2.rectangle(img, pt1, pt2, color, 1)
# python round()参数:返回四舍五入值,小数点位数
cv2.putText(img,
# detection[0].decode() +
detection[0] +
" [" + str(round(float(detection[1]))) + "% ]",
(pt1[0], pt1[1] - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5,
color, 2)
count += 1
cv2.putText(img, "Num: %s" % str(count), (25, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, [255, 255, 0], 2)
return img
netMain = None
# metaMain = None
metaMain = ['bottle', 'plastic_bag', 'leaves_branches', 'float_plants', 'rubbish']
altNames = None
def YOLO():
global metaMain, netMain, altNames
configPath = "/home/alex/darknet_sanhu/cfg/yolo-obj.cfg"
weightPath = "/home/alex/darknet_sanhu/backup/yolo-obj_6000.weights"
metaPath = "./cfg/obj.data"
if not os.path.exists(configPath):
raise ValueError("Invalid config path `" +
os.path.abspath(configPath)+"`")
if not os.path.exists(weightPath):
raise ValueError("Invalid weight path `" +
os.path.abspath(weightPath)+"`")
if not os.path.exists(metaPath):
raise ValueError("Invalid data file path `" +
os.path.abspath(metaPath)+"`")
if netMain is None:
netMain = darknet.load_net_custom(configPath.encode(
"ascii"), weightPath.encode("ascii"), 0, 1) # batch size = 1
if metaMain is None:
metaMain = darknet.load_meta(metaPath.encode("ascii"))
if altNames is None:
try:
with open(metaPath) as metaFH:
metaContents = metaFH.read()
import re
match = re.search("names *= *(.*)$", metaContents,
re.IGNORECASE | re.MULTILINE)
if match:
result = match.group(1)
else:
result = None
try:
if os.path.exists(result):
with open(result) as namesFH:
namesList = namesFH.read().strip().split("\n")
altNames = [x.strip() for x in namesList]
except TypeError:
pass
except Exception:
pass
cap = cv2.VideoCapture(0) #摄像头测试
#cap = cv2.VideoCapture("/home/alex/video/smoke/smoke.mp4") #路径测试
#cap = cv2.VideoCapture("/home/alex/darknet-master/4.mp4")
cap.set(3, 1280)
cap.set(4, 720)
#out = cv2.VideoWriter(
# "output.mp4", cv2.VideoWriter_fourcc(*"MJPG"), 10.0,
# (darknet.network_width(netMain), darknet.network_height(netMain)))
out = cv2.VideoWriter(
"output.avi", cv2.VideoWriter_fourcc('M', 'P', '4', '2'), 10.0,
(darknet.network_width(netMain), darknet.network_height(netMain)))
print("Starting the YOLO loop...")
# Create an image we reuse for each detect
darknet_image = darknet.make_image(darknet.network_width(netMain),
darknet.network_height(netMain),3)
while True:
prev_time = time.time()
ret, frame_read = cap.read()
frame_rgb = cv2.cvtColor(frame_read, cv2.COLOR_BGR2RGB)
frame_resized = cv2.resize(frame_rgb,
(darknet.network_width(netMain),
darknet.network_height(netMain)),
interpolation=cv2.INTER_LINEAR)
darknet.copy_image_from_bytes(darknet_image,frame_resized.tobytes())
detections = darknet.detect_image(netMain, metaMain, darknet_image, thresh=0.25)
for detection in detections:
x, y, w, h = detection[2][0],\
detection[2][1],\
detection[2][2],\
detection[2][3]
a=x-207.5
b=y-207.5
c=280/h
print(a,b)
print("x: %.2f mm y: %.2f mm"%(a*c,b*c))
image = cvDrawBoxes(detections, frame_resized)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
print(1/(time.time()-prev_time))
cv2.imshow('Demo', image)
cv2.waitKey(3)
cap.release()
out.release()
if __name__ == "__main__":
YOLO()
迁移学习
有时候训练的结果不够满意,或者训练到一半突然终止了,这时候从头开始训练又很费时间,此时我们可以将自己之前保存的权重作为预训练权重。但是直接使用yolo-obj_last.weights会报错。需要做出如下转变。
#首先用第一行代码将yolo-obj_last.weights转化为olo-obj_last.conv.23
./darknet partial cfg/yolo-obj.cfg backup/yolo-obj_last.weights backup/yolo-obj_last.conv.23 23
#第二行将我们刚转化好的yolo-obj_last.conv.23作为预训练权重训练
./darknet detector train cfg/obj.data cfg/yolo-obj.cfg backup/yolo-obj_last.conv.23