提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
基于opencv4的yolo推理,支持v3、v4
一.编译opencv4(cuda)
详见我的另一篇博客:docker制作深度学习环境中的opencv4的安装编译。
二.完整代码
inference cpp
#include <fstream>
#include <sstream>
#include <iostream>
#include <cassert>
#include <vector>
#include <stdio.h>
#include <stdlib.h>
#include <exception>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <opencv2/core.hpp>
#include <opencv2/dnn.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
using namespace cv;
using namespace dnn;
using namespace std;
// Initialize the parameters
float confThreshold; // Confidence threshold
float nmsThreshold; // Non-maximum suppression threshold
int inpWidth; // Width of network's input image
int inpHeight; // Height of network's input image
vector<float>config;
vector<string> classes;
Net net;
void drawPred(int classId, float conf, int left, int top, int right, int bottom, Mat& frame);
void postprocess(Mat& frame, const vector<Mat>& outs, string txt_path);
Mat detectImg(Mat frame, string img_path);
vector<String> getOutputsNames(const Net& net);
inline bool exists_test (const std::string& name);
void SplitString(const string& s, vector<string>& v, const string& c);
inline bool exists_test(const std::string& name) {
ifstream f(name.c_str());
return f.good();
}
void SplitString(const string& s, vector<string>& v, const string& c)
{
string::size_type pos1, pos2;
pos2 = s.find(c);
pos1 = 0;
while(string::npos != pos2)
{
v.push_back(s.substr(pos1, pos2-pos1));
pos1 = pos2 + c.size();
pos2 = s.find(c, pos1);
}
if(pos1 != s.length())
v.push_back(s.substr(pos1));
}
void GetFileNames(string path,vector<string>& filenames)
{
DIR *pDir;
struct dirent* ptr;
if(!(pDir = opendir(path.c_str()))){
cout<<"Folder doesn't Exist!"<<endl;
return;
}
while((ptr = readdir(pDir))!=0) {
if (strcmp(ptr->d_name, ".") != 0 && strcmp(ptr->d_name, "..") != 0){
filenames.push_back(path + "/" + ptr->d_name);
}
}
closedir(pDir);
}
// Draw the predicted bounding box
void drawPred(int classId, float conf, int left, int top, int right, int bottom, Mat& frame)
{
//Draw a rectangle displaying the bounding box
rectangle(frame, Point(left, top), Point(right, bottom), Scalar(255, 255, 255), 1);
//Get the label for the class name and its confidence
string conf_label = format("%.2f", conf);
string label="";
if (!classes.empty())
{
label = classes[classId] + ":" + conf_label;
}
//Display the label at the top of the bounding box
int baseLine;
Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
top = max(top, labelSize.height);
rectangle(frame, Point(left, top - labelSize.height), Point(left + labelSize.width, top + baseLine), Scalar(255, 255, 255), FILLED);
putText(frame, label, Point(left, top), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0,0,0),1,LINE_AA);
}
// Remove the bounding boxes with low confidence using non-maxima suppression
void postprocess(Mat& frame, const vector<Mat>& outs, string txt_path)
{
vector<int> classIds;
vector<float> confidences;
vector<Rect> boxes;
for (size_t i = 0; i < outs.size(); ++i)
{
// Scan through all the bounding boxes output from the network and keep only the
// ones with high confidence scores. Assign the box's class label as the class
// with the highest score for the box.
float* data = (float*)outs[i].data;
for (int j = 0; j < outs[i].rows; ++j, data += outs[i].cols)
{
Mat scores = outs[i].row(j).colRange(5, outs[i].cols);
Point classIdPoint;
double confidence;
// Get the value and location of the maximum score
minMaxLoc(scores, 0, &confidence, 0, &classIdPoint);
if (confidence > confThreshold)
{
int centerX = (int)(data[0] * frame.cols);
int centerY = (int)(data[1] * frame.rows);
int width = (int)(data[2] * frame.cols);
int height = (int)(data[3] * frame.rows);
int left = centerX - width / 2;
int top = centerY - height / 2;
classIds.push_back(classIdPoint.x);
confidences.push_back((float)confidence);
boxes.push_back(Rect(left, top, width, height));
}
}
}
// Perform non maximum suppression to eliminate redundant overlapping boxes with
// lower confidences
cout<<"postprocess before nms!"<<endl;
vector<int> indices;
NMSBoxes(boxes, confidences, confThreshold, nmsThreshold, indices);
FILE *fp = fopen(txt_path.c_str(),"w");
if(NULL==fp)
{
for (size_t i = 0; i < indices.size(); ++i)
{
int idx = indices[i];
Rect box = boxes[idx];
drawPred(classIds[idx], confidences[idx], box.x, box.y,
box.x + box.width, box.y + box.height, frame);
fprintf(fp,"%d %.2f %d %d %d %d\n",classIds[idx],confidences[idx],box.x, box.y,
box.x + box.width, box.y + box.height);
}
fclose(fp);
}
}
Mat detectImg(Mat frame, string img_path){
Mat blob;
if (frame.empty()) {
cout << "No input image" << endl;
return blob;
}
auto start1 = std::chrono::system_clock::now();
// Create a 4D blob from a frame.
blobFromImage(frame, blob, 1/255.0, Size(inpWidth, inpHeight), Scalar(0,0,0), true, false);
//Sets the input to the network
net.setInput(blob);
// Runs the forward pass to get output of the output layers
vector<Mat> outs;
net.forward(outs, getOutputsNames(net));
cout<<"forward done!"<<endl;
// Remove the bounding boxes with low confidence
postprocess(frame, outs, img_path);
cout<<"postprocess done!"<<endl;
auto end1 = std::chrono::system_clock::now();
std::cout << "the inference time is" <<std::chrono::duration_cast<std::chrono::milliseconds>(end1 - start1).count() << "ms" << std::endl;
return frame;
}
// Get the names of the output layers
vector<String> getOutputsNames(const Net& net)
{
static vector<String> names;
if (names.empty())
{
//Get the indices of the output layers, i.e. the layers with unconnected outputs
vector<int> outLayers = net.getUnconnectedOutLayers();
//get the names of all the layers in the network
vector<String> layersNames = net.getLayerNames();
// Get the names of the output layers in names
names.resize(outLayers.size());
for (size_t i = 0; i < outLayers.size(); ++i){
names[i] = layersNames[outLayers[i] - 1];
}
}
return names;
}
int main(int argc, char** argv)
{
// Load names of classes
if (argc == 5 && std::string(argv[1]) == "-i") {
string imgs_path = std::string(argv[2]);
string config_path = std::string(argv[3]);
string config_file = config_path + "config.ini";
ifstream infile;
infile.open(config_file.data());
assert(infile.is_open());
try{
string s;
while(getline(infile,s))
{
config.push_back(std::stof(s));
}
infile.close();
confThreshold = config[0];
nmsThreshold = config[1];
inpWidth = (int)config[2];
inpHeight = (int)config[3];
}
catch(exception e)
{
cout<<"the config file is not right, this program has stopped"<<e.what()<<endl;
return 0;
}
cout<<config_file<<" confThreshold:"<<to_string(confThreshold)<<" nmsThreshold:"<<to_string(nmsThreshold)<<" inpWidth:"<<to_string(inpWidth)<<" inpHeight:"<<to_string(inpHeight)<<endl;
string model_path = std::string(argv[4]);
string classesFile = model_path + "/coco.names";
String modelConfiguration = model_path + "/yolo.cfg";
String modelWeights = model_path +"/yolo.weights";
/*if(!exists_test(classesFile)||!exists_test(modelConfiguration)||!exists_test(modelWeights))
{
cout<<"the model file doesn't exists, this program has stopped"<<endl;
return 0;
}*/
cout<<classesFile<<" "<<modelConfiguration<<" "<<modelWeights<<endl;
ifstream ifs(classesFile.c_str());
string line;
while (getline(ifs, line)) classes.push_back(line);
// Load the network
net = readNet(modelConfiguration,modelWeights);
net.setPreferableBackend(DNN_BACKEND_OPENCV);
net.setPreferableTarget(DNN_TARGET_CPU);
/*net.setPreferableBackend(DNN_BACKEND_CUDA);
net.setPreferableTarget(DNN_TARGET_CUDA_FP16);*/
vector<string>imgs_file;
GetFileNames(imgs_path,imgs_file);
vector<string> v1;
vector<string> v2;
for(int i=0;i<imgs_file.size();i++)
{
Mat input = imread(imgs_file[i]);
SplitString(imgs_file[i], v1,".");
SplitString(v1[v1.size()-2], v2,"/");
string txt_str = "./outputs/" + v2[v2.size()-1] + "_detected.txt";
string img_str = "./outputs/" + v2[v2.size()-1] + "_detected.jpg";
Mat detect_img = detectImg(input, txt_str);
imwrite(img_str, detect_img);
v1.clear();
v2.clear();
}
}
else if (argc == 5 && std::string(argv[1]) == "-v") {
string video_path = std::string(argv[2]);
string config_path = std::string(argv[3]);
string config_file = config_path + "config.ini";
ifstream infile;
infile.open(config_file.data());
assert(infile.is_open());
try{
string s;
while(getline(infile,s))
{
config.push_back(std::stof(s));
}
infile.close();
confThreshold = config[0];
nmsThreshold = config[1];
inpWidth = (int)config[2];
inpHeight = (int)config[3];
}
catch(exception e)
{
cout<<"the config file is not right, this program has stopped"<<e.what()<<endl;
return 0;
}
cout<<"读取config文件成功,文件位置:"<<config_file<<" confThreshold:"<<to_string(confThreshold)<<" nmsThreshold:"<<to_string(nmsThreshold)<<" inpWidth:"<<to_string(inpWidth)<<" inpHeight"<<to_string(inpHeight)<<endl;
string model_path = std::string(argv[4]);
string classesFile = model_path + "/coco.names";
String modelConfiguration = model_path + "/yolo.cfg";
String modelWeights = model_path +"/yolo.weights";
cout<<"类别文件位置:"<<classesFile<<" 配置文件位置:"<<modelConfiguration<<" 模型文件位置:"<<modelWeights<<endl;
ifstream ifs(classesFile.c_str());
string line;
while (getline(ifs, line)) classes.push_back(line);
// Load the network
net = readNetFromDarknet(modelConfiguration, modelWeights);
/*net.setPreferableBackend(DNN_BACKEND_OPENCV);
net.setPreferableTarget(DNN_TARGET_CPU);*/
net.setPreferableBackend(DNN_BACKEND_CUDA);
net.setPreferableTarget(DNN_TARGET_CUDA_FP16);
VideoCapture capture;
Mat input;
capture.open(video_path);
if(!capture.isOpened())
{
printf("can not open ...\n");
return -1;
}
int num = 1;
while (capture.read(input))
{
string txt_str = "./outputs/"+ to_string(num) + "_detected.txt";
Mat detect_img = detectImg(input, txt_str);
if(!detect_img.empty()){
num++;
imwrite("./outputs/"+ to_string(num) + "_detected.jpg", detect_img);
}
}
capture.release();
}
else {
std::cerr << "arguments not right!" << std::endl;
std::cerr << "./yolov -i // input the images path and run inference" << std::endl;
std::cerr << "./yolov -v // input the video path and run inference" << std::endl;
return -1;
}
return 0;
}
CMakeList.txt
cmake_minimum_required(VERSION 3.5)
project(opencv_yolo)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
add_definitions(-std=c++11)
option(CUDA_USE_STATIC_CUDA_RUNTIME OFF)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_BUILD_TYPE Relaese)
find_package(CUDA REQUIRED)
message(STATUS "CUDA版本: ${CUDA_VERSION}")
message(STATUS " 头文件目录:${CUDA_INCLUDE_DIRS}")
message(STATUS " 库文件列表:${CUDA_LIBRARIES}")
set(CUDA_NVCC_PLAGS ${CUDA_NVCC_PLAGS};-std=c++11;-g;-G;-gencode;arch=compute_75;code=sm_75)
set(OpenCV_DIR /opt/opencv440/lib/cmake/opencv4/)
find_package(OpenCV REQUIRED)
include_directories(
../include
${OpenCV_INCLUDE_DIRS})
message(${OpenCV_DIR})
message(STATUS ${OpenCV_LIBS})
add_executable(yolo main.cpp )
target_link_libraries(yolo ${OpenCV_LIBS})