1、配置:在win10系统下,仅使用CPU(也可以使用GPU,根据电脑配置决定),通过VS2015和OpenCV4.2.0编译实现。不过在Linux系统下编译,同时使用GPU加速最好。
2、代码中的配置文件,权重文件,以及数据下载:
配置文件+数据:https://github.com/PanJinquan/opencv-learning-tutorials
配置文件+权重文件:https://pjreddie.com/darknet/yolo
3、代码实现,该代码是根据网上的代码做简单的整理
yolov3.h:
#pragma once
#include
#include
#include
#include
#include
#include
using namespace cv;
using namespace std;
class YOLOV3
{
public:
YOLOV3(float confThreshold = 0.5, float nmsThreshold = 0.4, int inpWidth = 416, int inpHeight = 416);
void detect_image(std::string image_path, std::string modelWeights, std::string modelConfiguration, std::string classesFile, std::string& outputFile);
void detect_video(std::string video_path, std::string modelWeights, std::string modelConfiguration, std::string classesFile, std::string& outputFile);
// Remove the bounding boxes with low confidence using non-maxima suppression
void postprocess(cv::Mat& frame, const std::vector& outs);
// Get the names of the output layers
std::vector getOutputsNames(const cv::dnn::Net& net);
// Draw the predicted bounding box
void drawPred(int classId, float conf, int left, int top, int right, int bottom, cv::Mat& frame);
private:
// Initialize the parameters
float mfConfThreshold; // Confidence threshold
float mfNmsThreshold; // Non-maximum suppression threshold
int mInpWidth; // Width of network's input image
int mInpHeight; // Height of network's input image
std::vector vClassIds; // The index corresponding to the category name
std::vector vClasses; // Classification name of a category
std::vector vConfidences;// Maximum confidence greater than confidence threshold
std::vector vBoxes; // Various category boxes
std::vector vIndices; // Candidate box index after non-maximum suppression
};
yolov3.cpp:
#include "yolov3.h"
YOLOV3::YOLOV3(float confThreshold, float nmsThreshold, int inpWidth, int inpHeight)
{
mfConfThreshold = confThreshold;
mfNmsThreshold = nmsThreshold;
mInpWidth = inpWidth;
mInpHeight = inpHeight;
}
void YOLOV3::detect_image(string image_path, string modelWeights, string modelConfiguration, string classesFile, std::string& outputFile)
{
// Load names of vClasses
ifstream ifs(classesFile.c_str());
std::string line;
while (getline(ifs, line)) vClasses.push_back(line);
// Load the network
dnn::Net net = dnn::readNetFromDarknet(modelConfiguration, modelWeights);
net.setPreferableBackend(dnn::DNN_BACKEND_OPENCV);
net.setPreferableTarget(dnn::DNN_TARGET_CPU);
// Create a window
static const string kWinName = "Deep learning object detection in OpenCV";
namedWindow(kWinName, WINDOW_NORMAL);
// Create a 4D blob from a frame.
cv::Mat blob;
cv::Mat frame = cv::imread(image_path);
// Scale transformation, scaling, subtracting mean, channel transformation
dnn::blobFromImage(frame, blob, 1 / 255.0, cv::Size(mInpWidth, mInpHeight), 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 outs;
net.forward(outs, getOutputsNames(net));
// Remove the bounding boxes with low confidence
postprocess(frame, outs);
// Put efficiency information. The function getPerfProfile returns the overall time for inference(t) and the timings for each of the layers(in layersTimes)
std::vector layersTimes;
double freq = getTickFrequency() / 1000;
double t = net.getPerfProfile(layersTimes) / freq;
std::string label = format("Inference time for a frame : %.2f ms", t);
putText(frame, label, Point(0, 15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 255));
// Write the frame with the detection boxes
imshow(kWinName, frame);
cv::imwrite(outputFile, frame);
}
void YOLOV3::detect_video(string video_path, string modelWeights, string modelConfiguration, string classesFile, std::string& outputFile)
{
// Load names of vClasses
ifstream ifs(classesFile.c_str());
string line;
while (getline(ifs, line)) vClasses.push_back(line);
// Load the network
dnn::Net net = dnn::readNetFromDarknet(modelConfiguration, modelWeights);
net.setPreferableBackend(dnn::DNN_BACKEND_OPENCV);
net.setPreferableTarget(dnn::DNN_TARGET_CPU);
// Open a video file or an image file or a camera stream.
VideoCapture cap;
VideoWriter video;
Mat frame, blob;
try {
// Open the video file
ifstream ifile(video_path);
if (!ifile) throw("error");
cap.open(video_path);
}
catch (...) {
cout << "Could not open the input image/video stream" << endl;
return;
}
// Get the video writer initialized to save the output video
video.open(outputFile, VideoWriter::fourcc('M', 'J', 'P', 'G'), 28, Size(cap.get(CAP_PROP_FRAME_WIDTH), cap.get(CAP_PROP_FRAME_HEIGHT)));
// Create a window
static const string kWinName = "Deep learning object detection in OpenCV";
namedWindow(kWinName, WINDOW_NORMAL);
// Process frames.
while (waitKey(1) < 0)
{
// get frame from the video
cap >> frame;
// Stop the program if reached end of video
if (frame.empty()) {
cout << "Done processing !!!" << endl;
cout << "Output file is stored as " << outputFile << endl;
waitKey(3000);
break;
}
// Create a 4D blob from a frame.
dnn::blobFromImage(frame, blob, 1 / 255.0, cv::Size(mInpWidth, mInpHeight), 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 outs;
net.forward(outs, getOutputsNames(net));
// Remove the bounding boxes with low confidence
postprocess(frame, outs);
// Put efficiency information. The function getPerfProfile returns the overall time for inference(t) and the timings for each of the layers(in layersTimes)
vector layersTimes;
double freq = getTickFrequency() / 1000;
double t = net.getPerfProfile(layersTimes) / freq;
string label = format("Inference time for a frame : %.2f ms", t);
cv::putText(frame, label, Point(0, 15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 255));
// Write the frame with the detection boxes
Mat detectedFrame;
frame.convertTo(detectedFrame, CV_8U);
video.write(detectedFrame);
imshow(kWinName, frame);
}
cap.release();
video.release();
}
// 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.
void YOLOV3::postprocess(cv::Mat& frame, const std::vector& outs)
{
for (size_t i = 0; i < outs.size(); ++i)
{
float* data = (float*)outs[i].data;
for (int j = 0; j < outs[i].rows; ++j, data += outs[i].cols)
{
cv::Point2i classIdPoint;
double confidence;
cv::Mat scores = outs[i].row(j).colRange(5, outs[i].cols);
// Get the maximum score value in a matrix or vector and locate it
minMaxLoc(scores, 0, &confidence, 0, &classIdPoint);
if (confidence > mfConfThreshold)
{
// Get the parameters of the rectangular box. But what is the fifth data of "data"?
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);
// Should we consider the case if the rectangular frame crosses the image border? who??
int left = centerX - width / 2;
int top = centerY - height / 2;
if (left < 0) left = 0;
if (left < 0) top = 0;
vClassIds.push_back(classIdPoint.x);
vConfidences.push_back((float)confidence);
vBoxes.push_back(Rect(left, top, width, height));
}
}
}
// Perform non maximum suppression to eliminate redundant overlapping boxes with lower confidences
dnn::NMSBoxes(vBoxes, vConfidences, mfConfThreshold, mfNmsThreshold, vIndices);
for (size_t i = 0; i < vIndices.size(); ++i)
{
int idx = vIndices[i];
Rect box = vBoxes[idx];
int right = box.x + box.width;
int bottom = box.y + box.height;
if (right > frame.cols) right = frame.cols;
if (bottom > frame.rows) bottom = frame.rows;
//Should we consider the case if the rectangular frame crosses the image border? who??
drawPred(vClassIds[idx], vConfidences[idx], box.x, box.y, right, bottom, frame);
}
}
// Draw the predicted bounding box
void YOLOV3::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, 178, 50), 3);
//Get the label for the class name and its confidence
string label = format("%.2f", conf);
if (!vClasses.empty())
{
CV_Assert(classId < (int)vClasses.size());
label = vClasses[classId] + ":" + label;
}
//Display the label at the top of the bounding box
Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, NULL);
top = max(top, labelSize.height);
cv::rectangle(frame, Point(left, top - round(1.5*labelSize.height)), Point(left + round(1.5*labelSize.width), top + labelSize.height), Scalar(255, 255, 255), FILLED);
cv::putText(frame, label, Point(left, top), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(0, 0, 0), 1);
}
// Get the names of the output layers
std::vector YOLOV3::getOutputsNames(const cv::dnn::Net& net)
{
static vector names;
if (names.empty())
{
//Get the indices of the output layers, i.e. the layers with unconnected outputs
vector outLayers = net.getUnconnectedOutLayers();
//get the names of all the layers in the network
vector 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;
}
main.cpp:
#include
#include "yolov3.h"
int main(int argc, char** argv)
{
// Give the configuration and weight files for the model
string modelConfiguration = "../data//models//yolov3//yolov3.cfg";
string modelWeights = "../data//models//yolov3//yolov3.weights";
string classesFile = "../data//models//yolov3//coco.names";
// Enter an image or video
string image_path = "../data//images/dog.jpg";
string video_path = "../data//images//run.mp4";
// Output path settings
std::string image_outputFile = "../result//yolov3.jpg";
std::string video_outputFile = "../result//yolov3_out.avi";
//Confidence threshold;Non-maximum suppression threshold;Width of network's input image;Height of network's input image
YOLOV3 yolov3(0.5, 0.4, 416, 416);
//yolov3.detect_image(image_path, modelWeights, modelConfiguration, classesFile, image_outputFile);
yolov3.detect_video(video_path, modelWeights, modelConfiguration, classesFile, video_outputFile);
cv::waitKey(0);
return 0;
}
只需修改路径,即可实现视频流的检测。单张图像检测需要注释掉视频检测部分即可。
贴一张检测图片:
至于如何训练自己的数据集,在前面的博客有提到:https://blog.csdn.net/qq_38589460/article/details/85337032