Cuda编程:一个简单的多线程CPU-GPU图像处理示例

编写一个简单完整的 C++ 实现,展示如何使用多线程处理 CPU-GPU 图像数据流程,包括:

  1. CPU 准备图像数据

  2. 将数据分配到 GPU

  3. GPU 进行图像处理

  4. CPU 获取结果并保存显示

使用 OpenCV 进行图像处理,CUDA 进行 GPU 计算,以及 C++ 多线程库。

#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
#include <queue>
#include <condition_variable>
#include <atomic>
#include <functional>
#include <memory>
#include <opencv2/opencv.hpp>
#include <opencv2/cudaimgproc.hpp>
#include <opencv2/cudaarithm.hpp>

// ==================== 线程池封装 ====================
class ThreadPool {
public:
    explicit ThreadPool(size_t num_threads) : stop(false) {
        for (size_t i = 0; i < num_threads; ++i) {
            workers.emplace_back([this] {
                while (true) {
                    std::function<void()> task;
                    {
                        std::unique_lock<std::mutex> lock(queue_mutex);
                        condition.wait(lock, [this] { return stop || !tasks.empty(); });
                        if (stop && tasks.empty()) return;
                        task = std::move(tasks.front());
                        tasks.pop();
                    }
                    task();
                }
            });
        }
    }

    template<class F, class... Args>
    auto enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type> {
        using return_type = typename std::result_of<F(Args...)>::type;
        
        auto task = std::make_shared<std::packaged_task<return_type()>>(
            std::bind(std::forward<F>(f), std::forward<Args>(args)...)
        );
        
        std::future<return_type> res = task->get_future();
        {
            std::unique_lock<std::mutex> lock(queue_mutex);
            if (stop) throw std::runtime_error("enqueue on stopped ThreadPool");
            tasks.emplace([task]() { (*task)(); });
        }
        condition.notify_one();
        return res;
    }

    ~ThreadPool() {
        {
            std::unique_lock<std::mutex> lock(queue_mutex);
            stop = true;
        }
        condition.notify_all();
        for (std::thread &worker : workers)
            if (worker.joinable()) worker.join();
    }

private:
    std::vector<std::thread> workers;
    std::queue<std::function<void()>> tasks;
    std::mutex queue_mutex;
    std::condition_variable condition;
    bool stop;
};

// ==================== GPU处理管道 ====================
class GPUPipeline {
public:
    GPUPipeline(int cpu_threads = 2, 
                int gpu_upload_threads = 1, 
                int gpu_process_threads = 1,
                int download_threads = 1) 
        : cpu_pool(cpu_threads),
          upload_pool(gpu_upload_threads),
          process_pool(gpu_process_threads),
          download_pool(download_threads) {
        
        // 初始化CUDA设备
        cv::cuda::printCudaDeviceInfo(cv::cuda::getDevice());
        
        // 启动结果收集线程
        result_thread = std::thread(&GPUPipeline::result_collector, this);
    }

    ~GPUPipeline() {
        stop();
    }

    void stop() {
        // 停止所有线程池
        cpu_pool.~ThreadPool();
        upload_pool.~ThreadPool();
        process_pool.~ThreadPool();
        download_pool.~ThreadPool();
        
        // 停止结果收集线程
        {
            std::lock_guard<std::mutex> lock(result_mutex);
            stop_flag = true;
        }
        result_cv.notify_one();
        if (result_thread.joinable()) result_thread.join();
    }

    void process_image(const cv::Mat& img) {
        // 第一阶段:CPU预处理
        cpu_pool.enqueue([this, img]() {
            cv::Mat processed;
            
            // 预处理 (调整大小和转灰度)
            cv::resize(img, processed, cv::Size(640, 480));
            cv::cvtColor(processed, processed, cv::COLOR_BGR2GRAY);
            
            // 第二阶段:上传到GPU
            upload_pool.enqueue([this, processed]() {
                cv::cuda::GpuMat gpu_img;
                gpu_img.upload(processed);
                
                // 第三阶段:GPU处理
                process_pool.enqueue([this, gpu_img = std::move(gpu_img)]() mutable {
                    // 每个GPU处理线程使用不同的CUDA流
                    cv::cuda::Stream stream;
                    
                    // GPU处理 (Canny + Sobel)
                    cv::cuda::GpuMat edges, sobel_x, sobel_y, combined;
                    
                    // Canny边缘检测
                    cv::Ptr<cv::cuda::CannyEdgeDetector> canny = 
                        cv::cuda::createCannyEdgeDetector(50, 100, 3, false);
                    canny->detect(gpu_img, edges, stream);
                    
                    // Sobel边缘检测
                    cv::cuda::Sobel(gpu_img, sobel_x, CV_32F, 1, 0, 3, 1, 0, cv::BORDER_DEFAULT, stream);
                    cv::cuda::Sobel(gpu_img, sobel_y, CV_32F, 0, 1, 3, 1, 0, cv::BORDER_DEFAULT, stream);
                    
                    // 合并结果
                    cv::cuda::addWeighted(edges, 0.5, sobel_x, 0.25, 0, combined, CV_8U, stream);
                    cv::cuda::addWeighted(combined, 1.0, sobel_y, 0.25, 0, combined, CV_8U, stream);
                    
                    stream.waitForCompletion();
                    
                    // 第四阶段:下载到CPU
                    download_pool.enqueue([this, combined = std::move(combined)]() mutable {
                        cv::Mat processed;
                        combined.download(processed);
                        
                        // 将结果放入队列
                        {
                            std::lock_guard<std::mutex> lock(result_mutex);
                            result_queue.push(processed);
                        }
                        result_cv.notify_one();
                    });
                });
            });
        });
    }

    bool get_processed_result(cv::Mat& result) {
        std::unique_lock<std::mutex> lock(result_mutex);
        if (result_cv.wait_for(lock, std::chrono::milliseconds(10), 
                             [this]() { return !result_queue.empty(); })) {
            result = std::move(result_queue.front());
            result_queue.pop();
            return true;
        }
        return false;
    }

private:
    void result_collector() {
        while (true) {
            std::unique_lock<std::mutex> lock(result_mutex);
            result_cv.wait(lock, [this]() { return !result_queue.empty() || stop_flag; });
            
            if (stop_flag && result_queue.empty()) break;
            
            // 这里可以添加额外的结果处理逻辑
            // 比如批量保存、结果分析等
        }
    }

    // 线程池
    ThreadPool cpu_pool;      // CPU预处理线程池
    ThreadPool upload_pool;   // 上传到GPU线程池
    ThreadPool process_pool;  // GPU处理线程池
    ThreadPool download_pool; // 下载到CPU线程池
    
    // 结果收集线程
    std::thread result_thread;
    std::queue<cv::Mat> result_queue;
    std::mutex result_mutex;
    std::condition_variable result_cv;
    std::atomic<bool> stop_flag{false};
};

// ==================== 性能监控工具 ====================
class PerformanceMonitor {
public:
    void start() {
        start_time = std::chrono::steady_clock::now();
        frame_count = 0;
    }
    
    void frame_processed() {
        frame_count++;
    }
    
    double get_fps() const {
        auto now = std::chrono::steady_clock::now();
        auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time).count();
        return frame_count * 1000.0 / (elapsed + 1);
    }
    
    void print_stats() const {
        std::cout << "处理帧数: " << frame_count 
                  << ", FPS: " << get_fps() 
                  << ", 平均延迟: " << (1000.0 / (get_fps() + 0.001)) << "ms" << std::endl;
    }

private:
    std::chrono::steady_clock::time_point start_time;
    int frame_count = 0;
};

// ==================== 主程序 ====================
int main() {
    // 创建处理管道
    GPUPipeline pipeline(
        4,  // CPU线程数
        2,  // 上传线程数
        2,  // GPU处理线程数 (建议不超过GPU流处理器数量)
        2   // 下载线程数
    );
    
    // 性能监控
    PerformanceMonitor monitor;
    monitor.start();
    
    // 创建显示窗口
    cv::namedWindow("原始图像", cv::WINDOW_NORMAL);
    cv::namedWindow("处理结果", cv::WINDOW_NORMAL);
    
    // 视频捕获
    cv::VideoCapture cap(0); // 摄像头
    if (!cap.isOpened()) {
        std::cerr << "无法打开摄像头" << std::endl;
        return -1;
    }
    
    // 主循环
    int save_counter = 0;
    while (true) {
        cv::Mat frame;
        cap >> frame;
        if (frame.empty()) break;
        
        // 显示原始图像
        cv::imshow("原始图像", frame);
        
        // 处理图像
        pipeline.process_image(frame);
        monitor.frame_processed();
        
        // 获取并显示处理结果
        cv::Mat processed;
        if (pipeline.get_processed_result(processed)) {
            // 显示FPS
            double fps = monitor.get_fps();
            cv::putText(processed, 
                       "FPS: " + std::to_string(static_cast<int>(fps)), 
                       cv::Point(10, 30),
                       cv::FONT_HERSHEY_SIMPLEX, 0.8, cv::Scalar(255, 255, 255), 2);
            
            cv::imshow("处理结果", processed);
            
            // 每100帧保存一次
            if (save_counter++ % 100 == 0) {
                std::string filename = "result_" + std::to_string(save_counter) + ".jpg";
                cv::imwrite(filename, processed);
                std::cout << "保存结果: " << filename << std::endl;
                monitor.print_stats();
            }
        }
        
        // 按ESC退出
        if (cv::waitKey(1) == 27) break;
    }
    
    pipeline.stop();
    cv::destroyAllWindows();
    monitor.print_stats();
    return 0;
}

关键点

  1. 线程池封装

    • 将线程池抽象为独立类ThreadPool,支持任意任务类型

    • 使用std::future获取异步结果

    • 自动管理线程生命周期

  2. 流水线优化

    • 四个独立线程池:CPU预处理、GPU上传、GPU处理、结果下载

    • 每个阶段的任务自动排队和执行

    • 使用lambda捕获实现任务链式传递

  3. GPU处理增强

    • 每个GPU任务使用独立的CUDA流

    • 异步执行多个GPU操作

    • 显式流同步确保操作完成

  4. 性能监控

    • 添加PerformanceMonitor类跟踪处理性能

    • 实时显示FPS和延迟

    • 定期输出统计信息

  5. 资源管理

    • 使用RAII管理所有资源

    • 安全停止机制

    • 异常安全设计

OpenCV C++ CUDA 编程

OpenCV 的 CUDA 模块 (cuda) 提供了一系列 GPU 加速的图像处理和计算机视觉算法。下面我将详细介绍如何使用 OpenCV C++ 进行 CUDA 编程。

1.1 包含头文件

#include <opencv2/opencv.hpp>
#include <opencv2/cudaarithm.hpp>    // CUDA 基础运算
#include <opencv2/cudaimgproc.hpp>   // CUDA 图像处理
#include <opencv2/cudafilters.hpp>   // CUDA 滤波器
#include <opencv2/cudafeatures2d.hpp> // CUDA 特征检测

1.2 初始化 CUDA 设备

// 打印 CUDA 设备信息
cv::cuda::printCudaDeviceInfo(cv::cuda::getDevice());

// 设置使用的 GPU 设备 (默认为 0)
cv::cuda::setDevice(0);

2.1 数据上传和下载

cv::Mat cpu_img = cv::imread("image.jpg", cv::IMREAD_COLOR);

// 上传到 GPU
cv::cuda::GpuMat gpu_img;
gpu_img.upload(cpu_img);

// 从 GPU 下载
cv::Mat downloaded;
gpu_img.download(downloaded);

2.2 异步操作与流

// 创建 CUDA 流
cv::cuda::Stream stream;

// 异步上传
cv::cuda::GpuMat async_gpu_img;
async_gpu_img.upload(cpu_img, stream);

// 异步处理
cv::cuda::GpuMat processed;
cv::cuda::cvtColor(async_gpu_img, processed, cv::COLOR_BGR2GRAY, 0, stream);

// 异步下载
cv::Mat async_downloaded;
processed.download(async_downloaded, stream);

// 等待所有操作完成
stream.waitForCompletion();

3.1 图像处理加速

// 颜色空间转换
cv::cuda::GpuMat gray;
cv::cuda::cvtColor(gpu_img, gray, cv::COLOR_BGR2GRAY);

// 阈值处理
cv::cuda::GpuMat thresholded;
cv::cuda::threshold(gray, thresholded, 128, 255, cv::THRESH_BINARY);

// 边缘检测
cv::Ptr<cv::cuda::CannyEdgeDetector> canny = cv::cuda::createCannyEdgeDetector(50, 100);
cv::cuda::GpuMat edges;
canny->detect(gray, edges);

// 直方图均衡化
cv::cuda::GpuMat equalized;
cv::cuda::equalizeHist(gray, equalized);

3.2 滤波操作加速

// 创建高斯滤波器
cv::Ptr<cv::cuda::Filter> gauss = cv::cuda::createGaussianFilter(
    CV_8UC1, CV_8UC1, cv::Size(5, 5), 1.5);

// 应用滤波器
cv::cuda::GpuMat filtered;
gauss->apply(gray, filtered);

// Sobel 边缘检测
cv::cuda::GpuMat sobel_x, sobel_y;
cv::cuda::Sobel(gray, sobel_x, CV_32F, 1, 0);
cv::cuda::Sobel(gray, sobel_y, CV_32F, 0, 1);

3.3 特征检测加速

// ORB 特征检测
cv::Ptr<cv::cuda::ORB> orb = cv::cuda::ORB::create();
std::vector<cv::KeyPoint> keypoints;
cv::cuda::GpuMat descriptors;
orb->detectAndComputeAsync(gpu_img, cv::cuda::GpuMat(), keypoints, descriptors);

// 特征匹配
cv::Ptr<cv::cuda::DescriptorMatcher> matcher = cv::cuda::DescriptorMatcher::createBFMatcher();
std::vector<cv::DMatch> matches;
matcher->match(descriptors1, descriptors2, matches);

4.1 内存管理优化

// 预分配 GPU 内存
cv::cuda::GpuMat buffer1(gpu_img.size(), gpu_img.type());
cv::cuda::GpuMat buffer2(gpu_img.size(), CV_32F);

// 使用页锁定内存 (Pinned Memory) 加速上传/下载
cv::Mat pinned_cpu_img;
cv::cuda::registerPageLocked(pinned_cpu_img);  // 注册为页锁定内存
// ... 处理 pinned_cpu_img ...
cv::cuda::unregisterPageLocked(pinned_cpu_img); // 使用完后取消注册

4.2 多流处理优化

// 创建多个流
cv::cuda::Stream stream1, stream2;

// 在不同的流上并行执行操作
cv::cuda::GpuMat result1, result2;
cv::cuda::cvtColor(gpu_img1, result1, cv::COLOR_BGR2GRAY, 0, stream1);
cv::cuda::cvtColor(gpu_img2, result2, cv::COLOR_BGR2GRAY, 0, stream2);

// 同步所有流
stream1.waitForCompletion();
stream2.waitForCompletion();

完整示例:实时视频处理

#include <opencv2/opencv.hpp>
#include <opencv2/cudaimgproc.hpp>
#include <opencv2/cudafilters.hpp>

int main() {
    // 初始化 CUDA
    cv::cuda::printCudaDeviceInfo(cv::cuda::getDevice());
    
    // 打开摄像头
    cv::VideoCapture cap(0);
    if (!cap.isOpened()) {
        std::cerr << "无法打开摄像头" << std::endl;
        return -1;
    }
    
    // 创建窗口
    cv::namedWindow("原始", cv::WINDOW_NORMAL);
    cv::namedWindow("处理结果", cv::WINDOW_NORMAL);
    
    // 创建 CUDA 流
    cv::cuda::Stream stream;
    
    // 创建滤波器
    cv::Ptr<cv::cuda::Filter> gauss = cv::cuda::createGaussianFilter(
        CV_8UC1, CV_8UC1, cv::Size(5, 5), 1.5);
    cv::Ptr<cv::cuda::CannyEdgeDetector> canny = cv::cuda::createCannyEdgeDetector(50, 100);
    
    // 帧率计算
    int frame_count = 0;
    auto start_time = std::chrono::steady_clock::now();
    
    while (true) {
        cv::Mat frame;
        cap >> frame;
        if (frame.empty()) break;
        
        // 上传到 GPU (异步)
        cv::cuda::GpuMat gpu_frame;
        gpu_frame.upload(frame, stream);
        
        // 转换为灰度图 (异步)
        cv::cuda::GpuMat gray;
        cv::cuda::cvtColor(gpu_frame, gray, cv::COLOR_BGR2GRAY, 0, stream);
        
        // 高斯模糊 (异步)
        cv::cuda::GpuMat blurred;
        gauss->apply(gray, blurred, stream);
        
        // Canny 边缘检测 (异步)
        cv::cuda::GpuMat edges;
        canny->detect(blurred, edges, stream);
        
        // 下载结果 (异步)
        cv::Mat result;
        edges.download(result, stream);
        
        // 等待所有操作完成
        stream.waitForCompletion();
        
        // 计算并显示 FPS
        frame_count++;
        auto current_time = std::chrono::steady_clock::now();
        auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
            current_time - start_time).count();
        double fps = frame_count * 1000.0 / elapsed;
        
        cv::putText(frame, 
                   "FPS: " + std::to_string((int)fps), 
                   cv::Point(10, 30),
                   cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(0, 255, 0), 2);
        
        // 显示图像
        cv::imshow("原始", frame);
        cv::imshow("处理结果", result);
        
        // 按 ESC 退出
        if (cv::waitKey(1) == 27) break;
    }
    
    cv::destroyAllWindows();
    return 0;
}

注意事项

  1. 设备兼容性:确保你的 GPU 支持 CUDA 并且安装了正确版本的驱动

  2. 内存管理:GPU 内存有限,处理大图像时要注意内存使用

  3. 异步操作:使用流时需要确保操作顺序正确

  4. 错误处理:CUDA 操作可能会失败,应添加适当的错误检查

通过合理使用 OpenCV 的 CUDA 模块,可以显著提高图像处理算法的性能,特别是在处理高分辨率视频或大批量图像时。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值