光流方法返回意外值

Posted

技术标签:

【中文标题】光流方法返回意外值【英文标题】:Optical Flow method returning unexpected values 【发布时间】:2014-06-13 18:24:18 【问题描述】:

在过去的两周里,我一直在尝试创建一个 android 应用程序,当我移动三星 Galaxy III 的相机时,它可以跟踪空间中的点。简而言之,我使用 OpenCV 库尝试使用以下两种方法跟踪所述点:

public static void goodFeaturesToTrack(
                   Mat image,
                   MatOfPoint corners,
                   int maxCorners,
                   double qualityLevel,
                   double minDistance);

如果 image 对应 8 位灰度图像,corners 对应于最好的输出要用于跟踪的点数,ma​​xCorners对应要获取的最大点数,qualityLevel对应一个分数,使得要获得的所有点必须是 >= BestQualityPointValue*qualityLevel 并且 minDistance 对应于要找到的点之间的最小距离。 (Link)

public static void calcOpticalFlowPyrLK(
                    Mat prevImg,
                    Mat nextImg,
                    MatOfPoint2f prevPts,
                    MatOfPoint2f nextPts,
                    MatOfByte status,
                    MatOfFloat err);

prevImg是否对应t时的8位灰度图,nextImg对应时间t+dt的8位灰度图,prevPts对应包含2D(x,y)的矩阵) 要跟踪的点,nextPts 对应于包含NEW POSITION 点的OUTPUT 矩阵,status 指示哪些点已被跟踪 (1) 和哪些未跟踪 (0) AND err 包含与已计算位移的那些点相关的误差。 (Link)

到目前为止,我已经成功使用 goodFeaturesToTrack(...) 方法,但我仍然无法使用 calcOpticalFlowPyrLK 计算点的 FLOW (...) 方法。

这是负责初始化变量和跟踪点的代码块:

  private static final double MIN_FEATURE_QUALITY = 0.05;
  private static final double MIN_FEATURE_DISTANCE = 4.0;
  
  private Mat               prevGray;
  private MatOfPoint2f      prev2D,next2D;
  private MatOfPoint        corners;
  private MatOfByte         status;
  private MatOfFloat        err;
  private Scalar            color;
  private Size              winSize;
  private int               maxCorners,maxLevel;
  
  ...
  
  public void onCameraViewStarted(int width, int height) 

  nextGray = new Mat(height, width, CvType.CV_8UC1); //unsigned char
  Rscale = new Mat(height, width, CvType.CV_8UC1);
  prevGray = new Mat(height, width, CvType.CV_8UC1);
  prev2D = new MatOfPoint2f(new Point());
  next2D = new MatOfPoint2f(new Point());                           
  status = new MatOfByte();                             
  err = new MatOfFloat();   
  corners = new MatOfPoint();
  maxLevel = 0;
  winSize = new Size(21,21);
  color = new Scalar(0, 255, 0);
  maxCorners = 1;
  
  
  //THIS IS THE METHOD THAT TAKES CARE OF TRACKING THE POINTS
  public Mat onCameraFrame(CvCameraViewFrame inputFrame) 
 
  nextGray = inputFrame.gray();
  Rscale = nextGray;
  
  if(!corners.empty())
      Video.calcOpticalFlowPyrLK(
              prevGray,
              nextGray,
              prev2D,
              next2D,
              status,
              err,
              winSize,
              maxLevel);
      System.out.println("status = " + status.toArray()[0]);
      System.out.println("err = " + err.toArray()[0]);
  
  
  
  
  prevGray = nextGray;
  prev2D = next2D;
  for(int i=0;i<next2D.toArray().length;i++)
      Core.circle(Rscale, next2D.toArray()[i], 3, color,-1);
  
  System.out.println("Prev2D = " + prev2D.toArray()[0].toString());
  System.out.println("Next2D = " + next2D.toArray()[0].toString());       
  return Rscale;
 
  

问题: 如前所述,参数 status 告诉用户是否已经为每个点计算了流。使用 System.out.println(...),我检查每个点的状态,它们都是 1。此外,我还检查了错误并得到 error = 0.0 但是,这让我很生气,新的计算点始终与输入点相同(即 nextPts = prevPts )。话虽如此,有时这些点可能会以难以察觉的微小幅度改变位置,但这种情况很少发生......

【问题讨论】:

maxLevel 设置为 3 或 4 而不是 0。 我已经这样做了。没用 在执行此 calcOpticalFlowPyrLK 之前,您应该执行此 next2D = new MatOfPoint2D()。成功了吗? PeterNL,我已经在onCameraViewStarted(...)方法中初始化了next2D = new MatOfPoint2f(new Point());(在onCameraFrame方法之前调用) 是的,我看到你这样做了。但我记得你必须先清除要填充的数组。因此,在调用 calcOpticalFlowPyrLK 之前,next2D 必须为空。目前还没有! 【参考方案1】:
prevGray = nextGray;

这个shallow 拷贝会导致两个Mat 的点指向相同的像素数据。所以,在下一次迭代中,当你说:

nextGray = inputFrame.gray();

prevGray 也会更新到相同的像素;)

你想要的是一个副本:

 prevGray = nextGray.clone();
 prev2D = next2D.clone();  // same story..

【讨论】:

我自己已经在一篇旧帖子 (***.com/questions/23998775/…) 中回答了这个问题。不过谢谢。

以上是关于光流方法返回意外值的主要内容,如果未能解决你的问题,请参考以下文章

计算平均值时的意外返回值?

返回意外值的指针/数组索引

java - 错误不兼容类型:意外返回值 - 遍历列表

新 Swift 类的 void 函数中出现意外的非 void 返回值

Java布尔“意外的返回值”

如何停止返回导致“void函数中出现意外的非void返回值”?