摘要
darknet框架相pyrtorch轻量化很多,将darknet和qt进行整合,大多项目都是基于单独的框架的,对于整合的工作相对来说低很多
最终效果
项目创建
创建完成的项目结构
编辑ui,用来展示视频
创建一个槽函数
show_video()
创建完成记得保存
对修改完的ui文件进行编译
打开yoloshow.h
头文件
实现刚才的show_video()
槽函数
将darknet提供接口进行封装, 封装到函数体内部
进入到cpp文件中进行实现刚.h文件中的函数
到此代码部分配置完成,下面配置环境部分
yoloshow.cpp
"yoloshow.h"
#include
<iostream>
#include <iomanip>
#include <string>
#include <vector>
#include <queue>
#include <fstream>
#include <thread>
#include <future>
#include <atomic>
#include <mutex> // std::mutex, std::unique_lock
#include <cmath>
#include
#define OPENCV // if you needn't to show img or video, you can comment the define and delete the useless code
// It makes sense only for video-Camera (not for video-File)
// To use - uncomment the following line. Optical-flow is supported only by OpenCV 3.x - 4.x
//#define TRACK_OPTFLOW
//#define GPU
// To use 3D-stereo camera ZED - uncomment the following line. ZED_SDK should be installed.
//#define ZED_STEREO
"yolo_v2_class.hpp" // imported functions from DLL
#include
#ifdef OPENCV
::vector<bbox_t> get_3d_coordinates(std::vector<bbox_t> bbox_vect, cv::Mat xyzrgba) {
stdreturn bbox_vect;
}
<opencv2/opencv.hpp> // C++
#include <opencv2/core/version.hpp>
#include // OpenCV 3.x and 4.x
#ifndef CV_VERSION_EPOCH <opencv2/videoio/videoio.hpp>
#include #define OPENCV_VERSION CVAUX_STR(CV_VERSION_MAJOR)"" CVAUX_STR(CV_VERSION_MINOR)"" CVAUX_STR(CV_VERSION_REVISION)
#ifndef USE_CMAKE_LIBScomment(lib, "opencv_world" OPENCV_VERSION ".lib")
#pragma
#ifdef TRACK_OPTFLOW/*
#pragma comment(lib, "opencv_cudaoptflow" OPENCV_VERSION ".lib")
#pragma comment(lib, "opencv_cudaimgproc" OPENCV_VERSION ".lib")
#pragma comment(lib, "opencv_core" OPENCV_VERSION ".lib")
#pragma comment(lib, "opencv_imgproc" OPENCV_VERSION ".lib")
#pragma comment(lib, "opencv_highgui" OPENCV_VERSION ".lib")
*/
#endif // TRACK_OPTFLOW
#endif // USE_CMAKE_LIBS
#else // OpenCV 2.x
#define OPENCV_VERSION CVAUX_STR(CV_VERSION_EPOCH)"" CVAUX_STR(CV_VERSION_MAJOR)"" CVAUX_STR(CV_VERSION_MINOR)
#ifndef USE_CMAKE_LIBScomment(lib, "opencv_core" OPENCV_VERSION ".lib")
#pragma comment(lib, "opencv_imgproc" OPENCV_VERSION ".lib")
#pragma comment(lib, "opencv_highgui" OPENCV_VERSION ".lib")
#pragma comment(lib, "opencv_video" OPENCV_VERSION ".lib")
#pragma #endif // USE_CMAKE_LIBS
#endif // CV_VERSION_EPOCH
void draw_boxes(cv::Mat mat_img, std::vector<bbox_t> result_vec, std::vector<std::string> obj_names,
int current_det_fps = -1, int current_cap_fps = -1)
{
int const colors[6][3] = { { 1,0,1 },{ 0,0,1 },{ 0,1,1 },{ 0,1,0 },{ 1,1,0 },{ 1,0,0 } };
for (auto& i : result_vec) {
::Scalar color = obj_id_to_color(i.obj_id);
cv::rectangle(mat_img, cv::Rect(i.x, i.y, i.w, i.h), color, 2);
cvif (obj_names.size() > i.obj_id) {
::string obj_name = obj_names[i.obj_id];
stdif (i.track_id > 0) obj_name += " - " + std::to_string(i.track_id);
::Size const text_size = getTextSize(obj_name, cv::FONT_HERSHEY_COMPLEX_SMALL, 1.2, 2, 0);
cvint max_width = (text_size.width > i.w + 2) ? text_size.width : (i.w + 2);
= std::max(max_width, (int)i.w + 2);
max_width //max_width = std::max(max_width, 283);
::string coords_3d;
stdif (!std::isnan(i.z_3d)) {
::stringstream ss;
std<< std::fixed << std::setprecision(2) << "x:" << i.x_3d << "m y:" << i.y_3d << "m z:" << i.z_3d << "m ";
ss = ss.str();
coords_3d ::Size const text_size_3d = getTextSize(ss.str(), cv::FONT_HERSHEY_COMPLEX_SMALL, 0.8, 1, 0);
cvint const max_width_3d = (text_size_3d.width > i.w + 2) ? text_size_3d.width : (i.w + 2);
if (max_width_3d > max_width) max_width = max_width_3d;
}
::rectangle(mat_img, cv::Point2f(std::max((int)i.x - 1, 0), std::max((int)i.y - 35, 0)),
cv::Point2f(std::min((int)i.x + max_width, mat_img.cols - 1), std::min((int)i.y, mat_img.rows - 1)),
cv, CV_FILLED, 8, 0);
colorputText(mat_img, obj_name, cv::Point2f(i.x, i.y - 16), cv::FONT_HERSHEY_COMPLEX_SMALL, 1.2, cv::Scalar(0, 0, 0), 2);
if (!coords_3d.empty()) putText(mat_img, coords_3d, cv::Point2f(i.x, i.y - 1), cv::FONT_HERSHEY_COMPLEX_SMALL, 0.8, cv::Scalar(0, 0, 0), 1);
}
}
if (current_det_fps >= 0 && current_cap_fps >= 0) {
::string fps_str = "FPS detection: " + std::to_string(current_det_fps) + " FPS capture: " + std::to_string(current_cap_fps);
stdputText(mat_img, fps_str, cv::Point2f(10, 20), cv::FONT_HERSHEY_COMPLEX_SMALL, 1.2, cv::Scalar(50, 255, 0), 2);
}
}
#endif // OPENCV
void show_console_result(std::vector<bbox_t> const result_vec, std::vector<std::string> const obj_names, int frame_id = -1) {
if (frame_id >= 0)// std::cout << " Frame: " << frame_id << std::endl;
for (auto& i : result_vec) {
if (obj_names.size() > i.obj_id) std::cout << obj_names[i.obj_id] << " - ";
::cout << "obj_id = " << i.obj_id << ", x = " << i.x << ", y = " << i.y
std<< ", w = " << i.w << ", h = " << i.h
<< std::setprecision(3) << ", prob = " << i.prob << std::endl;
}
}
::vector<std::string> objects_names_from_file(std::string const filename) {
std::ifstream file(filename);
std::vector<std::string> file_lines;
stdif (!file.is_open()) return file_lines;
for (std::string line; getline(file, line);) file_lines.push_back(line);
::cout << "object names loaded \n";
stdreturn file_lines;
}
<typename T>
templateclass send_one_replaceable_object_t {
const bool sync;
::atomic<T*> a_ptr;
stdpublic:
void send(T const& _obj) {
* new_ptr = new T;
T*new_ptr = _obj;
if (sync) {
while (a_ptr.load()) std::this_thread::sleep_for(std::chrono::milliseconds(3));
}
::unique_ptr<T> old_ptr(a_ptr.exchange(new_ptr));
std}
receive() {
T ::unique_ptr<T> ptr;
stddo {
while (!a_ptr.load()) std::this_thread::sleep_for(std::chrono::milliseconds(3));
.reset(a_ptr.exchange(NULL));
ptr} while (!ptr);
= *ptr;
T obj return obj;
}
bool is_object_present() {
return (a_ptr.load() != NULL);
}
send_one_replaceable_object_t(bool _sync) : sync(_sync), a_ptr(NULL)
{}
};
/* code under the the annotation is part of QT */
::YoloShow(QWidget* parent)
YoloShow: QWidget(parent)
{
.setupUi(this);
ui
}
void YoloShow::YoloShow::show_video() {
darknet_yolo_detection();
}
// this method packages the detection
void YoloShow::darknet_yolo_detection()
{
::string names_file = "D:/project/c/qt_demo/YoloShow/YoloShow/x64/Release/coco.names";
std::string cfg_file = "D:/project/c/qt_demo/YoloShow/YoloShow/x64/Release/yolov3-tiny.cfg";
std::string weights_file = "D:/project/c/qt_demo/YoloShow/YoloShow/x64/Release/yolov3-tiny.weights";
std::string filename;
std
/* filename = http://192.168.2.23:8080/video?dummy=param.mjpg */
= "D:/project/c/qt_demo/YoloShow/YoloShow/x64/Release/test.mp4";
filename
float const thresh = 0.2;
detector(cfg_file, weights_file);
Detector
= objects_names_from_file(names_file);
auto obj_names
for (size_t i = 0; i < obj_names.size(); i++)
{
::cout << obj_names[i] << std::endl;
std}
::string out_videofile = "result.avi";
stdbool const save_output_videofile = false; // true - for history
bool const send_network = false; // true - for remote detection
bool const use_kalman_filter = false; // true - for stationary camera
bool detection_sync = true; // true - for video-file
// for slow GPU
#ifdef TRACK_OPTFLOW = false;
detection_sync ;
Tracker_optflow tracker_flow//detector.wait_stream = true;
#endif // TRACK_OPTFLOW
while (true)
{
::cout << "input image or video filename: ";
std
if (filename.size() == 0) std::cin >> filename;
if (filename.size() == 0) break;
try {
#ifdef OPENCVlarge_preview(100, 150, false), small_preview(50, 50, true);
preview_boxes_t bool show_small_boxes = false;
::string const file_ext = filename.substr(filename.find_last_of(".") + 1);
std::string const protocol = filename.substr(0, 7);
std
if (file_ext == "avi" || file_ext == "mp4" || file_ext == "mjpg" || file_ext == "mov" || // video file
== "rtmp://" || protocol == "rtsp://" || protocol == "http://" || protocol == "https:/" || // video network stream
protocol == "zed_camera" || file_ext == "svo" || filename == "web_camera") // ZED stereo camera
filename
{
if (protocol == "rtsp://" || protocol == "http://" || protocol == "https:/" || filename == "zed_camera" || filename == "web_camera")
= false;
detection_sync
::Mat cur_frame;
cv::atomic<int> fps_cap_counter(0), fps_det_counter(0);
std::atomic<int> current_fps_cap(0), current_fps_det(0);
std::atomic<bool> exit_flag(false);
std::chrono::steady_clock::time_point steady_start, steady_end;
stdint video_fps = 25;
bool use_zed_camera = false;
;
track_kalman_t track_kalman
::VideoCapture cap;
cvif (filename == "web_camera") {
.open(0);
cap>> cur_frame;
cap }
else if (!use_zed_camera) {
.open(filename);
cap>> cur_frame;
cap }
// OpenCV 2.x
#ifdef CV_VERSION_EPOCH = cap.get(CV_CAP_PROP_FPS);
video_fps #else
= cap.get(cv::CAP_PROP_FPS);
video_fps #endif
::Size const frame_size = cur_frame.size();
cv//cv::Size const frame_size(cap.get(CV_CAP_PROP_FRAME_WIDTH), cap.get(CV_CAP_PROP_FRAME_HEIGHT));
::cout << "\n Video size: " << frame_size << std::endl;
std
::VideoWriter output_video;
cvif (save_output_videofile)
// OpenCV 2.x
#ifdef CV_VERSION_EPOCH .open(out_videofile, CV_FOURCC('D', 'I', 'V', 'X'), std::max(35, video_fps), frame_size, true);
output_video#else
.open(out_videofile, cv::VideoWriter::fourcc('D', 'I', 'V', 'X'), std::max(35, video_fps), frame_size, true);
output_video#endif
struct detection_data_t {
::Mat cap_frame;
cv::shared_ptr<image_t> det_image;
std::vector<bbox_t> result_vec;
std::Mat draw_frame;
cvbool new_detection;
;
uint64_t frame_idbool exit_flag;
::Mat zed_cloud;
cv::queue<cv::Mat> track_optflow_queue;
stddetection_data_t() : new_detection(false), exit_flag(false) {}
};
const bool sync = detection_sync; // sync data exchange
<detection_data_t> cap2prepare(sync), cap2draw(sync),
send_one_replaceable_object_tprepare2detect(sync), detect2draw(sync), draw2show(sync), draw2write(sync), draw2net(sync);
::thread t_cap, t_prepare, t_detect, t_post, t_draw, t_write, t_network;
std
// capture new video-frame
if (t_cap.joinable()) t_cap.join();
= std::thread([&]()
t_cap {
= 0;
uint64_t frame_id ;
detection_data_t detection_datado {
= detection_data_t();
detection_data
{
>> detection_data.cap_frame;
cap }
++;
fps_cap_counter.frame_id = frame_id++;
detection_dataif (detection_data.cap_frame.empty() || exit_flag) {
::cout << " exit_flag: detection_data.cap_frame.size = " << detection_data.cap_frame.size() << std::endl;
std.exit_flag = true;
detection_data.cap_frame = cv::Mat(frame_size, CV_8UC3);
detection_data}
if (!detection_sync) {
.send(detection_data); // skip detection
cap2draw}
.send(detection_data);
cap2prepare} while (!detection_data.exit_flag);
::cout << " t_cap exit \n";
std});
// pre-processing video frame (resize, convertion)
= std::thread([&]()
t_prepare {
::shared_ptr<image_t> det_image;
std;
detection_data_t detection_datado {
= cap2prepare.receive();
detection_data
= detector.mat_to_image_resize(detection_data.cap_frame);
det_image .det_image = det_image;
detection_data
/* --------------------------------source images-------------------------------------*/
/*cv::imshow("det_image", detection_data.cap_frame);
cv::waitKey(1);*/
.send(detection_data); // detection
prepare2detect
} while (!detection_data.exit_flag);
::cout << " t_prepare exit \n";
std});
// detection by Yolo
if (t_detect.joinable()) t_detect.join();
= std::thread([&]()
t_detect {
::shared_ptr<image_t> det_image;
std;
detection_data_t detection_datado {
= prepare2detect.receive();
detection_data = detection_data.det_image;
det_image ::vector<bbox_t> result_vec;
std
if (det_image)
= detector.detect_resized(*det_image, frame_size.width, frame_size.height, thresh, true); // true
result_vec ++;
fps_det_counter//std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // this can delay detection thread
.new_detection = true;
detection_data.result_vec = result_vec;
detection_data.send(detection_data);
detect2draw} while (!detection_data.exit_flag);
::cout << " t_detect exit \n";
std});
// draw rectangles (and track objects)
= std::thread([&]()
t_draw {
::queue<cv::Mat> track_optflow_queue;
std;
detection_data_t detection_datado {
// for Video-file
if (detection_sync) {
= detect2draw.receive();
detection_data }
// for Video-camera
else
{
// get new Detection result if present
if (detect2draw.is_object_present()) {
::Mat old_cap_frame = detection_data.cap_frame; // use old captured frame
cv= detect2draw.receive();
detection_data if (!old_cap_frame.empty()) detection_data.cap_frame = old_cap_frame;
}
// get new Captured frame
else {
::vector<bbox_t> old_result_vec = detection_data.result_vec; // use old detections
std= cap2draw.receive();
detection_data .result_vec = old_result_vec;
detection_data}
}
::Mat cap_frame = detection_data.cap_frame;
cv::Mat draw_frame = detection_data.cap_frame.clone();
cv::vector<bbox_t> result_vec = detection_data.result_vec;
std
// track ID by using kalman filter
if (use_kalman_filter) {
if (detection_data.new_detection) {
= track_kalman.correct(result_vec);
result_vec }
else {
= track_kalman.predict();
result_vec }
}
// track ID by using custom function
else {
int frame_story = std::max(5, current_fps_cap.load());
= detector.tracking_id(result_vec, true, frame_story, 40);
result_vec }
if (use_zed_camera && !detection_data.zed_cloud.empty()) {
= get_3d_coordinates(result_vec, detection_data.zed_cloud);
result_vec }
//small_preview.set(draw_frame, result_vec);
//large_preview.set(draw_frame, result_vec);
draw_boxes(draw_frame, result_vec, obj_names, current_fps_det, current_fps_cap);
show_console_result(result_vec, obj_names, detection_data.frame_id);
//large_preview.draw(draw_frame);
//small_preview.draw(draw_frame, true);
.result_vec = result_vec;
detection_data.draw_frame = draw_frame;
detection_data.send(detection_data);
draw2showif (send_network) draw2net.send(detection_data);
if (output_video.isOpened()) draw2write.send(detection_data);
} while (!detection_data.exit_flag);
::cout << " t_draw exit \n";
std});
// write frame to videofile
= std::thread([&]()
t_write {
if (output_video.isOpened()) {
;
detection_data_t detection_data::Mat output_frame;
cvdo {
= draw2write.receive();
detection_data if (detection_data.draw_frame.channels() == 4) cv::cvtColor(detection_data.draw_frame, output_frame, CV_RGBA2RGB);
else output_frame = detection_data.draw_frame;
<< output_frame;
output_video } while (!detection_data.exit_flag);
.release();
output_video}
::cout << " t_write exit \n";
std});
// send detection to the network
= std::thread([&]()
t_network {
if (send_network) {
;
detection_data_t detection_datado {
= draw2net.receive();
detection_data
.send_json_http(detection_data.result_vec, obj_names, detection_data.frame_id, filename);
detector
} while (!detection_data.exit_flag);
}
::cout << " t_network exit \n";
std});
// show detection
;
detection_data_t detection_datado {
= std::chrono::steady_clock::now();
steady_end float time_sec = std::chrono::duration<double>(steady_end - steady_start).count();
if (time_sec >= 1) {
= fps_det_counter.load() / time_sec;
current_fps_det = fps_cap_counter.load() / time_sec;
current_fps_cap = steady_end;
steady_start = 0;
fps_det_counter = 0;
fps_cap_counter }
= draw2show.receive();
detection_data ::Mat draw_frame = detection_data.draw_frame;
cv
//if (extrapolate_flag) {
// cv::putText(draw_frame, "extrapolate", cv::Point2f(10, 40), cv::FONT_HERSHEY_COMPLEX_SMALL, 1.0, cv::Scalar(50, 50, 0), 2);
//}
//cv::imshow("window name", draw_frame);
cvtColor(draw_frame, draw_frame, cv::COLOR_BGR2RGB);
= QImage((const unsigned char*)(draw_frame.data), draw_frame.cols, draw_frame.rows, QImage::Format_RGB888);
QImage qimg_draw .label_show->setPixmap(QPixmap::fromImage(qimg_draw.scaled(ui.label_show->size(), Qt::KeepAspectRatio)));
ui
int key = cv::waitKey(3); // 3 or 16ms
if (key == 'f') show_small_boxes = !show_small_boxes;
if (key == 'p') while (true) if (cv::waitKey(100) == 'p') break;
//if (key == 'e') extrapolate_flag = !extrapolate_flag;
if (key == 27) { exit_flag = true; }
//std::cout << " current_fps_det = " << current_fps_det << ", current_fps_cap = " << current_fps_cap << std::endl;
} while (!detection_data.exit_flag);
::cout << " show detection exit \n";
std
::destroyWindow("window name");
cv// wait for all threads
if (t_cap.joinable()) t_cap.join();
if (t_prepare.joinable()) t_prepare.join();
if (t_detect.joinable()) t_detect.join();
if (t_post.joinable()) t_post.join();
if (t_draw.joinable()) t_draw.join();
if (t_write.joinable()) t_write.join();
if (t_network.joinable()) t_network.join();
break;
}
else if (file_ext == "txt") { // list of image files
::ifstream file(filename);
stdif (!file.is_open()) std::cout << "File not found! \n";
else
for (std::string line; file >> line;) {
::cout << line << std::endl;
std::Mat mat_img = cv::imread(line);
cv::vector<bbox_t> result_vec = detector.detect(mat_img);
stdshow_console_result(result_vec, obj_names);
//draw_boxes(mat_img, result_vec, obj_names);
//cv::imwrite("res_" + line, mat_img);
}
}
else { // image file
// to achive high performance for multiple images do these 2 lines in another thread
::Mat mat_img = cv::imread(filename);
cv= detector.mat_to_image_resize(mat_img);
auto det_image
= std::chrono::steady_clock::now();
auto start ::vector<bbox_t> result_vec = detector.detect_resized(*det_image, mat_img.size().width, mat_img.size().height);
std= std::chrono::steady_clock::now();
auto end ::chrono::duration<double> spent = end - start;
std::cout << " Time: " << spent.count() << " sec \n";
std
//result_vec = detector.tracking_id(result_vec); // comment it - if track_id is not required
draw_boxes(mat_img, result_vec, obj_names);
::imshow("window name", mat_img);
cvshow_console_result(result_vec, obj_names);
::waitKey(0);
cv}
#else // OPENCV
//std::vector<bbox_t> result_vec = detector.detect(filename);
= detector.load_image(filename);
auto img ::vector<bbox_t> result_vec = detector.detect(img);
std.free_image(img);
detectorshow_console_result(result_vec, obj_names);
#endif // OPENCV
}
catch (std::exception& e) { std::cerr << "exception: " << e.what() << "\n"; getchar(); }
catch (...) { std::cerr << "unknown exception \n"; getchar(); }
.clear();
filename}
}
环境配置
开发环境使用的是: vs2022+Qt5.15.2 项目使用的是AlexeyAB的项目:https://github.com/AlexeyAB/darknet 开发过程皆根据该项目ReadMe文档进行操作(环境搭建, 模型训练, dll的调用) 个人推荐官方的ReadMe文档
对于项目的应用问题来说, 直接将代码嵌入到项目中, 项目的体积和维护成本都会很大. 为了简化应用, AlexeyAB程序进行封装成多个版本,分别针对不同语言的调用, 也简化了开发.但是对于刚接触的人环境会是一个比较大的问题.
环境配置需要的文件
incldue
文件夹
config
文件夹
lib
文件夹![在这里插入图片描述](https://img-blog.csdnimg.cn/3ed4fe4c6d0747468feff46ee63b3596.png)
配置项目属性
链接器附加库目录设置
链接器中附加目录中的附加依赖项
opencv版本根据自身开发环境进行配置
Release模式下进行启动
debug模式下没有ide报错, 切换到Release模式下ide摸错不用担心,直接忽略
生成
生成后将项目下config文件夹中的文件全部复制到Release目录下(exe所在目录)
利用windows自带的搜索,搜索出qt的msvc
打开并进入
exe所在目录
(Release目录)
.exe windeployqt YoloShow
执行后目录中多出很多文件
双击即可运行
点击右上角的 x 关闭只关闭了界面, 系统检测进程还是在后台运行, 需要重写closeEvent 进行关闭系统.