Yolov3在windows系统下的配置与使用

最近在做工业产品缺陷检测相关的项目,调研了目前目标检测方向的一些深度神经网络方法,最终确定使用yolov3。

Yolov3源码及vs工程

首先到 https://github.com/AlexeyAB/darknet 克隆最新版本的代码。这里面有VS2015的工程,如果安装有vc140的编译工具集,则使用vs2017和vs2019都可以。
注:如果使用高于vs2015的版本,则一定要选择不升级平台工具集,使用默认的vc140。

Vs解决方案共有4个,每个解决方案下面各一个工程。看名字就知道每个工程的作用。

如果要在命令行中使用yolo的网络,选择不带_dll的,如果要在别的程序中调用yolo,选择带dll的。根据是否有gpu选择带不带_no_gpu。

打开后编译配置选择release和x64,配置opencv和cuda(如果选择了gpu版本)的路径。Opencv目前还不支持4.x.x版本。

训练voc数据集

默认配置修改

使用cfg文件夹的yolov3-voc.cfg配置文件,里面有关键的几行需要修改
测试时

batch=1
subdivisions=1

二者必须都是1。
训练时

batch=64
subdivisions=32

batch必须是64,subdivisions可以是1,2,4,8,16,32,64。数字越大,需要的gpu显存就越少。

[yolo]
classes = ?

classes设为要分类的目标种类,voc数据集是20,一共有3处需要修改。
然后把这个[yolo]组的上一组[convolutional]分组的

filters=?

设为(Classes+5)*3,这里需要写计算出的值,不能写表达式。

其他还需要准备的文件

voc.data和voc.names。这两个文件在build\darknet\x64\data文件夹下。
下载预训练权重文件darknet53.conv.74放到build\darknet\x64目录中
下载voc数据集VOCtrainval_11-May-2012.tar,VOCtrainval_06-Nov-2007.tar,VOCtest_06-Nov-2007.tar,并放到build\darknet\x64\data\voc\VOCdevkit\目录中。
下载python文件voc_label.py 到build\darknet\x64\data\voc目录。
以上需要的文件都放到百度网盘中
链接:https://pan.baidu.com/s/18ZKVJtrs6pUtaq--Ma2sQQ
提取码:xtlp

模型训练及测试

所有数据准备就绪,并且darknet的非dll版本编译成功,就可以开始模型的训练和测试。
为了方便,需要写几个脚本
训练的脚本 darknet_train_voc.cmd

darknet.exe detector train data/voc.data cfg/yolov3-voc.cfg darknet53.conv.74
pause

恢复训练的脚本 darknet_train_voc_resume.cmd

darknet.exe detector train data/voc.data cfg/yolov3-voc.cfg backup/yolov3-voc_last.weights -map

-map参数代表在可视化的图形中显示在验证集上的准确度。
测试的脚本 darknet_test_voc.cmd

darknet.exe detector test data/voc.data cfg/yolov3-voc.cfg backup/yolov3-voc_14000.weights test/person.jpg

pause

记得测试前修改yolov3-voc.cfg文件中的

batch=1
subdivisions=1

都为1。

结果

Yolov3在windows系统下的配置与使用_第1张图片
Yolov3在windows系统下的配置与使用_第2张图片

使用自己的训练数据

数据集的制备

生成训练用的所有图片路径的txt,使用脚本dir.cmd

dir /s /b /oe "E:\WorkSpace\darknet\build\darknet\x64\mydataset\img">"E:\WorkSpace\darknet\build\darknet\x64\mydataset\train.txt"

myobj.names中写自己的类别。
Yolov3在windows系统下的配置与使用_第3张图片

需要有和每张图片同名的txt文件保存图片中的目标区域和类别。
规则是

类别 目标框中心x相对坐标 目标框中心x相对坐标 目标框相对宽度 目标框相对高度

相对的意思是把图片的总宽度和总长度看做1,等比例缩放的意思。比如如下的形式:

0 0.366406 0.470833 0.028125 0.038889
0 0.403906 0.625694 0.017188 0.031944

这种格式文件的生成有个标注工具可以使用
https://github.com/AlexeyAB/Yolo_mark

Yolov3在windows系统下的配置与使用_第4张图片

为方便执行标注文件,写一个脚本yolo_mark.cmd

yolo_mark.exe E:/WorkSpace-Python/DataAugmentationForObjectDetection/mydataset/img/aug mydataset/train.txt mydataset/myobj.names
pause

参数分别是:需要输入图像的路径;图像的文件名txt,每行一个;目标类别名称文件。

数据增强

https://github.com/Paperspace/DataAugmentationForObjectDetection

由于使用的坐标系和标注结果定义不一样,需要进行转换,转换的python程序为:

from data_aug.data_aug import *
from data_aug.bbox_util import *
import numpy as np 
import cv2 
import matplotlib.pyplot as plt 
import pickle as pkl
from glob import glob

image_path = './mydataset/img'
file_names=glob(image_path+"/*bmp")
for file_name in file_names:
    name = os.path.split(file_name)[1]
    path = os.path.split(file_name)[0]
    img = cv2.imread(file_name)
    bboxesraw = np.loadtxt(path + "/" + name[:-4] + ".txt")
    
    if np.ndim(bboxesraw) == 1:
        bboxes = np.expand_dims(bboxesraw, axis=0)
    else:
        bboxes = bboxesraw

    # 对bboxes进行操作
    bboxesnew = bboxes.copy()
    bboxesnew[:,4] = bboxes[:,0]
    bboxesnew[:,0] = (bboxes[:,1] - bboxes[:,3]/2) * img.shape[1]
    bboxesnew[:,1] = (bboxes[:,2] - bboxes[:,4]/2) * img.shape[0] 
    bboxesnew[:,2] = (bboxes[:,1] + bboxes[:,3]/2) * img.shape[1] 
    bboxesnew[:,3] = (bboxes[:,2] + bboxes[:,4]/2)* img.shape[0]

    bboxes = bboxesnew

    for i in range(5):
        
        if i == 0: # 翻转
            img_, bboxes_ = RandomHorizontalFlip(1)(img.copy(), bboxes.copy())
        elif i == 1: #
            img_, bboxes_ = RandomScale(0.3, diff = True)(img.copy(), bboxes.copy())
        elif i == 2: #
            img_, bboxes_ = RandomTranslate(0.3, diff = True)(img.copy(), bboxes.copy())
        elif i == 3: #
            img_, bboxes_ = RandomRotate(20)(img.copy(), bboxes.copy())
        elif i == 4: #
            img_, bboxes_ = RandomShear(0.2)(img.copy(), bboxes.copy())
        # elif i == 5: #
        #     img_, bboxes_ = RandomHSV(100, 100, 100)(img.copy(), bboxes.copy())

        # 把bboxes改回去
        bboxesnew_ = bboxes_.copy()
        bboxesnew_[:,0] = bboxes_[:,4]
        bboxesnew_[:,1] = (bboxes_[:,2] / img.shape[1] + bboxes_[:,0] / img.shape[1])/2
        bboxesnew_[:,2] = (bboxes_[:,3] / img.shape[0] + bboxes_[:,1] / img.shape[0])/2
        bboxesnew_[:,3] = (bboxes_[:,2] / img.shape[1] - bboxes_[:,0] / img.shape[1])
        bboxesnew_[:,4] = (bboxes_[:,3] / img.shape[0] - bboxes_[:,1] / img.shape[0])
        
        # 保存图像和bboxes
        cv2.imwrite(path + "/aug/" + name[:-4] + "-aug" + str(i) +".bmp",img_)
        np.savetxt(path + "/aug/" + name[:-4] + "-aug" + str(i) +".txt",bboxesnew_, fmt = ['%i','%10.6f','%10.6f','%10.6f','%10.6f'])
        print(path + "/aug/" + name[:-4] + "-aug" + str(i))

根据要识别的类数量,更改配置文件

classes = ?
filters=?

总共有3处。

训练及测试

写一个脚本darknet_train_myobject.cmd

darknet.exe detector train mydataset/myobj.data mydataset/yolov3.cfg darknet53.conv.74

写一个测试脚本darknet_test_myobject.cmd

darknet.exe detector test mydataset/myobj.data mydataset/yolov3.cfg backup/yolov3_last.weights myobjtest/6.bmp

结果

Yolov3在windows系统下的配置与使用_第5张图片
Yolov3在windows系统下的配置与使用_第6张图片

在c++中调用yolo的dll

首先编译带_dll的工程,编译配置为debug/release,x64。
在c++程序中,使用如下方式调用,识别单张图片

#include "include/yolo_v2_class.hpp" 

void draw_boxes(cv::Mat mat_img, std::vector result_vec, std::vector 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 max_width = (text_size.width > i.w + 2) ? text_size.width : (i.w + 2);
			max_width = std::max(max_width, (int)i.w + 2);
			//max_width = std::max(max_width, 283);
			std::string coords_3d;
			if (!std::isnan(i.z_3d)) {
				std::stringstream ss;
				ss << std::fixed << std::setprecision(2) << "x:" << i.x_3d << "m y:" << i.y_3d << "m z:" << i.z_3d << "m ";
				coords_3d = ss.str();
				cv::Size const text_size_3d = getTextSize(ss.str(), cv::FONT_HERSHEY_COMPLEX_SMALL, 0.8, 1, 0);
				int const max_width_3d = (text_size_3d.width > i.w + 2) ? text_size_3d.width : (i.w + 2);
				if (max_width_3d > max_width) max_width = max_width_3d;
			}

			cv::rectangle(mat_img, cv::Point2f(std::max((int)i.x - 1, 0), std::max((int)i.y - 35, 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 - 16), cv::FONT_HERSHEY_COMPLEX_SMALL, 1.2, cv::Scalar(0, 0, 0), 2);
			if (!coords_3d.empty()) putText(mat_img, coords_3d, cv::Point2f(i.x, i.y - 1), cv::FONT_HERSHEY_COMPLEX_SMALL, 0.8, cv::Scalar(0, 0, 0), 1);
		}
	}
	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);
	}
}
#endif    // OPENCV


void show_console_result(std::vector const result_vec, std::vector const obj_names, int frame_id = -1) {
	if (frame_id >= 0) std::cout << " Frame: " << frame_id << std::endl;
	for (auto& i : result_vec) {
		if (obj_names.size() > i.obj_id) std::cout << obj_names[i.obj_id] << " - ";
		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;
	}
}

	std::string names_file = "E:/WorkSpace-git/QEL_IMUCS_Project/Yolov3/mydataset/myobj.names";
	std::string cfg_file = "E:/WorkSpace-git/QEL_IMUCS_Project/Yolov3/mydataset/yolov3.cfg";
	std::string weights_file = "E:/WorkSpace-git/QEL_IMUCS_Project/Yolov3/mydataset/yolov3_last.weights";
	std::string filename = “1.bmp”;
	Detector detector(cfg_file, weights_file);

	auto obj_names = objects_names_from_file(names_file);
				cv::Mat mat_img = cv::imread(filename);
				std::vector result_vec = detector.detect(mat_img);
				draw_boxes(mat_img, result_vec, obj_names);
				cv::imshow("window name", mat_img);
				show_console_result(result_vec, obj_names);
				cv::waitKey(0);

要包含yolo_cpp_dll.lib,把yolo_cpp_dll.dll加入的环境变量。
注意:如果dll是使用release配置编译的,那么在调用dll的工程中如果使用debug的配置,则会有cfg_file不能识别的bug。

你可能感兴趣的:(图像识别,深度学习)