相机运动补偿

7
我正在使用openCV实现相机运动补偿的应用程序。我知道需要计算光流,然后找到两个帧之间的基本矩阵来转换图像。
以下是我目前所做的内容:
void VideoStabilization::stabilize(Image *image) {
    if (image->getWidth() != width || image->getHeight() != height) reset(image->getWidth(), image->getHeight());

    IplImage *currImage = toCVImage(image);
    IplImage *currImageGray = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);

    cvCvtColor(currImage, currImageGray, CV_BGRA2GRAY);

    if (baseImage) {
        CvPoint2D32f currFeatures[MAX_CORNERS];
        char featuresFound[MAX_CORNERS];

        opticalFlow(currImageGray, currFeatures, featuresFound);

        IplImage *result = transformImage(currImage, currFeatures, featuresFound);
        if (result) {
            updateImage(image, result);
            cvReleaseImage(&result);
        }
    }

    cvReleaseImage(&currImage);

    if (baseImage) cvReleaseImage(&baseImage);
    baseImage = currImageGray;

    updateGoodFeatures();
}

void VideoStabilization::updateGoodFeatures() {
    const double QUALITY_LEVEL = 0.05;
    const double MIN_DISTANCE = 5.0;

    baseFeaturesCount = MAX_CORNERS;

    cvGoodFeaturesToTrack(baseImage, eigImage,
                          tempImage, baseFeatures, &baseFeaturesCount, QUALITY_LEVEL, MIN_DISTANCE);

    cvFindCornerSubPix(baseImage, baseFeatures, baseFeaturesCount,
                       cvSize(10, 10), cvSize(-1,-1), TERM_CRITERIA);
}

void VideoStabilization::opticalFlow(IplImage *currImage, CvPoint2D32f *currFeatures, char *featuresFound) {
    const unsigned int WIN_SIZE = 15;
    const unsigned int PYR_LEVEL = 5;

    cvCalcOpticalFlowPyrLK(baseImage, currImage,
                           NULL, NULL,
                           baseFeatures,
                           currFeatures,
                           baseFeaturesCount,
                           cvSize(WIN_SIZE, WIN_SIZE),
                           PYR_LEVEL,
                           featuresFound,
                           NULL,
                           TERM_CRITERIA,
                           0);
}

IplImage *VideoStabilization::transformImage(IplImage *image, CvPoint2D32f *features, char *featuresFound) const {
    unsigned int featuresFoundCount = 0;
    for (unsigned int i = 0; i < MAX_CORNERS; ++i) {
        if (featuresFound[i]) ++featuresFoundCount;
    }

    if (featuresFoundCount < 8) {
        std::cout << "Not enough features found." << std::endl;
        return NULL;
    }

    CvMat *points1 = cvCreateMat(2, featuresFoundCount, CV_32F);
    CvMat *points2 = cvCreateMat(2, featuresFoundCount, CV_32F);

    CvMat *fundamentalMatrix = cvCreateMat(3, 3, CV_32F);

    unsigned int pos = 0;
    for (unsigned int i = 0; i < featuresFoundCount; ++i) {
        while (!featuresFound[pos]) ++pos;

        cvSetReal2D(points1, 0, i, baseFeatures[pos].x);
        cvSetReal2D(points1, 1, i, baseFeatures[pos].y);
        cvSetReal2D(points2, 0, i, features[pos].x);
        cvSetReal2D(points2, 1, i, features[pos].y);
        ++pos;
    }

    int fmCount = cvFindFundamentalMat(points1, points2, fundamentalMatrix, CV_FM_RANSAC, 1.0, 0.99);
    if (fmCount < 1) {
        std::cout << "Fundamental matrix not found." << std::endl;
        return NULL;
    }

    std::cout << fundamentalMatrix->data.fl[0] << " " << fundamentalMatrix->data.fl[1] << " " << fundamentalMatrix->data.fl[2] << "\n";
    std::cout << fundamentalMatrix->data.fl[3] << " " << fundamentalMatrix->data.fl[4] << " " << fundamentalMatrix->data.fl[5] << "\n";
    std::cout << fundamentalMatrix->data.fl[6] << " " << fundamentalMatrix->data.fl[7] << " " << fundamentalMatrix->data.fl[8] << "\n";

    cvReleaseMat(&points1);
    cvReleaseMat(&points2);

    IplImage *result = transformImage(image, *fundamentalMatrix);

    cvReleaseMat(&fundamentalMatrix);

    return result;
}

MAX_CORNERS的值为100,通常可以找到约70-90个特征。

使用此代码,我得到了一个奇怪的基础矩阵,如下所示:

-0.000190809 -0.00114947 1.2487
0.00127824 6.57727e-05 0.326055
-1.22443 -0.338243 1

由于我只是手持相机并尽量避免晃动(且没有物体移动),所以我预期矩阵接近于单位矩阵。我做错了什么吗?

另外,我不确定用什么来转换图像。cvWarpAffine需要一个2x3的矩阵,我应该舍弃最后一行还是使用另一个函数?


这个问题可能有助于理解基本矩阵:https://dev59.com/kXI95IYBdhLWcg3wtwRe - Martin Beckett
+1 对于这个(非常有趣的)问题 - BlackBear
1个回答

9
你要找的不是基础矩阵,而是仿射变换或透视变换。
基础矩阵描述了两个视角差异明显的相机之间的关系。它的计算方法是,如果你有两个点x(在一个图像上)和x'(在另一个图像上),它们是同一空间点的投影,那么x F x'(乘积)等于零。如果x和x'几乎相同......那么唯一的解决方案就是使F几乎为零(并且实际上没有用)。这就是你得到的结果。
应该接近单位矩阵的矩阵是一个转换A,它将点x转换为x'= A x(旧图像转换成新图像)。根据你想包括哪些类型的变换(仿射或透视),你可以使用cvGetAffineTransform或cvGetPerspectiveTransform函数来计算变换。对于这个,你需要3或4个点对。
然而,我认为最好的选择是cvFindHomograpy。它基于所有可用的点对估计透视变换,并使用异常值过滤算法(例如RANSAC),给出一个3x3矩阵。
然后你可以使用cvWarpPerspective来转换图像本身。

我仍然需要继续努力去修复抖动问题,但看起来现在是朝着正确的方向前进了,谢谢。请注意,该函数名称为cvFindHomography。 - fbafelipe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接