在之前的文章《【TensorRT(2)】研究美团tech的yolov6的TensorRT部署》说明了tensorRT 的使用流程。今天尝试将其并入QT 项目中。
文章目录
- 项目地址
- 参考资料:
- 该分支主要做的工作
- Task 1:读取视频文件,然后通过 dds 发送的事情
- Task 2.1:将 yolov6 作为deeplib 库。
- # Task 2.2:接收 dds 信息,用 deeplib 库推理图片。
- 演示gif 动画
项目地址
库地址:https://gitee.com/hiyanyx/qt5.14-cpp_dds_-project/tree/dds_-tensorRT-yolov6/
参考资料:
1、代码参考:https://gitee.com/xiaoyuerCV/tensorrt-yolov6/,已经下载到了目录 ./reference/YOLOv6-Trt8
2、pytorch 权重下载:美团yolov6,已经下载到了目录 ./reference/YOLOv6-main-meituan/deployONNX
原理:下载 pytorch 的权重,yolov6s.pt,转换成 onnx。然后,把onnx 复制到 ./reference/YOLOv6-Trt8。然后trt 里面的c代码会自动把onnx 序列化为 yolov6s.engine 文件。
3、行人视频:reference/YOLOv6-Trt8/xingren.mp4
该分支主要做的工作
1、在 ./src/main/example.cpp 中实现了读取视频文件,然后通过 dds 发送的事情。
2、将 ./reference/YOLOv6-Trt8 作为 库 引入到 c++ 代码工程中,库名字为 deeplib,位置:./deps/deeplib。 具体工作为修改cmakelist文件,生成动态库 deeplib.so,增加deeplib.h 作为yolo检测器的接口(实现了Inferclass
这个对象进行检测)。
Task 1:读取视频文件,然后通过 dds 发送的事情
1、在之前的文章【QT开发(6)】0926-QT 中加入 fastDDS 通信库的程序使用说明中说明了dds 的使用方法。
2、我们基于上次的项目,修改 examples.hpp, 在 class Example 增加一个对象成员 cv::VideoCapture video_capture;
。 特别说明以下,关于对象成员的初始化可以查看我的博客【C++学习(8)】类对象作为成员变量 和 【C++学习(10)】将一个类作为另一个类的成员: 类对象和类指针; std::unique_ptr 智能管理类指针。成员对象的初始化还是有很多知识点,作为基础知识大家需要学习。
3、修改 examples.cpp对 video_capture 的一些属性进行修改。
video_capture.open("/var/files/yanyixiong/qt5.14-cpp_-empty_-project/deps/deeplib/xingren.mp4");
// 视频mp4解码int codec = cv::VideoWriter::fourcc('D', 'I', 'V', 'X');
//获得double fps = video_capture.get(cv::CAP_PROP_FPS);
// 宽度int width = video_capture.get(cv::CAP_PROP_FRAME_WIDTH);
//高度int height = video_capture.get(cv::CAP_PROP_FRAME_HEIGHT);
4、然后在50ms定时器中设置发送dds 信息。如果不明白我说的这个定时器是什么东西,请查看博文【QT开发(6)】0926-QT 中加入 fastDDS 通信库的程序使用说明
我把那篇的截图放一下:
void Example::timer_WorkingStatus_callback()
{// Test Envstd::vector<unsigned char> vec_data;cv::Mat frame_pub;video_capture.read(frame_pub);if (!frame_pub.data ) {debug::log("fail to imread");return ;}else{//cv::imshow("source image", img1);//cv::waitKey(1);//cv::resize(frame_pub, frame_pub, cv::Size(),0.1, 0.1);debug::log("success to imread");}cv::imencode(".jpg", frame_pub, vec_data);auto_msg::msg::FrameData msg1;msg1.time_stamp() = get_time_ms();msg1.data() = vec_data;//msg1.data.insert(msg1.data.end(), vec_data.begin(), vec_data.end());simulation_raw_images_pub_->publish(msg1);
}
5、至此,dds 发送视频就完成了。接下来,我们需要接受dds 信息,并用 yolov6 推理。
Task 2.1:将 yolov6 作为deeplib 库。
将 ./reference/YOLOv6-Trt8 作为 库 引入到 c++ 代码工程中,库名字为 deeplib,位置:./deps/deeplib。 具体工作为修改cmakelist文件,生成动态库 deeplib.so,增加deeplib.h 作为yolo检测器的接口(实现了Inferclass
这个对象进行检测)。
1、 ./deps/deeplib 修改cmakelists,增加库
ADD_LIBRARY(deeplib SHARED ${SOURCES_FILES})
target_include_directories(deeplib PUBLIC ${SOURCES_PATH})
target_link_libraries(deeplib cuda tensorrt opencv)
2、写deeplib.h 接口程序
写一个对象 Inferclass,包含,构造函数,析构函数,推理接口inferIOmain,以及复制构造函数。
包含 私有成员类指针 Yolov6Detector,这个是在https://gitee.com/xiaoyuerCV/tensorrt-yolov6/
里面已经实现的
class Inferclass {
public:Inferclass(std::string model_path = "/var/files/yanyixiong/qt5.14-cpp_-empty_-project/deps/deeplib/yolov6s.onnx");Inferclass(const Inferclass &S);void inferIOmain(cv::Mat input_image);~Inferclass();private:std::string model_path ;std::unique_ptr<Yolov6Detector> yolov6_detector;};
在 构造函数 中完成对 Yolov6Detector 对象的初始化。
Inferclass::Inferclass(std::string model_path):model_path(model_path)
{std::cout << "Start" << std::endl;
// 导入onnx 地址this->yolov6_detector.reset(new Yolov6Detector(model_path));if (!this->yolov6_detector->Init()) {std::cout << "Failed to initialize Yolov6 detector!";}}
析构函数 ,因为我们使用的是std::unique_ptr 类指针,不需要手动new和delete 指针。需要注意的是,如果我们是使用普通指针,一定到在 析构函数里面把 这个类指针释放,否则是内存泄漏!
Inferclass::~Inferclass()
{std::cout << "End" << std::endl;//-----------------代码-------------------------------if(NULL != pA)//此处需要初始化时设置指针为空。{delete pA;pA = new A;} else {pA = new A;}//-----------------代码-------------------------------}
推理接口 inferIOmain函数:
void Inferclass::inferIOmain(cv::Mat input_image) {//cv::Mat input_image;
// 建立目标列表std::vector<Object> objects;
// 检测图像 this->yolov6_detector->Detect(input_image, &objects);//画图DrawObjects(input_image, objects);
// 展示图片cv::imshow("YOLOv6 Object detection", input_image);cv::waitKey(1);}
3、我们有了 deeplib.h 和cmakelist文件,这就是一个库了,可以被其他项目使用。这是个基础的工作,如果你不懂怎么在主项目里面引入库,可以看之前的git仓库学习cmake practice。
# Task 2.2:接收 dds 信息,用 deeplib 库推理图片。
在之前的文章【QT开发(6)】0926-QT 中加入 fastDDS 通信库的程序使用说明中,引入deeplib 库。
1、修改 cmakelists,增加头文件
# ===== 设置 include 目录路径 =====
target_include_directories ( emptyApp PUBLIC# === 第三方库 ===# 在此处添加更多 第三方库# 具体写法,请查阅各个 库文档${CMAKE_CURRENT_SOURCE_DIR}/deeplib/src)
2、修改 cmakelists,增加 cmaklist 子目录
#---------------------------------------------------#
# 子目录 CMakeLists.txt
#---------------------------------------------------#
#-- 子 CMakeLists.txt 执行的 中间产物,将分别放在
# build/src build/libhello 目录中。
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/deps/deeplib EXCLUDE_FROM_ALL)
3、修改 cmakelists,增加一个 target_link_libraries
target_link_libraries( emptyApp deeplib)
4、至此,修改完了 cmakelists。然后在 Examples.h 中增加成员类。
在博客【C++学习(10)】将一个类作为另一个类的成员: 类对象和类指针; std::unique_ptr 智能管理类指针,我们谈到,如果是在 一个 class 中增加另一个 类成员,首先,要将A类的对象作为B类的成员,你必须在B类声明前声明A类。其次,就是像声明一个成员变量一样,在B类中添加一个成员,如A m_a; 最后,初始化m_a。因为这个成员是一个类对象,因此需要调用构造函数才能初始化。因为这个对象m_a在B类中,m_a对象的构造必然是在B类对象构造之前进行,因此不能在B类构造函数中进行,又因为m_a不是全局对象,也不能在函数外部。如何解决这个问题呢?这就是C++提出的成员初始化列表,用来解决这类问题的。
因此,我们增加一个 类成员
class Example : public rclcpp::Node
{public:Example(std::string model_path);~Example();int Init();int Start();int Stop();Inferclass InferPr;
...
...
}
然后在 Examples.cpp 中class Examples 成员初始化列表 初始化。
Example::Example(std::string model_path) : rclcpp::Node("example"),InferPr(model_path)
{running_ = false;workingStatus_timer_ = nullptr;
}
完成了初始化,就可以使用这个 InferPr 成员,在50ms 定时器中用它进行推理
void Example::timer_WorkingStatus_callback()
{auto_msg::msg::FrameData stitch_img = stitch_img_.Get();if ( running_ == true && stitch_img.time_stamp()>0) {cv::Mat frame = cv::imdecode(stitch_img.data(), CV_LOAD_IMAGE_COLOR);InferPr.inferIOmain(frame);}else{std::cout << "Timer trick 50ms, waiting" << std::endl;}}
4、至此,完成了工作
演示gif 动画
ScreenToGif 很好用呀,推荐。
正文结束