这里以windows环境为例,其他环境看官网说明
源码下载
git clone https://github.com/AlexeyAB/darknet.git
Configure >> Generate >> Open Project
然后在vs 上编译出 darknet.exe
在输出文件夹中得到daeknet.exe
这里用labelImg工具打标签
pip install labelImg
选择高级模式Advancedmode 和 自动保存 auto save mode
格式选择yolo格式 ,保存位置选择图片文件夹
然后对所有的图片开始打标签
删除图片文件夹里面的classes.txt ,然后将标签文件全部拷贝到labels文件夹里面(注意这里是拷贝过去,images文件夹下存放图片和标签,labels文件夹下存放标签)
将darknet文件夹下的cfg文件夹下面的yolov4_custom.cfg文件拷贝到前面新建的cfg文件夹下并修改以下几个地方
#batch和sub是结合使用的,例如这儿的batch=64,sub=16表示训练的过程中将一次性加载64张图片进内存,然后分16次完成前向传播,
#意思是每次4张,前向传播的循环过程中累加loss求平均,待64张图片都完成前向传播后,再一次性后传更新参数
#调参经验:sub一般设置16,不能太大或太小,且为8的倍数;
#batch的值可以根据显存占用情况动态调整,一次性加减sub大小即可,通常情况下batch越大越好,
#还需注意一点,在测试的时候batch和sub都设置为1,避免发生神秘错误!
batch=64
subdivisions=16
width=416 #将网络的size改为416*416 (需要是32的倍数)
height=416
max_batches = 2000 #训练次数(由于我这里图片比较少,所以先设为2000次,一般设置为classes*2000 ,不要小于训练图片数,也不要小于classes * 2000)
steps=1600,1800 (一般设置为max_batches的80%,90%)
修改所有的yolo层上面的filters=3*(classes + 5),以及yolo层的classes种类数
import os
import random
import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join
import math
trainval_percent = 0.2 #测试集比例
train_percent = 0.8 #训练集比例
picSuffix='.jpg' #图片格式
sets = ['train', 'test','val']
labelfilepath = 'data/labels'
txtsavepath = 'data/ImageSets'
total_label = os.listdir(labelfilepath)
def StepOne():
num = len(total_label)
list = range(num)
tv = int(num * trainval_percent)
tr = int(tv * train_percent)
#tv = int(num * trainval_percent)
#tr = int(tv * train_percent)
trainval = random.sample(list, tv)
train = random.sample(trainval, tr)
ftrainval = open('data/ImageSets/trainval.txt', 'w')
ftest = open('data/ImageSets/test.txt', 'w')
ftrain = open('data/ImageSets/train.txt', 'w')
fval = open('data/ImageSets/val.txt', 'w')
for i in list:
name = total_label[i][:-4] + '\n'
if i in trainval:
ftrainval.write(name)
if i in train:
ftest.write(name)
else:
fval.write(name)
else:
ftrain.write(name)
ftrainval.close()
ftrain.close()
fval.close()
ftest.close()
def StepTow():
for image_set in sets:
if not os.path.exists('data/labels/'):
os.makedirs('data/labels/')
image_ids = open('data/ImageSets/%s.txt' % (image_set)).read().strip().split()
list_file = open('data/%s.txt' % (image_set), 'w')
for image_id in image_ids:
list_file.write('data/images/%s%s\n' % (image_id,picSuffix))
list_file.close()
if __name__ == "__main__":
StepOne()
StepTow()
python Process.py
data同级目录下新建weights文件夹,并下载预训练模型存放其中
下载预训练模型
anchors其实就是在训练之前人为设定的先验框,网络输出结果的框就是在anchors的基础上进行调整的。所以说先验框设定的好坏对于模型的输出效果影响还是挺大的。在yolo中一般设定一个物体的先验框的个数一般是9个
darknet.exe detector calc_anchors data/config.data -num_of_clusters 9 -width 416 -height 416
成功后会在当前路径下生成anchors.txt文件
将其中的内容拷贝下来,修改cfg/yolov4_custom.cfg文件的anchors (下面三个地方)
执行命令:
darknet.exe detector train data/config.data cfg/yolov4-custom.cfg weights/yolov4.conv.137 -map
如果出现memory error 则将配置文件cfg/yolov4-custom.cfg的batch调小再试
正在训练:
训练完成:
生成的文件在backup文件夹中
写一个简单的测试程序测试一下
#include
#include
#include
#include
#include
#include
#include
#include "yolo_v2_class.hpp"
double const lbk_get_time_ms(){
double t;
struct timeval tv;
gettimeofday(&tv, NULL);
t = tv.tv_sec*1000. + tv.tv_usec/1000.;
return t;
}
int main(){
std::string cfg_file = "model/yolov4-custom.cfg";
std::string weights_file = "model/yolov4-custom_final.weights";
std::string filename = "testImage/img0.jpg";
Detector detector(cfg_file, weights_file);
cv::Mat mat_img = cv::imread(filename);
auto det_image = detector.mat_to_image_resize(mat_img);
double t0 = lbk_get_time_ms();
std::vector<bbox_t> result_vec = detector.detect_resized(*det_image, mat_img.size().width, mat_img.size().height);
for (auto &i : result_vec) {
std::cout << "obj_id = " << i.obj_id << ", x = " << i.x << ", y = " << i.y << ", w = " << i.w << ", h = " << i.h << std::setprecision(3) << ", prob = " << i.prob << std::endl;
}
float min_prop = 0.6f; //可信度过滤
for (auto &i : result_vec) {
if(i.prob > min_prop){
cv::rectangle (mat_img, cv::Rect(i.x,i.y,i.w,i.h), cv::Scalar(0, 0, 255), 2, 8, 0);
if(i.obj_id == 0){
cv::putText(mat_img, "basketball", cv::Point(i.x,i.y), cv::FONT_HERSHEY_COMPLEX, 2, cv::Scalar(0, 255, 255), 2, 8, 0);
}else{
cv::putText(mat_img, std::to_string(i.obj_id - 1), cv::Point(i.x,i.y), cv::FONT_HERSHEY_COMPLEX, 2, cv::Scalar(0, 255, 255), 2, 8, 0);
}
}
}
cv::imwrite("testImage/res.jpg",mat_img);
std::cout << "time :" << (lbk_get_time_ms() - t0) << std::endl;
}