cv::cuda::Stream 是 OpenCV CUDA 模块中的一个类,它表示一个 CUDA 流。
CUDA 流是 CUDA 并行计算模型中的一个核心概念,允许在 GPU 上并行执行多个任务。通过将任务分配到不同的流中,可以实现任务间的并行化,从而提高 GPU 的利用率和计算效率。
在 CUDA 编程中,流是一系列按照特定顺序执行的命令。这些命令可以是内存传输操作(如从主机到设备的数据传输)、核函数执行等。同一个流中的命令会按照它们被添加到流中的顺序执行,而不同流中的命令则可以并行执行,具体取决于 GPU 的硬件资源和调度策略。
cv::cuda::Stream 类提供了创建和操作 CUDA 流的功能。你可以使用这个类来创建新的流,或者使用默认流(通常是流0)。在 OpenCV 的 CUDA 模块中,许多函数都接受一个 Stream 参数,以便你可以指定在哪个流上执行该操作。
以下是使用 cv::cuda::Stream 的基本示例:
#include <opencv2/opencv.hpp>
#include <opencv2/cudaimgproc.hpp>
int main()
{
// 初始化 CUDA
cv::cuda::setDevice(0); // 设置要使用的 GPU 设备
// 创建两个 CUDA 流
cv::cuda::Stream stream1;
cv::cuda::Stream stream2;
// 分配 GPU 内存并上传数据到 GPU
cv::Mat hostMat1 = ...; // 从某处获取或创建图像数据
cv::Mat hostMat2 = ...;
cv::cuda::GpuMat gpuMat1(hostMat1);
cv::cuda::GpuMat gpuMat2(hostMat2);
// 在流1上执行某个操作(例如阈值处理)
cv::cuda::threshold(gpuMat1, gpuMat1, 128.0, 255.0, cv::THRESH_BINARY, stream1);
// 在流2上执行另一个操作(例如模糊)
cv::cuda::GaussianBlur(gpuMat2, gpuMat2, cv::Size(5, 5), 0, 0, stream2);
// 等待流1和流2上的操作完成
stream1.waitForCompletion();
stream2.waitForCompletion();
// 下载结果到 CPU(如果需要的话)
cv::Mat result1(gpuMat1);
cv::Mat result2(gpuMat2);
// ... 处理或显示 result1 和 result2 ...
return 0;
}
在实际应用中,通常不会在每个操作之后都调用 waitForCompletion(),因为这样会失去并行化的优势。正确的做法是让操作在后台异步执行,直到你需要使用结果时才同步流。
正确的示例应该是这样的:
#include <opencv2/opencv.hpp>
#include <opencv2/cudaimgproc.hpp>
int main()
{
// 初始化 CUDA
cv::cuda::setDevice(0); // 设置要使用的 GPU 设备
// 创建两个 CUDA 流
cv::cuda::Stream stream1 = cv::cuda::Stream::Null(); // 使用默认流或创建新流
cv::cuda::Stream stream2 = cv::cuda::Stream::Null(); // 这里为了简化示例使用默认流,实际应该创建新流
// 分配 GPU 内存并上传数据到 GPU
cv::Mat hostMat1 = ...; // 从某处获取或创建图像数据
cv::Mat hostMat2 = ...;
cv::cuda::GpuMat gpuMat1(hostMat1);
cv::cuda::GpuMat gpuMat2(hostMat2);
// 在流1上执行某个操作(例如阈值处理)
// 注意:这里由于使用了默认流,实际上并没有并行化。为了并行化,应该创建不同的非默认流。
cv::cuda::threshold(gpuMat1, gpuMat1, 128.0, 255.0, cv::THRESH_BINARY); // 默认使用 Null 流
// 在流2上执行另一个操作(例如模糊)
// 注意:同上,这里也并没有真正并行化。
cv::cuda::GaussianBlur(gpuMat2, gpuMat2, cv::Size(5, 5), 0, 0); // 默认使用 Null 流
// ... 如果需要的话,可以在这里同步流 ...
// 但由于我们使用的是默认流,所以不需要(也不应该)调用 waitForCompletion()。
// 下载结果到 CPU(如果需要的话)
cv::Mat result1(gpuMat1); // 这会自动同步流(如果使用了非默认流的话)
cv::Mat result2(gpuMat2); // 同上
// ... 处理或显示 result1 和 result2 ...
return 0;
}
然而,上面的代码示例实际上并没有利用到 CUDA 的并行性,因为我们没有创建真正的独立流。为了真正实现并行处理,你需要创建不同的流并将它们传递给 CUDA 函数。
这里是一个更正确的示例框架:
#include <opencv2/opencv.hpp>
#include <opencv2/cudaimgproc.hpp>
int main()
{
// 初始化 CUDA 并设置设备
cv::cuda::setDevice(0);
// 创建独立的 CUDA 流以实现并行处理
cv::cuda::Stream stream1 = cv::cuda::Stream::Stream(); // 创建新流1
cv::cuda::Stream stream2 = cv::cuda::Stream::Stream(); // 创建新流2
// 准备数据并上传到 GPU
cv::Mat hostMat1 = ...; // 加载或生成图像1
cv::Mat hostMat2 = ...; // 加载或生成图像2
cv::cuda::GpuMat gpuMat1(hostMat1); // 上传到 GPU
cv::cuda::GpuMat gpuMat2(hostMat2); // 上传到 GPU
// 在不同流上异步执行操作
cv::cuda::threshold(gpuMat1, gpuMat1, 128.0, 255.0, cv::THRESH_BINARY, stream1); // 在流1上执行阈值处理
cv::cuda::GaussianBlur(gpuMat2, gpuMat2, cv::Size(5, 5), 0, 0, stream2); // 在流2上执行高斯模糊
// ... 这里可以继续添加其他操作到不同的流 ...
// 在需要结果之前同步流(通常是在下载数据到 CPU 之前)
// 可以通过调用 cuda::Stream::waitForCompletion() 或者简单地在下载数据时隐式同步
stream1.waitForCompletion(); // 等待流1完成(可选,因为下载操作会隐式同步)
stream2.waitForCompletion(); // 等待流2完成(可选,因为下载操作会隐式同步)
// 将结果从 GPU 下载到 CPU(如果需要)
// 注意:这个操作会隐式地同步流,所以上面的 waitForCompletion() 调用实际上是多余的。
cv::Mat result1(gpuMat1); // 下载结果1到 CPU(隐式同步流1)
cv::Mat result2(gpuMat2); // 下载结果2到 CPU(隐式同步流2)
// ... 处理或显示 result1 和 result2 ...
return 0;
}