YOLOV3 模型训练及模型部署(YOLO动态链接库的封装和调用)
一、 配置环境
- windows10 + VS2017 community
- cuda10.0.130_411.31
- cunn-10.0-v7.6.5.32
- opencv3.2
二、制作COCO数据集
参考链接:yolov3 训练自己的数据(coco数据制作篇)
1.准备工作
- 安装labelme软件安装教程及下载地址,也可参考Windows下的labelme数据标注工具安装教程
2.制作样本
(1)labelme制作样本,生成对应图像的json文件
(2)将生成的json文件和原始图像jpg,放入同一个文件夹中如:(D:\labelme-master\examples\instance_segmentation\data_annotated)
(3)下载labelme压缩包解压后,会产生D:\labelme-master\examples\文件夹里面有如下几个文件夹:
bbox_detection
classification
instance_segmentation 实例检测
primitives
semantic_segmentation 语义分割
tutorial
video_annotation
(4)进入instance_segmentation文件夹,instance_segmentation包含:
data_annotated 原始图像(将所需转换的原始图像放入该文件夹中,放入前清空文件夹)
data_dataset_coco 生成的coco数据集
data_dataset_voc 生成的voc数据集
labelme2coco.py
labelme2voc.py
labels.txt 标签类别
将labels.txt进行修改,本例是二分类front、behind两类别,加上背景类_background,还需要加上__ignore__类别,否则会报错,在labme2coco.py中有定义。即将labels.txt修改为:
__ignore__
_background_
front
behind
(5)打开终端并切换路径至instance_segmentation文件夹处,运行如下代码(运行之前先删除data_dataset_coco文件夹或者将其改名均可):
python labelme2coco.py data_annotated data_dataset_coco --labels labels.txt
(6)运行后,会将生成新的data_dataset_coco文件夹,此文件夹内包含JPEGImages文件夹(包含了原始图像)、Visulaization文件夹以及annotations.json。运行coco_txt.py将生成对象图片的txt图像信息保存至JPEGImages文件夹。
from __future__ import print_function
import os, sys, zipfile
import json
def convert(size, box):
dw = 1. / (size[0])
dh = 1. / (size[1])
x = box[0] + box[2] / 2.0
y = box[1] + box[3] / 2.0
w = box[2]
h = box[3]
x = x * dw
w = w * dw
y = y * dh
h = h * dh
return (x, y, w, h)
json_file = 'annotations.json' # # Object Instance 类型的标注
data = json.load(open(json_file, 'r'))
ana_txt_save_path = "data/coco/" # 保存的路径
if not os.path.exists(ana_txt_save_path):
os.makedirs(ana_txt_save_path)
for img in data['images']:
# print(img["file_name"])
filename = img["file_name"]
img_width = img["width"]
img_height = img["height"]
# print(img["height"])
# print(img["width"])
img_id = img["id"]
ana_txt_name = filename.split(".")[0] + ".txt" # 对应的txt名字,与jpg一致
print(ana_txt_name.replace('\\', '/'))
with open(os.path.join(ana_txt_save_path, ana_txt_name).replace('\\', '/'), 'w') as f_txt:
# f_txt = open()
for ann in data['annotations']:
if ann['image_id'] == img_id:
# annotation.append(ann)
# print(ann["category_id"], ann["bbox"])
box = convert((img_width, img_height), ann["bbox"])
f_txt.write("%s %s %s %s %s\n" % (ann["category_id"], box[0], box[1], box[2], box[3]))
f_txt.close()
(7)使用train_val_txt.py生成train和val的绝对路径,该文件只是生成了train.txt,需要将train.txt中的txt改为jpg,因为针对的是image,这个是根据网上教程来的,其实是可以直接一步到位的。再在train.txt中选取部分作为val.txt的内容。新建labels文件夹,并将JPEGImages文件夹中的txt文件导入其中。到此coco数据集制作完毕
# -*- coding: utf-8 -*-
import time
import os
import shutil
import string
def readFilename(path, allfile):
filelist = os.listdir(path)
for filename in filelist:
filepath = os.path.join(path, filename)
if os.path.isdir(filepath):
readFilename(filepath, allfile)
else:
allfile.append(filepath.replace('\\', '/'))
return allfile
if __name__ == '__main__':
path1 = "data/coco/JPEGImages" #修改为自己的路径
allfile1 = []
allfile1 = readFilename(path1, allfile1)
print("allfile1 = ", allfile1)
allname1 = []
txtpath = "data/coco/" + "train.txt" # 修改为自己的路径
for name in allfile1:
file_cls = name.split("/")[-1].split(".")[-1]
if file_cls == 'txt':
with open(txtpath, 'a+') as fp:
fp.write("".join(name) + "\n")
三、darknet编译生成可执行exe文件并进行模型训练和预测
参考链接:Windows10+YOLOV3+VisualStudio2017最新版本超详细过程
项目地址
1.darknet.exe生成
(1)git项目,项目目录如下
(2)进入build>darknet文件中,主要有用的文件
如果出现找不到相应的dll文件可以将dll文件复制到×64文件夹中
(3)用VS2017打开darknet.sln文件,生成darknet.exe文件。
在生成操作之前需要配置darknet.sln,release ×64模式,如果是GPU模式还需要在darknet目录下修改Makefile中的前几行参数(GPU=1
CUDNN=1
CUDNN_HALF=1
OPENCV=1)。还需要修改darknet.vcxproj中的相关配置(CUDA版本修改一般就两处,VS版本修改如VS2017需要将对应版本修改为v141)
配置如下(具体还需要根据自己的位置进行配置):
常规:
windows sdk版本 10.0.19041.0(10.0.17763.0)
平台工具集 Visual Studio 2017(V141)
C++ —常规
KaTeX parse error: Undefined control sequence: \include at position 13: (OPENCV_DIR)\̲i̲n̲c̲l̲u̲d̲e̲;..\..\include;…(CudaToolkitIncludeDir);KaTeX parse error: Undefined control sequence: \include at position 8: (CUDNN)\̲i̲n̲c̲l̲u̲d̲e̲;(cudnn)\include
VC++ 目录
----可执行文件目录
D:\opencv\opencv\build\include;D:\opencv\opencv\build\include\opencv;D:\opencv\opencv\build\include\opencv2;$(ExecutablePath)
----包含目录
D:\opencv\opencv\build\include;D:\opencv\opencv\build\include\opencv;D:\opencv\opencv\build\include\opencv2;$(IncludePath)
----库目录
D:\opencv\opencv\build\x64\vc14\lib;$(LibraryPath)
链接器—常规—附加库目录
D:\opencv\opencv\build\x64\vc14\lib;$(CUDA_PATH)\lib$(PlatformName);KaTeX parse error: Undefined control sequence: \lib at position 8: (CUDNN)\̲l̲i̲b̲\x64;(cudnn)\lib\x64;…\3rdparty\pthreads\lib;opencv_world320.lib;%(AdditionalLibraryDirectories)
链接器—输入—附加依赖项
pthreadVC2.lib
cublas.lib
opencv_world320.lib
opencv_world320d.lib
curand.lib
cudart.lib
右键生成后会生成darknet.exe文件。
2.yolov3模型训练
(1).修改coco.data, coco.names为自身对应的类别和路径
(2).需要修改yolov3.cfg 一个是将test注释掉,train注释取消,将classes类别数设置正确,卷积核filter个数设置为3*(类别数+1+4)(只需要将部分filters对应个数修改即可)
darknet.exe detector train data/coco.data cfg/yolov3.cfg darknet53.conv.74
这里的darknet53.conv.74网络需要另外下载
训练的结果保存在:backup//yolov3_final.weights
3.yolov3模型预测(如果只需要进行测试,需要下载yolov3.weights)
单张图像预测:
darknet.exe detector test data/coco.data cfg/yolov3.cfg backup/yolov3_final.weights test.jpg
视频预测:
darknet.exe detector demo data/coco.data cfg/yolov3.cfg backup/yolov3_final.weights "test.mp4"
也可以使用如下命令:
yolo_console_dll.exe data/coco.names cfg/yolov3.cfg backup/yolov3.weights "test.mp4"
如果需要将测试视频结果保存可以在预测命令行后面添加
-out_filename output_filename.mp4
注:yolov3.cfg需要将训练部分给注释,留下test部分,同样在训练时需要将test部分注释掉(如下是测试用的yolov3.cfg,将Testing部分给注释解开,并将Training部分注释掉,训练时相反)
[net]
# Testing
batch=1
subdivisions=1
# Training
#batch=64
#subdivisions=16
四、yolov3模型部署
参考链接:Windows系统下YOLO动态链接库的封装和调用(Windows10+VS2015+OpenCV3.4.0+CUDA9.0+cuDNN7.0)
参考链接:YOLO动态链接库的编译和使用
1.编译动态链接库:
打开yolo_cpp_dll.sln进行配置后release ×64进行生成,生成成功后会生成yolo_cpp_dll.lib和yolo_cpp_dll.dll两个文件
2.调用动态链接库
1、动态链接库(均在darknet-master\build\darknet\x64目录下)
(1)yolo_cpp_dll.lib
(2)yolo_cpp_dll.dll
(3)pthreadGC2.dll
(4)pthreadVC2.dll
2、OpenCV库(取决于使用debug还是release模式)
(1)opencv_world340d.dll
(2)opencv_world340.dll
3、YOLO模型文件(第一个文件在darknet-master\build\darknet\x64\data目录下,第二个文件在darknet-master\build\darknet\x64目录下,第三个文件需要自己下载,下载链接见前一篇文章)
(1)coco.names
(2)yolov3.cfg
(3)yolov3.weights
4、头文件
(1)yolo_v2_class.hpp
头文件包含了动态链接库中具体的类的定义,调用时需要引用,这个文件在darknet-master\build\darknet目录下的yolo_console_dll.sln中,将其复制到记事本保存成.hpp文件即可。
3.在VS2017中新建空项目添加yolov3.cpp,修改相应代码后进行生成,会生成yolov3.exe
#include <iostream>
#ifdef _WIN32
#define OPENCV
#define GPU
#endif
#include "yolo_v2_class.hpp" // imported functions from DLL
#include <opencv2/opencv.hpp> // C++
#include "opencv2/highgui/highgui.hpp"
#pragma comment(lib, "opencv_world320.lib")//引入链接库
#pragma comment(lib, "yolo_cpp_dll.lib") //引入YOLO动态链接库
//以下两段代码来自yolo_console_dll.sln
void draw_boxes(cv::Mat mat_img, std::vector<bbox_t> result_vec, std::vector<std::string> obj_names,
int current_det_fps = -1, int current_cap_fps = -1)
{
int const colors[6][3] = { { 1,0,1 },{ 0,0,1 },{ 0,1,1 },{ 0,1,0 },{ 1,1,0 },{ 1,0,0 } };
for (auto &i : result_vec) {
cv::Scalar color = obj_id_to_color(i.obj_id);
cv::rectangle(mat_img, cv::Rect(i.x, i.y, i.w, i.h), color, 2);
if (obj_names.size() > i.obj_id) {
std::string obj_name = obj_names[i.obj_id];
if (i.track_id > 0) obj_name += " - " + std::to_string(i.track_id);
cv::Size const text_size = getTextSize(obj_name, cv::FONT_HERSHEY_COMPLEX_SMALL, 1.2, 2, 0);
int const max_width = (text_size.width > i.w + 2) ? text_size.width : (i.w + 2);
cv::rectangle(mat_img, cv::Point2f(std::max((int)i.x - 1, 0), std::max((int)i.y - 30, 0)),
cv::Point2f(std::min((int)i.x + max_width, mat_img.cols - 1), std::min((int)i.y, mat_img.rows - 1)),
color, CV_FILLED, 8, 0);
putText(mat_img, obj_name, cv::Point2f(i.x, i.y - 10), cv::FONT_HERSHEY_COMPLEX_SMALL, 1.2, cv::Scalar(0, 0, 0), 2);
}
}
if (current_det_fps >= 0 && current_cap_fps >= 0) {
std::string fps_str = "FPS detection: " + std::to_string(current_det_fps) + " FPS capture: " + std::to_string(current_cap_fps);
putText(mat_img, fps_str, cv::Point2f(10, 20), cv::FONT_HERSHEY_COMPLEX_SMALL, 1.2, cv::Scalar(50, 255, 0), 2);
}
}
std::vector<std::string> objects_names_from_file(std::string const filename) {
std::ifstream file(filename);
std::vector<std::string> file_lines;
if (!file.is_open()) return file_lines;
for (std::string line; getline(file, line);) file_lines.push_back(line);
std::cout << "object names loaded \n";
return file_lines;
}
int main()
{
std::string names_file = "coco.names";
std::string cfg_file = "yolov3.cfg";
std::string weights_file = "yolov3_final.weights";
Detector detector(cfg_file, weights_file);//初始化检测器
//std::vector<std::string> obj_names = objects_names_from_file(names_file); //调用获得分类对象名称
//或者使用以下四行代码也可实现读入分类对象文件
std::vector<std::string> obj_names;
std::ifstream ifs(names_file.c_str());
std::string line;
while (getline(ifs, line)) obj_names.push_back(line);
//测试是否成功读入分类对象文件
for (size_t i = 0; i < obj_names.size(); i++)
{
std::cout << obj_names[i] << std::endl;
}
cv::VideoCapture capture;
capture.open(0);
if (!capture.isOpened())
{
printf("文件打开失败");
}
cv::Mat frame;
while (true)
{
capture >> frame;
std::vector<bbox_t> result_vec = detector.detect(frame);
draw_boxes(frame, result_vec, obj_names);
cv::namedWindow("test", CV_WINDOW_NORMAL);
cv::imshow("test", frame);
cv::waitKey(3);
}
return 0;
}
4.直接运行yolov3.exe会报错,找不到dll文件,需要将所有的文件导入yolov3.exe所在文件夹中
5.可以调整yolo_v2_class.hpp内的std::vector<bbox_t> detect(cv::Mat mat, float thresh = 0.5, bool use_mean = false)某些参数来选取合适的thresh
6.进行编译后直接运行yolov3.exe即可