我正在研究一个使用光流算法估算无人机位置的项目。我目前正在使用cv::calcOpticalFlowFarneback
来实现此目的
我的硬件是Odroid U3,最终将连接到无人机飞行控制器。
问题是这个方法对于这个硬件非常重要,我正在寻找其他方法来优化/加速它。
我已尝试过的事情:
WITH_TBB=ON BUILD_TBB=ON
编译并添加-ltbb
)。添加我的代码的相关部分:
int opticalFlow(){
// capture from camera
VideoCapture cap(0);
if( !cap.isOpened() )
return -1;
// Set Resolution - The Default Resolution Is 640 x 480
cap.set(CV_CAP_PROP_FRAME_WIDTH,WIDTH_RES);
cap.set(CV_CAP_PROP_FRAME_HEIGHT,HEIGHT_RES);
Mat flow, cflow, undistortFrame, processedFrame, origFrame, croppedFrame;
UMat gray, prevgray, uflow;
currLocation.x = 0;
currLocation.y = 0;
// for each frame calculate optical flow
for(;;)
{
// take out frame- still distorted
cap >> origFrame;
// Convert to gray
cvtColor(origFrame, processedFrame, COLOR_BGR2GRAY);
// rotate image - perspective transformation
rotateImage(processedFrame, gray, eulerFromSensors.roll, eulerFromSensors.pitch, 0, 0, 0, 1, cameraMatrix.at<double>(0,0),
cameraMatrix.at<double>(0,2),cameraMatrix.at<double>(1,2));
if( !prevgray.empty() )
{
// calculate flow
calcOpticalFlowFarneback(prevgray, gray, uflow, 0.5, 3, 10, 3, 3, 1.2, 0);
uflow.copyTo(flow);
// get average
calcAvgOpticalFlow(flow, 16, corners);
/*
Some other calculations
.
.
.
Updating currLocation struct
*/
}
//break conditions
if(waitKey(1)>=0)
break;
if(end_run)
break;
std::swap(prevgray, gray);
}
return 0;
}
callgrind
运行,瓶颈与calcOpticalFlowFarneback
功能一致。答案 0 :(得分:3)
光流估计通常是安静的耗时操作。我建议改变光流法。
DualTVL1OpticalFlow
是您可以使用的OpenCV中更高效的方法。如果此方法仍然慢,则应使用calcOpticalFlowPyrLK
。然而,该方法是稀疏运动估计方法,并且不直接返回密集运动场。
要这样做:初始化帧的网格上的一组点(例如,网格步长= 10),使用这些点来跟踪它们calcOpticalFlowPyrLK
。跟踪点和初始点之间的差异为您提供每个网格位置的光流。最后,您必须在网格点之间进行插值。例如。使用最近邻居或线性插值。
答案 1 :(得分:3)
首先,我要感谢下面的this回答我用来构建我的最终解决方案,我会尽可能详细地解释。
我的解决方案分为两部分:
多线程 - 将每个帧拆分为4个矩阵,每个四分之一位于不同的矩阵中。创建4个线程并在不同的线程中运行每个季度的处理。我创建了4个四分之一矩阵,使得它们之间会有一些(5%)重叠,这样我就不会失去它们之间的连接(见下图 - 黄色部分宽度为55%,高度为55%)
Q1 = cv::UMat(gray, Range(0, HEIGHT_RES*0.55), Range(0, WIDTH_RES*0.55));
Q2 = cv::UMat(gray, Range(0, HEIGHT_RES*0.55), Range(WIDTH_RES*0.45, WIDTH_RES));
Q3 = cv::UMat(gray, Range(0.45*HEIGHT_RES, HEIGHT_RES), Range(0, WIDTH_RES*0.55));
Q4 = cv::UMat(gray, Range(0.45*HEIGHT_RES, HEIGHT_RES), Range(WIDTH_RES*0.45, WIDTH_RES));
每个线程在四分之一处进行光流处理(下面的第2部分),主循环正在等待所有线程完成以收集结果和平均值。
使用稀疏方法 - 在选定的ROI网格中使用calcOpticalFlowPyrLK
方法,而不是使用calcOpticalFlowFarneback
。使用Lucas-Kanade稀疏方法而不是Farneback密集方法消耗的CPU时间要少得多。就我而言,我使用gridstep=10
创建了一个网格。这是创建网格的简单功能:
void createGrid(vector<cv::Point2f> &grid, int16_t wRes, int16_t hRes, int step){
for (int i= 0; i < wRes ; i+=step)
for (int j= 0; j < hRes; j+=step)
grid.push_back(cv::Point2f(i,j));
}
请注意,如果网格在整个运行过程中保持不变,最好只在进入主循环之前创建一次。
在实施这两个部分之后,在运行程序时,Odroid U3的所有4个核心都在不断地工作60%-80%并且性能加速。