Производительность calcOpticalFlowPyrLK хуже при высоком FPS

Я использую cv::calcOpticalFlowPyrLK для расчета оптического потока от одного кадра к другому в видеопоследовательности. Я заметил, что отслеживание менее точно при высокой частоте кадров по сравнению с низкой частотой кадров.

Исходный fps составляет 30 кадров в секунду, и я обнаружил, что если я уменьшу его на 8, отслеживание будет намного точнее, чем при использовании всех кадров.

Размер кадра 360*480, размер окна поиска 21*21.

Любая помощь приветствуется!


person aha    schedule 05.03.2014    source источник
comment
Я заметил аналогичную проблему. Я сделал кадровый буфер на 4 кадра и сделал оптический поток LK между текущим кадром и 4-м кадром перед ним. Я опубликую свой код позже.   -  person DXM    schedule 05.03.2014


Ответы (3)


основной cpp

#include <iostream>
#include <queue>
#include <opencv2/opencv.hpp>
#include "corner_tracker.h"
using namespace std;
using namespace cv;

int main(int argc, char** argv) {
  if (argc != 2) {
    cout << "usage: " << argv[0] << " <video path>" << endl;
    exit(1);
  }
  int frame_lag = 4;
  string video_filepath(argv[1]);
  VideoCapture vidcap(video_filepath);
  Mat ref_frame, curr_frame, prev_frame;
  queue<Mat> frame_buffer;
  vector<Point2f> tracked_corners;
  vector<Point2f> optical_flow;
  CornerTrackerParameterBlock param;
  CornerTracker corner_tracker(param);
  Mat mask;
  while (true){
    vidcap >> ref_frame;
    if (ref_frame.empty()) break;
    cvtColor(ref_frame, curr_frame, CV_BGR2GRAY);

    Mat tmp_frame;
    curr_frame.copyTo(tmp_frame);
    frame_buffer.push(tmp_frame);
    if ((int)frame_buffer.size() < frame_lag+1 ) {
      continue;
    }
    prev_frame = frame_buffer.front();
    frame_buffer.pop();
    corner_tracker.TrackCorners(prev_frame, curr_frame, mask, 100, tracked_corners, optical_flow);
    for (int i = 0; i < (int)tracked_corners.size(); i++) {
      //because optical flow is calculated between current frame and the frame_lag frame before it
      //the actual value of the optical flow vector has to be normalized
      Point2f normalized_optical_flow = optical_flow[i]*(1.0/(double)frame_lag);
      line(ref_frame, tracked_corners[i], tracked_corners[i] + normalized_optical_flow, Scalar(0,255,0));
      circle(ref_frame, tracked_corners[i], 2, Scalar(0,0,255));
    }
    imshow("window",ref_frame);
    if((char)waitKey(30) == 27) {
      break;
    }
  }
  return 0;
}

Заголовочный файл углового трекера

#ifndef CORNER_TRACKER_H_
#define CORNER_TRACKER_H_
#include <opencv2/core/core.hpp>

struct CornerTrackerParameterBlock {
  double lkt_max_bidirectioal_error;
  int lkt_maxlevel;
  int lkt_winsize;
  int feature_blocksize;
  double feature_k;
  double feature_mindist;
  double feature_quality_level;

//default constructor
CornerTrackerParameterBlock(void) :
  lkt_max_bidirectioal_error(2.0),
  lkt_maxlevel(3),
  lkt_winsize(16),
  feature_blocksize(3),
  feature_k(0.04),
  feature_mindist(5.0),
  feature_quality_level(0.01)
  {}
};

class CornerTracker {
public:
  CornerTracker(const CornerTrackerParameterBlock& param);
  void TrackCorners(const cv::Mat& prev_frame, const cv::Mat& curr_frame, const cv::Mat& mask, int max_corners, std::vector<cv::Point2f>& tracked_corners, std::vector<cv::Point2f>& optical_flow_vectors) const;
private:
  void AddAdditionalCorners(const cv::Mat& curr_frame, const cv::Mat& mask, int max_corners, std::vector<cv::Point2f>& tracked_corners) const;
  CornerTrackerParameterBlock m_param;
};

#endif //CORNER_TRACKER_H_

cpp-файл углового трекера

#include <iostream>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/video/tracking.hpp>
#include "corner_tracker.h"

using namespace std;
using namespace cv;

CornerTracker::CornerTracker(const CornerTrackerParameterBlock& param) :
  m_param(param)
{}

void CornerTracker::AddAdditionalCorners(const cv::Mat& curr_frame, const cv::Mat& mask, int max_corners, std::vector<cv::Point2f>& tracked_corners) const {
  //detect additional features
  int additional_corners = max_corners - tracked_corners.size();
  if (additional_corners <= 0) return;
  //generate mask
  Mat tmp_mask;
  if (mask.rows != curr_frame.rows || mask.cols != curr_frame.cols || mask.type() != CV_8U) {
    tmp_mask.create(curr_frame.rows, curr_frame.cols, CV_8U);
    tmp_mask = Scalar::all(255);
  }
  else {
    mask.copyTo(tmp_mask);
  }
  //mask out current points
  for (const Point2f& p : tracked_corners) {
    circle(tmp_mask, p, m_param.feature_mindist, Scalar::all(0), -1); //filled black circle
  }
  vector<Point2f> corners;
  goodFeaturesToTrack(curr_frame, corners, additional_corners, m_param.feature_quality_level, m_param.feature_mindist, tmp_mask, m_param.feature_blocksize, true, m_param.feature_k );
  for (const Point2f& p : corners) {
    tracked_corners.push_back(p);
  }
}

void CornerTracker::TrackCorners(const cv::Mat& prev_frame, const cv::Mat& curr_frame, const cv::Mat& mask, int max_corners, std::vector<cv::Point2f>& tracked_corners, std::vector<cv::Point2f>& optical_flow_vectors) const {
  AddAdditionalCorners(curr_frame, mask, max_corners, tracked_corners);
  vector<Point2f> prev_corners(tracked_corners);
  vector<Point2f> next_corners(tracked_corners);
  //optical flow corner tracking
  vector<uchar> status1,status2;
  vector<float> error1,error2;
  calcOpticalFlowPyrLK(curr_frame, prev_frame, tracked_corners, prev_corners, status1, error1, Size(m_param.lkt_winsize,m_param.lkt_winsize), m_param.lkt_maxlevel, TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, 0.01), OPTFLOW_USE_INITIAL_FLOW);
  calcOpticalFlowPyrLK(prev_frame, curr_frame, prev_corners, next_corners, status2, error2, Size(m_param.lkt_winsize,m_param.lkt_winsize), m_param.lkt_maxlevel, TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, 0.01), OPTFLOW_USE_INITIAL_FLOW);
  //check tracked corner quality
  vector<Point2f> temp_corners;
  optical_flow_vectors.clear();
  for (unsigned int i = 0; i < tracked_corners.size(); i++) {
    if (status1[i] == 0 || status2[i] == 0) {
      continue;
    }
    float bidirectional_error = norm(next_corners[i] - tracked_corners[i]);
    //bidirectional error check
    if (bidirectional_error > m_param.lkt_max_bidirectioal_error) {
      continue;
    }
    optical_flow_vectors.push_back(tracked_corners[i] - prev_corners[i]);
    temp_corners.push_back(tracked_corners[i]);
  }
  tracked_corners.swap(temp_corners);
}
person DXM    schedule 05.03.2014
comment
Спасибо за публикацию вашего кода! Я делаю то же самое, только мои точки выбираются вручную, и мне нужно отслеживать их по видеокадрам. Надеюсь, кто-то знает, что происходит с проблемой высокой частоты кадров. - person aha; 05.03.2014
comment
Я на самом деле надеюсь, что с увеличением частоты кадров точность должна возрасти. - person aha; 05.03.2014
comment
Возможно, попробуйте использовать SIFT или другое обнаружение характерных точек в сочетании с LKT. - person DXM; 05.03.2014

На самом деле, я обнаружил, что моя проблема заключается в ошибке преобразования float в int в моем коде.

В моем коде я перебирал все кадры и выполнял преобразование из точек отслеживания оптического потока в точки IOS (CGPoint) и обратно. Во время этого процесса я случайно преобразовал float в int (я использовал cv::Point вместо cv::Point2f).

Производительность ухудшилась на высоких fps, потому что ошибка накапливалась намного чаще, так как трекинг вызывается намного чаще.

person aha    schedule 06.03.2014

Это также может произойти, если качество видео изменится. При более низком FPS, но том же kBPS некоторые типы видеокодеров (например, h.264) имеют больше битов для кодирования каждого кадра, что приводит к более высокому качеству.

person damian    schedule 20.03.2014