OpenCV 4.11.0
开源计算机视觉库
加载中…
搜索中…
未找到匹配项
仿射变换

上一篇教程: 重新映射
下一篇教程: 直方图均衡化

原作者Ana Huamán
兼容性OpenCV >= 3.0

目标

在本教程中,您将学习如何:

理论

什么是仿射变换?

  1. 一种可以表示为矩阵乘法(线性变换)后跟向量加法(平移)形式的变换。
  2. 从上面可以看出,我们可以使用仿射变换来表达:

    1. 旋转(线性变换)
    2. 平移(向量加法)
    3. 缩放操作(线性变换)

    您可以看到,本质上,仿射变换表示两幅图像之间的关系

  3. 表示仿射变换的常用方法是使用一个 \(2 \times 3\) 矩阵。

    \[ A = \begin{bmatrix} a_{00} & a_{01} \\ a_{10} & a_{11} \end{bmatrix}_{2 \times 2} B = \begin{bmatrix} b_{00} \\ b_{10} \end{bmatrix}_{2 \times 1} \]

    \[ M = \begin{bmatrix} A & B \end{bmatrix} = \begin{bmatrix} a_{00} & a_{01} & b_{00} \\ a_{10} & a_{11} & b_{10} \end{bmatrix}_{2 \times 3} \]

    考虑到我们想使用 \(A\) 和 \(B\) 来变换一个 2D 向量 \(X = \begin{bmatrix}x \\ y\end{bmatrix}\),我们可以用以下方法做到:

    \(T = A \cdot \begin{bmatrix}x \\ y\end{bmatrix} + B\) 或 \(T = M \cdot [x, y, 1]^{T}\)

    \[T = \begin{bmatrix} a_{00}x + a_{01}y + b_{00} \\ a_{10}x + a_{11}y + b_{10} \end{bmatrix}\]

如何获得仿射变换?

  1. 我们提到仿射变换基本上是两幅图像之间的关系。关于这种关系的信息大致可以通过两种方式获得:
    1. 我们知道 \(X\) 和 \(T\),并且我们也知道它们是相关的。那么我们的任务就是找到 \(M\)。
    2. 我们知道 \(M\) 和 \(X\)。为了获得 \(T\),我们只需要应用 \(T = M \cdot X\)。我们关于 \(M\) 的信息可能是显式的(即具有 2x3 矩阵),也可能来自点之间的几何关系。
  2. 让我们更好地解释一下 (b)。由于 \(M\) 关联两幅图像,我们可以分析最简单的情况,即它关联两幅图像中的三个点。看下图:

点 1、2 和 3(在图像 1 中形成一个三角形)被映射到图像 2,仍然形成一个三角形,但现在它们发生了显著的变化。如果我们用这 3 个点找到仿射变换(您可以随意选择它们),那么我们就可以将这个找到的关系应用于图像中的所有像素。

代码

  • 这个程序做什么?
    • 加载图像
    • 将仿射变换应用于图像。此变换是从三个点之间的关系中获得的。为此,我们使用函数 cv::warpAffine
    • 在图像变换后,对其应用旋转。此旋转相对于图像中心。
    • 等待用户退出程序

说明

  • 加载图像

  • 仿射变换:如上所述,我们需要两组3个点来推导出仿射变换关系。请看

    您可以绘制这些点,以便更好地了解它们是如何变化的。它们的位置与示例图(理论部分)中描绘的位置大致相同。您可能会注意到,由这3个点定义的三角形的尺寸和方向发生了变化。

  • 有了这两组点,我们使用OpenCV函数cv::getAffineTransform 计算仿射变换。

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

  • 然后我们将刚刚找到的仿射变换应用于源图像

    参数如下:

    • src:输入图像
    • warp_dst:输出图像
    • warp_mat:仿射变换
    • warp_dst.size():输出图像的所需大小

    我们得到了第一张变换后的图像!我们将以一位显示它。在此之前,我们还想旋转它……

  • 旋转:要旋转图像,我们需要知道两件事

    1. 图像将围绕其旋转的中心
    2. 旋转角度。在OpenCV中,正角度表示逆时针方向
    3. 可选:比例因子

    我们使用以下代码片段定义这些参数

  • 我们使用OpenCV函数cv::getRotationMatrix2D 生成旋转矩阵,该函数返回一个\(2 \times 3\)矩阵(在本例中为rot_mat

  • 现在我们将找到的旋转应用于先前变换的输出

  • 最后,我们将结果显示在两个窗口中,再加上原始图像以便更好地进行比较

  • 我们只需要等待用户退出程序即可

结果

  • 编译上面的代码后,我们可以将图像路径作为参数传递给它。例如,对于如下所示的图片:

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

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