O OpenCV GPU Farneback Optical Flow funciona mal em multi-threading
Meu aplicativo usa a classe Opencv gpugpu::FarnebackOpticalFlow
para calcular o fluxo óptico entre um par de quadros consecutivos de um vídeo de entrada. Para acelerar o processo, explorei o suporte TBB do OpenCV para executar o método em multiencadeamento. No entanto, o desempenho de multiencadeamento não se comporta como o de thread único. Apenas para lhe dar uma idéia do comportamento diferente, aqui estão dois instantâneos, respectivamente, da implementação de thread único e da multi thread.
A implementação multiencadeada pressupõe dividir a imagem em 8 faixas diferentes (o número de núcleos no meu pc), e o método gpu para a implementação Farneback do fluxo óptico é aplicado em cada uma delas. Aqui estão as linhas de código correspondentes para os dois métodos:
Implementação de thread único
/* 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];
}
}
}
Implementação multithread
/* 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));
Os códigos parecem equivalentes nos dois casos. Alguém pode me explicar por que existem esses comportamentos diferentes? Ou se houver algum erro no meu código? Agradecemos antecipadamente por suas respostas