【OpenCV】透视变换应用——实现鸟瞰图与贴图

透视变换是3D转换,透视变换的本质是将图像投影到一个新的视平面;

据此,我们可以使用透视变化来实现鸟瞰图和图形贴图的效果;

一、鸟瞰图

实现前:

 实现效果:

 

1.准备一个空的mat对象 用于保存转换后的图

    Mat image=imread("road.jpg");
    imshow("image",image);
    Mat result=Mat::zeros(500,600,CV_8UC1);
    //存储转换后的图像坐标 按顺时针 左上、右上、右下、左下(可自己定顺序)
    vector<Point2f> obj;
    obj.push_back(Point2f(0,0));
    obj.push_back(Point2f(600,0));
    obj.push_back(Point2f(600,500));
    obj.push_back(Point2f(0,500));

2.在原图窗口里做鼠标操作,通过setMouseCallback鼠标回调函数,获取原图四个坐标

在原图中点击四个点获取四个坐标,顺序与上述1中设置的坐标对应,如果1中是顺时针,按选择坐标时也应该按顺时针操作,并且按照左上、右上、右下、左下顺序;

    //2.在原图窗口里做鼠标操作,通过鼠标回调函数,获取原图四个坐标
    struct imagedata data;
    data.img=image;//将原图传入
    setMouseCallback("image",mouseHundle,&data);//鼠标回调函数
    waitKey(0);//键盘输入 向下执行
    //点击按键之后结束鼠标操作,得到原图四个坐标和转换后的坐标
//用到的结构体和鼠标处理函数
struct imagedata
{
    Mat img;
    vector<Point2f>points;
    //该points是test1原图需要做转换的坐标
    //在test2中是原图转换后的坐标
};

void mouseHundle(int event,int x,int y,int flag,void *arg)
{
    struct imagedata* ind=(struct imagedata *)arg;
    if(event==EVENT_LBUTTONDOWN)//鼠标左键按下
    {
        //画圆
        circle(ind->img,Point(x,y),3,Scalar(0,0,255),3,CV_AA);
        imshow("image",ind->img);//test1鸟瞰图显示原图带圆点
        //imshow("dst",ind->img);//test2贴图显示city图带圆点
        if(ind->points.size()<4)
        {
            ind->points.push_back(Point2f(x,y));//转换的坐标只需要收集四个
        }
    }
}

3.开始计算坐标映射矩阵

Mat res=findHomography(data.points,obj,CV_RANSAC);

4.进行透视转换

将原图转变为效果图进行显示

warpPerspective(image,result,res,result.size());
imshow("result",result);
waitKey(0);

二、贴图

city图实现前: 

 贴图实现后:

左边大屏幕更改

 贴图与鸟瞰图有些不同,鸟瞰图是将一张原图,通过选择四个点转换成鸟瞰图;

转换后的坐标固定的,转换前的4个坐标由鼠标选择;

而贴图的话,是将原图通过选择的点进行转换,再贴到city图中;

转换前的坐标固定的,就是普通的图片,转换后的坐标由鼠标选择;

1.准备原图要贴上去的图,即1.jpg

     Mat image1=imread("1.jpg");//原图
    Mat image2=imread("city.jpg");//city图
    Mat dst=image2.clone();//克隆一份城市图
    //1.准备原图要贴上去的图,即1.jpg
    //Mat result=Mat::zeros(image1.rows,image1.cols,CV_8UC1);
    //存储转换前的图像坐标
    vector<Point2f> obj;
    obj.push_back(Point2f(0,0));
    obj.push_back(Point2f(image1.cols,0));
    obj.push_back(Point2f(image1.cols,image1.rows));
    obj.push_back(Point2f(0,image1.rows));

    imshow("dst",image2);

2.在city窗口里做鼠标操作,通过鼠标回调函数,获取转换后的四个坐标

    struct imagedata data;
    data.img=dst;//将city图传入
    setMouseCallback("dst",mouseHundle,&data);//鼠标回调函数
    waitKey(0);

 3.开始计算坐标映射矩阵

//点击按键之后结束鼠标操作,得到原图四个坐标和转换后的坐标
    Mat res=findHomography(obj,data.points,CV_RANSAC);

 4.透视转换

warpPerspective(image1,dst,res,dst.size());
    imshow("image1",dst);

 做到这一步,将原图变成这样子

 

5. 获取四个坐标,将city图中选择的位置填充成黑色,防止两个图片颜色冲突

最后通过image2+=dst把图片贴到city图上,这样就完成了

    Point pts[4];
    for(int i=0;i<4;i++)
    {
        pts[i]=data.points[i];
    }
    //在city图中选择的位置填充成黑色,防止两图颜色冲突
    fillConvexPoly(image2,pts,4,Scalar(0),CV_AA);

    image2+=dst;//把图片贴到city图上
    imshow("final",image2);
    waitKey(0);

 完整源码

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace  cv;

struct imagedata
{
    Mat img;
    vector<Point2f>points;
    //该points是test1原图需要做转换的坐标
    //在test2中是原图转换后的坐标
};

void mouseHundle(int event,int x,int y,int flag,void *arg)
{
    struct imagedata* ind=(struct imagedata *)arg;
    if(event==EVENT_LBUTTONDOWN)//鼠标左键按下
    {
        //画圆
        circle(ind->img,Point(x,y),3,Scalar(0,0,255),3,CV_AA);
        //imshow("image",ind->img);//test1显示原图带圆点
        imshow("dst",ind->img);//test2显示city图带圆点
        if(ind->points.size()<4)
        {
            ind->points.push_back(Point2f(x,y));//转换的坐标只需要收集四个
        }
    }
}

void test1()
{
    Mat image=imread("road.jpg");
    imshow("image",image);

    //1.准备空的mat对象  用于保存转换后的图
    Mat result=Mat::zeros(500,600,CV_8UC1);
    //存储转换后的图像坐标 按顺时针 左上、右上、右下、做下(可自己定顺序)
    vector<Point2f> obj;
    obj.push_back(Point2f(0,0));
    obj.push_back(Point2f(600,0));
    obj.push_back(Point2f(600,500));
    obj.push_back(Point2f(0,500));

    //2.在原图窗口里做鼠标操作,通过鼠标回调函数,获取原图四个坐标
    struct imagedata data;
    data.img=image;//将原图传入
    setMouseCallback("image",mouseHundle,&data);//鼠标回调函数
    waitKey(0);
    //点击按键之后结束鼠标操作,得到原图四个坐标和转换后的坐标
    //3.开始计算坐标映射矩阵
    Mat res=findHomography(data.points,obj,CV_RANSAC);

    //4.透视转换
    warpPerspective(image,result,res,result.size());
    imshow("result",result);
    waitKey(0);
}
void test2()
{
    Mat image1=imread("1.jpg");
    Mat image2=imread("city.jpg");
    Mat dst=image2.clone();//克隆一份城市图

    //1.准备原图要贴上去的图,即1.jpg
    //Mat result=Mat::zeros(image1.rows,image1.cols,CV_8UC1);
    //存储转换前的图像坐标
    vector<Point2f> obj;
    obj.push_back(Point2f(0,0));
    obj.push_back(Point2f(image1.cols,0));
    obj.push_back(Point2f(image1.cols,image1.rows));
    obj.push_back(Point2f(0,image1.rows));

    imshow("dst",image2);

    //2.在city窗口里做鼠标操作,通过鼠标回调函数,获取需要四个坐标
    struct imagedata data;
    data.img=dst;//将city图传入
    setMouseCallback("dst",mouseHundle,&data);//鼠标回调函数
    waitKey(0);
    //点击按键之后结束鼠标操作,得到原图四个坐标和转换后的坐标
    //3.开始计算坐标映射矩阵
    Mat res=findHomography(obj,data.points,CV_RANSAC);

    //4.透视转换
    warpPerspective(image1,dst,res,dst.size());
    imshow("image1",dst);

    Point pts[4];
    for(int i=0;i<4;i++)
    {
        pts[i]=data.points[i];
    }
    //在city图中选择的位置填充成黑色,防止两图颜色冲突
    fillConvexPoly(image2,pts,4,Scalar(0),CV_AA);

    image2+=dst;//把图片贴到city图上
    imshow("final",image2);
    waitKey(0);
}

int main()
{
    //test1();
    test2();
    return 0;
}

感谢观看!!!!

以上就是全部内容,如果对您有帮助,欢迎点赞评论,或者发现有哪里写错的,欢迎指正!

猜你喜欢

转载自blog.csdn.net/logani/article/details/128355493