Windows10, Visual Studio 2019, CMake 3.21.0, Nvidia 显卡1050ti 驱动 Version: 471.41, CUDA 11.0, cudnn 8.2.1, OpenCV 4.5.3。
实习的地方暂时没有电脑给我用,我于是直接把自己的电脑系统重装了,之前的环境做过毕设,做过老师的任务,配过各种各样的东西,实在是太乱了,全部重来或许更好一点。重装好之后,还是经典地重装显卡驱动,直接上NVIDIA官网找推荐的驱动安装。
再安装CUDA,之前做tensorflow的项目的时候就头铁什么都安装的最新的,于是环境死活配不好。现在实在是不敢了,于是退而求其次装了11.0,而且pytorch官网上还没有支持最新的11.4版本。再去官网找到和CUDA版本对应的cudnn即可,这里需要注意版本的对应问题(之前毕设的时候,版本都是对应的也还是不行,最后全都降级成了老版本驱动和CUDA9.0才搞定)。这里详细的教程各种博客都有,就不细表了。
显卡需要的部分安装完之后,再安装Cmake和VS。安装VS其实只需要C++就可以了,其他的组件大家按需下载安装。之后下载OpenCV和OpenCV contribute,解压放到一起,再配置环境变量。这里有个地方需要注意,在之后用VS编译后还要再配一次环境变量,记得把VS编译出的路径上移放在opencv自己路径的前面。
OpenCV应该可以不编译直接用,但是不知道那样的话contribute里的gpu能不能用,大家可以试试。我这里用CMAKE编译,然后就开始麻烦起来了。我建议看一下OpenCV的官方教程,然后用cmake的gui来做。进入cmake先选好和自己VS版本对应的选项,然后开始配置opencv。
第一个选源码文件夹,第二个选安装路径。 然后configure,会出来一堆红色的,这里记得去掉和添加一些选项,要不然以后没注意重新编译又要好久。首先就可以把wechar_qrcode去掉,这个不挂梯子基本上下载不了,然后项目一般都用不到。
然后就是这几个选项记得勾上,千万别直接默认选项就generate,大家按需选择就好。然后再configure一下,可能会提示一些包下载失败,如果真的需要的话就去提示里的下载日志文件,应该是在build文件夹里,找到下载地址,或者用迅雷下载试试,然后再按照下载日志里的文件名和路径修改存放就好。多configure一次,看看有没有红色的提示信息,如果没有了一般就没问题了,如果还有的话就耐心解决一下,不要贸然的generate。
configure结束之后就可以generate了,完成之后可以直接从cmake里打开项目。然后配置一下项目的包含路径和链接器,基本上CSDN的都大同小异。这里的配置只需要注意该包含的都包含上就好了,然后就是debug版本和release版本选择要对应的问题。
之后就点重新生成解决方案进入漫长的编译过程,编译完了,如果最后全部成功没有失败就没啥问题。
我测试了两个sample,然后就发现了两个组件在cmake的时候没选只能重新编译。大家也可以试试。
第一个sample:其实opencv contribute里就有需要的sample data,直接用就行,用一些网上找的可能还不行。SURF算法不免费,所以要之前Cmake的时候要把那个NONFREE选上。
#include
#include "opencv2/opencv_modules.hpp"
#include "opencv2/core.hpp"
#include "opencv2/features2d.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/cudafeatures2d.hpp"
#include "opencv2/xfeatures2d/cuda.hpp"
using namespace std;
using namespace cv;
using namespace cv::cuda;
int main01()
{
GpuMat img1, img2;
img1.upload(imread("left.jpg", IMREAD_GRAYSCALE));
img2.upload(imread("right.jpg", IMREAD_GRAYSCALE));
cout << "uploaded" << endl;
cv::cuda::printShortCudaDeviceInfo(cv::cuda::getDevice());
SURF_CUDA surf;
// detecting keypoints & computing descriptors
GpuMat keypoints1GPU, keypoints2GPU;
GpuMat descriptors1GPU, descriptors2GPU;
surf(img1, GpuMat(), keypoints1GPU, descriptors1GPU);
surf(img2, GpuMat(), keypoints2GPU, descriptors2GPU);
cout << "FOUND " << keypoints1GPU.cols << " keypoints on first image" << endl;
cout << "FOUND " << keypoints2GPU.cols << " keypoints on second image" << endl;
// matching descriptors
Ptr matcher = cv::cuda::DescriptorMatcher::createBFMatcher(surf.defaultNorm());
vector matches;
matcher->match(descriptors1GPU, descriptors2GPU, matches);
// downloading results
vector keypoints1, keypoints2;
vector descriptors1, descriptors2;
surf.downloadKeypoints(keypoints1GPU, keypoints1);
surf.downloadKeypoints(keypoints2GPU, keypoints2);
surf.downloadDescriptors(descriptors1GPU, descriptors1);
surf.downloadDescriptors(descriptors2GPU, descriptors2);
// drawing the results
Mat img_matches;
drawMatches(Mat(img1), keypoints1, Mat(img2), keypoints2, matches, img_matches);
namedWindow("matches", 0);
imshow("matches", img_matches);
waitKey(0);
return 0;
}
第二个sample出现了一些问题,按理来讲WITH_NVCUVID和OPENGL都选上了应该没问题了才对,但是发现cmake选上advanced之后发现NVCUVID.LIB的路径都没有,原来装的CUDA11.1里没有自动下载解码器,所以从NVIDIA官网上下载了Video_Codec_SDK 11.0,但是又发现里面的编码器库和cmake里面的文件名不一样,Video Codec里的库名叫nvencodeapi,cmake里叫nvcuvnc,不知道为什么会这样,暂且先把.h和改名后的.lib文件放到CUDA的对应文件夹里。然后重新编译工程报错:contrib里的一堆文件找不到NVEncodeAPI.h,可是下载的文件是叫nvEncodeAPI.h,nv是小写的啊。难道是版本问题??下载了一下之前几个版本的Video Codec,发现没有过nvcuvnc.lib和NVEncodeAPI.h文件,为什么会这样呢?如果是版本问题,OpenCV 4.5对应的CUDA和codec版本应该是什么呢?
暂且先改了文件名之后再编译试试,感觉应该是凶多吉少,如果不行那就再想办法吧。。。
更新:只尝试了解码部分,用于编码的nvencodeapi没有复制进去,最后成功了,不过官方源码好像还是有些问题。直接用imshow展示GPU显存中的视频报错需要调用download()方法,即把d_frame中的内容download到一个普通的Mat中再imshow,但是这样虽然解码成功,但是imshow仍然不显示视频,原因未知。
#include
#include "opencv2/opencv_modules.hpp"
#if defined(HAVE_OPENCV_CUDACODEC)
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
const std::string fname("vtest.avi"); //视频文件
cv::namedWindow("CPU", cv::WINDOW_NORMAL);
cv::namedWindow("GPU", cv::WINDOW_OPENGL);
cv::cuda::setGlDevice();
cv::Mat frame;
cv::VideoCapture reader(fname);
cv::cuda::GpuMat d_frame;
cv::Ptr d_reader = cv::cudacodec::createVideoReader(fname);
cv::TickMeter tm;
std::vector cpu_times;
std::vector gpu_times;
int gpu_frame_count = 0, cpu_frame_count = 0;
for (;;)
{
tm.reset(); tm.start();
if (!reader.read(frame))
break;
tm.stop();
cpu_times.push_back(tm.getTimeMilli());
cpu_frame_count++;
cv::imshow("CPU", frame);
if (cv::waitKey(3) > 0)
break;
}
for (;;)
{
tm.reset(); tm.start();
if (!d_reader->nextFrame(d_frame))
break;
tm.stop();
gpu_times.push_back(tm.getTimeMilli());
gpu_frame_count++;
cv::imshow("GPU", d_frame);
if (cv::waitKey(3) > 0)
break;
}
if (!cpu_times.empty() && !gpu_times.empty())
{
std::cout << std::endl << "Results:" << std::endl;
std::sort(cpu_times.begin(), cpu_times.end());
std::sort(gpu_times.begin(), gpu_times.end());
double cpu_avg = std::accumulate(cpu_times.begin(), cpu_times.end(), 0.0) / cpu_times.size();
double gpu_avg = std::accumulate(gpu_times.begin(), gpu_times.end(), 0.0) / gpu_times.size();
std::cout << "CPU : Avg : " << cpu_avg << " ms FPS : " << 1000.0 / cpu_avg << " Frames " << cpu_frame_count << std::endl;
std::cout << "GPU : Avg : " << gpu_avg << " ms FPS : " << 1000.0 / gpu_avg << " Frames " << gpu_frame_count << std::endl;
}
return 0;
}
#else
int main()
{
std::cout << "OpenCV was built without CUDA Video decoding support\n" << std::endl;
return 0;
}
#endif