加速OpticalFlow算法 - OpenCV

时间:2016-05-29 08:12:01

标签: c++ opencv image-processing optimization opticalflow

我正在研究一个使用光流算法估算无人机位置的项目。我目前正在使用cv::calcOpticalFlowFarneback来实现此目的 我的硬件是Odroid U3,最终将连接到无人机飞行控制器。

问题是这个方法对于这个硬件非常重要,我正在寻找其他方法来优化/加速它。

我已尝试过的事情:

  • 将分辨率降低至320x240甚至160x120。
  • 使用OpenCV TBB(使用WITH_TBB=ON BUILD_TBB=ON编译并添加-ltbb)。
  • 根据建议[{3}}
  • 更改光流参数

添加我的代码的相关部分:

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功能一致。
  • 我在运行程序时检查了CPU核心负载,并没有大量使用所有4个核心,在给定时间只有一个核心处于100%(即使使用TBB):

here

2 个答案:

答案 0 :(得分:3)

光流估计通常是安静的耗时操作。我建议改变光流法。

DualTVL1OpticalFlow是您可以使用的OpenCV中更高效的方法。如果此方法仍然慢,则应使用calcOpticalFlowPyrLK。然而,该方法是稀疏运动估计方法,并且不直接返回密集运动场。 要这样做:初始化帧的网格上的一组点(例如,网格步长= 10),使用这些点来跟踪它们calcOpticalFlowPyrLK。跟踪点和初始点之间的差异为您提供每个网格位置的光流。最后,您必须在网格点之间进行插值。例如。使用最近邻居或线性插值。

答案 1 :(得分:3)

首先,我要感谢下面的this回答我用来构建我的最终解决方案,我会尽可能详细地解释。

我的解决方案分为两部分:

  1. 多线程 - 将每个帧拆分为4个矩阵,每个四分之一位于不同的矩阵中。创建4个线程并在不同的线程中运行每个季度的处理。我创建了4个四分之一矩阵,使得它们之间会有一些(5%)重叠,这样我就不会失去它们之间的连接(见下图 - 黄色部分宽度为55%,高度为55%)

    enter image description here

    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部分),主循环正在等待所有线程完成以收集结果和平均值。

  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));
    }
    

    请注意,如果网格在整个运行过程中保持不变,最好只在进入主循环之前创建一次。

  3. 在实施这两个部分之后,在运行程序时,Odroid U3的所有4个核心都在不断地工作60%-80%并且性能加速。