【发布时间】:2020-03-23 08:22:02
【问题描述】:
在并行使用 OpenCV CUDA 密集光流时,我注意到 有时我会得到损坏的光流结果,尽管我在不同的 cuda::GpuMats 和单独的 cuda::Streams 上运行它并带有单独的 @ 987654330@ 个实例。
在对代码进行一些实验后,我发现如果我用mutex 保护DenseOpticalFlow::calc() 调用或只运行一个线程,我总是得到正确(未损坏)的结果。
我将我的代码缩减为最小的可重现示例,其中我在多个线程中对相同的输入图像多次运行光流算法:
#include <thread>
#include <vector>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main() {
Mat frames[2] = { imread("im0.jpg"), imread("im1.jpg") };
for (int i = 0; i < 2; i++) cvtColor(frames[i], frames[i], COLOR_BGR2GRAY);
Size frameSize = frames[0].size();
vector<thread> workers;
mutex m;
for (int id = 0; id < 2; ++id) // 2 threads
workers.emplace_back([&, id]()
{ // lambda
// Separate Stream, Algorithm and GpuMats for each thread
auto algoGpu = cuda::OpticalFlowDual_TVL1::create();
cuda::Stream stream;
cuda::GpuMat optFlow(frameSize, CV_32FC2);
cuda::GpuMat gpuFrame[2] = {
cuda::GpuMat(frameSize, CV_8UC1),
cuda::GpuMat(frameSize, CV_8UC1)
};
Mat downloaded, converted;
Mat channels[2] = { Mat(), Mat() };
for (int i = 0; i < 2; ++i) gpuFrame[i].upload(frames[i], stream);
for (int i = 0; i < 1000; i++) { // run 1000 times with SAME input
{
//unique_lock<mutex> l(m); // WORKS OK IF UNCOMMENTED !!!!!
algoGpu->calc(gpuFrame[0], gpuFrame[1], optFlow, stream);
}
if (id == 0) { // show results from same single thread
optFlow.download(downloaded, stream);
stream.waitForCompletion();
split(downloaded, channels);
channels[0].convertTo(converted, CV_8UC1, 100, 128);
imshow("flow", converted);
waitKey(1);
}
}
stream.waitForCompletion();
});
for (auto& worker : workers) worker.join();
return 0;
}
如果我取消注释 //unique_lock<mutex> l(m);,每次调用都会得到相同的有效结果图像。但如果我留下评论,大约一半的结果会损坏。
输入图像:
正确结果:
损坏结果示例:
我使用 OpenCV 4.0 和 CUDA 10.1。
【问题讨论】:
-
显然有一个竞争条件,你可以通过使用锁来避免。你问的是这个吗?
-
@mkrieger1,不,我在问我为什么需要那个锁?为什么会有竞态条件?
-
请参阅此链接上的详细说明注释部分。 docs.opencv.org/master/d9/df3/classcv_1_1cuda_1_1Stream.html 这似乎暗示用不同的数据调用相同的 API(就像你在调用 calc(...) 时所做的那样,可能会导致 GPU 内存在前一个任务完成之前被其他异步任务更新的问题) .
-
我的同事将此发布到 OpenCV 存储库并被标记为错误:github.com/opencv/opencv/issues/16013
-
这是一个典型的问题:最小,切中要害,完全可重现。赞!你的贡献导致了一个错误被修复。感谢您为我们所有人改进 OpenCV 所做的努力!签署后,一个用户大约一个月后就不得不运行并行光流问题了。你救了我一些头痛!
标签: c++ multithreading opencv