3.3. 图像的几何变换

3.3.1. 目标

  • 学习将不同的几何变换应用于图像,如平移、旋转、仿射变换等。

  • 您将看到这些功能: cv2.getPerspectiveTransform

3.3.2. 变换

OpenCV提供了两个转换函数, cv2.warpAffinecv2.warpPerspective ,可以进行各种转换。 cv2.warpAffine 获取2x3转换矩阵,而 cv2.warpPerspective 以3x3变换矩阵作为输入。

缩放比例

缩放只是调整图像的大小。OpenCV带有一个函数 调整大小() 为此目的。可以手动指定图像的大小,也可以指定缩放因子。使用不同的插值方法。较好的插值方法是 cv2.INTER_AREA 为了收缩和 cv2.INTER_CUBIC (慢)& cv2.INTER_LINEAR 用于缩放。默认情况下,使用的插值方法是 cv2.INTER_LINEAR 用于所有调整大小的目的。可以使用以下方法之一调整输入图像的大小:

>>> import cv2
>>> import numpy as np
>>>
>>> img = cv2.imread('/cvdata/messi5.jpg')
>>>
>>> res = cv2.resize(img,None,fx=2, fy=2, interpolation = cv2.INTER_CUBIC)
>>>
>>> #OR
>>>
>>> height, width = img.shape[:2]
>>> res = cv2.resize(img,(2*width, 2*height), interpolation = cv2.INTER_CUBIC)

翻译

翻译是物体位置的转移。如果你知道(x,y)方向的变化,就让它 \((t_x,t_y)\) ,可以创建转换矩阵 \(\textbf{{M}}\) 如下:

\[开始{aligned}M=begin{bmatrix}1&0&t戥x\0&1&t戥yend{bmatrix}end{aligned}\]

你可以把它变成 np.float32 把它传给 变形() 功能。关于(100,50)的移位,请参见下面的示例:

>>> import cv2
>>> import numpy as np
>>>
>>> img = cv2.imread('/cvdata/messi5.jpg',0)
>>> rows,cols = img.shape
>>>
>>> M = np.float32([[1,0,100],[0,1,50]])
>>> dst = cv2.warpAffine(img,M,(cols,rows))
>>>
>>> # cv2.imshow('img',dst)
>>> # cv2.waitKey(0)
>>> # cv2.destroyAllWindows()
>>> %matplotlib inline
>>> import matplotlib.pyplot as plt
>>> plt.imshow(dst)
<matplotlib.image.AxesImage at 0x7ff412133828>
../_images/sec03-geometric-transformations_4_1.png

警告

cv2.warpAffine() 函数第三个参数是输出图像的大小,它应该是 (宽度,高度) . 记住width=列数,height=行数。

结果如下:

旋转

图像旋转一个角度 \(\theta\) 由形式的转换矩阵实现

\[开始{aligned}M=begin{bmatrix}costheta&-sintheta\sintheta&costhetaend{bmatrix}end{aligned}\]

但是OpenCV提供了旋转中心可调的缩放旋转,因此您可以在任何您喜欢的位置旋转。修正的变换矩阵由

\[开始{aligned}begin{bmatrix}alpha&beta&(1-alpha)cdot center.x-betacdot center.y-beta&alpha&betacdot center.x+(1-alpha)cdot center.yend{bmatrix}end{aligned}\]

在哪里?

\[开始{aligned}begin{array}{l}alpha=scalecdotcostheta,\beta=scalecdotsinthetaend{array}end{aligned}\]

为了找到这个转换矩阵,OpenCV提供了一个函数, cv2.getRotationMatrix2D . 选中下面的示例,该示例将图像相对于中心旋转90度而不进行任何缩放。

>>> # img = cv2.imread('messi5.jpg',0)
>>> rows,cols = img.shape
>>>
>>> M = cv2.getRotationMatrix2D((cols/2,rows/2),90,1)
>>> dst = cv2.warpAffine(img,M,(cols,rows))
>>>
>>> plt.imshow(dst)
<matplotlib.image.AxesImage at 0x7ff4108574e0>
../_images/sec03-geometric-transformations_6_1.png

查看结果:

仿射变换

在仿射变换中,原始图像中的所有平行线在输出图像中仍然是平行的。为了找到变换矩阵,我们需要从输入图像中提取三个点,并在输出图像中找到它们的对应位置。那么 cv2.getAffineTransform 将创建一个2x3矩阵,传递给 cv2.warpAffine .

检查下面的示例,并查看我选择的点(这些点用绿色标记):

>>> # img = cv2.imread('/cvdata/drawing.png')
>>> import cv2
>>>
>>> img = cv2.imread('/cvdata/cards.png')
>>>
>>> rows,cols,ch = img.shape
>>>
>>> pts1 = np.float32([[50,50],[200,50],[50,200]])
>>> pts2 = np.float32([[10,100],[200,50],[100,250]])
>>>
>>> M = cv2.getAffineTransform(pts1,pts2)
>>>
>>> dst = cv2.warpAffine(img,M,(cols,rows))
>>>
>>> plt.subplot(121),plt.imshow(img),plt.title('Input')
>>> plt.subplot(122),plt.imshow(dst),plt.title('Output')
>>> plt.show()
../_images/sec03-geometric-transformations_8_0.png

查看结果:

透视变换

对于透视变换,需要3x3变换矩阵。即使经过改造,直线仍将保持笔直。要找到这个变换矩阵,需要输入图像上的4个点和输出图像上的相应点。在这四点中,有三点不应共线。然后通过函数求出变换矩阵 cv2.getPerspectiveTransform . 然后申请 cv2.warpPerspective 用这个3x3变换矩阵。

请参见下面的代码:

>>> img = cv2.imread('/cvdata/sudoku.png')
>>> rows,cols,ch = img.shape
>>>
>>> pts1 = np.float32([[56,65],[368,52],[28,387],[389,390]])
>>> pts2 = np.float32([[0,0],[300,0],[0,300],[300,300]])
>>>
>>> M = cv2.getPerspectiveTransform(pts1,pts2)
>>>
>>> dst = cv2.warpPerspective(img,M,(300,300))
>>>
>>> plt.subplot(121),plt.imshow(img),plt.title('Input')
>>> plt.subplot(122),plt.imshow(dst),plt.title('Output')
>>> plt.show()
../_images/sec03-geometric-transformations_10_0.png

结果:

3.3.3. 额外资源

  1. “计算机视觉:算法和应用”,Richard Szeliski

3.3.4. 练习