赞
踩
opencv大多数只使用到了cpu的版本,实际上对于复杂的图像处理过程用cuda(特别是高分辨率的图像)可能会有加速效果。是否需要使用cuda需要思考:
gpu_dst.download(dst_cpu)
将gpu_dst数据搬运到dst_cpu,数据是8976*4960*3,耗时约37ms,如果你的图像处理比较简单,说不定数据搬运的耗时比直接在cpu上运行更长。这里的前提是你的nvidia驱动、cuda以及cudnn都安装完成,可以正常使用。
首先下载版本一致的opencv和opencv-contrib(cuda库所在包),然后解压待用。
然后查询你显卡的Compute Capability,进入opencv-4.8.1后创建build文件夹,终端在build中打开后,执行:
cmake \
-D CMAKE_BUILD_TYPE=RELEASE \
-D BUILD_CUDA_STUBS=ON \
-D WITH_CUDA=ON \
-D CUDA_ARCH_BIN=8.9 \
-D OPENCV_EXTRA_MODULES_PATH=../../opencv_contrib-4.8.1/modules ..
注意,CUDA_ARCH_BIN
是你查询到自己显卡的Compute Capability,OPENCV_EXTRA_MODULES_PATH
指向你的opencv_contrib-4.8.1/modules。(最后的..
不能省略)
可以看到成功检测到我的11.8的cuda,但是没有cuDNN。不知道是不是新版的原因,我安装好cudnn后通过命令cat /usr/local/cuda/include/cudnn.h | grep CUDNN_MAJOR -A 2
查询cudnn版本没有任何输出,但是确实存在cudnn.h
,并在在使用cuda时也没有问题,就没有管这个问题了(后面在opencv使用cuda也没有报错)。
然后:sudo make –j15
,表示使用15个线程make,因cpu而异。
最后sudo make install
。
后续的操作参考ubuntu20.04+opencv+vscode添加环境变量。
编写c++代码测试:
#include <opencv2/opencv.hpp>
#include <opencv2/core/cuda.hpp>
// 编译命令:g++ main.cpp -o main `pkg-config --cflags --libs opencv4`
int main()
{
cv::cuda::printCudaDeviceInfo(cv::cuda::getDevice());
int count = cv::cuda::getCudaEnabledDeviceCount();
printf("GPU Device Count : %d \n", count);
return 0;
}
如果是不支持cuda的cv,则会报错:error: (-216:No CUDA support) The library is compiled without CUDA support in function 'throw_no_cuda'
实际上,在gpu上使用cv总体分为三步:1)将内存中的数据搬运到gpu上;2)使用cuda方法进行图像处理;3)将处理结果搬运到cpu上;
下面是一个将图像逆时针旋转90度的代码,其中Timer
类是一个计时器,从创建起计时,到离开作用域被销毁时的耗时。对于4960*8976的图像进行测试,RGB指3通道,Gray指单通道,测量upload、rotate和download三个阶段的耗时:
RGB(ms) | Gray(ms) | |
---|---|---|
upload | 9 | 3 |
rotate | 4 | 3 |
download | 37 | 12 |
可以看到对于简单的操作实际上耗时在数据的上传和下载。
#include <opencv2/opencv.hpp> #include <opencv2/cudawarping.hpp> #include "timer.h" int main(int argc, char *argv[]) { if (argc != 2) { // 检查是否传入图片路径 std::cout << "参数错误" << std::endl; } // 以灰度图模式读取输入图像 cv::Mat src = cv::imread(argv[1]); // 实际上对于gpu上的data,可以直接创建cv::cuda::GpuMat imgdata(w,h,*data),将gpudata的地址传给imgdata直接实例化 if (src.empty()) { std::cerr << "Failed to read input image!" << std::endl; return -1; } cv::Mat dst_cpu; // 在cpu创建一个Mat,接受处理后的图像结果 cv::cuda::GpuMat gpu_src, gpu_dst; // 在gpu创建两个Mat,分别储存旋转前后的图像(因为旋转前后尺寸不一样,所以必须要两个Mat) gpu_dst.create(8976, 4960, CV_8UC3); // 定义旋转后图像尺寸的Mat cv::Mat colorImage(8976, 4960, CV_8UC3); // 在cpu创建Mat,一个将灰度图转为RGB图的Mat { { Timer time("upload"); gpu_src.upload(src); // 将cpu上的src搬运到gpu的gpu_src中 } { Timer time("rotate"); // 计时器,从此刻计时直到离开作用域被销毁 // 逆时针旋转90度,将4960*8976转8976*4960,流程是按左上角旋转后,向下平移8976,然后用8976*4960的Mat接受 cv::cuda::rotate(gpu_src, gpu_dst, gpu_dst.size(), 90, 0, 8976); } // 将gpu的gpu_dst数据搬运到dst_cpu中(好像只有gpu的数据才有方法) { Timer time("download"); gpu_dst.download(dst_cpu); // gpu到cpu搬运数据很耗时,RGB数据耗时37ms,Gray数据耗时12ms } } return 0; }
# CMakeLists.txt cmake_minimum_required(VERSION 3.0) set(CMAKE_BUILD_TYPE Debug) project(MyProject) # 添加可执行文件 add_executable(draft draft.cpp src/timer.cpp) # 设置包含目录 target_include_directories(draft PRIVATE src) # 查找 OpenCV 库 find_package(OpenCV REQUIRED) # 将 OpenCV 库链接到可执行文件 target_link_libraries(draft PRIVATE ${OpenCV_LIBS} opencv_cudawarping)
当数据在cpu和gpu之间传输时,一定会有耗时。但是在cpu中存在虚拟内存,即在cpu上的数据可能是保存在位于磁盘的虚拟内存,这和直接在cpu物理内存上肯定是要慢的。所以在cv::cuda中提供了锁页的api,专门从物理内存中开辟空间存放数据:
cv::Mat snapshot(8976, 4960, CV_8UC1); // cpu上的数据
cv::cuda::registerPageLocked(snapshot); // 按大小分配锁页内存
gpu_dst.upload(snapshot);
// 处理代码
gpu_dst.download(snapshot);
cv::cuda::unregisterPageLocked(snapshot); // 下载后释放
该方法经过测试,在我的例子中将download从13ms下降到3ms,提升明显
声明时定义大小,可以显著提高效率
// 只声明不分配大小,cvtColor耗时22ms
cv::Mat img1;
cv::cvtColor(snapshot, img1, cv::COLOR_GRAY2BGR);
// 声明且分配大小,cvtColor耗时8ms
cv::Mat img1(8976, 4960, CV_8UC3);
cv::cvtColor(snapshot, img1, cv::COLOR_GRAY2BGR);
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。