CUDA图像处理加速demo

环境搭建和安装就不提了。

主要步骤是申请显存,将内存复制到显存,执行核函数,将显存复制回内存。

核函数是可以认为是线程的worker函数。

直接上代码:

这段是初始化

cpp 复制代码
   cv::Mat inputImage = cv::imread("");
    cv::cvtColor(inputImage, inputImage, cv::COLOR_BGR2GRAY);
    int w = 43;
    int h = 43;
    cv::Mat inputImage2;

    int numGPUs;
    cudaGetDeviceCount(&numGPUs);
    if (numGPUs <= 0) {
        std::cerr << "No CUDA-capable devices found." << std::endl;
        return 1;
    }
    int deviceId = 0; 
    cudaDeviceProp deviceProp;

    // 查询设备属性
    cudaGetDeviceProperties(&deviceProp, deviceId);

下面这段显存初始化和拷贝显存

cudaMalloc 和 cudaMemcpy,注意cudaMemcpyHostToDevice这是从Host到Device,device就是显卡

cpp 复制代码
    int inRows = inputImage.rows;
    int inCols = inputImage.cols;
    uchar* d_inputImage, * d_outputImage, * d_tempImage;
    size_t inputSize = inRows * inCols * sizeof(uchar);
    size_t outputSize = (inRows) * (inCols) * sizeof(uchar);

    cudaMalloc(&d_inputImage, inputSize);
    cudaMalloc(&d_outputImage, outputSize);
    cudaMalloc(&d_tempImage, outputSize);
    cudaMemcpy(d_inputImage, inputImage.data, inputSize, cudaMemcpyHostToDevice);

下面这段是分块,执行核函数,还有计时功能

一般的卡是几百个核心。block是每个核心的线程数,grid是核心数。

我们让每个线程只处理一个像素,因此grid和block有图像大小确定关系。

erodeKernel2 是定义的核函数。

cpp 复制代码
    dim3 block(16, 16);
    dim3 grid((inCols + block.x - 1) / block.x, (inRows + block.y - 1) / block.y);

    cudaEvent_t start, stop;
    cudaEventCreate(&start);
    cudaEventCreate(&stop);
    cudaEventRecord(start);


    erodeKernel2 << <grid, block >> > (d_inputImage, d_tempImage, inRows, inCols, w, h);
    erodeKernel2_1 << <grid, block >> > (d_tempImage, d_outputImage, inRows, inCols, w, h);
    // 记录结束事件
    cudaEventRecord(stop);
    // 等待事件完成
    cudaEventSynchronize(stop);
    {
        // 计算运行时间
        float milliseconds = 0;
        cudaEventElapsedTime(&milliseconds, start, stop);
        std::cout << "GPU runtime: " << milliseconds << " ms" << std::endl;
    }

下面来看核函数,先拿一个最普通的核函数。

核函数要用__global__ 修饰。这是一个腐蚀的核函数,也就是最小值滤波。

熟悉多线程的知道可以通过线程的ID来确定要处理哪个位置的像素,CUDA也不例外。x和y是图像坐标,我们以上面的划分为例,则blockDim.x是0-gridx的一个数,blockIdx.x是16,threadIdx.x是0-16的一个数。

为了能处理全部的图像我们划分的block*grid往往大于图像的尺寸,因此要判断x,y是否在图像范围内,如果在范围内才处理。

cpp 复制代码
__global__ void erodeKernel_junk(const uchar* input, uchar* output, int rows, int cols, int kernelSize, int kernelSize2) {
    int x = blockIdx.x * blockDim.x + threadIdx.x;
    int y = blockIdx.y * blockDim.y + threadIdx.y;
    if (x < cols && y < rows) {
        const int halfSize = kernelSize / 2;
        const int halfSize2 = kernelSize2 / 2;
        uchar minVal = 255;
        for (int i = -halfSize2; i <= halfSize2; ++i) {
            for (int j = -halfSize; j <= halfSize; ++j) {
                int px = x + j;
                int py = y + i;
                if (px >= 0 && px < cols && py >= 0 && py < rows) {
                    uchar val = input[py * cols + px];
                    if (val < minVal) {
                        minVal = val;
                    }
                }
            }
        }
        output[y * cols + x] = minVal;
    }
}

下面是一个有共享内存的核函数

下次空了再补充,代码先放在这里

cpp 复制代码
/*行处理 共享内存*/
__global__ void erodeKernel2(const uchar* input, uchar* output, int rows, int cols, int kernelSize, int kernelSize2) {
    __shared__ uchar sharedBlock[33 * 33];  // 假设每个线程块的共享内存大小为32x32
    int x = blockIdx.x * blockDim.x + threadIdx.x;
    int y = blockIdx.y * blockDim.y + threadIdx.y;
    const int halfSize = kernelSize / 2;
    if (x > halfSize && x < cols - halfSize && y < rows - halfSize && y>halfSize) {

        int px = threadIdx.x;
        int py = threadIdx.y;
        sharedBlock[py * blockDim.x + px] = input[y * cols + x];
        __syncthreads();
        uchar minVal = 255;
        for (int j = -halfSize; j <= halfSize; ++j) {
            uchar val = 254;
            if (px + j < 0 || px + j >= blockDim.x) {
                //continue;
                val = input[y * cols + x + j];
                //val = sharedBlock[py * blockDim.x + 0];
            }
            else {
                val = sharedBlock[py * blockDim.x + px + j];
            }
            //val = sharedBlock[py * blockDim.x + px + j];
            //val = input[y * cols + x + j];
            if (val < minVal) {
                minVal = val;
            }
        }

        output[y * cols + x] = minVal;
    }

}
相关推荐
会飞的老朱2 小时前
医药集团数智化转型,智能综合管理平台激活集团管理新效能
大数据·人工智能·oa协同办公
聆风吟º3 小时前
CANN runtime 实战指南:异构计算场景中运行时组件的部署、调优与扩展技巧
人工智能·神经网络·cann·异构计算
Codebee5 小时前
能力中心 (Agent SkillCenter):开启AI技能管理新时代
人工智能
聆风吟º6 小时前
CANN runtime 全链路拆解:AI 异构计算运行时的任务管理与功能适配技术路径
人工智能·深度学习·神经网络·cann
uesowys6 小时前
Apache Spark算法开发指导-One-vs-Rest classifier
人工智能·算法·spark
AI_56786 小时前
AWS EC2新手入门:6步带你从零启动实例
大数据·数据库·人工智能·机器学习·aws
User_芊芊君子6 小时前
CANN大模型推理加速引擎ascend-transformer-boost深度解析:毫秒级响应的Transformer优化方案
人工智能·深度学习·transformer
智驱力人工智能7 小时前
小区高空抛物AI实时预警方案 筑牢社区头顶安全的实践 高空抛物检测 高空抛物监控安装教程 高空抛物误报率优化方案 高空抛物监控案例分享
人工智能·深度学习·opencv·算法·安全·yolo·边缘计算
qq_160144877 小时前
亲测!2026年零基础学AI的入门干货,新手照做就能上手
人工智能
Howie Zphile7 小时前
全面预算管理难以落地的核心真相:“完美模型幻觉”的认知误区
人工智能·全面预算