【从零学习OpenCV 4】图像透视变换

  • 2019 年 12 月 13 日
  • 筆記

经过几个月的努力,小白终于完成了市面上第一本OpenCV 4入门书籍《从零学习OpenCV 4》。为了更让小伙伴更早的了解最新版的OpenCV 4,小白与出版社沟通,提前在公众号上连载部分内容,请持续关注小白。

本小节将介绍图像的另一种变换——透视变换。透视变换是按照物体成像投影规律进行变换,即将物体重新投影到新的成像平面,示意图如图3-24所示。透视变换常用于机器人视觉导航研究中,由于相机视场与地面存在倾斜角使得物体成像产生畸变,通常通过透视变换实现对物体图像的校正。透视变换中,透视前的图像和透视后的图像之间的变换关系可以用一个3×3的矩阵变换矩阵表示,该矩阵可以通过两张图像中四个对应点的坐标求取,因此透视变换又称作“四点变换”。与仿射变换一样,OpenCV 4中提供了根据四个对应点求取变换矩阵的getPerspectiveTransform()函数和进行透视变换的warpPerspective()函数,接下来将介绍这两个函数的使用方法,两个函数的函数原型在代码清单3-35和代码清单3-36中给出。

图3-24 透视变换原理示意图

代码清单3-35 getPerspectiveTransform()函数原型  1.  Mat cv::getPerspectiveTransform (const Point2f src[],  2.                                         const Point2f dst[],  3.                                         int  solveMethod = DECOMP_LU  4.                                         )
  • src[]:原图像中的四个像素坐标。
  • dst[]:目标图像中的四个像素坐标。
  • solveMethod:选择计算透视变换矩阵方法的标志,可以选择参数及含义在表3-6中给出。

该函数两个输入量都是存放浮点坐标的数组,在生成数组的时候像素点的输入顺序无关,但是需要注意像素点的对应关系,函数的返回值是一个3×3的变换矩阵。函数中最后一个参数是根据四个对应点坐标计算透视变换矩阵方法的选择标志,其可以选择的参数标志在表3-6中给出,默认情况下选择的是最佳主轴元素的高斯消元法DECOMP_LU。

表3-6 getPerspectiveTransform()函数计算方法标志

标志参数

简记

作用

DECOMP_LU

0

最佳主轴元素的高斯消元法

DECOMP_SVD

1

奇异值分解(SVD)方法

DECOMP_EIG

2

特征值分解法

DECOMP_CHOLESKY

3

Cholesky分解法

DECOMP_QR

4

QR分解法

DECOMP_NORMAL

16

使用正规方程公式,可以去前面的标志一起使用

代码清单3-36 warpPerspective()函数原型  1.  void cv::warpPerspective(InputArray src,  2.                                OutputArray dst,  3.                                InputArray M,  4.                                Size dsize,  5.                                int  flags = INTER_LINEAR,  6.                                int  borderMode = BORDER_CONSTANT,  7.                                const Scalar & borderValue = Scalar()  8.                                )
  • src:输入图像。
  • dst:透视变换后输出图像,与src数据类型相同,但是尺寸与dsize相同。
  • M:3×3的变换矩阵。
  • dsize:输出图像的尺寸。
  • flags:插值方法标志。
  • borderMode:像素边界外推方法的标志。
  • borderValue:填充边界使用的数值,默认情况下为0

该函数所有参数含义都与warpAffine()函数的参数含义相同,这里不再进行赘述。为了说明该函数在实际应用中的作用,在代码清单3-37中给出了将相机视线不垂直于二维码平面拍摄的图像经过透视变换变成相机视线垂直于二维码平面拍摄的图像。在图3-25中给出了相机拍摄到的二维码图像和经过程序透视变换后的图像。为了寻找透视变换关系,我们需要寻找拍摄图像中二维码四个角点的像素坐标和透视变换后角点对应的理想坐标。本程序中,我们事先通过Image Watch插件查看了拍摄图像二维码四个角点的坐标,并希望透视变换后二维码可以充满全部的图像,因此我们在程序中手动输入四对对应点的像素坐标。但是在实际工程中,二维码的角点坐标可以通过角点检测的方式获取,具体方式我们将在后面介绍。

代码清单3-37 myWarpPerspective.cpp二维码图像透视变换  1.  #include <opencv2opencv.hpp>  2.  #include <iostream>  3.  4.  using namespace cv;  5.  using namespace std;  6.  7.  int main()  8. {  9.    Mat img = imread("noobcvqr.png");  10.    if (img.empty())  11.    {  12.      cout << "请确认图像文件名称是否正确" << endl;  13.      return -1;  14.    }  15.  16.    Point2f src_points[4];  17.    Point2f dst_points[4];  18.    //通过Image Watch查看的二维码四个角点坐标  19.    src_points[0] = Point2f(94.0, 374.0);  20.    src_points[1] = Point2f(507.0, 380.0);  21.    src_points[2] = Point2f(1.0, 623.0);  22.    src_points[3] = Point2f(627.0, 627.0);  23.    //期望透视变换后二维码四个角点的坐标  24.    dst_points[0] = Point2f(0.0, 0.0);  25.    dst_points[1] = Point2f(627.0, 0.0);  26.    dst_points[2] = Point2f(0.0, 627.0);  27.    dst_points[3] = Point2f(627.0, 627.0);  28.    Mat rotation, img_warp;  29.    rotation = getPerspectiveTransform(src_points, dst_points); //计算透视变换矩阵  30.    warpPerspective(img, img_warp, rotation, img.size()); //透视变换投影  31.    imshow("img", img);  32.    imshow("img_warp", img_warp);  33.    waitKey(0);  34.    return 0;  35.  }

图3-25 myWarpPerspective.cpp程序运行结果