c/c++环境下YOLO4的配置和试运行

c/c++开发环境下YOLO4的配置方法和试运行

本次试验配置环境如下:

  1. opencv 4.0  (踩坑警告: 推荐优先将其配置为系统变量)
  2. yolo4   下载官网:  git clone https://github.com/pjreddie/darknet.git
  3. CMAKE  cmake-3.12.2-win64-x64
  4. cuda cudnn  10.1   (请自行确保cuda已经正确安装)
  5. GPU  navida2060  (踩坑警告:如果你的显存小于2GB,后续试运行demo会GPU out of memory)
  6. CPU  I7 九代
  7. vs2017 (踩坑警告: 不要使用vs2015, 否则可能在生成时会出现莫名的"找不到darknet.h"警告!)

首先我们去官方网站/git拿到yolo4的源码:

然后打开你的CMAKE.执行生成,所有配置参数如下:

如果你cmake成功,Generate done ,你会在生成目录得到如下项目:

然后启动这个项目,选择release  64位  ALL_BUILD右键 生成

然后你就可以在Release文件夹下看到以下文件,图中框选的文件需要你手动复制到此文件夹:

图中文件的位置在这里:

当你将上述文件复制完毕后,就可以在release文件夹下执行命令来测试环境配置是否成功了,测试命令为:

.\darknet.exe detector demo cfg/coco.data cfg/yolov4.cfg yolov4.weights -c 0

>>>>>>>>>>>>>>>>>>>>>>>>分割线<><><><><><><><><><<><><>>

当然,上面只是过了一遍配置过程,详情在官网或者其他大佬的博客上也是可以见到的,下面我来讲一下如何使用yolo4搭建起小白也能玩的开发环境.

<><><><<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>>

新建一个空vs控制台项目,添加如下引用:

这里你需要注意一下,我们通常是使用opencv来读取图片的,所以这里我也采用opencv.

除了引用上述我们下载下来的darknet的include项外,请不要忘记引入它依赖的第三方库3rdpaty/pthreads下的include

配置库目录,也就是我们上述生成的release目录:

注:我这里是直接将release目录里的文件复制出来放到了DLLS文件夹里方便自己查找使用,希望不要造成误解.

同时连接器的输入 不要忘记了:

这里提及一个踩到的坑,希望大家不要再踩了,直接在预处理器里添加如下内容,否则你会获得一个重定义大礼包:

下面是实现代码部分,哔哔了很多,这里就直接贴代码好了,我想大多数人也是这样期待的:

你需要一个名为Improcess.h的头文件:

#pragma once
#ifndef IMPROCESS_H
#define IMPROCESS_H

#include<opencv2/opencv.hpp>

void imgConvert(const cv::Mat& img, float* dst);

void imgResize(float* src, float* dst, int srcWidth, int srcHeight, int dstWidth, int dstHeight);

void resizeInner(float *src, float* dst, int srcWidth, int srcHeight, int dstWidth, int dstHeight);

#endif // IMPROCESS_H

同时你需要它的cpp实现:

#include "Improcess.h"

void imgConvert(const cv::Mat& img, float* dst) {
	uchar *data = img.data;
	int h = img.rows;
	int w = img.cols;
	int c = img.channels();

	for (int k = 0; k < c; ++k) {
		for (int i = 0; i < h; ++i) {
			for (int j = 0; j < w; ++j) {
				dst[k*w*h + i * w + j] = data[(i*w + j)*c + k] / 255.;
			}
		}
	}
}

void imgResize(float *src, float* dst, int srcWidth, int srcHeight, int dstWidth, int dstHeight) {
	int new_w = srcWidth;
	int new_h = srcHeight;
	if (((float)dstWidth / srcWidth) < ((float)dstHeight / srcHeight)) {
		new_w = dstWidth;
		new_h = (srcHeight * dstWidth) / srcWidth;
	}
	else {
		new_h = dstHeight;
		new_w = (srcWidth * dstHeight) / srcHeight;
	}

	float* ImgReInner;
	size_t sizeInner = new_w * new_h * 3 * sizeof(float);
	ImgReInner = (float*)malloc(sizeInner);
	resizeInner(src, ImgReInner, srcWidth, srcHeight, new_w, new_h);

	for (int i = 0; i < dstWidth*dstHeight * 3; i++) {
		dst[i] = 0.5;
	}

	for (int k = 0; k < 3; ++k) {
		for (int y = 0; y < new_h; ++y) {
			for (int x = 0; x < new_w; ++x) {
				float val = ImgReInner[k*new_w*new_h + y * new_w + x];
				dst[k*dstHeight*dstWidth + ((dstHeight - new_h) / 2 + y)*dstWidth + (dstWidth - new_w) / 2 + x] = val;
			}
		}
	}
	free(ImgReInner);
}

void resizeInner(float *src, float* dst, int srcWidth, int srcHeight, int dstWidth, int dstHeight) {
	float* part;
	size_t sizePa = dstWidth * srcHeight * 3 * sizeof(float);
	part = (float*)malloc(sizePa);

	float w_scale = (float)(srcWidth - 1) / (dstWidth - 1);
	float h_scale = (float)(srcHeight - 1) / (dstHeight - 1);

	for (int k = 0; k < 3; ++k) {
		for (int r = 0; r < srcHeight; ++r) {
			for (int c = 0; c < dstWidth; ++c) {
				float val = 0;
				if (c == dstWidth - 1 || srcWidth == 1) {
					val = src[k*srcWidth*srcHeight + r * srcWidth + srcWidth - 1];
				}
				else {
					float sx = c * w_scale;
					int ix = (int)sx;
					float dx = sx - ix;
					val = (1 - dx) * src[k*srcWidth*srcHeight + r * srcWidth + ix] + dx * src[k*srcWidth*srcHeight + r * srcWidth + ix + 1];
				}
				part[k*srcHeight*dstWidth + r * dstWidth + c] = val;
			}
		}
	}

	for (int k = 0; k < 3; ++k) {
		for (int r = 0; r < dstHeight; ++r) {
			float sy = r * h_scale;
			int iy = (int)sy;
			float dy = sy - iy;
			for (int c = 0; c < dstWidth; ++c) {
				float val = (1 - dy) * part[k*dstWidth*srcHeight + iy * dstWidth + c];
				dst[k*dstWidth*dstHeight + r * dstWidth + c] = val;
			}
			if (r == dstHeight - 1 || srcHeight == 1)
				continue;
			for (int c = 0; c < dstWidth; ++c) {
				float val = dy * part[k*dstWidth*srcHeight + (iy + 1)*dstWidth + c];
				dst[k*dstWidth*dstHeight + r * dstWidth + c] += val;
			}
		}
	}
	free(part);
}

最后,你需要一个执行他们的main.cpp,当然叫什么你随意,有main函数就行:

#include <iostream>
#include <fstream>
#include <opencv2/opencv.hpp>
#include <darknet.h>
#include "Improcess.h"

// 在实用时应注意一下几个异常
//首先:找不到pthread.h 请添加include引用.在3rdparty文件夹下的include
//第二:重定义timespec 在预处理中添加 HAVE_STRUCT_TIMESPEC
//第三 : 加载神经网络时 cuda out of memory

using namespace std;
using namespace cv;

//颜色生成器
float colors[6][3] = { {1,0,1}, {0,0,1},{0,1,1},{0,1,0},{1,1,0},{1,0,0} };
float get_color(int c, int x, int max) {
	float ratio = ((float)x / max) * 5;
	int i = floor(ratio);
	int j = ceil(ratio);
	ratio -= i;
	float r = (1 - ratio) * colors[i][c] + ratio * colors[j][c];
	return r;
}

//opencv_mat 转 yolo_image (可能引起内存泄漏,因此暂时弃用)
void covertMatToImage(const cv::Mat& mat, image &img)
{
	
	uchar *data = mat.data;
	int h = mat.rows;
	int w = mat.cols;
	int c = mat.channels(); //默认使用3通道
	float* content;
	size_t contentSize = h * w * c * sizeof(float);
	content = (float*)malloc(contentSize);

	for (int k = 0; k < c; ++k) {
		for (int i = 0; i < h; ++i) {
			for (int j = 0; j < w; ++j) {
				content[k*w*h + i * w + j] = data[(i*w + j)*c + k] / 255.;
			}
		}
	}

	img.c = c;
	img.h = h;
	img.w = w;
	img.data = content;
}


int main()
{
	std::cout << "hello , this is the test !" << std::endl;


	//加载神经网络,不要用load_network,显卡内存会炸
	float thresh = 0.5;
	float nms = 0.35;
	int classes = 80;  //coco的分类模型就是80个,我们这里用它来做示例
	std::string cfgfile = "F:/YOLO4/DLLS/cfg/yolov4.cfg";
	std::string weights = "F:/YOLO4/DLLS/yolov4.weights";
	int clear = 1;
	int batch = 1;
	network *net = load_network_custom((char*)cfgfile.c_str(), (char*)weights.c_str(), clear, batch);

	//读取coco80个种类分类标签
	vector<string> classNamesVec;
	ifstream classNamesFile("F:/YOLO4/DLLS/cfg/coco.names");//标签文件coco有80类
	if (classNamesFile.is_open()) {
		string className = "";
		while (getline(classNamesFile, className))
			classNamesVec.push_back(className);
	}

	//读取视频,请自行修改相应路径 或者改为0 来获取摄像机
	VideoCapture capture("F:/YOLO4/DLLS/lukou.mp4");
	//VideoCapture capture(0);
	Mat frame;
	Mat rgbImg;

	//循环视频,进行检测
	bool stop = false;
	while (!stop) {
		if (!capture.read(frame)) {
			printf("fail to read.\n");
			return 0;
		}

		//空图像则跳过本次
		if (frame.rows <= 0 || frame.cols <= 0) {
			cv::waitKey(1000);
			continue;
		}

		//我新增的一步,调整图像格式
		//cv::resize(frame, frame, cv::Size(600, 600));

		//opencv图像色彩转换
		cvtColor(frame, rgbImg, cv::COLOR_BGR2RGB);

		//将Mat图像转为yolo图像
		float* srcImg;
		size_t srcSize = rgbImg.rows*rgbImg.cols * 3 * sizeof(float);
		srcImg = (float*)malloc(srcSize);
		imgConvert(rgbImg, srcImg);

		float* resizeImg;
		size_t resizeSize = net->w * net->h * 3 * sizeof(float);
		resizeImg = (float*)malloc(resizeSize);
		imgResize(srcImg, resizeImg, frame.cols, frame.rows, net->w, net->h);//缩放图像

		//网络推理
		network_predict(*net, resizeImg);
		int nboxes = 0;
		detection *dets = get_network_boxes(net, frame.cols, frame.rows, thresh, 0.5, 0, 1, &nboxes,1);

		//进行nms极大值抑制
		if (nms) {
			do_nms_sort(dets, nboxes, classes, nms);
		}

		vector<cv::Rect>boxes;
		boxes.clear();
		vector<int>classNames;

		for (int i = 0; i < nboxes; i++) {
			bool flag = 0;
			int className;
			for (int j = 0; j < classes; j++) {
				if (dets[i].prob[j] > thresh) {
					if (!flag) {
						flag = 1;
						className = j;
					}
				}
			}
			if (flag) {

				//如果使用的是相对位置,启用这段代码
				int left = (dets[i].bbox.x - dets[i].bbox.w / 2.)*frame.cols;
				int right = (dets[i].bbox.x + dets[i].bbox.w / 2.)*frame.cols;
				int top = (dets[i].bbox.y - dets[i].bbox.h / 2.)*frame.rows;
				int bot = (dets[i].bbox.y + dets[i].bbox.h / 2.)*frame.rows;

				if (left < 0)
					left = 0;
				if (right > frame.cols - 1)
					right = frame.cols - 1;
				if (top < 0)
					top = 0;
				if (bot > frame.rows - 1)
					bot = frame.rows - 1;
				Rect box(left, top, fabs(left - right), fabs(top - bot));

				boxes.push_back(box);
				classNames.push_back(className);
			}
		}
		free_detections(dets, nboxes);

		for (int i = 0; i < boxes.size(); i++) {
			int offset = classNames[i] * 123457 % 80;
			float red = 255 * get_color(2, offset, 80);
			float green = 255 * get_color(1, offset, 80);
			float blue = 255 * get_color(0, offset, 80);

			rectangle(frame, boxes[i], Scalar(blue, green, red), 2);

			String label = String(classNamesVec[classNames[i]]);
			int baseLine = 0;
			Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
			putText(frame, label, Point(boxes[i].x, boxes[i].y + labelSize.height),
				FONT_HERSHEY_SIMPLEX, 1, Scalar(red, blue, green), 2);
		}

		//展现数据
		imshow("video", frame);

		int c = waitKey(10);
		if ((char)c == 27)
			break;
		else if (c >= 0)
			waitKey(0);

		free(srcImg);
		free(resizeImg);
	}
	free_network(*net);
	capture.release();
	
	std::cout << "test over !" << std::endl;

	return 1;
}

这里提一个关键坑,如果没看到这里就复制代码匆匆溜走,你们的显卡会烤肉的!

注意下面这段代码:

int batch = 1;
network *net = load_network_custom((char*)cfgfile.c_str(), (char*)weights.c_str(), clear, batch);

在darknet中设置有多个load_network方法,请不要直接使用load_network那个方法加载神经网络,内存会溢出.

使用上面这个方法,设定batch为1,在本机只会占用2.3GB现存.

顺带一提,采用yolo3的配置文件会占用1.7GB显存

运行吧,如果你没出bug,祝贺你:

 

 

 

  • 3
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值