工作里用到yolov3,记录一下。
v3和v4用起来差不多,yolov5部署时遇到很多问题(比如focus层移植失败),就舍弃了。
先放官方的仓库地址:AlexeyAB/darknet
里面有英文版教程,也可以看这个英文教程:YOLO: Real-Time Object Detection
好文分享:YOLO优秀博文合集
一、构建自己的数据集
我的方法是先用标注软件人工画框,得到xml文件,再通过脚本转成yolo能用的txt文件。
我用的软件是labelImg,提取码:623i
为了方便查看和管理,我的文件目录层次如下,可根据个人爱好来改:
其中,annotations文件夹用来存储xml文件(也就是labelImg的保存路径),images文件存放图片,labels用来存放xml转换后的txt文件。
然后通过下面的脚本将xml文件转换成txt然后保存到label文件夹
# xml转txt
class_names = ['car'] # 这里改成自己的类,我只识别汽车,所以就填一个‘car’,这个名字要跟labelImg里标注时的框名一致。
xmlpath='C:/Users/admin/Desktop/yolo_car_dataset/annotations/test/' #原xml路径
txtpath='C:/Users/admin/Desktop/yolo_car_dataset/labels/test/' #转换后txt文件存放路径
files = []
for root, dirs, files in os.walk(xmlpath):
None
number = len(files)
print(number)
i = 0
while i < number:
name = files[i][0:-4]
xml_name = name + ".xml"
txt_name = name + ".txt"
xml_file_name = xmlpath + xml_name
txt_file_name = txtpath + txt_name
xml_file = open(xml_file_name)
tree = ET.parse(xml_file)
root = tree.getroot()
filename = root.find('filename').text
image_name = root.find('filename').text
w = int(root.find('size').find('width').text)
h = int(root.find('size').find('height').text)
print(w,h)
if w or h == 0:
w = 1920
h = 1080
f_txt = open(txt_file_name, 'w+')
content = ""
first = True
for obj in root.iter('object'):
name = obj.find('name').text
class_num = class_names.index(name)
xmlbox = obj.find('bndbox')
x1 = int(xmlbox.find('xmin').text)
x2 = int(xmlbox.find('xmax').text)
y1 = int(xmlbox.find('ymin').text)
y2 = int(xmlbox.find('ymax').text)
if first:
content += str(class_num) + " " + \
str((x1 + x2) / 2 / w) + " " + str((y1 + y2) / 2 / h) + " " + \
str((x2 - x1) / w) + " " + str((y2 - y1) / h)
first = False
else:
content += "\n" + \
str(class_num) + " " + \
str((x1 + x2) / 2 / w) + " " + str((y1 + y2) / 2 / h) + " " + \
str((x2 - x1) / w) + " " + str((y2 - y1) / h)
# print(str(i / (number - 1) * 100) + "%\n")
print(content)
f_txt.write(content)
f_txt.close()
xml_file.close()
i += 1
print("done!")
可能会出现w和h为零的报错情况,这是因为将png转成jpg之后软件无法读取宽高。用下面的脚本就可以打印出出问题的图片,然后自己替换掉再标注这些新图片即可。
#查看哪张图片的xml文件里w和h读取失败
for root, dirs, files in os.walk(xmlpath):
None
number = len(files)
i = 0
while i < number:
name = files[i][0:-4]
xml_name = name + ".xml"
txt_name = name + ".txt"
xml_file_name = xmlpath + xml_name
txt_file_name = txtpath + txt_name
xml_file = open(xml_file_name)
tree = ET.parse(xml_file)
root = tree.getroot()
filename = root.find('filename').text
image_name = root.find('filename').text
w = int(root.find('size').find('width').text)
h = int(root.find('size').find('height').text)
if w == 0 or h == 0:
print(image_name)
i += 1
每张图片都会得到一个对应的相同名字的txt文件,其中每行的格式为:
< object-class > < x_center > < y_center > < width > < height >
- < object-class > 物体的类别,取值从整数 0 到 (类别数-1)
- < x_center > < y_center > < width> < height> 框中心点的x和y,框的w和h,取值是 (0.0 to 1.0]
- 比如:< x > = < absolute_x > / < image_width >
或 < height > = < absolute_height > / < image_height >
看起来像是这样:
1 0.716797 0.395833 0.216406 0.147222
0 0.687109 0.379167 0.255469 0.158333
1 0.420312 0.395833 0.140625 0.166667
得到txt文件后,我们还需train.txt和test.txt两个文件,这两个文件里包含了每张图片的路径,如:
/home/louis/yolo_car_dataset/images/train/1.jpg
/home/louis/yolo_car_dataset/images/train/10.jpg
/home/louis/yolo_car_dataset/images/train/100.jpg
可以通过下面脚本自动生成:
def make_txt(path1, path2, flag):
file_list=os.listdir(path1)
txt = open(flag, 'w+')
for file in file_list:
txt.write(path2+file+'\n')
txt.close()
# path1 是本地数据集的位置,因为我在本地运行这个脚本。path2是服务器上的数据集位置。
path1 = 'C:/Users/admin/Desktop/yolo_car_dataset/images/test/'
path2 = '/home/louis/yolo_car_dataset/images/test/'
flag = 'test.txt' # 'train.txt'
make_txt(path1, path2, flag)
二、修改训练配置文件
我在服务器上跑,系统是ubuntu,以yolov3为例:
下载darknet:
git clone https://github.com/AlexeyAB/darknet
cd darknet/
下载预训练权重:darknet53.conv.74,提取码:861y
也可以去文章开头提到的官方仓库下载。
创建一个自己的文件夹(纯属为了文件夹没那么乱)
mkdir yolov3
然后将上面生成的train.txt和test.txt复制到yolov3目录下。
复制一个yolov3.cfg文件并修改名字为:yolov3-cus.cfg存放到我们的文件夹里:
cp ./cfg/yolov3.cfg ./yolov3/yolov3-cus.cfg
打开这个文件做修改:
vim ./yolov3/yolov3-cus.cfg
- 修改前面部分
batch = 64
subdivisions = 16
max_batches = (类别数 * 2000)# 但不能少于训练图片的数目,也不能少于6000(如一共3个类,则填6000)
steps = (max_batches的80%到90%)# 比如max_batches是6000,则填4800,5400
width和height按自己需求改,这是输入的宽和高,不过必须为32的倍数。
- 往下拉,修改每一层yolo层的[classes = 类别数](一共三个yolo层)
- 再修改每一层yolo层前面的卷积层的[filter = 255]为[filter = (类别数 + 5) *3]
如classes=1 则 filters=18
同样的,复制coco.names文件并改名:
cp ./data/coco.names ./yolov3/obj.names
然后将里面的类名改为自己的类名即可。
在darknet目录下创建一个obj.data文件
vim obj.data
在里面填写并保存:
classes = 2 #你的类别数目
train = yolov3/train.txt
valid = yolov3/test.txt
names = yolov3/obj.names
backup = backup/
以上,我们的准备工作就完成啦~
三、训练
./darknet detector train yolov3/obj.data yolov3/yolov3-cus.cfg darknet53.conv.74
如果想在web端看训练过程的图表,可在命令后面加:
-dont_show -mjpeg_port 8090 -map
然后在浏览器上打开:http://你服务器的IP地址:8090
效果如下:
基本的训练过程就是这样了,如果想知道其他方法或训练技巧,可以看文章开头的两个链接。peace~