2

LinuxでOpenCV2.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;
}

ImageA: http: //oi50.tinypic.com/14kv05v.jpg

ImageB: 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実装は、バイナリイメージ上の長方形を追跡できないようです。私は何か間違ったことをしていますか、それともこの機能は機能しませんか?

4

2 に答える 2

8

Lucas Kanade メソッドは、領域内の勾配を使用して領域の動きを推定します。勾配がメソッドを下降する場合です。したがって、x AND y 方向にグラデーションがない場合、メソッドは失敗します。2 番目の重要な注意点は、Lucas Kanade 方程式

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

は、強度不変制約の一次テイラー近似です。

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

したがって、レベルのない方法 (画像ピラミッド) の制限は、画像が線形関数である必要があることです。実際には、これは、選択した winsize に応じて、小さな動きのみを推定できることを意味します。そのため、画像を線形化するレベルを使用します (It)。したがって、レベル 5 は少し高すぎますが、3 で十分です。あなたの場合、トップレベルの画像のサイズは 640x480 / 2^5 = 20 x 15 です。

最後に、コードの問題は次の行です。

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

lucas kanade メソッドから返されるエラーは、結果の SSD です。つまり、次のことを意味します。

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

エラーが 0 になる可能性はほとんどありません。そのため、最終的にすべての機能をスキップします。エラーを無視することで良い経験ができました。これは単なる信頼度です。前方/後方信頼度として、非常に優れた代替信頼度があります。あまりにも多くのフィーチャーが破棄された場合、ステータス フラグを無視して実験を開始することもできます。

于 2013-01-16T20:49:24.767 に答える
2

KLT は、特定のウィンドウに関する 2 つのポイント セット間の変換を見つけることによって、ポイント トラッキングを行います。ウィンドウ サイズは、各ポイントが他のフレームと一致するように追跡される領域です。

これは、追跡するのに適した機能を見つける勾配に基づく別のアルゴリズムです。

通常、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

提案 :

  • 自然な写真で試してみましたか?(たとえば 2 枚の写真)、追跡する機能がさらに多くなります。4以下はキープするのがかなり難しいです。私はこれを最初に試してみます
于 2012-07-27T14:00:36.227 に答える