3

我通过使用 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);
4

3 回答 3

13

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

第 1 步:使用 findHomography 找到单应性 H,您将获得单应性的经典结构

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

step 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 (第一行的最大值) miny 和 maxy 为第二行。

所以扭曲图像的大小应该是 cvSize(maxx-minx, maxy-miny)

第 4 步:对单应性添加更正 检查 minx 和/或 miny 是否为负,如果 minx < 0 则将 -minx 添加到 h02,如果 miny < 0,则将 -miny 添加到 h12

所以 H 应该是:

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

第5步:扭曲图像

于 2014-04-13T16:34:44.287 回答
2

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

所以我也在这里给你我的答案https://stackoverflow.com/a/372 ​​75961/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());
}
于 2016-05-20T09:19:06.890 回答
1

如果我理解正确,基本上问题需要计算正确偏移以平移扭曲图像的方法。我将解释如何获得正确的翻译偏移量。想法是两个图像中的匹配特征在最终拼接图像中应该具有相同的坐标。

假设我们按如下方式引用图像:

  • 'source image' ( si): 需要变形的图像
  • 'destination image' ( di): 透视图 'source image' 将被扭曲的图像
  • 'warped source image'( wsi):将源图像扭曲到目标图像透视图后

以下是计算平移偏移量所需执行的操作:

  1. 在对好的匹配进行采样并从单应性中找到掩码后,将最佳匹配的关键点(一个具有最小距离且是内点的关键点(应该从单应性计算获得的掩码中获得 1 的值))存储在si 和中disi and 让我们分别在di isbm_si bm_di`中说最佳匹配的关键点and..

    bm_si = [x1, y1,1]

    bm_di = [x2, y2, 1]

  2. 只需将 in 与单应矩阵 ( ) 相乘即可找到bm_siin的位置。wsiHbm_wsi = np.dot(H,bm_si)

    bm_wsi = [x/bm_wsi[2] for x in bm_wsi]

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

    假设您要从左图像变形到右图像(例如左图像si和右图像di),那么您将放置di在右侧wsi,因此bm_di[0] += si.shape[0]

  4. 现在经过上述步骤

    x_offset = bm_di[0] - bm_si[0]

    y_offset = bm_di[1] - bm_si[1]

  5. 使用计算出的偏移量找到新的单应矩阵并扭曲si.

    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

于 2020-08-25T13:25:29.050 回答