前言
下载的YOLO3源码及其应用程序都是基于命令行的,但很多应用中需要把YOLO3集成到图形界面应用里,YOLO作者已经做了YOLO3的DLL,即yolo_cpp_dll,编译方法见我的上一篇文章“Win10+VS2015安装配置YOLO3(CPU/GPU)”,编译后,如果需要图形界面,调用这个DLL就可以了。
YOLO3源码里附带了yolo_console_dll示例程序,其中涵盖了对视频、图像等的应用,仿照这个程序就可以编制自己的各类应用了。
1 步骤
1.1 新建工程
新建项目-MFC-MFC 应用程序,我这里取名YOLO3Test
应用程序类型-基于对话框,完成
删除资源对话框中确定取消按钮和Static控件
放两个按钮(IDC_LOAD和IDC_CALCULATE)和一个编辑框控件(IDC_EDIT1)
配置选择Release,X64。
然后工程右键-属性-配置属性
C/C++-常规-附加包含目录:加入两个项目,一个是opencv的include路径;一个是yolo3源码yolo_v2_class.hpp所在路径;
链接器-常规-附加库目录:加入两个项目,一个是opencv的lib路径;一个是yolo3源码编译出的yolo_cpp_dll.lib所在路径;
链接器-输入-附加依赖项:加入两个项目,yolo_cpp_dll.lib和opencv_world340.lib
然后在YOLO3源码目录找到yolo_cpp_dll.dll所在路径,拷贝这个路径,回到桌面-此电脑-右键-属性-高级系统设置-环境变量-系统变量-双击变量Path,在最后加上一行粘贴刚才拷贝的路径。
1.2 添加代码
1.2.1 添加头文件和预编译代码
打开YOLO3TestDlg.h,在前面加上
#include "opencv2/opencv.hpp"
在CYOLO3TestDlg类中添加一个成员
cv::Mat mat_img;
打开YOLO3TestDlg.cpp
在前面加上以下代码
#define OPENCV
#define GPU
#include <mutex>
#include "yolo_v2_class.hpp"
注意这个#define OPENCV一定要加在yolo_v2_class.hpp前面,这样才能使能opencv
1.2.2 添加两个按钮的处理程序
1.2.2.1 装入图像(IDC_LOAD)
void CYOLO3TestDlg::OnBnClickedLoad()
{
// TODO: 在此添加控件通知处理程序代码
CFileDialog dlg(TRUE, _T("*.jpg;*.jpeg"), 0, 6UL, _T("Image(*.jpg;*.jpeg)|*.jpg;*.jpeg||"));
if (dlg.DoModal() != IDOK)return;
std::string filename = CT2A(dlg.GetPathName());
mat_img = cv::imread(filename);
cv::imshow("window name", mat_img);
}
1.2.2.2 处理(IDC_CALCULATE)
void CYOLO3TestDlg::OnBnClickedCalculate()
{
// TODO: 在此添加控件通知处理程序代码
std::string names_file = "E:/DeepLearning/YOLO3/darknet-master/build/darknet/x64/data/coco.names";
std::string cfg_file = "E:/DeepLearning/YOLO3/darknet-master/build/darknet/x64/cfg/yolov3.cfg";
std::string weights_file = "E:/DeepLearning/YOLO3/darknet-master/build/darknet/x64/yolov3.weights";
auto obj_names = objects_names_from_file(names_file);
Detector detector(cfg_file, weights_file);
auto start = std::chrono::steady_clock::now();
std::vector<bbox_t> result_vec = detector.detect(mat_img);
auto end = std::chrono::steady_clock::now();
std::chrono::duration<double> spent = end - start;
CString msg;
msg.Format(_T("%lf sec"), spent.count());
SetDlgItemText(IDC_EDIT1, msg);
draw_boxes(mat_img, result_vec, obj_names);
cv::imshow("window name", mat_img);
}
上面代码中的三个文件名根据实际位置来写。
1.2.2.3 拷贝两个函数
以上用到两个函数,可以直接从yolo_console_dll示例程序拷贝,或者从下面代码拷贝也可以
1) 一个是从文件中取物体名称列表的函数,用于把结果框上标识上物体名称
std::vector<std::string> objects_names_from_file(std::string const filename) {
std::ifstream file(filename);
std::vector<std::string> file_lines;
if (!file.is_open()) return file_lines;
for (std::string line; getline(file, line);) file_lines.push_back(line);
std::cout << "object names loaded \n";
return file_lines;
}
2) 还一个是往图像上画结果框的函数
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) {
cv::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);
if (obj_names.size() > i.obj_id) {
std::string obj_name = obj_names[i.obj_id];
if (i.track_id > 0) obj_name += " - " + std::to_string(i.track_id);
cv::Size const text_size = getTextSize(obj_name, cv::FONT_HERSHEY_COMPLEX_SMALL, 1.2, 2, 0);
int const max_width = (text_size.width > i.w + 2) ? text_size.width : (i.w + 2);
cv::rectangle(mat_img, cv::Point2f(std::max((int)i.x - 1, 0), std::max((int)i.y - 30, 0)),
cv::Point2f(std::min((int)i.x + max_width, mat_img.cols - 1), std::min((int)i.y, mat_img.rows - 1)),
color, CV_FILLED, 8, 0);
putText(mat_img, obj_name, cv::Point2f(i.x, i.y - 10), cv::FONT_HERSHEY_COMPLEX_SMALL, 1.2, cv::Scalar(0, 0, 0), 2);
}
}
if (current_det_fps >= 0 && current_cap_fps >= 0) {
std::string fps_str = "FPS detection: " + std::to_string(current_det_fps) + " FPS capture: " + std::to_string(current_cap_fps);
putText(mat_img, fps_str, cv::Point2f(10, 20), cv::FONT_HERSHEY_COMPLEX_SMALL, 1.2, cv::Scalar(50, 255, 0), 2);
}
}
2 程序结果
编译,运行,点Load按钮选择一个图像文件,图像显示出来,然后点Calc按钮,即可看到运算结果和运算花费时间。
这里是用opencv的imshow弹出窗口显示图像,如果需要可以自己编码把mat_img图像嵌入显示在程序界面上。
以上程序编译了个X64的Debug版,运行到detector的构造函数即出错闪退,所以暂时只能在x64 Release下运行,这个问题事后找了一下,是因为Debug版程序使用了Release版的yolo_cpp_dll.dll,我又编译了Debug版的yolo_cpp_dll.dll,这样各自使用,就没有问题了。
全部程序附在后面:VS2015图形界面YOLO3应用程序