神奇的透视变换

  1. 理论公式

透视变换(Pespective Transform)是将一个视平面上的物体转换到一个新的视平面。变换公式如下:

神奇的透视变换

其中等式右边的u,v是源图片的坐标,在变换后图像中的对应坐标x, y,可以用下式计算得到:

神奇的透视变换

据此,原图像和透视变换后的目标图像中的点,对应转换关系如下:

神奇的透视变换

神奇的透视变换

变换矩阵的子矩阵神奇的透视变换表示线性变换,比如scaling(缩放),shearing和rotation(旋转)。神奇的透视变换表示平移。神奇的透视变换产生透视变换。所以可以认为仿射变换是透视变换的特殊形式。到此,我们解释了透视变换的理论公式,那透视变换矩阵中的9个参数该如何求解呢?强大的OpenCV库粉墨登场。

  1. OpenCV的getPerspectiveTransform函数,warpPerspective函数和findHomography函数

在第一部分介绍的透视变换矩阵可以使用OpenCV库的getPerspectiveTransform函数求解,它在OpenCV 2.4.13中的函数原型如下:

Mat getPerspectiveTransform(InputArray src, InputArray dst)

参数: src为原图像四边形定点的坐标集合。dst为目标图像对应四边形定点的坐标集合。在这里建议使用std::vector 数据结构存储四个点的坐标。Note: 坐标值必须是32f,也就是float类型,使用std::vector是不行的。

warPerspective函数是对一个图像做透视变换,它的C++格式的函数原型声明如下:

void warpPerspective(InputArray src, OutputArray dst, InputArray M, Size dsize, int flags=INTER_LINEAR, int borderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar())

参数: src是源图像,也就是我们想操作的图像,dst是变换后的目标图像,M就是我们之前得到的透视变换矩阵。dsize是得到的目标图像的尺寸,这是个很有意思的参数,我一般是设为,之前计算透视矩阵时,选取的目标四边形的大小。如若不然,会得到很丑的黑色填充区域。至于剩下的参数,他们都有缺省值,一般用不到,读者如果感兴趣,可以翻阅一下reference manual,目前最新版本是2.4.13.

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

该函数是计算两个面之间对应点之间的单应性矩阵,点对之间如需要十分精准的映射关系,我们就使用该函数,而不是getPerspectiveTransform。

参数:srcPoints是原图像中的点坐标,dstPoints是目标图像中的点坐标。在选取点时,越均匀,映射关系越好。method有:0-使用所有点的常规方法,CV_RANSAC-基于RANSAC的鲁棒方法,CV_LMEDS-最小中位数鲁棒方法。

void perspectiveTransform(InputArray src, OutputArray dst, InputArray m)

该函数实现视角转换,实现点点之间的映射。src是源图像中的点,dst是目标图像中的点,m就是之前的getPerspectiveTransform函数和findHomography函数中计算出来的映射矩阵。

  1. 撸代码

来来来,代码撸起来!!!程序思路如下:使用OpenCV的鼠标点击响应函数,手动选取出四边形四个点坐标(坐标点自动识别比较难,对于一些特殊的图形,可以试试直线霍夫变换,角点检测实现自动识别)。然后设定目标图像的尺寸,计算透视转换矩阵,完成透视转换。最后,我希望我在原图像里点击一个位置,转换后图像的对应位置,也能有一致的响应。

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

using namespace cv;
using namespace std;
void mouse(int event, int x, int y, int flags, void*);
Mat src, gray, dst_img, h;
vector<Point2f> selected;
vector<Point2f> dst;
int width =500;
int height =400;

int flag=0;
int main()
{
    src = imread("book.jpg", 1);
    imshow("book", src);
    setMouseCallback("book", mouse, 0); //void setMouseCallback(const string& winname, MouseCallback onMouse, void* userdata=0)
    dst.push_back(Point2f(0, 0));
    dst.push_back(Point2f(width-1, 0));
    dst.push_back(Point2f(width-1, height-1));
    dst.push_back(Point2f(0, height-1));
    waitKey();
}

void mouse(int event, int x, int y, int flags, void*)
{

    if(event == EVENT_LBUTTONDOWN)  //如果鼠标按下了。
    {
        circle(src, Point(x,y), 3, Scalar(0,0,255), -1);
        imshow("book", src);
        selected.push_back(Point2f(x,y));
        ++flag;
        if(flag==4)
        {
            h= getPerspectiveTransform(selected, dst);
            warpPerspective(src, dst_img, h, Size(width, height));
            imshow("dst", dst_img);
            waitKey(1);
        }

        if(flag>4) //我在透视转换前的图像里点击一个位置,我们希望在透视转换后的图像里,也可以有相应的响应.
        {

            double h11=h.at<double>(0, 0);
            double h12=h.at<double>(0, 1);
            double h13=h.at<double>(0, 2);
            double h21=h.at<double>(1, 0);
            double h22=h.at<double>(1, 1);
            double h23=h.at<double>(1, 2);
            double h31=h.at<double>(2, 0);
            double h32=h.at<double>(2, 1);
            double h33=h.at<double>(2, 2);

            int tr_x=(int)(h11*x+h12*y+h13)/(h31*x+h32*y+h33);
            int tr_y=(int)(h21*x+h22*y+h23)/(h31*x+h32*y+h33);            //也可以使用perspectiveTransform函数,直接转换            //vector<Point2f> tmp(1);            //tmp.push_back(Point2f(tr_x, tr_y));            //vector<Point2f> p(1);            //perspectiveTransform(tmp, p, h);            //circle(dst_img, p[0], 3, Scalar(0,0,255), -1);
            circle(dst_img, Point(tr_x,tr_y), 3, Scalar(0,0,255), -1);
            imshow("dst", dst_img);
            waitKey();

        }
    }
}
  1. 成果展示

神奇的透视变换 神奇的透视变换

左图是源图像,我选取了它的四个角,进行透视变换,得到了右边方方正正的书!至于点击响应,羽毛的中间部分已经被我点满啦!
原文链接: https://www.cnblogs.com/cbyzju/p/6288252.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/247801

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月14日 上午2:29
下一篇 2023年2月14日 上午2:30

相关推荐