【发布时间】: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