光流方法返回意外值
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 对应于最好的输出要用于跟踪的点数,maxCorners对应要获取的最大点数,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/…) 中回答了这个问题。不过谢谢。以上是关于光流方法返回意外值的主要内容,如果未能解决你的问题,请参考以下文章