【问题标题】:OpenCV 2.4.2 calcOpticalFlowPyrLK doesn't find any pointsOpenCV 2.4.2 calcOpticalFlowPyrLK 找不到任何点
【发布时间】:2012-07-26 04:32:00
【问题描述】:

我在 Linux 上使用 OpenCV 2.4.2。我正在用 C++ 编写。我想跟踪简单的对象(例如白色背景上的黑色矩形)。首先,我使用 goodFeaturesToTrack,然后使用 calcOpticalFlowPyrLK 在另一个图像上找到这些点。问题是 calcOpticalFlowPyrLK 没有找到这些点。

我找到了在 C 中执行此操作的代码,但在我的情况下不起作用:http://dasl.mem.drexel.edu/~noahKuntz/openCVTut9.html

我已经把它转换成C++了:

int main(int, char**) {
    Mat imgAgray = imread("ImageA.png", CV_LOAD_IMAGE_GRAYSCALE);
    Mat imgBgray = imread("ImageB.png", CV_LOAD_IMAGE_GRAYSCALE);
    Mat imgC = imread("ImageC.png", CV_LOAD_IMAGE_UNCHANGED);

    vector<Point2f> cornersA;

    goodFeaturesToTrack(imgAgray, cornersA, 30, 0.01, 30);

    for (unsigned int i = 0; i < cornersA.size(); i++) {
        drawPixel(cornersA[i], &imgC, 2, blue);
    }

    // I have no idea what does it do
//    cornerSubPix(imgAgray, cornersA, Size(15, 15), Size(-1, -1),
//            TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 20, 0.03));

    vector<Point2f> cornersB;
    vector<uchar> status;
    vector<float> error;

    // winsize has to be 11 or 13, otherwise nothing is found
    int winsize = 11;
    int maxlvl = 5;

    calcOpticalFlowPyrLK(imgAgray, imgBgray, cornersA, cornersB, status, error,
            Size(winsize, winsize), maxlvl);

    for (unsigned int i = 0; i < cornersB.size(); i++) {
        if (status[i] == 0 || error[i] > 0) {
            drawPixel(cornersB[i], &imgC, 2, red);
            continue;
        }
        drawPixel(cornersB[i], &imgC, 2, green);
        line(imgC, cornersA[i], cornersB[i], Scalar(255, 0, 0));
    }

    namedWindow("window", 1);
    moveWindow("window", 50, 50);
    imshow("window", imgC);

    cvWaitKey(0);

    return 0;
}

图片A:http://oi50.tinypic.com/14kv05v.jpg

图片B:http://oi46.tinypic.com/4l3xom.jpg

ImageC:http://oi47.tinypic.com/35n3uox.jpg

我发现它仅适用于 winsize = 11。我尝试在移动矩形上使用它来检查它与原点的距离。它几乎无法检测到所有四个角。

int main(int, char**) {
    std::cout << "Compiled at " << __TIME__ << std::endl;

    Scalar white = Scalar(255, 255, 255);
    Scalar black = Scalar(0, 0, 0);
    Scalar red = Scalar(0, 0, 255);
    Rect rect = Rect(50, 100, 100, 150);

    Mat org = Mat(Size(640, 480), CV_8UC1, white);
    rectangle(org, rect, black, -1, 0, 0);

    vector<Point2f> features;
    goodFeaturesToTrack(org, features, 30, 0.01, 30);
    std::cout << "POINTS FOUND:" << std::endl;
    for (unsigned int i = 0; i < features.size(); i++) {
        std::cout << "Point found: " << features[i].x;
        std::cout << " " << features[i].y << std::endl;
    }

    bool goRight = 1;

    while (1) {

        if (goRight) {
            rect.x += 30;
            rect.y += 30;
            if (rect.x >= 250) {
                goRight = 0;
            }
        } else {
            rect.x -= 30;
            rect.y -= 30;
            if (rect.x <= 50) {
                goRight = 1;
            }
        }

        Mat frame = Mat(Size(640, 480), CV_8UC1, white);
        rectangle(frame, rect, black, -1, 0, 0);

        vector<Point2f> found;
        vector<uchar> status;
        vector<float> error;
        calcOpticalFlowPyrLK(org, frame, features, found, status, error,
                    Size(11, 11), 5);

        Mat display;
        cvtColor(frame, display, CV_GRAY2BGR);

        for (unsigned int i = 0; i < found.size(); i++) {
            if (status[i]  == 0 || error[i] > 0) {
                continue;
            } else {
                line(display, features[i], found[i], red);
            }
        }

        namedWindow("window", 1);
        moveWindow("window", 50, 50);
        imshow("window", display);

        if (cvWaitKey(300) > 0) {
            break;
        }
    }

}

Lucas-Kanade 的 OpenCV 实现似乎无法跟踪二进制图像上的矩形。是我做错了什么还是这个功能不起作用?

【问题讨论】:

    标签: c++ linux opencv opticalflow


    【解决方案1】:

    KLT 通过查找关于某个窗口的两组点之间的转换来进行点跟踪。窗口大小是一个区域,每个点将在该区域上被追逐,以便在另一帧上匹配它。

    这是另一种基于梯度的算法,可以找到要跟踪的好特征。

    通常,KLT 使用金字塔形方法,以便即使在大动作时也能保持跟踪。对于您指定的“窗口大小”,它可能会在“maxLevel”时间使用。

    从未在二进制图像上尝试过 KLT。问题可能出在 KLT 实现上,它以错误的方向开始搜索,然后就失去了分数。当您更改窗口大小时,搜索算法也会更改。在您的图片上,您最多只有 4 个兴趣点,并且只有 1 个像素。

    这些是您感兴趣的参数:

    winSize – Size of the search window at each pyramid level
    maxLevel – 0-based maximal pyramid level number. If 0, pyramids are not used (single level), if 1, two levels are used etc.
    criteria – Specifies the termination criteria of the iterative search algorithm (after the specified maximum number of iterations criteria.maxCount or when the search window moves by less than criteria.epsilon
    

    建议:

    • 您尝试过自然图片吗? (例如两张照片),您将拥有更多要跟踪的功能。 4个或更少是很难保持的。我会先试试这个

    【讨论】:

    • 是的,我尝试过使用自然图片。我已经在数百张图像上测试了高达 150 的窗口大小和高达 100 的最大级别。对于这些值中的任何一个,它甚至没有检测到一个点。
    • 嗯,这肯定不是正常行为。我个人在使用 openCV 时遇到了一些障碍,我觉得它不是很直接,示例/文档也不是很有帮助。您可能想尝试一下 ViSP(在 GPL 下)ViSP Website 有一个为您制作的关于使用 KLT 进行点跟踪的教程
    【解决方案2】:

    Lucas Kanade 方法通过使用该区域中的梯度来估计该区域的运动。在这种情况下,梯度下降方法。因此,如果您在 x 和 y 方向上没有渐变,则该方法将失败。第二个重要的注意事项是 Lucas Kanade 方程

    E = sum_{winsize} (Ix * u + Iy * v * It)²

    是强度恒定约束的一阶泰勒近似。

    I(x,y,t) = I(x+u,y+v,t+1)

    所以没有水平的方法(图像金字塔)的一个限制是图像需要是一个线性函数。在实践中,这意味着只能估计小的运动,取决于您选择的 winsize。这就是为什么你使用水平,线性化图像(它)。所以5级有点高,3级就够了。在您的情况下,顶级图像的大小为 640x480 / 2^5 = 20 x 15。

    最后,你的代码中的问题是这一行:

     if (status[i]  == 0 || error[i] > 0) {
    

    您从 lucas kanade 方法返回的错误是生成的 SSD,这意味着:

    误差 = sum(winSize) (I(x,y,0) - I(x+u,y+u,1)^2) / (winsize * winsize)

    错误是 0 的可能性很小。所以最后你跳过所有功能。通过忽略错误,我有很好的经验,这只是一个信心衡量标准。有非常好的替代置信度度量,如前向/后向置信度。如果丢弃了太多的特征,您也可以通过忽略状态标志来开始实验

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-18
      • 1970-01-01
      • 2016-12-11
      • 2013-03-15
      • 2014-04-16
      相关资源
      最近更新 更多