Advertisement

OpenCV仿射变换Affine Transformations

阅读量:

OpenCV仿射变换用于图像重新映射和平移操作。该教程介绍如何使用cv::warpAffine函数实现简单的几何转换,并通过cv::getRotationMatrix2D获取旋转矩阵以调整图像位置和比例。理论部分解释了仿射变换的数学表示(线性变换加平移)及由两点三角形确定其关系的方法。代码展示加载图像、定义源与目标三角形点、计算并应用仿射变换的过程,并结合旋转操作生成最终效果示例图。

OpenCV仿射变换Affine Transformations

  • 仿射变换Affine Transformations
    • 目标
      • 理论基础

      • 如何理解仿射变换?

        • 探讨获取仿射变换的方法
      • 代码

        • 这个程序做什么?
      • 解释

      • 结果

仿射变换Affine Transformations

目标

在本教程中,我们将为您呈现如何利用OpenCV函数cv::warpAffine完成图像重映射过程。此外,请您注意以下操作步骤:通过调用OpenCV函数cv::getRotationMatrix2D可以获得一个2×3的旋转矩阵。

理论

什么是仿射变换?

该线性变换可采用矩阵乘法与向量加法相结合的方式进行描述。
基于上述分析,我们能够通过仿射变换来表征这一关系。
旋转变换(一种线性操作)是其中的基本组成部分。
平移操作(基于向量叠加)是其另一重要特性。
缩放操作(另一种线性转换)则构成了其完整的数学基础。
由此可见,在本质上仿射变换表征了两个图像之间的几何关系。

表示仿射变换的通常方法是使用 2 × 3 矩阵。

在这里插入图片描述

考虑到我们想通过使用A和B变换2D向量

在这里插入图片描述

通过使用A和B,我们可以执行以下操作:

在这里插入图片描述

我们如何获得仿射变换?

我们涉及仿射变换作为两个图像之间关系的核心概念。获取此类信息主要通过两种途径完成:
已知X与T两者相关联,则我们的目标便是确定中号这一关键元素。
了解 中号 与X之间的联系至关重要,在这种情况下我们只需计算Ť= M⋅ X即可获取所需信息。其中 中号 可能表现为明确的形式(例如具有一些特定矩阵特征),也可能反映点与点之间的几何关系。
为了更好地理解这一机制(见图b),我们需考虑最简单的场景:即一个变换仅涉及两个图像及其对应的三个关键点之间的对应关系。

在这里插入图片描述

在图1中构成一个三角形的三个关键点(即点1、点2和点3)经过映射后依旧保持三角形形状(尽管此时该三角形已臭名昭著),这一过程并未改变其几何特性。通过这样的方式——识别出包含这三个关键点的仿射变换——我们可以将其关系延伸至整个图像中的每个像素。

代码

这个程序做什么?

读取图像并应用仿射变换至其上。通过分析三键关系计算该变换参数。为此,我们调用OpenCV中的cv::warpAffine函数来实现这一过程。对图像执行旋转变换以适应需求,并确保每次旋转都是围绕图像中心进行的。等待用户退出程序流程以完成操作。

复制代码
    #include "opencv2/imgcodecs.hpp"
    #include "opencv2/highgui.hpp"
    #include "opencv2/imgproc.hpp"
    #include <iostream>
    using namespace cv;
    using namespace std;
    int main( int argc, char** argv )
    {
    CommandLineParser parser( argc, argv, "{@input | lena.jpg | input image}" );
    Mat src = imread( samples::findFile( parser.get<String>( "@input" ) ) );
    if( src.empty() )
    {
        cout << "Could not open or find the image!\n" << endl;
        cout << "Usage: " << argv[0] << " <Input image>" << endl;
        return -1;
    }
    Point2f srcTri[3];
    srcTri[0] = Point2f( 0.f, 0.f );
    srcTri[1] = Point2f( src.cols - 1.f, 0.f );
    srcTri[2] = Point2f( 0.f, src.rows - 1.f );
    Point2f dstTri[3];
    dstTri[0] = Point2f( 0.f, src.rows*0.33f );
    dstTri[1] = Point2f( src.cols*0.85f, src.rows*0.25f );
    dstTri[2] = Point2f( src.cols*0.15f, src.rows*0.7f );
    Mat warp_mat = getAffineTransform( srcTri, dstTri );
    Mat warp_dst = Mat::zeros( src.rows, src.cols, src.type() );
    warpAffine( src, warp_dst, warp_mat, warp_dst.size() );
    Point center = Point( warp_dst.cols/2, warp_dst.rows/2 );
    double angle = -50.0;
    double scale = 0.6;
    Mat rot_mat = getRotationMatrix2D( center, angle, scale );
    Mat warp_rotate_dst;
    warpAffine( warp_dst, warp_rotate_dst, rot_mat, warp_dst.size() );
    imshow( "Source image", src );
    imshow( "Warp", warp_dst );
    imshow( "Warp + Rotate", warp_rotate_dst );
    waitKey();
    return 0;
    }

解释

加载图像:

复制代码
    CommandLineParser parser( argc, argv, "{@input | lena.jpg | input image}" );
    Mat src = imread( samples::findFile( parser.get<String>( "@input" ) ) );
    if( src.empty() )
    {
        cout << "Could not open or find the image!\n" << endl;
        cout << "Usage: " << argv[0] << " <Input image>" << endl;
        return -1;
    }

仿射变换:正如我们在上面的解释中所述,我们需要两组3点来得出仿射变换关系。看一看:

复制代码
    Point2f srcTri[3];
    srcTri[0] = Point2f( 0.f, 0.f );
    srcTri[1] = Point2f( src.cols - 1.f, 0.f );
    srcTri[2] = Point2f( 0.f, src.rows - 1.f );
    Point2f dstTri[3];
    dstTri[0] = Point2f( 0.f, src.rows*0.33f );
    dstTri[1] = Point2f( src.cols*0.85f, src.rows*0.25f );
    dstTri[2] = Point2f( src.cols*0.15f, src.rows*0.7f );

建议绘制这些关键点以便更好地把握它们的变化趋势。这些点的大致位置与示例图中所述位置(见"理论"部分)基本一致。通过这三个关键点所构成的三角形的规模和朝向确实有所变化

获得了对应点集后,通过调用OpenCV函数cv::getAffineTransform完成仿射变换矩阵的估计:

复制代码
     Mat warp_mat = getAffineTransform( srcTri, dstTri );

我们得到一个 2 × 3矩阵作为输出(在本例中为warp_mat)

然后,我们将刚发现的仿射变换应用于src图像

复制代码
    Mat warp_dst = Mat::zeros( src.rows, src.cols, src.type() );
    warpAffine( src, warp_dst, warp_mat, warp_dst.size() );

具有以下参数:

源图像被指定为输入。目标图像是通过仿射变换得到的。仿射变换矩阵用于空间映射。目标图像的尺寸将由后续操作确定。我们刚刚生成了第一个变形图像,并计划逐步展示它,在生成之前还希望对其进行旋转调整。

旋转:要旋转图像,我们需要了解两件事:

旋转中心
指定旋转角度
在OpenCV中,默认采用逆时针方向作为正角
可选设置一个缩放比例因子
以下是用于定义这些参数的具体代码段:

复制代码
    Point center = Point( warp_dst.cols/2, warp_dst.rows/2 );
    double angle = -50.0;
    double scale = 0.6;

基于OpenCV库中的cv::getRotationMatrix2D函数生成旋转矩阵。该函数会生成一个2×3大小的矩阵,在本例中命名为rot_mat。

复制代码
    Mat rot_mat = getRotationMatrix2D( center, angle, scale );

现在,我们将找到的旋转应用于之前的转换的输出:

复制代码
    Mat warp_rotate_dst;
    warpAffine( warp_dst, warp_rotate_dst, rot_mat, warp_dst.size() );

最后,我们在两个窗口以及原始图像中很好地显示了我们的结果:

复制代码
    imshow( "Source image", src );
    imshow( "Warp", warp_dst );
    imshow( "Warp + Rotate", warp_rotate_dst );

我们只需要等到用户退出程序

复制代码
       waitKey();

结果

完成上述代码的编译后,我们可以通过提供该图像文件路径来指定参数。如以下所示:

完成上述代码的编译后,我们可以通过提供该图像文件路径来指定参数。如以下所示:

在这里插入图片描述

应用第一个仿射变换后,我们得到:

在这里插入图片描述

最后,在应用负旋转(记住负表示顺时针旋转)和比例因子后,我们得到:

在这里插入图片描述

全部评论 (0)

还没有任何评论哟~