本文是在docker中进行的,所以环境配置的默认配置好了。如果不懂docker,建议安装学习一下,然后在 这里可以把环境为cuda8.0-cudnn5-devel-ubuntu16.04 + OpenCV 3.2.0 + Darknet的docker 镜像给pull下来,就可以直接用了。多说一句,开启docker时如果要用显卡,要安装nvidia-docker,启动的命令开头为sudo nvidia-docker run ...,具体的可以百度或google一下。下面是我的docker路径及文件。
在scripts文件夹,创建一个你自己要训练的数据集,比如我的,我就命名为danger吧,然后进入danger,分别再创建三个文件夹:
Annotations:用来放xml标注文件的JPEGImages:用来放数据集的,即图片放这里ImageSets:这个文件夹下还得创建一个Main文件夹 Main:用来存放train.txt和val.txt的,这两个文件就是存放训练集和验证集的图片的名字最后各个文件夹的分布如下: 建好以上文件夹,就把图片放进JPEGImages,把标注文件放进Annotations里。
刚刚我们不是说ImageSets中的Main文件夹是放train.txt和val.txt的吗?现在我们就用代码来生成这两个文件。
# -*- coding: UTF-8 -*- ''' @Author: sanjayzhong @Github: https://github.com/sanjayzzzhong @Date: 2019-05-23 @Filename: generate_txt.py ''' import os from os import listdir, getcwd from os.path import join if __name__ == '__main__': # 图片路径 source_folder='/jimmy/darknet/scripts/danger/JPEGImages/' # 产生train.txt的路径 dest='/jimmy/darknet/scripts/danger/ImageSets/Main/train.txt' # 生成val.txt的路径 dest2='/jimmy/darknet/scripts/danger/ImageSets/Main/val.txt' # 列出所有图片 file_list=os.listdir(source_folder) # 打开train.txt,以附加的形式打开 train_file=open(dest,'a') val_file=open(dest2,'a') # 看看文件一共有多少 num_images = len(file_list) # 自定义训练集和测试的比例 rate = 0.7 #就是10张图片7张训练,3张验证 num_train = int(num_images * rate) print("The number of train is {}".format(num_train)) print("The number of validation is {}".format(num_images-num_train)) for i, file_obj in enumerate(file_list): file_path=os.path.join(source_folder,file_obj) file_name,file_extend=os.path.splitext(file_obj) i += 1 # 如果还在num_train的范围内,就把文件名字写到train_file中 if(i <= num_train): train_file.write(file_name+'\n') # 否者就写刀val_file中 else : val_file.write(file_name+'\n') # 关闭文件流,释放资源 train_file.close() val_file.close()把上诉代码复制,并且运行,如果出错,可能是编码的原因。如果是编码的原因,去看看我这里的解决方法。 以下是我的运行结果:
因为YOLOv3的标注格式是相对的,所以要用以下代码进行转换。在scripts目录下进行,创建python文件,然后执行。最后的结果就是danger文件夹里面出来一个文件夹labels,里面存放的是对应xml文件转换后的YOLOv3格式,文件都为txt的。还有就是在scripts下有danger_train.txt,danger_val.txt,以及train.txt,这三个文件都是存放图片的绝对路径的。 代码如下:
# -*- coding: UTF-8 -*- ''' @Author: sanjayzhong @Github: https://github.com/sanjayzzzhong @Date: 2019-05-23 @Filename: conver_annotation.py 这个文件的作用主要是将xml标记文件转换为YOLOv3的相对标记位置坐标 ''' # 导入解析xml文件的包 import xml.etree.ElementTree as ET import pickle import os from os import listdir, getcwd from os.path import join # 要修改的 sets=[('danger', 'train'), ('danger', 'val')] # 你的分类 classes=['gun'] # 把标记信息转为yolo的相对框信息 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(folder, image_id): in_file = open('%s/Annotations/%s.xml'%(folder, image_id)) out_file = open('%s/labels/%s.txt'%(folder, image_id), 'w') #将特定的xml文件的根节点解析为树 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)) bb = convert((w,h), b) out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n') # 获取当前工作目录 wd = getcwd() for folder, image_set in sets: # 如果没有labels这个目录,便创建 if not os.path.exists('%s/labels/'%(folder)): os.makedirs('%s/labels/'%(folder)) # Python strip() 方法用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列。 # 注意:该方法只能删除开头或是结尾的字符,不能删除中间部分的字符。 # str.split(str="", num=string.count(str)). # 获取所有图片的名字,返回列表 image_ids = open('%s/ImageSets/Main/%s.txt'%(folder, image_set)).read().strip().split() # 在当前文件夹下生成一个文件,方便以后将其合成为一个 list_file = open('%s_%s.txt'%(folder, image_set), 'w') for image_id in image_ids: # 将图片的绝对路径写入list_file中 list_file.write('%s/%s/JPEGImages/%s.jpg\n'%(wd, folder, image_id)) # 将对应imag_id的xml文件写到labels中的.txt文件中 convert_annotation(folder, image_id) list_file.close() os.system("cat danger_train.txt danger_val.txt > train.txt")执行完之后,可以随便看看danger_train.txt文件的内容:
也可以发现danger文件夹下多了个labels文件夹,labels文件夹里面存的是:
这个很简单,你可以直接在darknet目录下进行:
wget https://pjreddie.com/media/files/darknet53.conv.74要修改的东西就一个,把你的类别信息改一下即可。可以将voc.names里面的东西改为你的样本集的标签名。也可以自己创建一个文件,把类别标签写进去。我自己创了一个,命名为danger.names。如下图:
要改的东西其实就2个,一个是你的数据文件在哪里,另一个就是模型的配置问题。从cfg文件夹我们就知道这个是configuration的缩写。
修改如下:
我的检测是1类的,所以我就以一类来为例了。要注意,前面的convolutional不要改,如果你用vim的,可以搜索yolo,具体方法是在命令行模式下输入/yolo即可到达。看一下文件的开头: 有些参数可以根据实际改,但是我还不懂,所以这里的改先跳过。我们看看经过搜索的: 上图展示了应该在哪里改,图中两行的注释已经说明了。一定要记得,convolutional和yolo层一定要连着的才要改,别的不用改。接着继续改完接下来的层即可。(一共有三个这样的层)
改完之后,我们就可以开始训练了。先回到darknet目录下,运行下面命令:
./darknet detector train cfg/voc.data cfg/yolov3-voc.cfg darknet53.conv.74 -gpus 0,1最后的-gpus 0,1 取决你有多少张显卡咯。
将训练得到权重文件(在backup文件夹下)拷贝刀darknet/weights文件夹下面,然后下面命令
./darknet detect cfg/yolov3-voc.cfg weights/yolov3.weights data/dog.jpg