从零开始学习SLAM六(单应矩阵)

本文参考:计算机视觉life

概念

单应性(homography)是指两个平面之间的一种保直线性的对应关系。如果一个平面上的点集经过某种变换后,在另一个平面上形成的新点集仍然保持原来的线性特性(如共线的点仍然共线),那么这种变换就称为单应性变换。在数学上,单应性变换可以用一个3x3的矩阵来表示,这个矩阵就是所谓的单应矩阵。

回顾相机成像

世界坐标系到相机坐标系的变换为:
在这里插入图片描述
我们简化表达式为, 其中M是3 x 3的矩阵:

( u v 1 ) = M ( x y z 1 ) \begin{pmatrix} u\\ v\\ 1 \end{pmatrix} = M\begin{pmatrix} x\\ y\\ z\\ 1 \end{pmatrix} uv1 =M xyz1

对于两个不同的相机,像素坐标和空间点坐标可以写成如下的表示
在这里插入图片描述
我们把上面两个式子合并一下就得到了下面这个式子,其中的H就是单应矩阵啦!H矩阵的两边是两张图像对应的匹配点对。也就是说单应矩阵H把三维空间中同一平面的点在两个相机的成像图片坐标进行了映射。
在这里插入图片描述
有时1会被平面方程替代,是因为
1.约束条件:在推导单应矩阵的过程中,通常会涉及到多个未知数。为了求解单应矩阵 H,我们需要建立足够的约束条件。平面方程在这里被用来增加额外的约束条件,以便降低问题的自由度。
2. 降维: 由于单应矩阵 H 是一个3x3的矩阵,理论上包含9个参数。但由于它是齐次坐标下的矩阵,所以可以任意缩放,这意味着有一个自由度是冗余的。因此,单应矩阵实际上有8个自由度。为了求解这8个自由度,我们需要至少4对匹配点来建立8个线性方程。
3. 平面方程: 在某些推导过程中,可能会引入平面方程作为约束条件。例如,假设我们有一个平面nTx+d=0,其中 n是平面的法向量,x 是平面上的点,d 是常数。在某些情况下,可以将这个平面方程中的 d 替换为1,从而简化计算或引入额外的约束。

平面方程:在讨论单应矩阵时提到的平面方程,通常指的是图像中的某个平面在三维空间中的方程。具体来说,当我们在处理单应矩阵时,我们关注的是两个图像之间对应点的关系,这些点通常位于同一个平面上。这个平面可以是场景中的任何一个平面,例如墙面、地面或者任何具有平面结构的物体表面。平面方程nTx+d=0中的x = (X,Y, Z)指的是三维世界坐标系中的点,世界坐标系是一个固定的参考坐标系,用于描述三维空间中的物体位置和方向。在这个坐标系中,每个点的位置可以用三个坐标(X,Y, Z)来表示。如下图:
在这里插入图片描述

下面给出代码:

#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

struct userdata {
    Mat im;
    vector<Point2f> points;
};

void mouseHandler(int event, int x, int y, int flags, void* data_ptr) {
    if (event == EVENT_LBUTTONDOWN) {
        userdata* data = (userdata*)data_ptr;
        circle(data->im, Point(x, y), 3, Scalar(0, 255, 255), 5, LINE_AA);
        imshow("Image", data->im);
        if (data->points.size() < 4) {
            data->points.push_back(Point2f(x, y));
        }
    }
}

int main(int argc, char** argv) {
    // Read in the logo and the image with the billboard
    Mat im_logo = imread("cy.png");
    Mat im_ad = imread("tm.jpg");

    if (im_logo.empty() || im_ad.empty()) {
        cout << "Could not open or find the images!" << endl;
        return -1;
    }

    // Define the four corners of the logo in the logo image (source points)
    Size logo_size = im_logo.size();
    vector<Point2f> pts_logo = {
        Point2f(0, 0),
        Point2f(logo_size.width - 1, 0),
        Point2f(logo_size.width - 1, logo_size.height - 1),
        Point2f(0, logo_size.height - 1)
    };

    // Destination image
    Mat im_temp = im_ad.clone();
    userdata data;
    data.im = im_temp;

    // Show the image and set mouse callback
    imshow("Image", im_temp);
    cout << "Click on four corners of the billboard and then press ENTER" << endl;
    setMouseCallback("Image", mouseHandler, &data);
    waitKey(0);

    // Check if we have exactly four points
    if (data.points.size() != 4) {
        cout << "You need to click exactly four points!" << endl;
        return -1;
    }

    // Define the four corners of the billboard in the destination image (destination points)
    vector<Point2f> pts_ad = data.points;

    // Compute the perspective transform matrix
    Mat H = findHomography(pts_logo, pts_ad);

    // Warp the logo image to fit the billboard area in the destination image
    Mat im_warped;
    warpPerspective(im_logo, im_warped, H, im_ad.size());

    // Create a mask for the warped logo
    Mat mask = Mat::zeros(im_ad.size(), CV_8UC1);
    vector<Point> mask_points(pts_ad.begin(), pts_ad.end());
    fillConvexPoly(mask, mask_points, Scalar(255));

    // Blend the warped logo with the destination image
    Mat im_ad_masked;
    im_ad.copyTo(im_ad_masked, ~mask);  // Copy the background
    im_warped.copyTo(im_ad_masked, mask);  // Overlay the warped logo

    // Display the final result
    imshow("Result", im_ad_masked);
    waitKey(0);

    return 0;
}

最后出图是一位绝顶大帅哥(不是秃头):
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值