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

opencv中的8位图像转16位图像 opencv图像变换_透视变换

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

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

opencv中的8位图像转16位图像 opencv图像变换_透视变换_02

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

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

#include<iostream>
#include<vector>
#include<string>
#include <opencv2/opencv.hpp>
#include "opencv/highgui.h"

using namespace std;
using namespace cv;

int main(int argc,char** argv) {
    cout<<"OpenCv Version: "<<CV_VERSION<<endl;
    Mat img=imread("noobcvqr.png");
    if(img.empty()){
        cout << "请确认图像文件名称是否正确" << endl;
        return -1;
    }
    Point2f src_points[4];
    Point2f dst_points[4];
    //通过Image Watch查看的二维码四个角点坐标
    src_points[0]=Point2f(94.0, 374.0);
    src_points[1]=Point2f(507.0, 380.0);
    src_points[2]=Point2f(1.0, 623.0);
    src_points[3]=Point2f(627.0, 627.0);
    //期望透视变换后二维码四个角点的坐标
    dst_points[0]=Point2f(0.0, 0.0);
    dst_points[1]=Point2f(627.0, 0.0);
    dst_points[2]=Point2f(0.0, 627.0);
    dst_points[3]=Point2f(627.0, 627.0);

    Mat rotation,img_warp;
    rotation=getPerspectiveTransform(src_points,dst_points);
    warpPerspective(img,img_warp,rotation,img.size());
    imshow("img",img);
    imshow("img_warp",img_warp);
    waitKey(0);
    return 0;
}