5.2. 14 几何变换

目标

  • 学习对图像进行各种几个变换,例如移动,旋转,仿射变换等。

  • 将要学到的函数有:cv2.getPerspectiveTransform。

变换

OpenCV 提供了两个变换函数,cv2.warpAffine 和 cv2.warpPerspective,使用这两个函数你可以实现所有类型的变换。cv2.warpAffine 接收的参数是2 × 3 的变换矩阵,而 cv2.warpPerspective 接收的参数是 3 × 3 的变换矩阵。

5.2.1. 14.1 扩展缩放

扩展缩放只是改变图像的尺寸大小。OpenCV 提供的函数 cv2.resize()可以实现这个功能。图像的尺寸可以自己手动设置,你也可以指定缩放因子。我们可以选择使用不同的插值方法。在缩放时我们推荐使用 cv2.INTER_AREA,在扩展时我们推荐使用 v2.INTER_CUBIC(慢) 和 v2.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)

5.2.2. 14.2 平移

平移就是将对象换一个位置。如果你要沿(x,y)方向移动,移动的距离是(t x ,t y ),你可以以下面的方式构建移动矩阵:

images/100002010000007A0000002D18483DB244DC5054.png

你可以使用 Numpy 数组构建这个矩阵(数据类型是 np.float32),然后把它传给函数 cv2.warpAffine()。看看下面这个例子吧,它被移动了(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 0x7fc8f10c8650>
_images/sec02_geometric_3_1.png

警告:函数 cv2.warpAffine() 的第三个参数的是输出图像的大小,它的格式应该是图像的(宽,高)。应该记住的是图像的宽对应的是列数,高对应的是行数。

下面就是结果:

5.2.3. 14.3 旋转

对一个图像旋转角度 θ, 需要使用到下面形式的旋转矩阵。

images/100002010000009D0000002D5404F0523C380F12.png

但是 OpenCV 允许你在任意地方进行旋转,但是旋转矩阵的形式应该修

改为

images/10000201000001430000002DC587501CFF8CBF20.png

其中:

images/100002010000008000000027FA992244582B3988.png

为了构建这个旋转矩阵,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 0x7fc8db2b9c50>
_images/sec02_geometric_5_1.png

5.2.4. 14.4 仿射变换

在仿射变换中,原图中所有的平行线在结果图像中同样平行。为了创建这个矩阵我们需要从原图像中找到三个点以及他们在输出图像中的位置。然后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/sec02_geometric_7_0.png

5.2.5. 14.5 透视变换

对于视角变换,我们需要一个 3x3 变换矩阵。在变换前后直线还是直线。要构建这个变换矩阵,你需要在输入图像上找 4 个点,以及他们在输出图像上对应的位置。这四个点中的任意三个都不能共线。这个变换矩阵可以有函数 cv2.getPerspectiveTransform() 构建。然后把这个矩阵传给函数cv2.warpPerspective。

代码如下:

>>> 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/sec02_geometric_9_0.png