当前位置: 首页 > news >正文

opencv中使用cuda加速图像处理

opencv大多数只使用到了cpu的版本,实际上对于复杂的图像处理过程用cuda(特别是高分辨率的图像)可能会有加速效果。是否需要使用cuda需要思考:

  • 1、opencv的cuda库是否提供了想要的算子。在CUDA-accelerated Computer Vision你可以看到cv的cuda库提供了哪些方法。
  • 2、如果要使用cv的cuda库,会涉及到数据从cpu和gpu之间的交换。一张图片首先会被cpu读取到内存中,然后通过api将cpu中的数据搬运到gpu中,而cpu和gpu之间的数据搬运也是很耗时的,比如gpu_dst.download(dst_cpu)将gpu_dst数据搬运到dst_cpu,数据是8976*4960*3,耗时约37ms,如果你的图像处理比较简单,说不定数据搬运的耗时比直接在cpu上运行更长。

1、带cuda的opencv安装

这里的前提是你的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添加环境变量。

2、测试

编写c++代码测试:

#include <opencv2/opencv.hpp>
#include <opencv2/core/cuda.hpp>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'

3、在gpu上旋转图像

实际上,在gpu上使用cv总体分为三步:1)将内存中的数据搬运到gpu上;2)使用cuda方法进行图像处理;3)将处理结果搬运到cpu上;

下面是一个将图像逆时针旋转90度的代码,其中Timer类是一个计时器,从创建起计时,到离开作用域被销毁时的耗时。对于4960*8976\的图像进行测试,RGB指3通道,Gray指单通道,测量upload、rotate和download三个阶段的耗时:

RGB(ms)Gray(ms)
upload93
rotate43
download3712

可以看到对于简单的操作实际上耗时在数据的上传和下载。

#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); // 定义旋转后图像尺寸的Matcv::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)

4、解决数据上传和下载的耗时

当数据在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,提升明显

5、Mat创建优化

声明时定义大小,可以显著提高效率

// 只声明不分配大小,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);

相关文章:

  • 故障诊断 | 一文解决,TCN时间卷积神经网络模型的故障诊断(Matlab)
  • 假期刷题打卡--Day26
  • kafka 文件存储机制
  • nextcloud 优化扩展
  • 基于YOLOv8算法的照片角度分类项目实践
  • 第十三、十四个知识点:用javascript获取表单的内容并加密
  • 电子电器架构 —— 网关测试脚本分析
  • ASP.NET Core MVC 控制查询数据表后在视图显示
  • 传输频宽是啥?对网速影响有多大?
  • Java基于微信小程序的驾校报名小程序,附源码
  • 十分钟学会用springboot制作微信小程序富文本编辑器
  • 负载均衡SLB
  • TCP 和 UDP的区别
  • Vite 下一代的前端工具链,前端开发与构建工具
  • 百面嵌入式专栏(面试题)C语言面试题22道
  • CentOS从零开始部署Nodejs项目
  • Flannel解读
  • flask接收请求并推入栈
  • iOS 系统授权开发
  • Linux下的乱码问题
  • Material Design
  • Nginx 通过 Lua + Redis 实现动态封禁 IP
  • PhantomJS 安装
  • Redis 懒删除(lazy free)简史
  • select2 取值 遍历 设置默认值
  • Swoft 源码剖析 - 代码自动更新机制
  • vue和cordova项目整合打包,并实现vue调用android的相机的demo
  • vue学习系列(二)vue-cli
  • Webpack 4 学习01(基础配置)
  • Windows Containers 大冒险: 容器网络
  • 基于MaxCompute打造轻盈的人人车移动端数据平台
  • 码农张的Bug人生 - 见面之礼
  • 容器服务kubernetes弹性伸缩高级用法
  • 带你开发类似Pokemon Go的AR游戏
  • #我与Java虚拟机的故事#连载18:JAVA成长之路
  • #在线报价接单​再坚持一下 明天是真的周六.出现货 实单来谈
  • (NSDate) 时间 (time )比较
  • (附源码)springboot美食分享系统 毕业设计 612231
  • (附源码)计算机毕业设计高校学生选课系统
  • (原創) 如何將struct塞進vector? (C/C++) (STL)
  • ... fatal error LINK1120:1个无法解析的外部命令 的解决办法
  • ./indexer: error while loading shared libraries: libmysqlclient.so.18: cannot open shared object fil
  • .mysql secret在哪_MYSQL基本操作(上)
  • .NET Standard 的管理策略
  • .NET 发展历程
  • .NET文档生成工具ADB使用图文教程
  • .vue文件怎么使用_我在项目中是这样配置Vue的
  • @RequestMapping用法详解
  • [idea]关于idea开发乱码的配置
  • [IE编程] IE中使网页元素进入编辑模式
  • [LeetCode] Copy List with Random Pointer 拷贝带有随机指针的链表
  • [leetcode]Flatten Binary Tree to Linked List
  • [NOIP 2015] Day.1 T1 神奇的幻方 [模拟]
  • [NOIP2013]华容道
  • [Oracle][Metadata]如何查找与某一个功能相关的数据字典名