Qt C++使用YOLOV2模型实时检测摄像头视频中的车辆的源代码(CPU检测)
一、主函数
#include <QApplication>
#include <opencv2/opencv.hpp>
#include "dnnvehicledetectclass.h"
using namespace cv;
using namespace std;
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
DnnVehicleDetectClass dnn;
VideoCapture cap("rtsp://admin:admin123@192.168.1.108:554/cam/realmonitor?channel=1&subtype=1");
if (!cap.isOpened()) {
cerr << "Failed to open video stream!" << endl;
return -1;
}
Mat frame;
while (true) {
cap.read(frame);
if (frame.empty()) {
cerr << "Failed to read frame from video stream!" << endl;
break;
}
dnn.dnnVehicleDetect(frame);
imshow("Frame", frame);
// Check for ESC key press
if (waitKey(30) == 27) { // 使用 30 毫秒延迟
break;
}
}
// Release resources
cap.release();
destroyAllWindows();
return 0;
}
#include "main.moc"
二、 DnnVehicleDetectClass的头文件
#ifndef DNNVEHICLEDETECTCLASS_H
#define DNNVEHICLEDETECTCLASS_H
#include <QDir>
#include <QString>
#include <QDebug>
#include <QTimer>
#include <direct.h>
#include <QThread>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
class DnnVehicleDetectClass: public QThread {
Q_OBJECT
public:
DnnVehicleDetectClass();
static vector<String> getOutputsNames(const cv::dnn::Net& net);
void drawPred(int classId, float conf, int left, int top, int right, int bottom, Mat& frame, const vector<string>& classes);
void postprocess(Mat& frame, const vector<Mat>& outs, const vector<string>& classes);
Mat dnnVehicleDetect(Mat img);
};
#endif // DNNVEHICLEDETECTCLASS_H
三、 DnnVehicleDetectClass的源文件
#include "dnnvehicledetectclass.h"
DnnVehicleDetectClass::DnnVehicleDetectClass()
{
}
// 从网络获取输出层名称的函数
vector<String> DnnVehicleDetectClass::getOutputsNames(const cv::dnn::Net& net) {
static vector<String> names;
if (names.empty()) {
//获取输出层的索引,即具有未连接输出的层
vector<int> outLayers = net.getUnconnectedOutLayers();
vector<String> layersNames = net.getLayerNames();
names.resize(outLayers.size());
for (size_t i = 0; i < outLayers.size(); ++i)
names[i] = layersNames[outLayers[i] - 1];
}
return names;
}
// 在图像上绘制边界框的函数
void DnnVehicleDetectClass::drawPred(int classId, float conf, int left, int top, int right, int bottom, Mat& frame, const vector<string>& classes) {
rectangle(frame, Point(left, top), Point(right, bottom), Scalar(255, 178, 50), 3);
// 获取类名及其可信度的标签
string label = format("%.2f", conf);
if (!classes.empty()) {
CV_Assert(classId < (int)classes.size());
label = classes[classId] + ": " + label;
}
// 在边界框的顶部显示标签
int baseLine;
Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
top = max(top, labelSize.height);
rectangle(frame, Point(left, top - round(1.5*labelSize.height)), Point(left + round(1.5*labelSize.width), top + baseLine), Scalar(255, 255, 255), FILLED);
putText(frame, label, Point(left, top), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(0,0,0), 1);
}
// 使用非最大值抑制删除低置信度边界框的功能
void DnnVehicleDetectClass::postprocess(Mat& frame, const vector<Mat>& outs, const vector<string>& classes) {
vector<int> classIds;
vector<float> confidences;
vector<Rect> boxes;
for (size_t i = 0; i < outs.size(); ++i) {
// 网络生成形状为NxC的输出blob,其中N是检测到的对象的数量
// C是类别数+4,其中前4个数字是[center_x,center_y,width,height]
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;
minMaxLoc(scores, 0, &confidence, 0, &classIdPoint);
if (confidence > 0.5) { // Confidence threshold
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));
}
}
}
//执行非最大抑制,以消除置信度较低的冗余重叠框。
vector<int> indices;
dnn::NMSBoxes(boxes, confidences, 0.5, 0.4, indices);
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, classes);
}
}
Mat DnnVehicleDetectClass::dnnVehicleDetect(Mat img) {
try {
//加载预训练模型
cv::dnn::Net net = cv::dnn::readNetFromDarknet("./yolo.cfg", "./yolov2.weights");
if (net.empty()) {
cerr << "Failed to load the network!" << endl; //网络加载失败
return cv::Mat();
}
cout << "Successfully loaded the network!" << endl; //网络加载成功
if (img.empty()) {
cerr << "Failed to load image!" << endl; //无法加载图像
return cv::Mat();
}
// 从图像创建blob并将其设置为网络的输入
/* 将图像大小调整为 416x416。
将像素值除以 255.0 进行归一化。
使用 Scalar() 表示没有均值减法。
true 表示交换 BGR 和 RGB 通道顺序。
false 表示不裁剪图像。
*/
Mat inputBlob = dnn::blobFromImage(img, 1 / 255.0, Size(416, 416), Scalar(), true, false);
net.setInput(inputBlob);
// 在网络中执行前向传递
vector<Mat> outs;
net.forward(outs, getOutputsNames(net));
// 定义类标签列表(这应该与您的YOLO配置文件相匹配)
vector<string> classes;
ifstream ifs("./coco.names"); //或等效名称的路径
string line;
while (getline(ifs, line))
classes.push_back(line);
// 对输出进行后处理以绘制边界框
postprocess(img, outs, classes); //使用非最大值抑制删除低置信度边界框的功能
return img;
} catch (const cv::Exception& e) {
cerr << "Error: " << e.what() << endl;
return cv::Mat();
}
}
四、需要解决的问题:
1、如何在GPU执行?
2、如何跟踪识别的车辆,并赋予唯一ID?并根据唯一ID计数?
3、如何做到实时显示过车数并且视频不卡顿?
请各位同仁完善代码!
五、需求环境:
Qt Creater 4.11.1
Qt 5.14.1
opencv 4.1.2
c++11
秋风写于淄博,技术交流与业务合作:Q375172665