cv :: warpPerspective仅显示扭曲图像的一部分

时间:2014-03-06 09:31:33

标签: c++ opencv image-processing perspectivecamera

我使用getHomography和warpPerspective将图像从前视角更改为出价视图。

它的作用是图像扭曲到所需的视角,但裁剪关闭。它将扭曲的图像移动到图像框之外。我假设原因是因为操作导致负坐标。

我已经手动计算了翻译矩阵的计算点,而不是通过使用任何opencv:s函数来计算,因为棋盘函数未能检测到正确的点。

我想这可以通过对转换矩阵进行其他更改来解决。但那怎么办?另外,有没有办法确保变换后的图像沿x轴居中,然后将y轴调整到所需的位置?

现在完成工作的代码段:

cv::Mat image; // image is loaded with the original image

cv::Mat warpPers; // The container for the resulting image
cv::Mat H;

std::vector<cv::Point2f> src;
std::vector<cv::Point2f> dst;

// In reality several more points.
src.push_back(cv::Point2f(264,301));
src.push_back(cv::Point2f(434,301));
src.push_back(cv::Point2f(243,356));
src.push_back(cv::Point2f(476,356));

dst.push_back(cv::Point2f(243,123));
dst.push_back(cv::Point2f(476,123));
dst.push_back(cv::Point2f(243,356));
dst.push_back(cv::Point2f(476,356));

H = cv::findHomography(src, dst, CV_RANSAC);

cv::warpPerspective(image, 
newPers,
H,
cv::Size(3000,3000),
cv::INTER_NEAREST | CV_WARP_FILL_OUTLIERS
);

cv::namedWindow("Warped persp", cv::WINDOW_AUTOSIZE );
cv::imshow( "Warped persp", newPers);

3 个答案:

答案 0 :(得分:7)

Opencv提供了非常方便的方式来进行转换。你唯一要做的就是通过findHomography来处理单应性返回。 实际上,您提供的图像中的某些点可能位于x轴或y轴的负部分。 所以你必须在扭曲图像之前做一些检查。

步骤1:使用findHomography找到单应性H. 你会得到一个经典的单应性结构

H = [ h00, h01, h02;
      h10, h11, h12;
      h20, h21,   1];

步骤2:在变形后搜索图像角落的位置

所以让我来定义角落的顺序:

(0,0) ________ (0, w)
     |        |
     |________|
(h,0)          (h,w)

要做到这一点,只需创建一个类似的矩阵:

P = [0, w, w, 0;
     0, 0, h, h;
     1, 1, 1, 1]

使用H制作产品并获得扭曲的坐标:

P' = H * P

步骤3:使用这些新的4点检查x和y的最小值,并获得扭曲图像的大小 之后,您已经完成了这样的产品:

P' = [s1*x1, s2*x2, s3*x3, s4*x4;
      s1*y1, s2*y2, s3*y3, s4*y4;
      s1   , s2   , s3   , s4]

因此,要获得新的有效坐标,只需将第1行和第2行除以第3行

之后检查第一行上列的最小值,以及第二行上行的最小值(使用cvReduce)

找到包含图像的边界框(即warpPerspective函数的dst矩阵的维度),用cvReduce查找每行的最大值

让minx成为第一行(即列)的最小值,maxx(1行的最大值) miny和maxy为第二排。

因此扭曲图像的大小应为cvSize(maxx-minx,maxy-miny)

步骤4:对单应性添加校正 检查minx和/或miny是否为负,如果minx < 0然后将-minx添加到h02并且如果miny&lt; 0,然后将-miny添加到h12

所以H应该是:

H = [ h00, h01, h02-minx; //if minx <0
      h10, h11, h12-miny; //if miny <0
      h20, h21,   1];

第5步:扭曲图像

答案 1 :(得分:1)

如果我理解正确,那么基本上的问题就是需要一种方法来计算正确的偏移量,以平移扭曲的图像。我将解释如何获得正确的偏移量进行翻译。想法是,两幅图像中的匹配特征在最终缝合的图像中应具有相同的坐标。

假设我们按以下方式引用图片:

  • “源图像”(si):需要变形的图像
  • “目标图片”(di):透视图“源图片”将变形的图片
  • “变形的源图像”(wsi):源图像 将其扭曲到目标图像透视图之后

以下是您需要执行的操作,以便计算平移的偏移量:

  1. 在对好的匹配进行采样并从单应性图中找到蒙版之后,存储最佳匹配的关键点(一个距离最小且为整数的(应从单应性计算中获得的蒙版中值为1))在sidi中。假设分别在si and di is bm_si and bm_di`中最匹配的关键点。

    bm_si = [x1, y1,1]

    bm_di = [x2, y2, 1]

  2. 只需将bm_si与单应性矩阵(wsi)相乘即可找到Hbm_wsi = np.dot(H,bm_si)中的位置。 bm_wsi = [x/bm_wsi[2] for x in bm_wsi]

    di

  3. 根据要在si变形(= {wsi)的输出上放置bm_di的位置,调整si

    让我们说,如果您要从左图向右图(例如,左图为di,右图为di)变形,那么您将wsi置于右图bm_di[0] += si.shape[0]边,因此x_offset = bm_di[0] - bm_si[0]

  4. 完成上述步骤后

    y_offset = bm_di[1] - bm_si[1]

    si

  5. 使用计算出的偏移量找到新的单应性矩阵并扭曲T = np.array([[1, 0, x_offset], [0, 1, y_offset], [0, 0, 1]])

    translated_H = np.dot(T.H)

    wsi_frame_size = tuple(2*x for x in si.shape)

    stitched = cv2.warpPerspective(si, translated_H, wsi_frame_size)

    stitched[0:si.shape[0],si.shape[1]:] = di

    list_1 = [i for i in list_1 if i[0] != i[1]]

答案 2 :(得分:0)

我认为这个问题OpenCV warpperspective与当前问题cv::warpPerspective only shows part of warped image

类似

所以我也在这里给你答案https://stackoverflow.com/a/37275961/15485

请尝试以下homography_warp

void homography_warp(const cv::Mat& src, const cv::Mat& H, cv::Mat& dst);

src是源图片。

H是你的单应性。

dst是扭曲的图像。

homography_warp按照https://stackoverflow.com/users/1060066/matt-freeman的答案调整你的单应性https://stackoverflow.com/a/8229116/15485

// Convert a vector of non-homogeneous 2D points to a vector of homogenehous 2D points.
void to_homogeneous(const std::vector< cv::Point2f >& non_homogeneous, std::vector< cv::Point3f >& homogeneous)
{
    homogeneous.resize(non_homogeneous.size());
    for (size_t i = 0; i < non_homogeneous.size(); i++) {
        homogeneous[i].x = non_homogeneous[i].x;
        homogeneous[i].y = non_homogeneous[i].y;
        homogeneous[i].z = 1.0;
    }
}

// Convert a vector of homogeneous 2D points to a vector of non-homogenehous 2D points.
void from_homogeneous(const std::vector< cv::Point3f >& homogeneous, std::vector< cv::Point2f >& non_homogeneous)
{
    non_homogeneous.resize(homogeneous.size());
    for (size_t i = 0; i < non_homogeneous.size(); i++) {
        non_homogeneous[i].x = homogeneous[i].x / homogeneous[i].z;
        non_homogeneous[i].y = homogeneous[i].y / homogeneous[i].z;
    }
}

// Transform a vector of 2D non-homogeneous points via an homography.
std::vector<cv::Point2f> transform_via_homography(const std::vector<cv::Point2f>& points, const cv::Matx33f& homography)
{
    std::vector<cv::Point3f> ph;
    to_homogeneous(points, ph);
    for (size_t i = 0; i < ph.size(); i++) {
        ph[i] = homography*ph[i];
    }
    std::vector<cv::Point2f> r;
    from_homogeneous(ph, r);
    return r;
}

// Find the bounding box of a vector of 2D non-homogeneous points.
cv::Rect_<float> bounding_box(const std::vector<cv::Point2f>& p)
{
    cv::Rect_<float> r;
    float x_min = std::min_element(p.begin(), p.end(), [](const cv::Point2f& lhs, const cv::Point2f& rhs) {return lhs.x < rhs.x; })->x;
    float x_max = std::max_element(p.begin(), p.end(), [](const cv::Point2f& lhs, const cv::Point2f& rhs) {return lhs.x < rhs.x; })->x;
    float y_min = std::min_element(p.begin(), p.end(), [](const cv::Point2f& lhs, const cv::Point2f& rhs) {return lhs.y < rhs.y; })->y;
    float y_max = std::max_element(p.begin(), p.end(), [](const cv::Point2f& lhs, const cv::Point2f& rhs) {return lhs.y < rhs.y; })->y;
    return cv::Rect_<float>(x_min, y_min, x_max - x_min, y_max - y_min);
}

// Warp the image src into the image dst through the homography H.
// The resulting dst image contains the entire warped image, this
// behaviour is the same of Octave's imperspectivewarp (in the 'image'
// package) behaviour when the argument bbox is equal to 'loose'.
// See http://octave.sourceforge.net/image/function/imperspectivewarp.html
void homography_warp(const cv::Mat& src, const cv::Mat& H, cv::Mat& dst)
{
    std::vector< cv::Point2f > corners;
    corners.push_back(cv::Point2f(0, 0));
    corners.push_back(cv::Point2f(src.cols, 0));
    corners.push_back(cv::Point2f(0, src.rows));
    corners.push_back(cv::Point2f(src.cols, src.rows));

    std::vector< cv::Point2f > projected = transform_via_homography(corners, H);
    cv::Rect_<float> bb = bounding_box(projected);

    cv::Mat_<double> translation = (cv::Mat_<double>(3, 3) << 1, 0, -bb.tl().x, 0, 1, -bb.tl().y, 0, 0, 1);

    cv::warpPerspective(src, dst, translation*H, bb.size());
}