OpenCV中的鸟瞰投影错误

时间:2013-04-24 20:16:29

标签: c++ opencv computer-vision perspective

我正在使用OpenCV尝试对此图像进行鸟瞰投影:this image
我首先找到棋盘的所有内角并绘制它们,如图image所示 然后我对它使用warpPerspective(),但它会产生一个极小的扭曲图像,如here所示。任何人都可以找出造成这种情况的原因吗?

这是我的代码:

#include <ros/ros.h>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/calib3d/calib3d.hpp>
#include <vector>

using namespace cv ;

int main(int argc, char* argv[] ) {
  ros::init( argc, argv, "bird_view" ) ;
  int board_w = atoi(argv[1]);
  int board_h = atoi(argv[2]);

  cv::Size board_sz( board_w, board_h );
  cv::Mat image = cv::imread( "image.jpg" ) ;
  cv::Mat gray_image, tmp, H , birds_image;
  cv::Point2f objPts[4], imgPts[4] ;
  std::vector<cv::Point2f> corners ;
  float Z = 1 ; //have experimented from values as low as .1 and as high as 100
  int key = 0 ;
  int found = cv::findChessboardCorners( image, board_sz, corners ) ;

  if (found) {
    cv::drawChessboardCorners(image, board_sz, corners, 1) ;
    cvtColor( image, gray_image, CV_RGB2GRAY ) ;
    cornerSubPix( gray_image, corners, Size(11, 11), Size(-1, -1),
                  TermCriteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 30, 0.1) ) ;
    cv::resize( image, tmp, Size(), .5, .5 ) ;
    namedWindow( "IMAGE" ) ;
    cv::imshow( "IMAGE" , tmp ) ;
    waitKey(0) ;
  }

  objPts[0].x = 0 ;
  objPts[0].y = 0 ;
  objPts[1].x = board_w-1 ;
  objPts[0].y = 0 ;
  objPts[0].x = 0 ;
  objPts[0].y = board_h-1 ;
  objPts[0].x = board_w-1 ;
  objPts[0].y = board_h-1 ;

  imgPts[0] = corners.at(0) ;
  imgPts[1] = corners.at(board_w-1) ;
  imgPts[2] = corners.at((board_h-1) * board_w) ;
  imgPts[3] = corners.at((board_h-1) * board_w + board_w-1) ;

  H = cv::getPerspectiveTransform( objPts, imgPts ) ;

  birds_image = image ;

  while ( key != 27 ) {

    H.at<float>(2,2) = Z ;

    cv::warpPerspective( image, birds_image, H, Size( 2 * image.cols, 2 * tmp.rows ) ,
                         CV_INTER_LINEAR | CV_WARP_INVERSE_MAP | CV_WARP_FILL_OUTLIERS ) ;

    cv::imshow( "IMAGE", birds_image ) ;
    cv::waitKey(0) ;
  }

  return 0 ;
}

所有这些代码都是基于O'Reilly的OpenCV书籍的鸟瞰投影示例。我认为产生的图片是正确的,但我不确定,直到我肯定看到它。

2 个答案:

答案 0 :(得分:2)

首先,我看到你正在覆盖objPts [0]而没有使用objPts 1到[3]:

objPts[0].x = 0 ;
objPts[0].y = 0 ;
objPts[1].x = board_w-1 ;
objPts[0].y = 0 ;
objPts[0].x = 0 ;
objPts[0].y = board_h-1 ;
objPts[0].x = board_w-1 ;
objPts[0].y = board_h-1 ;

现在, getPerspectiveTransform 做的是找到将一组点p0 ... p3转换为p0'... p3'的转换,假设它们与{{3}相关联}。如果你想使用 warpPerspective 进行这样的转换,那么p0 ... p3和p0'... p3'都必须以图像坐标(像素)表示,而objPts不是这种情况。以任意空间坐标表示。

您要对 getPerspectiveTransform 说,您希望在图像坐标(像素)中的国际象棋图案的角落与生成的图像坐标(像素)之间进行以下映射:

corner0 -> (0,0)
corner1 -> (board_w-1, 0)
corner2 -> (0, board_h-1)
corner3 -> (board_w-1, board_h-1)

所以,如果board_w是10,你将棋盘映射到10像素宽的图像!这解释了您获得的结果。

要获得您想要的效果,您应该在上面示例的第二列中使用您想要鸟类视图图像中的国际象棋图案的图像(像素)坐标。例如,如果您希望每个方格为10x10像素,请将值乘以10。

此外,请记住,您没有校正镜头失真(这看起来似乎并不大),并且图案看起来不是完全平坦的,因此您可能会在结果图像中以某种形式出现“瑕疵”。略微弯曲的线条。

希望这有帮助!

答案 1 :(得分:2)

感谢Milo澄清了getPerspectiveTransform的作用,我略微改变了在指定地图点时的所作所为:

std::vector<cv::Point2f> objPts(4) ;
objPts[0].x = 250 ;
objPts[0].y = 250 ;
objPts[1].x = 250 + (board_w-1) * 25 ;
objPts[1].y = 250 ;
objPts[2].x = 250 ;
objPts[2].y = 250 + (board_h-1) * 25 ;
objPts[3].x = 250 + (board_w-1) * 25 ;
objPts[3].y = 250 + (board_h-1) * 25 ;

它很棒!它会因每个图像而异,因此请使用基本值以及要将图像转换为大小的范围。