OpenCV中文网站

 找回密码
 立即注册
搜索
热搜: 安装 配置
查看: 40958|回复: 22

opencv透视变换

[复制链接]
发表于 2015-1-14 10:42:46 | 显示全部楼层 |阅读模式
opencv透视变换

实现透视变换
目标:
在这篇教程中你将学到:
1、如何进行透视变化
2、如何生存透视变换矩阵
理论:
什么是透视变换:
1、透视变换(Perspective Transformation)是将图片投影到一个新的视平面(Viewing Plane),也称作投影映射(Projective Mapping)。
2、换算公式

u,v是原始图片左边,对应得到变换后的图片坐标x,y,其中
变换矩阵可以拆成4部分,表示线性变换,比如scaling,shearing和ratotion。用于平移,产生透视变换。所以可以理解成仿射等是透视变换的特殊形式。经过透视变换之后的图片通常不是平行四边形(除非映射视平面和原来平面平行的情况)。

重写之前的变换公式可以得到:


所以,已知变换对应的几个点就可以求取变换公式。反之,特定的变换公式也能新的变换后的图片。简单的看一个正方形到四边形的变换:
变换的4组对应点可以表示成:

根据变换公式得到:


定义几个辅助变量:


都为0时变换平面与原来是平行的,可以得到:


不为0时,得到:


求解出的变换矩阵就可以将一个正方形变换到四边形。反之,四边形变换到正方形也是一样的。于是,我们通过两次变换:四边形变换到正方形+正方形变换到四边形就可以将任意一个四边形变换到另一个四边形。





代码:
#include  "opencv2/highgui.hpp"
#include  "opencv2/imgproc.hpp"
#include  <iostream>
#include  <stdio.h>
using  namespace  cv;
using  namespace  std;
/**  @function  main  */
int  main(  int  argc,  char**  argv  )
{
             cv::Mat src= cv::imread( "test.jpg",0);
                 if (!src.data)
                                 return 0;
                vector<Point> not_a_rect_shape;
                not_a_rect_shape.push_back(Point(122,0));
                not_a_rect_shape.push_back(Point(814,0));
                not_a_rect_shape.push_back(Point(22,540));
                not_a_rect_shape.push_back(Point(910,540));
                 // For debugging purposes, draw green lines connecting those points
                 // and save it on disk
                const Point* point = &not_a_rect_shape[0];
                int n = (int )not_a_rect_shape.size();
                Mat draw = src.clone();
                polylines(draw, &point, &n, 1, true, Scalar(0, 255, 0), 3, CV_AA);
                imwrite( "draw.jpg", draw);
                 //  topLeft, topRight, bottomRight, bottomLeft
                cv::Point2f src_vertices[4];
                src_vertices[0] = not_a_rect_shape[0];
                src_vertices[1] = not_a_rect_shape[1];
                src_vertices[2] = not_a_rect_shape[2];
                src_vertices[3] = not_a_rect_shape[3];

                Point2f dst_vertices[4];
                dst_vertices[0] = Point(0, 0);
                dst_vertices[1] = Point(960,0);
                dst_vertices[2] = Point(0,540);
                dst_vertices[3] = Point(960,540);
                Mat warpMatrix = getPerspectiveTransform(src_vertices, dst_vertices);
                cv::Mat rotated;
                warpPerspective(src, rotated, warpMatrix, rotated.size(), INTER_LINEAR, BORDER_CONSTANT);
                 // Display the image
                cv::namedWindow( "Original Image");
                cv::imshow( "Original Image",src);
                cv::namedWindow( "warp perspective");
                cv::imshow( "warp perspective",rotated);
                imwrite( "result.jpg",src);
                cv::waitKey();
                 return 0;
}

代码解释:
1、获取图片,如果输入路径为空的话程序直接退出
  cv::Mat src= cv::imread( "test.jpg",0);
                 if (!src.data)
                                 return 0;
2、定义边界点,输入到std::vector数据结构中。注意这里的顺序如上图。
  vector<Point> not_a_rect_shape;
                not_a_rect_shape.push_back(Point(122,0));
                not_a_rect_shape.push_back(Point(814,0));
                not_a_rect_shape.push_back(Point(22,540));
                not_a_rect_shape.push_back(Point(910,540));
并将这几个点标注出来
        const Point* point = &not_a_rect_shape[0];
                int n = (int )not_a_rect_shape.size();
                Mat draw = src.clone();
                polylines(draw, &point, &n, 1, true, Scalar(0, 255, 0), 3, CV_AA);
                imwrite( "draw.jpg", draw);
3、生成透视变换矩阵
   cv::Point2f src_vertices[4];
                src_vertices[0] = not_a_rect_shape[0];
                src_vertices[1] = not_a_rect_shape[1];
                src_vertices[2] = not_a_rect_shape[2];
                src_vertices[3] = not_a_rect_shape[3];

                Point2f dst_vertices[4];
                dst_vertices[0] = Point(0, 0);
                dst_vertices[1] = Point(960,0);
                dst_vertices[2] = Point(0,540);
                dst_vertices[3] = Point(960,540);
                Mat warpMatrix = getPerspectiveTransform(src_vertices, dst_vertices);
4、执行转换
    cv::Mat rotated;
                warpPerspective(src, rotated, warpMatrix, rotated.size(), INTER_LINEAR, BORDER_CONSTANT);
5、显示并保存结果
       // Display the image
                cv::namedWindow( "Original Image");
                cv::imshow( "Original Image",src);
                cv::namedWindow( "warp perspective");
                cv::imshow( "warp perspective",rotated);
                imwrite( "result.jpg",src);
结果:
原始图片

标注四个边界点

透视变换后的图片

需要注意的是,这里变化后的图像丢失了一些边界细节,这在具体实现的时候是需要注意的。



回复

使用道具 举报

发表于 2015-1-14 16:01:49 | 显示全部楼层
分享精神值学习,好贴
回复 支持 反对

使用道具 举报

发表于 2015-1-14 20:04:55 | 显示全部楼层
楼主的帖子对我的帮助很大,谢谢楼主
回复 支持 反对

使用道具 举报

发表于 2015-1-14 22:57:58 | 显示全部楼层
Mark
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-1-15 11:25:30 | 显示全部楼层
此贴的目的在于opencv官方的教程里面没有透视变化的例子,我希望能够加入进去。所以先写中文的,如果没有什么问题修改成英文的。希望有能力者能够一起来做哦!
回复 支持 反对

使用道具 举报

发表于 2015-1-21 10:00:38 | 显示全部楼层
楼主,我想问一下:透视映射矩阵,源图像的四个点可以知道,与之相对应的目标图像的四个点是怎么知道的?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-1-21 21:19:17 | 显示全部楼层
纷飞 发表于 2015-1-21 10:00
楼主,我想问一下:透视映射矩阵,源图像的四个点可以知道,与之相对应的目标图像的四个点是怎么知道的? ...

透视变换只是一种方法,变换成什么样子是你自己控制的。比如要做图片矫正的话,目标的四个点就基本是个矩形的样子。
回复 支持 反对

使用道具 举报

发表于 2015-1-21 21:59:56 | 显示全部楼层
jsxyheu2014 发表于 2015-1-15 11:25
此贴的目的在于opencv官方的教程里面没有透视变化的例子,我希望能够加入进去。所以先写中文的,如果没有什 ...

有一些问题,我改了下,顺便把not_a_rect_shape四个点用鼠标点,坐上为点1,右上点2,右下点3,左下点4.目标的四个点为原图像大小。亲测
有问题请指教

#include "opencv2/core/core.hpp"
#include "opencv2/calib3d/calib3d.hpp"
#include  <opencv2/highgui/highgui.hpp>
#include  <opencv2/imgproc/imgproc.hpp>
#include  <iostream>
#include  <stdio.h>

using  namespace  cv;
using  namespace  std;

Mat src;
vector<Point> input_points;
Point2f dst_vertices[4];

void on_mouse( int event, int x, int y, int d, void *ptr)  
{  
        //如果没有这句,src的内容为0,但是主函数里的src是有内容的,为什么不加这句src在这里就是空数据
        //导致下面的warpPerspective的函数出错
        Mat src=imread("D:\\image\\left3.jpg",1);  
                  
    if( event == CV_EVENT_LBUTTONDOWN )  
    {  
                if(input_points.size()< 4)
                {               
                        input_points.push_back(Point2f(float(x),float(y)));
                        printf("Point[%d]:",input_points.size());
                        cout << x << " "<< y <<endl;
                }
                else
                {
                        cout << "Calculating Homography " <<endl;
                        // Deactivate callback
                        cv::setMouseCallback("Display window", NULL, NULL);
                               
                        Point2f src_vertices[4];
            src_vertices[0] = input_points[0];
            src_vertices[1] = input_points[1];
            src_vertices[2] = input_points[2];
            src_vertices[3] = input_points[3];
               
                        cout<<"dst_vertices[0]="<<dst_vertices[0]<<endl;
                        cout<<"dst_vertices[1]="<<dst_vertices[1]<<endl;
                        cout<<"dst_vertices[2]="<<dst_vertices[2]<<endl;
                        cout<<"dst_vertices[3]="<<dst_vertices[3]<<endl;

                        //Applies a perspective transformation to an image.
                        Mat warpMatrix = getPerspectiveTransform(src_vertices, dst_vertices);
                        cout<<"H="<<warpMatrix<<endl;

                        Mat warped;
                        warpPerspective(src, warped, warpMatrix, warped.size());

                        cv::namedWindow( "Original Image");
                        cv::imshow( "Original Image",src);
                        cv::namedWindow( "warp perspective");
                        cv::imshow( "warp perspective",warped);
                }

    }   
}

int main()
{
        Mat src=cvLoadImage("D:\\image\\left3.jpg",1);  
       
        dst_vertices[0] = Point(float(0),float(0));
        dst_vertices[1] = Point(float(src.cols),float(0));
        dst_vertices[2] = Point(float(src.cols),float(src.rows));
        dst_vertices[3] = Point(float(0),float(src.rows));

    cvNamedWindow("src",1);  
    cvSetMouseCallback( "src", on_mouse, 0 );

    imshow("src",src);
    cvWaitKey(0);   
  
    return 0;
}
回复 支持 反对

使用道具 举报

发表于 2015-1-22 09:20:12 | 显示全部楼层
在计算透视映射矩阵 的时候,参数有三个:源图像四个点的坐标,输出图像四个点的坐标,映射矩阵。源图像的四个点,是棋盘上的四个角点,那么我想请问,输出图像的四个点的坐标,如何获得呢?谢谢!
回复 支持 反对

使用道具 举报

发表于 2015-1-22 09:21:44 | 显示全部楼层
我在做鸟瞰图生成,哎,这个映射矩阵我觉得有问题。因为鸟瞰图不居中,偏很多,甚至根本显示不全
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|OpenCV中文网站

GMT+8, 2024-7-14 19:41 , Processed in 0.011322 second(s), 15 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表