【问题标题】:Accelerating OpticalFlow Algorithm - OpenCV加速光流算法 - OpenCV
【发布时间】:2023-04-01 00:45:01
【问题描述】:

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

问题是这种方法对于这个硬件来说真的很重,我正在寻找其他一些方法来优化/加速它。

我已经尝试过的事情:

  • 将分辨率降低到 320x240 甚至 160x120。
  • 使用 OpenCV TBB(使用 WITH_TBB=ON BUILD_TBB=ON 编译并添加 -ltbb)。
  • 按照建议更改光流参数here

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

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):

【问题讨论】:

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


    【解决方案1】:

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

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

    【讨论】:

    • 一个使用calcOpticalFlowPyrLK CPU实现的例子可以在opencv_source/samples/cpp/lkdemo.cpp找到
    【解决方案2】:

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

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

    1. 多线程 - 将每一帧分割成 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 部分),主循环正在等待所有线程完成以收集结果并取平均值。

    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%持续工作,性能得到了加速。

    【讨论】:

      猜你喜欢
      • 2019-04-12
      • 2014-07-07
      • 2013-10-19
      • 1970-01-01
      • 2012-10-14
      • 1970-01-01
      • 2019-12-21
      • 2016-04-05
      • 1970-01-01
      相关资源
      最近更新 更多