我一直在想一个东西,当我训练好了一个模型,比如我训练好了yolo系列的模型,然后我可以通过opencv来加载这个模型然后去预测物体,但是opencv默认是cpu运行,这就导致了运行这个模型的速度很慢,也就导致了检测物体的速度很慢,我习惯了使用python,所以我就想着在python上调用opencv的gpu接口,但是网上一搜索,基本上都是针对opencv在c++上进行gpu接口的调用,找来找去,总算找到一个文档了,按照里面的操作,将我之前写的yolov3-opencv代码改了一下,如下所示:

model = cv2.dnn.readNetFromDarknet(model_layer, model)
    # 加载yolov3模型,第一个参数是网络的每一层的信息,第二个参数是训练好的模型,我们现在使用的是官方训练的模型
    # 下面的两行就是用来调用gpu接口的
    model.setPreferableBackend(cv2.dnn.DNN_BACKEND_CUDA)
    model.setPreferableTarget(cv2.dnn.DNN_TARGET_CUDA)

然后满怀期待的去运行去了,但是,报错了,如下:

[ WARN:0] global C:\projects\opencv-python\opencv\modules\dnn\src\dnn.cpp (1363) cv::dnn::dnn4_v20191202::Net::Impl::setUpNet DNN module was not built with CUDA backend; switching to CPU

这个错误也就说明了调用gpu失败,然后造成依旧是在cpu上运行,在我的amd2700上,速度大概是0.9s左右识别一张照片。很慢了,慢的我无奈。既然调用接口失败,就得想办法解决了,看见那位博主下面的评论,发现还是得去把opencv重新编译一下,好吧,那就开干吧,既然逃不过,那就没办法了。

开始准备工作:opencv+contrib+cmake文件,如果打开是404,很可能是待审核 分享两个链接:这篇文章没有编译过程遇到问题解决的方式,我们称之为A篇这篇文章有解决问题的方式,我们称之为B

个人推荐B,因为A篇中,感觉作者重开头到结束很顺,没有遇到什么问题,但是我在编译的过程中,遇到了各种问题,遇到问题才是常态好吧。A篇中可以借鉴的是用vs进行工程生成和测试的那部分,B篇可以借鉴的是cmake部分。

我vs选的是2017版本的,最初用vs2015版本,有让我懵逼的错误,建议你们也使用vs2017版本。cmake我是3.17版本,这个应该关系不大,我3.15版本也可以进行,建议3.17,因为最终成功的版本是3.17下完成的。

我最开始是完全按照A篇进行的,导致cmake opencv的过程中没关注没有下载的文件,然后搞了好几次,每次在vs生成的过程中都失败,顺便说一句,vs生成的过程中,看见不停的生成重复的相同的warning警告不要害怕,让程序接着跑,整个过程看你配置,我amd2700,跑完一两个小时。我最初看见生成循环重复的warning还以为是哪一步错了,搞了好几个小时去找毛病,浪费了时间。

在cmake opencv的过程中,注意B篇中对于下载失败的提醒,文件下载失败,我这边原因就是访问下载的地址失败,为了解决这个问题,除了B篇中说的方法外,还有一种方式:

打开C:\Windows\System32\drivers\etc\hosts
然后在末尾添加上199.232.68.133 raw.githubusercontent.com

这属于一种ip映射,这样搞之后,我这cmake的过程就能正常下载东西了,但是,这种下载很慢,我这提供一份资料,opencv在cmake成功后下载的文件,如果打开是404,很可能是待审核,不需要积分,下载之后直接替换D:\Program Files (x86)\opencv4.2.0\opencv\sources\.cache路径下的.cache文件,替换之后看看cmake的过程还会不会出问题,我没试过,你们可以试试,注意看看你的.cache文件夹md5也就是文件真正名字前的那一部分字母是否跟我上传的文件一致。还有注意我是opencv4.2.0版本的,你版本不一样的话,可能不能直接使用这些文件,那就只能通过上面改映射的方式了,对了,改了映射后如果还不能访问,可能就代表需要挂个vpn了,不一定,我编译的过程中是将vpn挂着的。

当你最后的效果跟B篇一致的时候,就可以按照A篇就行VS编译了,我这,当我跟B篇Cmake一致后,然后按照A篇进行VS编译,过了一两小时后,完美的编译成功了,瞬间觉得整个天空都亮了。

在测试过程中,A篇给的代码链接其实已经更新了,作者那时候的代码是测试的图片,但是更新后测试的是视频,有几个地方需要注意,代码如下:

// yo.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <queue>
#include <iterator>
#include <sstream>
#include <fstream>
#include <iomanip>
#include <chrono>

#include <opencv2/core.hpp>
#include <opencv2/dnn.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>

constexpr float confidence_threshold = 0.5;
constexpr float nms_threshold = 0.4;
constexpr int num_classes = 80;

// colors for bounding boxes
const cv::Scalar colors[] = {
	{0, 255, 255},
	{255, 255, 0},
	{0, 255, 0},
	{255, 0, 0}
};
const auto num_colors = sizeof(colors) / sizeof(colors[0]);

int main()
{
	std::vector<std::string> class_names;
	{
		std::ifstream class_file("classes.txt");//这里相当于yolov3里面的coco.names
		if (!class_file)
		{
			std::cerr << "failed to open classes.txt\n";
			return 0;
		}
		class_names.assign(std::istream_iterator<std::string>(class_file), {});
	}

	cv::VideoCapture source("1.mp4");//这里是读取视频文件

	auto net = cv::dnn::readNetFromDarknet("yolov3.cfg", "yolov3.weights");//这里是yolo的网络文件和训练模型
	net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);
	net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA);
	auto output_names = net.getUnconnectedOutLayersNames();

	cv::Mat frame, blob;
	std::vector<cv::Mat> detections;
	while (cv::waitKey(1) < 1)
	{
		source >> frame;
		if (frame.empty())
		{
			cv::waitKey();
			break;
		}

		auto total_start = std::chrono::steady_clock::now();
		cv::dnn::blobFromImage(frame, blob, 0.00392, cv::Size(416, 416), cv::Scalar(), true, false, CV_32F);
		net.setInput(blob);

		auto dnn_start = std::chrono::steady_clock::now();
		net.forward(detections, output_names);
		auto dnn_end = std::chrono::steady_clock::now();

		std::vector<cv::Rect> boxes;
		std::vector<int> class_id;
		std::vector<float> scores;
		for (auto& output : detections)
		{
			const auto num_boxes = output.rows;
			for (size_t i = 0; i < num_boxes; i++)
			{
				auto itr = std::max_element(output.ptr<float>(i, 5), output.ptr<float>(i, 5 + num_classes));
				auto confidence = *itr;
				auto classid = itr - output.ptr<float>(i, 5);
				if (confidence >= confidence_threshold)
				{
					auto x = output.at<float>(i, 0) * frame.cols;
					auto y = output.at<float>(i, 1) * frame.rows;
					auto width = output.at<float>(i, 2) * frame.cols;
					auto height = output.at<float>(i, 3) * frame.rows;
					cv::Rect rect(x - width / 2, y - height / 2, width, height);

					boxes.push_back(rect);
					class_id.push_back(classid);
					scores.push_back(confidence);
				}
			}
		}

		std::vector<int> indices;
		cv::dnn::NMSBoxes(boxes, scores, 0.0, nms_threshold, indices);

		for (size_t i = 0; i < indices.size(); ++i)
		{
			const auto color = colors[i % num_colors];

			auto idx = indices[i];
			const auto& rect = boxes[idx];
			cv::rectangle(frame, cv::Point(rect.x, rect.y), cv::Point(rect.x + rect.width, rect.y + rect.height), color, 3);

			std::ostringstream label_ss;
			label_ss << class_names[class_id[idx]] << ": " << std::fixed << std::setprecision(2) << scores[idx];
			auto label = label_ss.str();

			int baseline;
			auto label_bg_sz = cv::getTextSize(label.c_str(), cv::FONT_HERSHEY_COMPLEX_SMALL, 1, 1, &baseline);
			cv::rectangle(frame, cv::Point(rect.x, rect.y - label_bg_sz.height - baseline - 10), cv::Point(rect.x + label_bg_sz.width, rect.y), color, cv::FILLED);
			cv::putText(frame, label.c_str(), cv::Point(rect.x, rect.y - baseline - 5), cv::FONT_HERSHEY_COMPLEX_SMALL, 1, cv::Scalar(0, 0, 0));
		}

		auto total_end = std::chrono::steady_clock::now();

		float inference_fps = 1000.0 / std::chrono::duration_cast<std::chrono::milliseconds>(dnn_end - dnn_start).count();
		float total_fps = 1000.0 / std::chrono::duration_cast<std::chrono::milliseconds>(total_end - total_start).count();
		std::ostringstream stats_ss;
		stats_ss << std::fixed << std::setprecision(2);
		stats_ss << "Inference FPS: " << inference_fps << ", Total FPS: " << total_fps;
		auto stats = stats_ss.str();

		int baseline;
		auto stats_bg_sz = cv::getTextSize(stats.c_str(), cv::FONT_HERSHEY_COMPLEX_SMALL, 1, 1, &baseline);
		cv::rectangle(frame, cv::Point(0, 0), cv::Point(stats_bg_sz.width, stats_bg_sz.height + 10), cv::Scalar(0, 0, 0), cv::FILLED);
		cv::putText(frame, stats.c_str(), cv::Point(0, stats_bg_sz.height + 5), cv::FONT_HERSHEY_COMPLEX_SMALL, 1, cv::Scalar(255, 255, 255));

		cv::namedWindow("output");
		cv::imshow("output", frame);
	}

	return 0;
}

直接在vs里面创建工程然后按照A篇进行依赖库这些之后将代码复制进去吧,但是我备注的地方,需要你们找到相关文件然后放到项目文件夹下,这些文件你们自己去找吧,如果评论区反映需要我再分享出来。

如果一切顺利,你就可以看见效果了,我只能说效果非常好,截张图给你们看看:

opencv dnn 默认 gpu opencv调用gpu_深度学习

我是2060,帧数基本在38帧左右。

由此可见,c++模式下调用opencv gpu是成功了,那么就要回到我最初的目的了,python下调用opencv的gpu,你cmd下使用pip list,你会发现多了一个opencv,最初的opencv-python已经没有了或者依旧存在(存在你就删掉opnecv-python这个库),如果没有opencv,我在D:\Program Files (x86)\opencv4.2.0\opencv\sources\build\python_loader可以找到一个setup.py文件,你cmd下运行python setup.py install就可以了。然后再去运行python下的yolo程序,没有报最初的那个错误了,但是速度嘛。。。。。嗯。。。0.6s左右识别一张图片,有一丢丢提升。我感觉还是没有使用到gpu,然后又花了个多小时找原因,网上的教程都没用,而且,删掉opencv-python这个库而去使用opencv这个库,我在pycharm里面就失去了cv2库的自动提醒功能,所以,哎,放弃再挣扎了,改天去研究研究在windows下配置yolo吧,然后直接使用官方的python接口。

2020 5.25 1:00