当前位置: 代码网 > 科技>电脑产品>CPU > opencv中使用cuda加速图像处理

opencv中使用cuda加速图像处理

2024年07月28日 CPU 我要评论
opencv大多数只使用到了cpu的版本,实际上对于复杂的图像处理过程用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中打开后,执行:

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

后续的操作参考添加环境变量。

2、测试

编写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'

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

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);
(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com