cuda在ubuntu的安装使用分享

原创 羽林君 2022-06-04 21:57


前言

之前给大家分享过opencv在jetson nano 2gb和ubuntu设备中使用并且展示了一些人脸识别等的小demo。但是对于图像处理,使用gpu加速是很常见 .(以下概念介绍内容来自百科和网络其他博主文章)

GPU介绍(从GPU诞生之日起,GPU的设计逻辑与CPU的设计逻辑相差很多。GPU从诞生之日起,它的定位是3D图形渲染设备。在设计GPU时从其功能出发,把更多的晶体管用于数据处理。这使得GPU相比CPU有更强的单精度浮点运算能力。人们为了充分利用GPU的性能,使用了很多方法。这)加速处理是比较常见的。

在而GPU加速的软件实现中我们可能会听到 opengl opencl cuda这些名词。下面再给大家分别介绍一下这几种区别:

OpenGL(英语:Open Graphics Library,译名:开放图形库或者“开放式图形库”)是用于渲染2D、3D矢量图形的跨语言、跨平台的应用程序编程接口(API)。这个接口由近350个不同的函数调用组成,用来绘制从简单的图形比特到复杂的三维景象。而另一种程序接口系统是仅用于Microsoft Windows上的Direct3D。OpenGL常用于CAD、虚拟现实、科学可视化程序和电子游戏开发。

OpenCl(是由苹果(Apple)公司发起,业界众多著名厂商共同制作的面向异构系统通用目的并行编程的开放式、免费标准,也是一个统一的编程环境。便于软件开发人员为高性能计算服务器、桌面计算系统、手持设备编写高效轻便的代码,而且广泛适用于多核心处理器(CPU)、图形处理器(GPU)、Cell类型架构以及数字信号处理器(DSP)等其他并行处理器,在游戏、娱乐、科研、医疗等各种领域都有广阔的发展前景。)

从2007年以后,基于CUDA和OpenCL这些被设计成具有近似于高阶语言的语法特性的新GPGPU语言,降低了人们使用GPGPU的难度,平缓了开始时的学习曲线。使得在GPGPU领域,OpenGL中的GLSL逐渐退出了人们的视线。来源:

简单说OpenCL与OpenGL一样,都是基于硬件API的编程。OpenGL是针对图形的,而OpenCL则是针对并行计算的API,而针对并行计算下面还有一个cuda。

CUDA(Compute Unified Device Architecture,统一计算架构)是由英伟达NVIDIA所推出的一种整合技术,是该公司对于GPGPU的正式名称。透过这个技术,使用者可利用NVIDIA的GeForce 8以后的GPU和较新的Quadro GPU进行计算。亦是首次可以利用GPU作为C-编译器的开发环境。NVIDIA行销的时候,往往将编译器与架构混合推广,造成混乱。实际上,CUDA可以相容OpenCL或者自家的C-编译器。无论是CUDA C-语言或是OpenCL,指令最终都会被驱动程式转换成PTX代码,交由显示核心计算。

通俗的介绍,cuda是nvidia公司的生态,它前者是配备完整工具包、针对单一供应商(NVIDIA)的成熟的开发平台,opencl是一个开源的标准。

CUDA是NVIDIA GPU编程语言, OpenCL是异构计算库。CUDA和C++虽然都可以用nvcc编译,但C++只能在CPU上跑,CUDA只能在GPU上跑;而OpenCL并不局限于某个计算设备,旨在将同样的任务通过其提供的抽象接口在多种硬件上运行(CPU,GPU,FPGA,etc)

跨平台性和通用性上OpenCL占有很大优势(这也是很多National Laboratory使用OpenCL进行科学计算的最主要原因)。OpenCL支持包括ATI,NVIDIA,Intel,ARM在内的多类处理器,并能支持运行在CPU的并行代码,同时还独有Task-Parallel Execution Mode,能够更好的支持Heterogeneous Computing。这一点是仅仅支持数据级并行并仅能在NVIDIA众核处理器上运行的CUDA无法做到的。

在开发者友好程度CUDA在这方面显然受更多开发者青睐。原因在于其统一的开发套件(CUDA Toolkit, NVIDIA GPU Computing SDK以及NSight等等)、非常丰富的库(cuFFT, cuBLAS, cuSPARSE, cuRAND, NPP, Thrust)以及NVCC(NVIDIA的CUDA编译器)所具备的PTX(一种SSA中间表示,为不同的NVIDIA GPU设备提供一套统一的静态ISA)代码生成、离线编译等更成熟的编译器特性。相比之下,使用OpenCL进行开发,只有AMD对OpenCL的驱动相对成熟。 来源

今天的主题来自介绍cuda的使用,前面这部分概念性的介绍(来源都是网络,只有一小部分是自己写的)只是帮助大家好理解今天要使用的cuda,那么开始进入正题。(自己本身的电脑带NVIDIA的显卡,以及手里面有jetson nano的NVIDIA板卡,所以才使用cuda,其他朋友使用请注意自己手中的硬件是否有NVIDIA的显卡)。

接下来,我大致分为三个部分介绍,一、cuda并行计算原理介绍 ,二、cuda环境安装和直接使用,三、opencv中cuda安装和使用

作者:良知犹存

转载授权以及围观:欢迎关注微信公众号:羽林君

或者添加作者个人微信:become_me

cuda并行计算原理介绍

GPU背景介绍

GPU里有很多Compute Unit(计算单元), 这些单元是由专门的处理逻辑, 很多register, 和L1 cache 组成. Memory access subsystem 把GPU 和RAM 连起来(通常是个L2 cache). Threads 以SIMT的形式运行: 多个thread共享一个instruction unit. 对NV GPU来说, 32 thread 组成一个warps, 对AMD GPU 来说, 64 threads 叫做一个wave fronts. 本文只使用warp的定义. 想达到最高速度, 一定要讨论SIMT, 因为它影响着memory access 也会造成code的序列化(serialization, parallelism的反面)

kernel function里会指出哪些code由一条thread处理, host program会决定多少条thread来处理一个kernel. 一个work group 里的thread可以通过barrier synchronization, 共享L1 cache 来互相协力. 再由compute unit处理这些work group. 能同时被conpute unit 运行的work group数量有限, 所以很多得等到其他完成之后再运行. 需要处理的work group的size和数量还有最大并行数量被称为kernel的execution configuration(运行配置).

kernel 的占用率就是指同时运行的thread数量除以最大数量. 传统建议就是提升这个占用率来获得更好的性能, 但也有一些其他因素比如ILP, MLP, instruction latencies, 也很关键.内容来源

  • GPU架构特点

首先我们先谈一谈串行计算和并行计算。我们知道,高性能计算的关键利用多核处理器进行并行计算。

当我们求解一个计算机程序任务时,我们很自然的想法就是将该任务分解成一系列小任务,把这些小任务一一完成。在串行计算时,我们的想法就是让我们的处理器每次处理一个计算任务,处理完一个计算任务后再计算下一个任务,直到所有小任务都完成了,那么这个大的程序任务也就完成了。

串行计算的缺点非常明显,如果我们拥有多核处理器,我们可以利用多核处理器同时处理多个任务时,而且这些小任务并没有关联关系(不需要相互依赖,比如我的计算任务不需要用到你的计算结果),那我们为什么还要使用串行编程呢?为了进一步加快大任务的计算速度,我们可以把一些独立的模块分配到不同的处理器上进行同时计算(这就是并行),最后再将这些结果进行整合,完成一次任务计算。下图就是将一个大的计算任务分解为小任务,然后将独立的小任务分配到不同处理器进行并行计算,最后再通过串行程序把结果汇总完成这次的总的计算任务。

所以,一个程序可不可以进行并行计算,关键就在于我们要分析出该程序可以拆分出哪几个执行模块,这些执行模块哪些是独立的,哪些又是强依赖强耦合的,独立的模块我们可以试着设计并行计算,充分利用多核处理器的优势进一步加速我们的计算任务,强耦合模块我们就使用串行编程,利用串行+并行的编程思路完成一次高性能计算。

接下来我们谈谈CPU和GPU有什么区别,他们俩各自有什么特点,我们在谈并行、串行计算时多次谈到“多核”的概念,现在我们先从“核”的角度开始这个话题。首先CPU是专为顺序串行处理而优化的几个核心组成。而GPU则由数以千计的更小、更高效的核心组成,这些核心专门为同时处理多任务而设计,可高效地处理并行任务。也就是,CPU虽然每个核心自身能力极强,处理任务上非常强悍,无奈他核心少,在并行计算上表现不佳;反观GPU,虽然他的每个核心的计算能力不算强,但他胜在核心非常多,可以同时处理多个计算任务,在并行计算的支持上做得很好。

GPU和CPU的不同硬件特点决定了他们的应用场景,CPU是计算机的运算和控制的核心,GPU主要用作图形图像处理。图像在计算机呈现的形式就是矩阵,我们对图像的处理其实就是操作各种矩阵进行计算,而很多矩阵的运算其实可以做并行化,这使得图像处理可以做得很快,因此GPU在图形图像领域也有了大展拳脚的机会。下图表示的就是一个多GPU计算机硬件系统,可以看出,一个GPU内存就有很多个SP和各类内存,这些硬件都是GPU进行高效并行计算的基础。

现在再从数据处理的角度来对比CPU和GPU的特点。CPU需要很强的通用性来处理各种不同的数据类型,比如整型、浮点数等,同时它又必须擅长处理逻辑判断所导致的大量分支跳转和中断处理,所以CPU其实就是一个能力很强的伙计,他能把很多事处理得妥妥当当,当然啦我们需要给他很多资源供他使用(各种硬件),这也导致了CPU不可能有太多核心(核心总数不超过16)。而GPU面对的则是类型高度统一的、相互无依赖的大规模数据和不需要被打断的纯净的计算环境,GPU有非常多核心(费米架构就有512核),虽然其核心的能力远没有CPU的核心强,但是胜在多, 在处理简单计算任务时呈现出“人多力量大”的优势,这就是并行计算的魅力。内容来源

整理一下两者特点就是:

  • 1.CPU:擅长流程控制和逻辑处理,不规则数据结构,不可预测存储结构,单线程程序,分支密集型算法
  • 2.GPU:擅长数据并行计算,规则数据结构,可预测存储模式
CUDA存储器类型:
每个线程拥有自己的 register寄存器 and loacal memory 局部内存
每个线程块拥有一块 shared memory 共享内存
所有线程都可以访问 global memory 全局内存

还有,可以被所有线程访问的
     只读存储器:
     constant memory (常量内容) and texture memory
    
a. 寄存器Register
   寄存器是GPU上的高速缓存器,其基本单元是寄存器文件,每个寄存器文件大小为32bit.
   Kernel中的局部(简单类型)变量第一选择是被分配到Register中。
       特点:每个线程私有,速度快。

b. 局部存储器 local memory

   当register耗尽时,数据将被存储到local memory。
   如果每个线程中使用了过多的寄存器,或声明了大型结构体或数组,
   或编译器无法确定数组大小,线程的私有数据就会被分配到local memory中。
   
       特点:每个线程私有;没有缓存,慢。
       注:在声明局部变量时,尽量使变量可以分配到register。如:
       unsigned int mt[3];
       改为:unsigned int mt0, mt1, mt2;

c. 共享存储器 shared memory
       可以被同一block中的所有线程读写
       特点:block中的线程共有;访问共享存储器几乎与register一样快.

d. 全局存储器 global memory
  特点:所有线程都可以访问;没有缓存

e. 常数存储器constant memory
   用于存储访问频繁的只读参数
   特点:只读;有缓存;空间小(64KB)
   注:定义常数存储器时,需要将其定义在所有函数之外,作用于整个文件

f. 纹理存储器 texture memory
       是一种只读存储器,其中的数据以一维、二维或者三维数组的形式存储在显存中。
   在通用计算中,其适合实现图像处理和查找,对大量数据的随机访问和非对齐访问也有良好的加速效果。
       特点:具有纹理缓存,只读。
threadIdx,blockIdx, blockDim, gridDim之间的区别与联系
 在启动kernel的时候,要通过指定gridsize和blocksize才行
 dim3 gridsize(2,2);   // 2行*2列*1页 形状的线程格,也就是说 4个线程块
    gridDim.x,gridDim.y,gridDim.z相当于这个dim3的x,y,z方向的维度,这里是2*2*1。
序号从0到3,且是从上到下的顺序,就是说是下面的情况:
 具体到 线程格 中每一个 线程块的 id索引为:
 
    grid 中的 blockidx 序号标注情况为:      0     2
                                          1     3
           
   dim3 blocksize(4,4);  // 线程块的形状,4行*4列*1页,一个线程块内部共有 16个线程
 
   blockDim.x,blockDim.y,blockDim.z相当于这个dim3的x,y,z方向的维度,
   这里是4*4*1.序号是0-15,也是从上到下的标注:

block中的 threadidx 序号标注情况   0       4       8      12 
      1       5       9      13
      2       6       10     14
      3       7       11     15
1.  1维格子,1维线程块,N个线程======

 实际的线程id tid =  blockidx.x * blockDim.x + threadidx.x
 块id   0 1 2 3 
 线程id 0 1 2 3 4
2. 1维格子,2D维线程块
 块id   1 2 3 
 线程id  0  2
        1  3
         块id           块线程总数
 实际的线程id tid =  blockidx.x * blockDim.x * blockDim.y + 
         当前线程行数    每行线程数
      threadidx.y * blockDim.x   + 
         当前线程列数
      threadidx.x

更加详细信息,大家可以参考这两篇文章:

https://www.cnblogs.com/skyfsm/p/9673960.html

https://github.com/Ewenwan/ShiYanLou/tree/master/CUDA

安装cuda使用环境

cuda使用中 首先我们直接安装CUDA toolkit (cuDNN是用于配置深度学习使用,本次我没有使用到,就是下载cuDNN的库放置到对应的链接库目录,大家可以自行去搜索安装),然后直接调用cuda的api进行一些计算的测试,还有是可以安装opencv中的cuda接口,通过opencv中的cuda使用。

直接使用cuda

直接安装CUDA ,首先需要下载安装包

  • CUDA toolkit(toolkit就是指工具包)
  • cuDNN 注:cuDNN 是用于配置深度学习使用(这次我没有使用就没有下载)

官方教程

CUDA:Installation Guide Windows :: CUDA Toolkit Documentation(链接)

  • 注:按照自己版本去查看资料,打开链接后,右上角处有一个older选项,大家可以选择自己对应的版本文档。

cuDNN:Installation Guide :: NVIDIA Deep Learning cuDNN Documentation(链接)

安装cuda要注意一个版本匹配问题,就是我们不同的显卡是对应不同的驱动。所以第一步就是查看显卡驱动版本,我使用的是Ubuntu,和window安装有些区别。第一如果我们安装过自己的显卡驱动, 使用 nvidia-smi命令查看支持的cuda版本

如果没有安装可以选择ubuntu的bash界面搜索附加驱动,然后进行安装。参考

当然由于ubuntu本身也有不同的版本支持,大家也可以直接到驱动的官网进行查找,里面有专门的ubuntu20适配版本选项,其中由于我使用的是ubuntu20,而CUDA11.0以上版本才支持Ubuntu20.04,所以直接就选择了最新的。

官方地址:https://developer.nvidia.com/cuda-toolkit-archive

选择CUDA Toolkit 11.6.0之后,里面有几种选项,分别是本地deb安装、网络下载安装和可执行文件安装,我因为网络不太顺畅原因选择了第一个选项,最后下载下载下来的deb文件有2.7G,大家做好下载大文件的准备。

wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64/cuda-ubuntu2004.pin
sudo mv cuda-ubuntu2004.pin /etc/apt/preferences.d/cuda-repository-pin-600
wget https://developer.download.nvidia.com/compute/cuda/11.6.0/local_installers/cuda-repo-ubuntu2004-11-6-local_11.6.0-510.39.01-1_amd64.deb
sudo dpkg -i cuda-repo-ubuntu2004-11-6-local_11.6.0-510.39.01-1_amd64.deb
sudo apt-key add /var/cuda-repo-ubuntu2004-11-6-local/7fa2af80.pub
sudo apt-get update
sudo apt-get -y install cuda

这时候cuda就安装好了,接下来就是使用了。

大家可以使用cuda命令,tab就可以出现对应的命令,或者使用nvcc编译对应的cuda的.cu文件进行验证。

大家可以通过以下链接里面的文档指导写符合自己需要的功能:https://docs.nvidia.com/cuda/archive/11.6.0/cuda-c-programming-guide/index.html

下面官网API文件目录:

Versioned Online Documentation

官方示例:

接下来开始进行cuda的使用:输出hello world

#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include 
#include 
using namespace std;

__global__ void kernel(void) { //带有了__global__这个标签,表示这个函数是在GPU上运行
  printf("hello world gpu \n");
}
int main() {
  kernel<<<1, 1>>>();//调用除了常规的参数之外,还增加了<<<>>>修饰,其中第一个1,代表线程格里只有一个线程块;第二个1,代表一个线程块里只有一个线程。
    cudaError_t cudaStatus;

    cudaStatus = cudaDeviceSynchronize();
    if (cudaStatus != cudaSuccess) {
        fprintf(stderr, "cudaDeviceSynchronize returned error code %d after launching addKernel!\n", cudaStatus);
    }

  return 0;
}

这里面记得使用cudaDeviceSynchronize函数,等待GPU完成运算。如果不加的话,你将看不到printf的输出

编译选项 nvcc test_cuda_hello.cu

这个程序中GPU调用的函数和普通的C程序函数的区别

  • 函数的调用除了常规的参数之外,还增加了<<<>>>修饰。
  • 调用通过<<<参数1,参数2>>>,用于说明内核函数中的线程数量,以及线程是如何组织的。
  • 以线程格(Grid)的形式组织,每个 线程格 由若干个 线程块(block)组成,
  • 而每个线程块又由若干个线程(thread)组成。
  • 是以block为单位执行的。

接下来,我们再来使用一个demo,使用一个简单的计算,将其放置于GPU进行计算

// 输入变量 以指针方式传递===================================
#include 
#include 
#include 
#include 

// 输入变量 全部为指针  类似数据的引用
__global__ void gpuAdd(int *d_a, int *d_b, int *d_c) 
{
 *d_c = *d_a + *d_b;
}

int main(void) 
{
 // CPU变量
 int h_a,h_b, h_c;
 // CPU  指针 变量 指向 GPU数据地址
 int *d_a,*d_b,*d_c;
 // 初始化CPU变量
 h_a = 1;
 h_b = 4;
  
 // 分配GPU 变量内存
 cudaMalloc((void**)&d_a, sizeof(int));
 cudaMalloc((void**)&d_b, sizeof(int));
 cudaMalloc((void**)&d_c, sizeof(int));
  
 // 输入变量 CPU 拷贝到 GPU   右 到 左
 cudaMemcpy(d_a, &h_a, sizeof(int), cudaMemcpyHostToDevice);
 cudaMemcpy(d_b, &h_b, sizeof(int), cudaMemcpyHostToDevice);
  
 // 调用核函数
 gpuAdd << <1, 1 >> > (d_a, d_b, d_c);
  
 // 拷贝GPU数据结果 d_c 到 CPU变量
 cudaMemcpy(&h_c, d_c, sizeof(int), cudaMemcpyDeviceToHost);
 printf("Passing Parameter by Reference Output: %d + %d = %d\n", h_a, h_b, h_c);
  
 // 清理GPU内存 Free up memory 
 cudaFree(d_a);
 cudaFree(d_b);
 cudaFree(d_c);
 return 0;
}

CUDA代码中比较重要的函数:

cudaMalloc与C语言中的malloc函数一样,只是此函数在GPU的内存你分配内存。

addKernel<<<1, size>>>

这里就涉及了GPU和主机之间的内存交换了,cudaMalloc是在GPU的内存里开辟一片空间, 然后通过操作之后,这个内存里有了计算出来内容,再通过cudaMemcpy这个函数把内容从GPU复制出来。可以在设备代码中使用cudaMalloc()分配的指针进行设备内存读写操作;cudaMalloc()分配的指针传递给在设备上执行的函数;但是不可以在主机代码中使用cudaMalloc()分配的指针进行主机内存读写操作(即不能进行解引用)。cudaFree与c语言中的free()函数一样,只是此函数释放的是cudaMalloc()分配的内存。

cudaMemcpy与c语言中的memcpy函数一样,只是此函数可以在主机内存和GPU内存之间互相拷贝数据。此外与C中的memcpy()一样,以同步方式执行,即当函数返回时,复制操作就已经完成了,并且在输出缓冲区中包含了复制进去的内容。相应的有个异步方式执行的函数cudaMemcpyAsync().

该函数第一个参数是目的指针,第二个参数是源指针,第三个参数是复制内存的大小,第四个参数告诉运行时源指针,这是一个什么类型的指针,即把内存从哪里复制到哪里。第四个参数可以选用以下的形式:参考

  • cudaMemcpyHostToDevice:从主机复制到设备;
  • cudaMemcpyDeviceToHost:从设备复制到主机;
  • cudaMemcpyDeviceToDevice:从设备复制到设备;
  • cudaMemcpyHostToHost:从主机复制到主机;

调用的核函数前面已经介绍过,此处不再赘述。还有其他函数大家可以自行参考官网的API介绍。

大家也可以看《GPU高性能编程CUDA实战》这本书

opencv中cuda库安装与使用

除了直接使用cuda,opencv也有相应的cuda的库,调用cuda从而在opencv实现gpu加速功能。这就需要安装CUDA,并从OpenCV编译安装时候安装好对应cv::cuda库。

首先要使用cv::cuda里面对应的cv::cuda::add 、cv::cuda::multiply,我们需要安装opencv要配合OpenCV Contrib库。

OpenCV Contrib库是非官方的第三方开发扩充库。通过这个库,我们能使用如dnn、相机标注、3D成像、ArUco、物体追踪,甚至是需付费的SURF、SIFT特征点提取算法

安装OpenCV Contrib也是需要和opencv进行版本匹配的。大家可以在下面位置进行对应版本下载:

  • contrib库:https://github.com/opencv/opencv_contrib/tags
  • opencv版本:https://opencv.org/releases.html

对与我个人而言,opencv版本是 4.4.5

$ pkg-config opencv4 --modversion 
4.4.5

下载好对应的opencv和opencv_contrib库,开始进行编译,和之前单独opencv编译类似,解压下载好的文件,然后在opencv建立一个build目录,在里面进行cmake 编译。

在这里给大家在注解一些相关选项的含义:

 cmake -D CMAKE_BUILD_TYPE=Release\
    -D ENABLE_CXX11=ON\
          -D CMAKE_INSTALL_PREFIX=/usr/local\
          -D WITH_CUDA=ON\
           -D CUDA_ARCH_BIN=${cuda_compute}\
           -D CUDA_ARCH_PTX=""\
           -D ENABLE_FAST_MATH=ON\
           -D CUDA_FAST_MATH=ON\
           -D WITH_CUBLAS=ON\
            -D WITH_LIBV4L=ON\
            -D WITH_GSTREAMER=ON\
             -D WITH_GSTREAMER_0_10=OFF\
             -D WITH_QT=ON\
             -D WITH_OPENGL=ON\
             -D CUDA_NVCC_FLAGS="--expt-relaxed-constexpr" \
             -D CUDA_TOOLKIT_ROOT_DIR=/usr/local/cuda-11.6\
             -D WITH_TBB=ON\
             -D OPENCV_EXTRA_MODULES_PATH=/home/lyn/Documents/application/opencv_contrib-4.5.5/modules          ../
  • CUDA_ARCH_BIN=${cuda_compute} 显卡算力,大家可以对照我最后给算力查询自行查询,也可以进行系统自己查找匹配版本。

  • WITH_QT=ON\ 这是是QT的编译选项 大家可以按照自己需要选择是否用QT进行编译开与关。

  • CUDA_TOOLKIT_ROOT_DIR 这是自己电脑安装的cuda 版本

  • OPENCV_EXTRA_MODULES_PATH=/home/lyn/Documents/application/opencv_contrib-4.5.5/modules 这是解压好后的opencv_contrib目录

安装时候可能会遇到如下问题:

In file included from /home/lyn/Documents/application/opencv-4.5.5/build/modules/python_bindings_generator/pyopencv_custom_headers.h:7,
                 from /home/lyn/Documents/application/opencv-4.5.5/modules/python/src2/cv2.cpp:88:
/home/lyn/Documents/application/opencv_contrib-4.5.5/modules/phase_unwrapping/misc/python/pyopencv_phase_unwrapping.hpp:2:13: error: ‘phase_unwrapping’ in namespace ‘cv’ does not name a type
    2 | typedef cv::phase_unwrapping::HistogramPhaseUnwrapping::Params HistogramPhaseUnwrapping_Params;

打开-D ENABLE_CXX11=ON 打开C++11的选项 (这就是上面命令使用C++11的原因)

最后编译成功:

sudo make install这个时候opencv中的cuda库就安装好了

介绍一个简单例子:

CMakeLists.txt文件

# 声明要求的 cmake 最低版本
cmake_minimum_required( VERSION 2.8 )
# 声明一个 cmake 工程
project(opencv_cuda)
# 设置编译模式
set( CMAKE_BUILD_TYPE "Debug" )
set( CMAKE_CXX_FLAGS "-std=c++11")
#添加OPENCV库
find_package(OpenCV REQUIRED)
#添加OpenCV头文件
include_directories(${OpenCV_INCLUDE_DIRS})
#显示OpenCV_INCLUDE_DIRS的值
message(${OpenCV_INCLUDE_DIRS})
add_executable(target test_cuda_cv.cpp)
# 将库文件链接到可执行程序上
target_link_libraries(target  ${OpenCV_LIBS} ) 

test_cuda_cv.cpp文件

#include 
#include "opencv2/opencv.hpp"
#include  
#include 
#include 

int main (int argc, char* argv[])
{
    //Read Two Images 
    cv::Mat h_img1 = cv::imread( "/home/lyn/Documents/work-data/test_code/opencv/learn_code/"
      "opencv_tutorial_data-master/images/sp_noise.png");
    cv::Mat h_img2 = cv::imread( "/home/lyn/Documents/work-data/test_code/opencv/learn_code/"
      "opencv_tutorial_data-master/images/sp_noise.png");      
    // cv::Mat h_img2 = cv::imread("/home/lyn/Documents/work-data/test_code/opencv/learn_code/c++/build/filter_line.png");
  
  // cv::namedWindow( "1", cv::WINDOW_FREERATIO); 
  // cv::imshow("1",h_img1);  

  // cv::namedWindow( "2", cv::WINDOW_FREERATIO); 
  // cv::imshow("2",h_img2);  

    cv::Mat h_result1;


    #if 0
    // cv::add(h_img1, h_img2,h_result1);//加法操作 api
    cv::multiply(h_img1,cv::Scalar(2, 2, 2),h_result1);//乘法操作 api

    #else
    // 定义GPU数据
    cv::cuda::GpuMat d_result1,d_img1, d_img2;
    
    // CPU 到 GPU   
    d_img1.upload(h_img1);
    d_img2.upload(h_img2);
    
    // 调用GPU接执行 mat substract
    // cv::cuda::add(d_img1, d_img2,d_result1);
    cv::cuda::multiply(d_img1,cv::Scalar(2, 2, 2),d_result1);//乘法操作 api

    // gpu 结果 到 cpu
    d_result1.download(h_result1);

    #endif 
 

    // 显示,保存
  // cv::namedWindow( "Image1", cv::WINDOW_FREERATIO); 
    cv::imshow("Image1 ", h_img1);

  // cv::namedWindow( "Image2", cv::WINDOW_FREERATIO); 
    cv::imshow("Image2 ", h_img2);

  // cv::namedWindow( "Result_Subtraction", cv::WINDOW_FREERATIO); 
    cv::imshow("Result_Subtraction ", h_result1);
    cv::imwrite("/home/lyn/Documents/work-data/test_code/opencv/learn_code/c++/result_add.png", h_result1);
    cv::waitKey();
    return 0;
}

大家也可以使用opencv中的其他cuda库,具体函数使用在下面

官方链接:https://docs.opencv.org/4.5.5/d1/d1e/group__cuda.html

大家可以在当前页面的版本选择中,选择和自己一致的opencv版本,进行参考:

例如我查询cv::cuda::add函数,就在 Operations on Matrices -> Per-element Operations

也可以看具体的数据结构含义:例如 cv::cuda::GpuMat

官方的解释是:具有引用计数的 GPU 内存的基本存储类。GpuMat类似于Mat,不过管理的是GPU内存,也就是显存。GpuMat和Mat相比,存在以下限制:

  • 不支持任意尺寸(只支持2D,也就是二维数据)
  • 没有返回对其数据的引用的函数(因为 GPU 上的引用对 CPU 无效)
  • 不支持c++的模板技术。

总之是很方便的,大家可以按照自己需要进行查询信息。

附录:这是官网提供算力表,大家可以参考选择自己的要求算力的显卡。

https://developer.nvidia.com/zh-cn/cuda-gpus

结语

这就是我自己的一些cuda的使用分享,因为篇幅所限,下一篇,我们聊聊cuda实际使用gpu加速和cpu使用的对比,以及它两使用场景分析。如果大家有更好的想法和需求,也欢迎大家加我好友交流分享哈。


作者:良知犹存,白天努力工作,晚上原创公号号主。公众号内容除了技术还有些人生感悟,一个认真输出内容的职场老司机,也是一个技术之外丰富生活的人,摄影、音乐 and 篮球。关注我,与我一起同行。

                              ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧  END  ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧

推荐阅读

【1】jetson nano开发使用的基础详细分享

【2】Linux开发coredump文件分析实战分享

【3】CPU中的程序是怎么运行起来的 必读

【4】cartographer环境建立以及建图测试

【5】设计模式之简单工厂模式、工厂模式、抽象工厂模式的对比

本公众号全部原创干货已整理成一个目录,回复[ 资源 ]即可获得。



羽林君 某嵌入式程序猿分享技术、生活、人生云云文字。如有诗云:去年今日此门中,人面桃花相映红。人面不知何处去,桃花依旧笑春风。
评论
  • 智能汽车可替换LED前照灯控制运行的原理涉及多个方面,包括自适应前照灯系统(AFS)的工作原理、传感器的应用、步进电机的控制以及模糊控制策略等。当下时代的智能汽车灯光控制系统通过车载网关控制单元集中控制,表现特殊点的有特斯拉,仅通过前车身控制器,整个系统就包括了灯光旋转开关、车灯变光开关、左LED前照灯总成、右LED前照灯总成、转向柱电子控制单元、CAN数据总线接口、组合仪表控制单元、车载网关控制单元等器件。变光开关、转向开关和辅助操作系统一般连为一体,开关之间通过内部线束和转向柱装置连接为多,
    lauguo2013 2024-12-10 15:53 78浏览
  • RK3506 是瑞芯微推出的MPU产品,芯片制程为22nm,定位于轻量级、低成本解决方案。该MPU具有低功耗、外设接口丰富、实时性高的特点,适合用多种工商业场景。本文将基于RK3506的设计特点,为大家分析其应用场景。RK3506核心板主要分为三个型号,各型号间的区别如下图:​图 1  RK3506核心板处理器型号场景1:显示HMIRK3506核心板显示接口支持RGB、MIPI、QSPI输出,且支持2D图形加速,轻松运行QT、LVGL等GUI,最快3S内开
    万象奥科 2024-12-11 15:42 66浏览
  • 肖特基具有很多的应用场景, 可以做同步整流,防止电流倒灌和电源反接等,但是随着电源电流的增大,肖特基导通正向压降0.3~0.7v的劣势也越发明显,产生了很多的热,对于工程师的散热设计是个考验,增加了工程师的设计难度和产品成本,目前一种新的理想二极管及其控制器,目前正在得到越来越广泛的应用- BMS,无人机,PLC,安防,家电,电动工具,汽车等都在快速普及理想二极管有三种架构,内置电荷泵的类似无锡明芯微MX5050T这种,驱动能力会弱点,静态功耗200uA,外置电荷泵MX74700T的这种驱动能力
    王萌 2024-12-10 08:51 85浏览
  • 全球知名半导体制造商ROHM Co., Ltd.(以下简称“罗姆”)宣布与Taiwan Semiconductor Manufacturing Company Limited(以下简称“台积公司”)就车载氮化镓功率器件的开发和量产事宜建立战略合作伙伴关系。通过该合作关系,双方将致力于将罗姆的氮化镓器件开发技术与台积公司业界先进的GaN-on-Silicon工艺技术优势结合起来,满足市场对高耐压和高频特性优异的功率元器件日益增长的需求。氮化镓功率器件目前主要被用于AC适配器和服务器电源等消费电子和
    电子资讯报 2024-12-10 17:09 84浏览
  • 【萤火工场CEM5826-M11测评】OLED显示雷达数据本文结合之前关于串口打印雷达监测数据的研究,进一步扩展至 OLED 屏幕显示。该项目整体分为两部分: 一、框架显示; 二、数据采集与填充显示。为了减小 MCU 负担,采用 局部刷新 的方案。1. 显示框架所需库函数 Wire.h 、Adafruit_GFX.h 、Adafruit_SSD1306.h . 代码#include #include #include #include "logo_128x64.h"#include "logo_
    无垠的广袤 2024-12-10 14:03 69浏览
  • 天问Block和Mixly是两个不同的编程工具,分别在单片机开发和教育编程领域有各自的应用。以下是对它们的详细比较: 基本定义 天问Block:天问Block是一个基于区块链技术的数字身份验证和数据交换平台。它的目标是为用户提供一个安全、去中心化、可信任的数字身份验证和数据交换解决方案。 Mixly:Mixly是一款由北京师范大学教育学部创客教育实验室开发的图形化编程软件,旨在为初学者提供一个易于学习和使用的Arduino编程环境。 主要功能 天问Block:支持STC全系列8位单片机,32位
    丙丁先生 2024-12-11 13:15 45浏览
  • 本文介绍Linux系统(Ubuntu/Debian通用)挂载exfat格式U盘的方法,触觉智能RK3562开发板演示,搭载4核A53处理器,主频高达2.0GHz;内置独立1Tops算力NPU,可应用于物联网网关、平板电脑、智能家居、教育电子、工业显示与控制等行业。修改对应的内核配置文件# 进入sdk目录cdrk3562_linux# 编辑内核配置文件vi./kernel-5.10/arch/arm64/configs/rockchip_linux_defconfig注:不清楚内核使用哪个defc
    Industio_触觉智能 2024-12-10 09:44 90浏览
  • 近日,搭载紫光展锐W517芯片平台的INMO GO2由影目科技正式推出。作为全球首款专为商务场景设计的智能翻译眼镜,INMO GO2 以“快、准、稳”三大核心优势,突破传统翻译产品局限,为全球商务人士带来高效、自然、稳定的跨语言交流体验。 INMO GO2内置的W517芯片,是紫光展锐4G旗舰级智能穿戴平台,采用四核处理器,具有高性能、低功耗的优势,内置超微高集成技术,采用先进工艺,计算能力相比同档位竞品提升4倍,强大的性能提供更加多样化的应用场景。【视频见P盘链接】 依托“
    紫光展锐 2024-12-11 11:50 44浏览
  • 一、SAE J1939协议概述SAE J1939协议是由美国汽车工程师协会(SAE,Society of Automotive Engineers)定义的一种用于重型车辆和工业设备中的通信协议,主要应用于车辆和设备之间的实时数据交换。J1939基于CAN(Controller Area Network)总线技术,使用29bit的扩展标识符和扩展数据帧,CAN通信速率为250Kbps,用于车载电子控制单元(ECU)之间的通信和控制。小北同学在之前也对J1939协议做过扫盲科普【科普系列】SAE J
    北汇信息 2024-12-11 15:45 73浏览
  • 时源芯微——RE超标整机定位与解决详细流程一、 初步测量与问题确认使用专业的电磁辐射测量设备,对整机的辐射发射进行精确测量。确认是否存在RE超标问题,并记录超标频段和幅度。二、电缆检查与处理若存在信号电缆:步骤一:拔掉所有信号电缆,仅保留电源线,再次测量整机的辐射发射。若测量合格:判定问题出在信号电缆上,可能是电缆的共模电流导致。逐一连接信号电缆,每次连接后测量,定位具体哪根电缆或接口导致超标。对问题电缆进行处理,如加共模扼流圈、滤波器,或优化电缆布局和屏蔽。重新连接所有电缆,再次测量
    时源芯微 2024-12-11 17:11 70浏览
  • 习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习笔记&记录学习习笔记&记学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记
    youyeye 2024-12-10 16:13 105浏览
  • 我的一台很多年前人家不要了的九十年代SONY台式组合音响,接手时只有CD功能不行了,因为不需要,也就没修,只使用收音机、磁带机和外接信号功能就够了。最近五年在外地,就断电闲置,没使用了。今年9月回到家里,就一个劲儿地忙着收拾家当,忙了一个多月,太多事啦!修了电气,清理了闲置不用了的电器和电子,就是一个劲儿地扔扔扔!几十年的“工匠式”收留收藏,只能断舍离,拆解不过来的了。一天,忽然感觉室内有股臭味,用鼻子的嗅觉功能朝着臭味重的方向寻找,觉得应该就是这台组合音响?怎么会呢?这无机物的东西不会腐臭吧?
    自做自受 2024-12-10 16:34 136浏览
  • 概述 通过前面的研究学习,已经可以在CycloneVGX器件中成功实现完整的TDC(或者说完整的TDL,即延时线),测试结果也比较满足,解决了超大BIN尺寸以及大量0尺寸BIN的问题,但是还是存在一些之前系列器件还未遇到的问题,这些问题将在本文中进行详细描述介绍。 在五代Cyclone器件内部系统时钟受限的情况下,意味着大量逻辑资源将被浪费在于实现较大长度的TDL上面。是否可以找到方法可以对此前TDL的长度进行优化呢?本文还将探讨这个问题。TDC前段BIN颗粒堵塞问题分析 将延时链在逻辑中实现后
    coyoo 2024-12-10 13:28 101浏览
  •         在有电流流过的导线周围会感生出磁场,再用霍尔器件检测由电流感生的磁场,即可测出产生这个磁场的电流的量值。由此就可以构成霍尔电流、电压传感器。因为霍尔器件的输出电压与加在它上面的磁感应强度以及流过其中的工作电流的乘积成比例,是一个具有乘法器功能的器件,并且可与各种逻辑电路直接接口,还可以直接驱动各种性质的负载。因为霍尔器件的应用原理简单,信号处理方便,器件本身又具有一系列的du特优点,所以在变频器中也发挥了非常重要的作用。  &nb
    锦正茂科技 2024-12-10 12:57 76浏览
  •         霍尔传感器是根据霍尔效应制作的一种磁场传感器。霍尔效应是磁电效应的一种,这一现象是霍尔(A.H.Hall,1855—1938)于1879年在研究金属的导电机构时发现的。后来发现半导体、导电流体等也有这种效应,而半导体的霍尔效应比金属强得多,利用这现象制成的各种霍尔元件,广泛地应用于工业自动化技术、检测技术及信息处理等方面。霍尔效应是研究半导体材料性能的基本方法。通过霍尔效应实验测定的霍尔系数,能够判断半导体材料的导电类型、载流子浓度及载流子
    锦正茂科技 2024-12-10 11:07 64浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦