c/c++开发环境下YOLO4的配置方法和试运行
本次试验配置环境如下:
- opencv 4.0 (踩坑警告: 推荐优先将其配置为系统变量)
- yolo4 下载官网: git clone https://github.com/pjreddie/darknet.git
- CMAKE cmake-3.12.2-win64-x64
- cuda cudnn 10.1 (请自行确保cuda已经正确安装)
- GPU navida2060 (踩坑警告:如果你的显存小于2GB,后续试运行demo会GPU out of memory)
- CPU I7 九代
- 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,祝贺你: