单应矩阵的简单应用

简述单应矩阵

单应性矩阵是描述两幅图像之间的变换,或者是描述三维世界的平面和像平面之间的变换关系。举个例子,在相机标定中,世界坐标系是一个三维坐标系,像素坐标系是一个二维坐标系,我们定义标定板为世界坐标系Z轴为0的平面上,原点位于标定板的一角,便可以用单应矩阵H去描述两个平面的转换关系,而两个平面上对应点的坐标都是已知的,就能求解出H矩阵。在这里插入图片描述
对于两张图片之间的转换可以理解为如下公式:

向量<u2,v2,1>和向量<u1,v1,1>分别是两张图片中的对应点,用齐次坐标表示,H是一个3x3的矩阵,若要求解出H矩阵,至少需要4组不同的对应点,详细讲解可参考从零开始学习「张氏相机标定法」(二)单应矩阵。OpenCV库已提供单应矩阵的计算,可以直接调用函数cv::findHomography。

输入矩阵srcPoints和dstPoints分别是原始平面和目标平面的点,这些都是二维的点,可以是Mat类型的坐标数据,CV_32FC2类型,或cv::Point2f的STL的vector对象。第三个输入变量method是计算单应性矩阵所使用的算法,cv::RANSAC(一致性随机抽样)和cv::LMEDS(最小平方中值)等。注意,为保证精确,我们只需要4个对应点,若提供更多的对应点,计算结果将会是最小二乘意义上的最优解。

实现简易P图


在这里插入图片描述
看完上面的三张图,会神奇地发现第二张图中人物的脸贴在第一张图上,便有了第三张图。那么这是如何实现的呢?
具体分为以为下三步:
1、通过鼠标点击选点
2、计算单应性矩阵和实现图像变换
3、画出掩膜和图像的融合

步骤一
为了方便表述,先将第一幅图称为obj,第二幅图称为scene。先将事件设置为鼠标按下左键,通过鼠标先后选取obj和scene两幅图像上选取ROI(感兴趣的区域,由4个点组成),并将点和图像共同储存在userdata定义的结构体中。

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),1,Scalar(0,0,255), 5, CV_AA);
        imshow("Image", data->im);
        if (data->points.size() < 4)
        {
            data->points.push_back(Point2f(x,y));
        }
    }
}

在这里插入图片描述
步骤二
结合步骤一获得4组对应点,调用计算单应矩阵的函数cv::findHomography和透视变换的函数cv::warpPerspective(利用单应矩阵实现图像变换)

Mat H = findHomography(data_of_obj.points,data_of_scene.points,CV_RANSAC);
warpPerspective(obj,result,H,scene.size()); 

在这里插入图片描述
步骤三
更据scene场景中的4个点,结合函数cv::fillpoly绘制出多边形,为copyTo函数提供掩膜,最后利用cv::copyTo实现融合。
{

myfillpoly(mask,data_of_scene);
scene.copyTo(result,mask);

在这里插入图片描述

完整代码

#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

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),1,Scalar(0,0,255), 5, CV_AA);
        imshow("Image", data->im);
        if (data->points.size() < 4)
        {
            data->points.push_back(Point2f(x,y));
        }
    }
}
void myfillpoly(Mat& bimage,userdata &s)  //绘制多边形,为copyTo函数提供掩膜。
{
	Point rookPoints[1][4];
	rookPoints[0][0] = s.points[0];
	rookPoints[0][1] = s.points[1];
	rookPoints[0][2] = s.points[2];
	rookPoints[0][3] = s.points[3];
	const Point* ppt[1]={rookPoints[0]};
	int npt[]={4};
	fillPoly(bimage,ppt,npt,1,Scalar(0,0,0),0);
}
int main()
{
	Mat obj = imread("obj.jpg",1);
	Mat scene = imread("scene.jpg",1);

    //物体取点
	Mat obj_temp = obj.clone();
	userdata data_of_obj;
	data_of_obj.im = obj_temp;
	
	imshow("Image",obj_temp);
	setMouseCallback("Image", mouseHandler, &data_of_obj);
	waitKey(0);
	
	//场景取点
	Mat scene_temp = scene.clone();
	userdata data_of_scene;
	data_of_scene.im = scene_temp;

	imshow("Image",scene_temp);
	setMouseCallback("Image", mouseHandler, &data_of_scene);
	waitKey(0);
	
	//计算单应性矩阵
	Mat H = findHomography(data_of_obj.points,data_of_scene.points,CV_RANSAC);

	Mat result,mask;
	mask = Mat(scene.size(),CV_8UC3,Scalar(255,255,255));
	warpPerspective(obj,result,H,scene.size()); //透视变换
	myfillpoly(mask,data_of_scene);
	
	scene.copyTo(result,mask);

	imshow("result",result);
	imwrite("/home/dh/Matrix_H/build/image.jpg",result);
	waitKey(0);
	return 0;
}

视频演示:

见bilibiliOpenCV实现简易P图
同时欢迎大家关注萌新up

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值