单应矩阵定义及应用

1. 单应矩阵介绍

在张正友标定法的时候,可以使用单应矩阵来计算标定板平面和像素平面之间的变换关系,其本身包含了相机的内参和标定板与相机的外参矩阵。而在射影几何中,单应矩阵更多地用来表征两个平面之间的变换关系。对于标定时

令世界点的Z_W=0,并将中间部分协作M,则有

s \begin{bmatrix} u\\v\\1\end{bmatrix}=M\begin{bmatrix} X_W\\Y_W\\1\end{bmatrix}

如果存在两个不同的相机,或者相同相机在不同位置派到同一个平面,则有

\begin{bmatrix} u_2\\v_2\\1\end{bmatrix}=M_2\begin{bmatrix} X_W\\Y_W\\1\end{bmatrix}=M_2M_1^{-1}\begin{bmatrix} u_1\\v_1\\1\end{bmatrix}=H\begin{bmatrix} u_1\\v_1\\1\end{bmatrix}

这表明,存在一组关系,可以实现两个像素平面的互相变换,具体的计算和标定时类似。

因为最后一行的1的缘故,H矩阵仍然只有8个自由度,所以只需要四组对应点即可计算出。

2. 单应矩阵在计算机视觉中的作用

1. 图像校正

使用四组对应点即可实现

2. 视角变换

 可以方便将普通视图转换为鸟瞰图

https://img-blog.csdnimg.cn/20181229155213634

3.图像拼接

因为单应矩阵可以进行视角转换,则可以将不同角度拍摄的图像转换到相同的视角,从而实现图像的拼接

 https://img-blog.csdnimg.cn/20181229155213680

 https://img-blog.csdnimg.cn/20181229155213726

4. 增强现实(AR)

平面二维标记图案长用来做AR展示,根据marker不同视角下的图像可以方便地得到虚拟物体的位置姿态并进行显示 

https://img-blog.csdnimg.cn/20181229155213765

 3.单应矩阵求解

h_9=1 

v_2=\frac{h_4u_1+h_5u_2+h_6}{h_7u_1+h_8v_1+1}

整理可得

u_2(h_7u_1+h_8v_1+1)=h_1u_1+h_2u_2+h_3

v_2(h_7u_1+h_8v_1+1)=h_4u_1+h_5u_2+h_6

整理成矩阵形式

所以只需要四组点即可求解出单应矩阵

但是再实际中,我们计算的关键点对会包含噪声,甚至出现误匹配的现象,所以只使用4组点来计算单应矩阵会出现很大的误差。因此一般会使用多于四组点来计算。另外直接法求解很难得到最优解,所以实际中会使用其他优化方法进行求解,比如SVD、LM等算法。

在opencv中有现成的函数可以进行调用

Mat findHomography(InputArray srcPoints, InputArray dstPoints, int method=0, double ransacReprojThreshold=3, OutputArray mask=noArray() )

从函数中看,只需要输入对应的匹配点,指定具体计算方法即可输出结果。

4. 图像拼接代码示例

输入:两张图片

​​​​​​

 输出:将图二拼接到图一

 代码:virtual_billboard.cpp

#include 
#include 
#include 
#include 
#include 
#include 
#include 

class ImageMosic{
public:
    ImageMosic(cv::Mat img_src,cv::Mat img_target){
        img_src_ = img_src.clone();
        img_target_ = img_target.clone();
        SetSrcPts();
        SetTargetPts();
        CalcHAndPerspective();
    }

private:
    struct UserData{
        cv::Mat img;
        std::vector pts;
    };
    void SetSrcPts()
    {
        cv::Size src_size = img_src_.size();
        src_pts_.push_back(cv::Point2f(0, 0));
        src_pts_.push_back(cv::Point2f(src_size.width-1, 0));
        src_pts_.push_back(cv::Point2f(src_size.width-1, src_size.height-1));
        src_pts_.push_back(cv::Point2f(0, src_size.height-1));
    }

    static void mouseHandler(int event, int x, int y, int flags,void* data_ptr)
    {
        if (event ==cv::EVENT_LBUTTONDOWN)
        {
            UserData *data = (UserData *)data_ptr;
            cv::circle( data->img, cv::Point(x, y), 3, cv::Scalar(0, 255, 255), 5);
            cv::imshow("Image_target", data->img);
            if (data->pts.size() < 4)
            {
                data->pts.push_back(cv::Point2f(x,y));
            }
        }
    }

    void SetTargetPts(){
        //show the image
        cv::imshow("Image_target", img_target_);
        std::cout << "Click on four corners of a billboard and then press ENTER" << std::endl;
        //set the callback function for any mouse event
        UserData user_data;
        user_data.img = img_target_;
        cv::setMouseCallback("Image_target", mouseHandler, &user_data);
        cv::waitKey(0);
        target_pts_ = user_data.pts;
    }
    void CalcHAndPerspective(){
        assert(target_pts_.size() == 4);
        cv::Mat H = cv::findHomography(src_pts_, target_pts_, 0);                   //计算单应矩阵
        cv::warpPerspective(img_src_, img_src_perspective_, H, img_target_.size()); //透视变换
        cv::Point pts_dst[4] = {target_pts_[0], target_pts_[1], target_pts_[2], target_pts_[3]};
        cv::fillConvexPoly(img_target_, pts_dst, 4, cv::Scalar(0));
        img_target_ = img_target_ + img_src_perspective_;

        cv::imshow("Image_target", img_target_);
        cv::waitKey(0);
    }
private:
    cv::Mat img_src_;
    cv::Mat img_src_perspective_;
    cv::Mat img_target_;
    std::vector src_pts_;
    std::vector target_pts_;
};

int main(){
    cv::Mat img_src = cv::imread("../images/cvlife.jpg");
    cv::Mat img_target = cv::imread("../images/ad.jpg");
    ImageMosic img_mosaic(img_src, img_target);
   
    return 0;
}

 CMakelists.txt

cmake_minimum_required(VERSION 1.0)
project(virtual_billboard)

find_package(OpenCV)

add_executable(virtual-billboard src/virtual_billboard.cpp)
target_link_libraries(virtual-billboard ${OpenCV_LIBS})

 操作步骤:在图片上从左上角顺时针选4个点,按enter结束

单应矩阵定义及应用_第1张图片

 结果:

5. 旋转平移恢复

        与本质矩阵类似,单应矩阵需要进行分解,才能得到旋转平移矩阵。分解的方法包含数值法[2][3]和解析法[4]。与本质矩阵类似,单应矩阵的分解也会返回4组旋转平移矩阵,并且同时可以解算出他们分别对应的场景点所在平面的法向量。如果已知成像的地图点的深度全为正值(即在相机前方),则可以排除两组。最后剩余两组解,需要更多的先验信息进行判断。通常我们可以通过假设已知场景平面的法向量来解决,如场景平面与相机平面平行,那么法向量的理论值为\textbf{1}^T

        单应性在SLAM中有重要意义。当特征点共面或相机发生纯旋转时,基础矩阵自由度下降,出现退化。现实中数据总会包含一些噪声,这是继续使用八点法求解基础矩阵,其多余出来的自由度将会主要由噪声决定。为了避免退化现象造成影响,通常我们会同时估计基础矩阵F和单应矩阵H,选择重投影误差较小的那个作为最终的运动估计矩阵。

6.参考

[1]单应性矩阵的理解及求解_机器视觉001的博客-CSDN博客_单应性矩阵

[2]Faugeras O D, Lustman F. Motion and structure from motion in a piecewise planar environment[J]. International Journal of Pattern Recognition and Artificial Intelligence, 1988, 2(03): 485-508.

[3]Zhang Z, Hanson A R. 3D reconstruction based on homography mapping[J]. Proc. ARPA96, 1996: 1007-1012.

[4]Malis E, Vargas M. Deeper understanding of the homography decomposition for vision-based control[D]. INRIA, 2007.

你可能感兴趣的:(矩阵,计算机视觉)