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都安装完成,可以正常使用。

首先下载版本一致的opencvopencv-contrib(cuda库所在包),然后解压待用。

然后查询你显卡的Compute Capability,进入opencv-4.8.1后创建build文件夹,终端在build中打开后,执行:

bash 复制代码
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++代码测试:

cpp 复制代码
#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)
upload 9 3
rotate 4 3
download 37 12

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

cpp 复制代码
#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;
}
bash 复制代码
# 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,专门从物理内存中开辟空间存放数据:

cpp 复制代码
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创建优化

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

cpp 复制代码
// 只声明不分配大小,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);
相关推荐
滨HI02 小时前
opencv 计算面积、周长
人工智能·opencv·计算机视觉
禁默5 小时前
第四届图像处理、计算机视觉与机器学习国际学术会议(ICICML 2025)
图像处理·机器学习·计算机视觉
Antonio9155 小时前
【图像处理】tiff格式介绍
图像处理·人工智能
Antonio9155 小时前
【图像处理】png 格式详解
图像处理
AndrewHZ5 小时前
【图像处理基石】什么是alpha matting?
图像处理·人工智能·计算机视觉·matting·发丝分割·trimap·人像模式
LabVIEW开发1 天前
LabVIEW液位边缘检测
图像处理·计算机视觉·labview·labview知识·labview功能·labview程序
诗句藏于尽头1 天前
MediaPipe+OpenCV的python实现交互式贪吃蛇小游戏
人工智能·python·opencv
嫂子的姐夫1 天前
21-webpack介绍
前端·爬虫·webpack·node.js
拾荒的小海螺1 天前
C#:OpenCvSharp 实现图像处理的技术指南
开发语言·图像处理·c#
漂流瓶jz2 天前
Webpack中各种devtool配置的含义与SourceMap生成逻辑
前端·javascript·webpack