【问题标题】:OpenCV GPU Farneback Optical Flow badly works in multi-threadingOpenCV GPU Farneback 光流在多线程中表现不佳
【发布时间】:2016-05-01 14:56:11
【问题描述】:

我的应用程序使用 Opencv gpu 类 gpu::FarnebackOpticalFlow 来计算输入视频的一对连续帧之间的光流。为了加快进程,我利用 OpenCV 的 TBB 支持在多线程中运行该方法。但是,多线程性能不像单线程那样表现。只是为了让您了解不同的行为,这里有两个快照,分别是单线程和多线程实现。

多线程实现假设将图像分割成 8 个不同的条带(我的 pc 上的核心数),并在每个条带上应用用于光流的 Farneback 实现的 gpu 方法。以下是这两种方法对应的代码行:

单线程实现

/* main.cpp */
//prevImg and img are the input Mat images extracted from the input video
...
GpuMat gpuImg8U(img);
GpuMat gpuPrevImg8U(prevImg);   
GpuMat u_flow, v_flow;
gpu::FarnebackOpticalFlow farneback_flow;
farneback_flow.numLevels = maxLayer;
farneback_flow.pyrScale = 0.5;
farneback_flow.winSize = windows_size;
farneback_flow.numIters = of_iterations;
farneback_flow(gpuPrevImg8U,gpuImg8U,u_flow,v_flow);
getFlowField(Mat(u_flow),Mat(v_flow),optical_flow);

...
}

void getFlowField(const Mat& u, const Mat& v, Mat& flowField){    
    for (int i = 0; i < flowField.rows; ++i){
        const float* ptr_u = u.ptr<float>(i);
        const float* ptr_v = v.ptr<float>(i);
        Point2f* row = flowField.ptr<Point2f>(i);

        for (int j = 0; j < flowField.cols; ++j){
            row[j].y = ptr_v[j];
            row[j].x = ptr_u[j];
        }
    }
}

多线程实现

/* parallel.h */
class ParallelOpticalFlow : public cv::ParallelLoopBody {

    private:
        int coreNum;
        cv::gpu::GpuMat img, img2;
        cv::gpu::FarnebackOpticalFlow& farneback_flow;
        const cv::gpu::GpuMat u_flow, v_flow;
        cv::Mat& optical_flow;

    public:
        ParallelOpticalFlow(int cores, cv::gpu::FarnebackOpticalFlow& flowHandler, cv::gpu::GpuMat img_, cv::gpu::GpuMat img2_, const cv::gpu::GpuMat u, const cv::gpu::GpuMat v, cv::Mat& of)
                    : coreNum(cores), farneback_flow(flowHandler), img(img_), img2(img2_), u_flow(u), v_flow(v), optical_flow(of){}

        virtual void operator()(const cv::Range& range) const;

};


/* parallel.cpp*/
void ParallelOpticalFlow::operator()(const cv::Range& range) const {

    for (int k = range.start ; k < range.end ; k ++){

        cv::gpu::GpuMat img_rect(img,cv::Rect(0,img.rows/coreNum*k,img.cols,img.rows/coreNum));
        cv::gpu::GpuMat img2_rect(img2,cv::Rect(0,img2.rows/coreNum*k,img2.cols,img2.rows/coreNum));
        cv::gpu::GpuMat u_rect(u_flow,cv::Rect(0,u_flow.rows/coreNum*k,u_flow.cols,u_flow.rows/coreNum));
        cv::gpu::GpuMat v_rect(v_flow,cv::Rect(0,v_flow.rows/coreNum*k,v_flow.cols,v_flow.rows/coreNum));
        cv::Mat of_rect(optical_flow,cv::Rect(0,optical_flow.rows/coreNum*k,optical_flow.cols,optical_flow.rows/coreNum));

        farneback_flow(img_rect,img2_rect,u_rect,v_rect);
        getFlowField(Mat(u_rect),Mat(v_rect),of_rect);
    }
}

/* main.cpp */

    parallel_for_(Range(0,cores_num),ParallelOpticalFlow(cores_num,farneback_flow,gpuPrevImg8U,gpuImg8U,u_flow,v_flow,optical_flow));

这两种情况下的代码看起来是等价的。谁能解释我为什么会有这些不同的行为?或者如果我的代码中有一些错误? 提前感谢您的回答

【问题讨论】:

  • img/img2/u_flow/v_flow/optical_flow.rows、img/img2/u_flow/v_flow/optical_flow.cols、coreNum、k(范围)的值是多少?你有没有使用上面相同的图片的原因?
  • [rows x cols] = [240x320], coreNum = 8, k 是索引每个核心的迭代器。我在这两种方法中使用相同的图像,不同的是每个图像和矩阵在多线程中被分成 8 个条纹,以使光流以并行方式工作
  • 您是否测试过仅将图像拆分为 2 个部分?可能无法在您的子图像边界上正确检测到流。
  • 是的,我试过了,结果还是更糟。
  • 如果您只使用单个子图像/切片(以相同方式创建),它可能有效吗?正如@jet47 所说,这可能是一场数据竞赛。但无论如何,我看不出拆分 GPU 调用在哪里有帮助。看起来您正在尝试使用多个 CPU 等待 GPU 瓶颈。因此,即使它有效,您也会降低流量质量(未检测到切片间流量)而不会加速(这不是来自降低的质量)。

标签: c++ multithreading opencv gpu opticalflow


【解决方案1】:

GPU 模块不是线程安全的。它使用了一些全局变量,例如__constant__ 内存和纹理引用API,如果在多线程环境中使用,可能会导致数据竞争。

【讨论】:

  • 我明白了。好吧,我将完全放弃这个选项。但是,我尝试将基于 gpu 的方法替换为基于 cpu 的等效函数。我在图像的每个切片的底部观察到一种噪声和不稳定,类似于单线程情况的效果。您认为这可能取决于 Farneback 函数在输入图像上的工作方式吗?
  • 从不同的主机线程使用不同的 gpu::Stream-s 调用 gpu 函数是否安全?从不同的主机线程调用不同的 gpu 函数是否安全?
  • 第一个答案是否定的。第二 - 是的,有例外。有些函数使用另一个,例如 ORB 内部使用 FAST,因此同时使用 ORB 和 FAST 是不安全的。
  • @jet47 GPU 模块不安全,但是如果我使用多个实例(多个进程),它可以工作吗?在我看来,它没有。
猜你喜欢
  • 1970-01-01
  • 2023-04-10
  • 2016-11-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-30
  • 2016-04-05
相关资源
最近更新 更多