2011-11-11 121 views
7

我正在研究OpenCV中的Panography/Panorama应用程序,我遇到了一个我实际弄不懂的问题。想了解全景照片是什么样子的,请看这里的Panography百科文章:http://en.wikipedia.org/wiki/PanographyOpenCV findHomography问题

到目前为止,我可以拍摄多张图像,并将它们拼接在一起,同时使任何图像成为我喜欢的参考图像;这是我的意思。

An example Panography image I've created

然而,正如你所看到的 - 它有很多的问题。我面对的主要问题是图像被切割(重新:最右边的图像,图像的顶部)。为了突出为什么发生这种情况,我就画已匹配点,并绘制了其中的转变将结束行:

The image matches

其中左图像是参考图像,右图像是它被翻译后的图像(原文如下) - 我画了绿线以突出图像。图像有以下角点:

TL: [234.759, -117.696] 
TR: [852.226, -38.9487] 
BR: [764.368, 374.84] 
BL: [176.381, 259.953] 

所以我的主要问题是,经过角度已经改变形象:

Original Image

遭受的损失,像这样:

Cut up image

现在已经足够的图像,一些代码。

我使用的是cv::SurfFeatureDetectorcv::SurfDescriptorExtractorcv::FlannBasedMatcher获得所有这些点,我计算了比赛和做更重要的是精彩的比赛如下:

/* calculate the matches */ 
for(int i = 0; i < descriptors_thisImage.rows; i++) { 
    double dist = matches[i].distance; 
    if(dist < min_dist) min_dist = dist; 
    if(dist > max_dist) max_dist = dist; 
} 

/* calculate the good matches */ 
for(int i = 0; i < descriptors_thisImage.rows; i++) { 
    if(matches[i].distance < 3*min_dist) { 
     good_matches.push_back(matches[i]); 
    } 
} 

这是非常标准的,并为此,我跟着教程这里找到:http://opencv.itseez.com/trunk/doc/tutorials/features2d/feature_homography/feature_homography.html

要复制图像彼此顶上,我用下面的方法(其中img1img2std::vector<cv::Point2f>

/* set the keypoints from the good matches */ 
for(int i = 0; i < good_matches.size(); i++) { 
    img1.push_back(keypoints_thisImage[ good_matches[i].queryIdx ].pt); 
    img2.push_back(keypoints_referenceImage[ good_matches[i].trainIdx ].pt); 
} 

/* calculate the homography */ 
cv::Mat H = cv::findHomography(cv::Mat(img1), cv::Mat(img2), CV_RANSAC); 

/* warp the image */ 
cv::warpPerspective(thisImage, thisTransformed, H, cv::Size(thisImage.cols * 2, thisImage.rows * 2), cv::INTER_CUBIC); 

/* place the contents of thisImage in gsThisImage */ 
thisImage.copyTo(gsThisImage); 

/* set the values of gsThisImage to 255 */ 
for(int i = 0; i < gsThisImage.rows; i++) { 
    cv::Vec3b *p = gsThisImage.ptr<cv::Vec3b>(i); 
    for(int j = 0; j < gsThisImage.cols; j++) { 
     for(int grb=0; grb < 3; grb++) { 
      p[j][grb] = cv::saturate_cast<uchar>(255.0f); 
     } 
    } 
} 

/* convert the colour to greyscale */ 
cv::cvtColor(gsThisImage, gsThisImage, CV_BGR2GRAY); 

/* warp the greyscale image to create an image mask */ 
cv::warpPerspective(gsThisImage, thisMask, H, cv::Size(thisImage.cols * 2, thisImage.rows * 2), cv::INTER_CUBIC); 

/* stitch the transformed image to the reference image */ 
thisTransformed.copyTo(referenceImage, thisMask); 

所以,我有坐标扭曲的图像将在哪里结束,我有点创建用于这些转换的同质矩阵 - 但我无法弄清楚我应该如何去翻译这些图像,以便它们不会被切断。任何帮助或指针非常感谢!

回答

5

首先,您为什么不使用新添加的拼接模块?它完全符合你想要做的事情。

其次,如果你想继续你的代码,纠正它很容易。在单应性矩阵中,翻译表示最后一列的值。

a11 a12 a13 t1 
a21 a22 a23 t2 
a31 a32 a33 t3 
a41 a42 a43 1 

(如果你有一个3x3矩阵,你将错过a13..a43列和a41..1一行。A33将(应该)变为1)。

所以,你需要做的是找出你应该放在最后一列,以便图像对齐。

请检查这篇文章,解释(某种程度上相反的问题)如何建立单应性,当你知道相机参数。它会帮助你理解矩阵值的作用。

Opencv virtually camera rotating/translating for bird's eye view

而且一切我告诉你最后一栏只是近似,因为在最后一列中的值实际上是翻译加上一些(未成年人)的因素。

+1

啊,我明白了 - 在我运行cv :: warpPerspective之前,我编辑了全息图中的值?现在非常明显!非常感谢!另外,我会研究图像拼接模块并将其添加为一个选项(也可以使用GPU和CPU比较差异) - 只是试图学习OpenCV。 :-) – krslynx

+1

再次感谢! http://krslynx.com/images/ – krslynx

+1

很高兴看到你的好成绩! – Sam

1

一旦找到矩阵,您应该计算角点的变换并收集变换点的最小值和最大值x和y值。

一旦你有了这个边框只是(-xmin,-ymin)翻译所有矩阵并分配结果的图像是(xmax-xmin)(ymax-ymin)高大,然后绘制所有图像转化成。

使用这种方法,您将在拼接周围产生黑色区域,但无裁剪。

自动查找包含在拼接中的最大矩形(以获得完全合并的图像,没有黑色区域和最小限幅),实现起来会相当麻烦。