编写一个简单完整的 C++ 实现,展示如何使用多线程处理 CPU-GPU 图像数据流程,包括:
-
CPU 准备图像数据
-
将数据分配到 GPU
-
GPU 进行图像处理
-
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;
}
关键点
-
线程池封装:
-
将线程池抽象为独立类
ThreadPool,支持任意任务类型 -
使用
std::future获取异步结果 -
自动管理线程生命周期
-
-
流水线优化:
-
四个独立线程池:CPU预处理、GPU上传、GPU处理、结果下载
-
每个阶段的任务自动排队和执行
-
使用lambda捕获实现任务链式传递
-
-
GPU处理增强:
-
每个GPU任务使用独立的CUDA流
-
异步执行多个GPU操作
-
显式流同步确保操作完成
-
-
性能监控:
-
添加
PerformanceMonitor类跟踪处理性能 -
实时显示FPS和延迟
-
定期输出统计信息
-
-
资源管理:
-
使用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;
}
注意事项
-
设备兼容性:确保你的 GPU 支持 CUDA 并且安装了正确版本的驱动
-
内存管理:GPU 内存有限,处理大图像时要注意内存使用
-
异步操作:使用流时需要确保操作顺序正确
-
错误处理:CUDA 操作可能会失败,应添加适当的错误检查
通过合理使用 OpenCV 的 CUDA 模块,可以显著提高图像处理算法的性能,特别是在处理高分辨率视频或大批量图像时。

1658

被折叠的 条评论
为什么被折叠?



