当前位置:   article > 正文

使用libtorch、opencv、mjpg-stream、yolov5基于C++的远程摄像头目标检测任务环境搭建_c++ libtorch yolo5

c++ libtorch yolo5

任务概述

我想基于yolov5模型完成一个目标检测任务,其中摄像头应该搭载在无人机上,我的机载电脑是树莓派,这意味着我有两种选择:

  • 将模型部署在树莓派上做推理,然后通过远程软件VNC等将视频信息同步到本地电脑。
    我了解到这种方式虽然简单,但是有它的弊端,那就是树莓派算力有限,如果部署yolov5s可能帧率只能达到3帧左右,我知道有些人使用yolov5lite来提高推理速度,但是我不清楚这种方式对精度的损害大不大,以及这种方式到底能提高多少帧率,基于这些疑问,我并没有选择这种方法。
  • 第二种方式就是将远程摄像头的数据发送到本地电脑。在本地电脑运行推理程序。
    我了解到树莓派的PIcamera库就有这个功能。同时motion、mjpg-streamer开源软件也可以帮助我完成这个任务,简单地说, MJPG-stream是一个 JPEG 文件的传输流。它最常用的用途就是采集摄像头的数据,然后启动 http server,用户就可以通过浏览器查看图像数据了。
    我的机载电脑是树莓派,同样我可以使用他做一个边缘计算模块来实现视频流信号的传输。
    我将采用python编程语言来训练yolov5模型,C++编程环境来进行推理。我知道在C++中进行部署会给自己引入很多麻烦,但是我想锻炼一下自己的代码能力。
    我了解到yolov5在C++中部署有多种方式,以下部分会涉及到两种方式分别是使用opencv的dnn和使用libtorch的方式。至于使用onnxruntime的方式我并没有试过,不会赘述,首先声明,我并非计算机视觉领域的研究者,这个项目只是我的课程作业,但是我相信我的踩坑经历对大部分C++新手是有帮助的。以下是正文:

树莓派远程摄像头配置

我购买的是USB免驱摄像头,因为我的树莓派是有壳子的,我暂时还不想拆了,所以没买CSI的摄像头,CSI的摄像头好像便宜些。
为了完成我的远程传输任务,首先我得配置好树莓派的环境,这个不再赘述,烧录好系统之后,配置好国内镜像源,然后固定IP地址,通过FinalShell和VNC远程连接树莓派,同时在enable摄像头和VNC以及SSH。友情提示,如果你是新手且想使用Ubuntu mate或其他Linux发行版,我劝你慎重,直接使用树莓派原生系统可以免去你90%的麻烦,而且原生系统网上资料多,出了问题好解决。另外,烧录系统时直接按照官网给的流程就好了,简单快捷,最多十分钟搞定,网络上很多资料都比较久远,很多资料都是使用Win32DiskImager等第三方工具完成的,我劝你不要这么做。
言归正传,我了解到,如果想实现实时远程传输视频的功能我可以实现树莓派的PiCamera库,你可以在网上找到他的教程,如果你配置成功了,恭喜你!,如果你跟我一样配置失败了,不要灰心,请依次做以下检查

  • 首先根据其他教程确定安装好了所有依赖库。
  • 输入命令:ls /dev/video*如果显示没有video 0那就是你的摄像头未识别,你可以检查你是否enable摄像头。如果显示有video 0
  • 输入命令vcgencmd get_camera如果显示如下:* supported=1 detected=0, libcamera interfaces=0你很可能遇到了跟我一样的问题。你可以在网上找找解决方法,大部分的方法我都试过,对我并不奏效。
    好在我的摄像头是可以检测到的,所以我转换了思路,我准备使用MJPG-streamer来完成这一项工作,你可以在网络上找到这个开源项目的下载地址。
    在编译完成之后,你可以使用网络摄像头了。
./mjpg_streamer -i "input_uvc.so -d /dev/video2 -r 640x480 -f 30" -o "output_http.so -w www"
  • 1

需要注意的是,你需要使用同一局域网的电脑来打开这个网页。你可以在浏览器输入如下地址来访问网络摄像头http://树莓派ip:8080/?action=stream&type=.mjpg
我知道有的教程给的地址可能没有&type=mjpg,但是最好加上,这样opencv的VideoCapture读取不会出错。如果你可以在本地电脑看到摄像头的画面,那么恭喜你已经完成树莓派端的配置了,可以进行下一步了,当中步骤我描述的不是很详细,但是网络上资料比较多,在这一步很少会出现错误。

opencv读取摄像头视频流

一开始我打算的是在python中训练读取模型一气呵成,这种最简单,不过我遇到了一些问题。虽然我可以在浏览器中看到我的画面,但是如果我使用opencv的VideoCapture来读取的话,居然失败了,网上各种答案众说纷纭,有人说在读取路径上加上&type=.mjpg会有用,但是我试了并没有用,最终我找到了问题所在,原来读取网络流opencv会依赖于ffmpeg这个dll,在python中我不知道如果操作,但是在C++中就简单了,我只需要把这个dll复制到debug目录下就可以了,很好,就这样,我在C++上测试了一下,发现已经可以看到视频了。

在C++中使用GPU推理yolov5

python环境不会配置+我想练一下我的C++代码能力,所以我将推理环境转到了C++,这样很好,也许可以提高推理的速度。
在正式写代码之前,我打算先找找别人的代码跑跑环境,我首先看到了这个大佬的实现方式https://blog.csdn.net/nihate/article/details/112731327
这个大佬写的很详细,相关代码都开源了,可以去试一下。我试了一下他的这个代码https://github.com/hpc203/yolov5-dnn-cpp-python,他只是用了opencv模块就完成了yolov5的C++部署,大家可以试一下,但是很遗憾,我报错了。
在forward函数中报错了
this->net.forward(outs, this->net.getUnconnectedOutLayersNames());
调试无果之后,果断放弃了。我观察到这位博主的实现方式已经比较老了,我发现还有其他的实现方式,我最终选择是使用libtorch来完成,因为考虑到都是torch嘛,可能兼容性更好一点,而且本人对pytorch还是有一些熟悉的。所以就用这个了。

  • 首先还是配置环境,这里推荐两篇博客https://blog.csdn.net/weixin_43917589/article/details/124509964
    https://blog.csdn.net/qq_40198848/article/details/112872844?spm=1001.2014.3001.5506
  • 提示几点,首先你必须保证你的pytorch版本和libtorch版本一致同时cuda版本一致,或者至少保证libtorch版本高于pytorch版本,其次我劝你在配置vs环境时同时配置debug和release环境,因为你很有可能在某一环境下运行不了而在另一环境可以运行。必须指出的是在连接器的输入中的附加依赖项如果你是debug环境,请填写opencv_worldd.lib,如果是release环境,填写opencv_world.lib。最后,如果你报了缺少xxdll的错误,你可以简单粗暴把opencv和libtorch 相关lib文件夹的dll都复制到debug目录或release目录。
  • 检查你的GPU是否能用,Torch::cuda::is_available()注意,这是返回Fasle你需要安装我发的两个参考链接中第一个设置一下,如果你返回的是True,你可能也无法调用cuda,这个你现在看不出来,但是你很可能无法加载模型,这时你需要按照这个链接来重新设置一下http://blog.17baishi.com/16743/。
  • 配置完环境,现在来转换yolov5模型,将.pt格式的模型转化为torchscript来供libtorch调用。这个直接按照官网的 https://github.com/ultralytics/yolov5教程来,很简单,我们使用官方的yolov5s模型来转化。
  • 转化完了模型,得到.torchscript格式的模型,现在我们编写一下测试代码,来看看能否导入模型,可以参考这个老哥的测试代码https://blog.csdn.net/Z960515/article/details/122048175
#include <torch/torch.h>
#include <torch/script.h>
#include <iostream>


class Detector {
public:
	/***
	 * @brief constructor
	 * @param model_path - path of the TorchScript weight file
	 * @param device_type - inference with CPU/GPU
	 */
	Detector(const std::string& model_path, bool use_gpu);
	
	// other methond
	...
	
private:
	torch::jit::script::Module module_;
	torch::Device device_;
	bool half_;
};

Detector::Detector(const std::string& model_path, bool use_gpu) :device_(torch::kCPU)
{
	if (torch::cuda::is_available() && use_gpu)
	{
		//std::cout << "use cuda...\n";
		device_ = torch::kCUDA;
	}
	else
	{
		//std::cout << "use cpu...\n";
		device_ = torch::kCPU;
	}

	try {
		// Deserialize the ScriptModule from a file using torch::jit::load().
		module_ = torch::jit::load(model_path);
	}
	catch (const c10::Error& e) {
		std::cerr << "Error loading the model!\n";
		std::exit(EXIT_FAILURE);
	}

	half_ = (device_ != torch::kCPU);
	module_.to(device_);

	if (half_) {
		module_.to(torch::kHalf);
	}

	module_.eval();
}


int main() 
{
	std::shared_ptr<Detector> detector = std::make_shared<Detector>(yourpath, true);
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61

如果你可以正常导入模型并且cuda也可用的话,那很好,你省去了很多麻烦,我在这一步卡了很久,我没有办法使用torch::fit::load导入模型,这个地方报错c10异常。我在网络上查找了很多资料,发现主要有这几方面原因:

  1. 模型路径写错了。
  2. libtorch版本cuda版本和转化模型用的pytorch版本cuda版本不一致
  3. .pt模型的挂在设备(cpu or cuda)和C++中tensor挂载设备不一致
    我很确定我没有犯第一二种错误。于是我查看了我是不是犯了第三种错误,我发yolov5官方转化模型的例子确实默认是转化为cpu模型的,但是我在libtorch中导入时选择的挂在设备是cuda这就发现了问题。我参照https://github.com/ultralytics/yolov5/pull/6963将我的模型转化为cuda版本。然后在libtorch中导入,发现居然还是在同一位置发现同一问题了。这就让我很崩溃,一度怀疑人生。这三种方法居然都不使用我。但是我并没有放弃,我使用torch官方的例子https://pytorch.org/docs/stable/generated/torch.jit.load.html转换了一个最简单的cpu模型,同时我将libtorch导入时设备选择为cpu来看看能否导入成功,结果还是失败了,这对我来说很难受,我不知道问题出在哪里。我想到了切换成release环境再试一次,天无绝人之路,结果这次成功了!!!,我是C++新手,我并不知道这是为什么。我可以导入cpu模型,这对我来说很激动,我在这里卡了很久。于是我试试能否将yolov5官方模型转为cpu版本来导入,结果也成功了,但是帧率有点低,推理的太慢了,还是想用gpu来做,可是我gpu一直load失败,即使切换成release环境也一样失败。最终我发现了问题所在。http://blog.17baishi.com/16743/参照这篇博客,修改了我的配置,终于实现了!!!GPU推理就是快,我用CPU推理延迟大概6s左右,GPU表示毫无压力。

后记

我看我的GPU还没有跑满,后面打算用一下多线程技术进一步提高帧率。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/178148
推荐阅读
相关标签
  

闽ICP备14008679号