OpenCV GPU Farneback 光流在多线程中运行不佳
OpenCV GPU Farneback Optical Flow badly works in multi-threading
我的应用程序使用 Opencv gpu class gpu::FarnebackOpticalFlow
来计算输入视频的一对连续帧之间的光流。为了加快这个过程,我利用了 OpenCV 的 TBB 支持来 运行 多线程中的方法。但是,多线程性能并不像单线程那样。只是为了让您了解不同的行为,这里有两个快照,分别是单线程和多线程实现的。
多线程实现假定将图像分成 8 个不同的条纹(我电脑上的核心数),并且在每个条纹上应用用于光流的 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));
两种情况下的代码看起来是等价的。谁能解释我为什么会有这些不同的行为?或者如果我的代码中有一些错误?
预先感谢您的回答
GPU 模块不是 thread-safe。它使用一些全局变量,如 __constant__
内存和纹理引用 API,如果在 multi-threaded 环境中使用,可能会导致数据竞争。
我的应用程序使用 Opencv gpu class gpu::FarnebackOpticalFlow
来计算输入视频的一对连续帧之间的光流。为了加快这个过程,我利用了 OpenCV 的 TBB 支持来 运行 多线程中的方法。但是,多线程性能并不像单线程那样。只是为了让您了解不同的行为,这里有两个快照,分别是单线程和多线程实现的。
多线程实现假定将图像分成 8 个不同的条纹(我电脑上的核心数),并且在每个条纹上应用用于光流的 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));
两种情况下的代码看起来是等价的。谁能解释我为什么会有这些不同的行为?或者如果我的代码中有一些错误? 预先感谢您的回答
GPU 模块不是 thread-safe。它使用一些全局变量,如 __constant__
内存和纹理引用 API,如果在 multi-threaded 环境中使用,可能会导致数据竞争。