C++编程:使用 CUDA 将 RGB 图像转换为灰度图的实例

0. 引言

本文将以一个简单的例子展示如何使用 CUDA 将 RGB 图像转换为灰度图,并结合 OpenCV 完成图像的加载与保存。

1. 实现功能概述

我们要实现以下功能:

  1. 使用 CUDA 内核,将每个像素从 RGBA 格式转换为灰度值。
  2. 灰度化公式为:Gray = 0.299 * R + 0.587 * G + 0.114 * B
  3. 使用 OpenCV 进行图像加载和保存处理。

代码分为三个部分:

  1. CUDA 核心逻辑:负责灰度化转换的 CUDA 内核实现。
  2. 主机端逻辑:完成内存管理、数据传递以及对 CUDA 内核的调用。
  3. OpenCV 图像操作:用于读取和保存图像文件。

2. 完整代码

// rgba_to_greyscale.h
#ifndef RGBA_TO_GREYSCALE_H_
#define RGBA_TO_GREYSCALE_H_

#include <cstdint>

// Callback function type for handling the output grey image.
typedef void (*CallbackFun)(int32_t height, int32_t width, uint8_t* h_grey_image);

// Converts an RGBA image to greyscale using CUDA.
// Parameters:
// - height: Image height.
// - width: Image width.
// - data: Pointer to RGBA image data.
// - callback: Callback function to handle the output grey image.
int32_t RgbaToGreyscale(int32_t height, int32_t width, uint8_t* data, CallbackFun callback = nullptr);

#endif  // RGBA_TO_GREYSCALE_H_
// rgba_to_greyscale.cu
#include "rgba_to_greyscale.h"

#include <cuda_runtime.h>
#include <cuda_runtime_api.h>
#include <iostream>
#include <cstring>

namespace {
   
    
    

// CUDA kernel for RGBA to greyscale conversion.
__global__ void RgbaToGreyscaleKernel(const uchar4* rgba_image, uint8_t* grey_image,
                                      int32_t num_rows, int32_t num_cols) {
   
    
    
  const int32_t id = blockIdx.x * blockDim.x * blockDim.y + threadIdx.y * blockDim.x + threadIdx.x;

  if (id < num_rows * num_cols) {
   
    
    
    const uint8_t r = rgba_image[id].x;
    const uint8_t g = rgba_image[id].y;
    const uint8_t b = rgba_image[id].z;
    grey_image[id] = static_cast<uint8_t>(0.299f * r + 0.587f * g + 0.114f * b);
  }
}

}  // namespace

int32_t RgbaToGreyscale(int32_t height, int32_t width, uint8_t* data, CallbackFun callback) {
   
    
    
  if (data == nullptr) {
   
    
    
    std::cerr << "Input data is null." << std::endl;
    return -1;
  }

  uchar4* h_rgba_image = reinterpret_cast<uchar4*>(data);
  int32_t num_pixels = width * height;

  uchar4* d_rgba_image = nullptr;
  uint8_t* d_grey_image = nullptr;
  uint8_t* h_grey_image = nullptr;

  if (cudaMalloc(&d_rgba_image, sizeof(uchar4) * num_pixels) != cudaSuccess) {
   
    
    
    std::cerr << "Failed to allocate device memory for RGBA image." << std::endl;
    return -1