OpenCV实现图像的几何变换

「这是我参与11月更文挑战的第29天,活动详情查看:2021最后一次更文挑战

图像的几何变换

几何变换主要包括缩放、平移、旋转、仿射变换、透视变换和图像裁剪等。执行这些几何变换的两个关键函数是 cv2.warpAffine()cv2.warpPerspective()

cv2.warpAffine() 函数使用以下 2 x 3 变换矩阵来变换源图像:

d s t ( x , y ) = s r c ( M 11 x + M 12 y + M 13 , M 21 x + M 22 y + M 23 ) dst(x,y)=src(M_{11}x+M_{12}y+M_{13}, M_{21}x+M_{22}y+M_{23})

cv2.warpPerspective() 函数使用以下 3 x 3 变换矩阵变换源图像:

d s t ( x , y ) = s r c ( M 11 x + M 12 y + M 13 M 31 x + M 32 y + M 33 , M 21 x + M 22 y + M 23 M 31 x + M 32 y + M 33 ) dst(x,y)=src(\frac {M_{11}x+M_{12}y+M_{13}} {M_{31}x+M_{32}y+M_{33}}, \frac {M_{21}x+M_{22}y+M_{23}} {M_{31}x+M_{32}y+M_{33}})

接下来,我们将了解最常见的几何变换技术。

缩放图像

缩放图像时,可以直接使用缩放后图像尺寸调用 cv2.resize()

# 指定缩放后图像尺寸
resized_image = cv2.resize(image, (width * 2, height * 2), interpolation=cv2.INTER_LINEAR)
复制代码

除了上述用法外,也可以同时提供缩放因子 fxfy 值。例如,如果要将图像缩小 2 倍:

# 使用缩放因子
dst_image = cv2.resize(image, None, fx=0.5, fy=0.5, interpolation=cv2.INTER_AREA)
复制代码

如果要放大图像,最好的方法是使用 cv2.INTER_CUBIC 插值方法(较耗时)或 cv2.INTER_LINEAR。如果要缩小图像,一般的方法是使用 cv2.INTER_LINEAROpenCV 提供的五种插值方法如下表所示:

插值方法 原理
cv2.INTER_NEAREST 最近邻插值
cv2.INTER_LINEAR 双线性插值
cv2.INTER_AREA 使用像素面积关系重采样
cv2.INTER_CUBIC 基于4x4像素邻域的3次插值
cv2.INTER_LANCZOS4 正弦插值

显示缩放后的图像:

def show_with_matplotlib(color_img, title, pos):
    # Convert BGR image to RGB
    img_RGB = color_img[:,:,::-1]
    ax = plt.subplot(1, 3, pos)
    plt.imshow(img_RGB)
    plt.title(title,fontsize=8)
    # plt.axis('off')
show_with_matplotlib(image, 'Original image', 1)
show_with_matplotlib(dst_image, 'Resized image', 2)
show_with_matplotlib(dst_image_2, 'Resized image 2', 3)
plt.show()
复制代码

可以通过坐标系观察图片的缩放情况:

图片缩放

平移图像

为了平移对象,需要使用 NumPy 数组创建 2 x 3 变换矩阵,其中提供了 xy 方向的平移距离(以像素为单位):

M = np.float32([[1, 0, x], [0, 1, y]])
复制代码

其对应于以下变换矩阵:

[ 1 0 t x 0 1 t y ] \begin{bmatrix} 1 & 0 & t_x \\ 0 & 1 & t_y \end{bmatrix}

创建此矩阵后,调用 cv2.warpAffine() 函数:

dst_image = cv2.warpAffine(image, M, (width, height))
复制代码

cv2.warpAffine() 函数使用提供的 M 矩阵转换源图像。第三个参数 (width, height) 用于确定输出图像的大小。

例如,如果图片要在 x 方向平移 200 个像素,在 y 方向移动 30 像素:

height, width = image.shape[:2]
M = np.float32([[1, 0, 200], [0, 1, 30]])
dst_image_1 = cv2.warpAffine(image, M, (width, height))
复制代码

平移也可以为负值,此时为反方向移动:

M = np.float32([[1, 0, -200], [0, 1, -30]])
dst_image_2 = cv2.warpAffine(image, M, (width, height))
复制代码

显示图片如下:

图片平移

旋转图像

为了旋转图像,需要首先使用 cv.getRotationMatrix2D() 函数来构建 2 x 3 变换矩阵。该矩阵以所需的角度(以度为单位)旋转图像,其中正值表示逆时针旋转。旋转中心 (center) 和比例因子 (scale) 也可以调整,使用这些元素,以下方式计算变换矩阵:

[ α β ( 1 a ) c e n t e r . x β c e n t e r . y β α β c e n t e r . x ( 1 α ) c e n t e r . y ] \begin{bmatrix} \alpha & \beta & (1-a)\cdot center.x-\beta\cdot center.y \\ -\beta & \alpha & \beta\cdot center.x-(1-\alpha)\cdot center.y \end{bmatrix}

其中:

α = s c a l e c o s θ , β = s c a l e s i n θ \alpha=scale\cdot cos\theta, \beta=scale\cdot sin\theta

以下示例构建 M 变换矩阵以相对于图像中心旋转 180 度,缩放因子为 1(不缩放)。之后,将这个 M 矩阵应用于图像,如下所示:

height, width = image.shape[:2]
M = cv2.getRotationMatrix2D((width / 2.0, height / 2.0), 180, 1)
dst_image = cv2.warpAffine(image, M, (width, height))
复制代码

接下来使用不同的旋转中心进行旋转:

M = cv2.getRotationMatrix2D((width/1.5, height/1.5), 30, 1)
dst_image_2 = cv2.warpAffine(image, M, (width, height))
复制代码

显示旋转后的图像:

旋转图像

2.4 图像的仿射变换

在仿射变换中,首先需要使用 cv2.getAffineTransform() 函数来构建 2 x 3 变换矩阵,该矩阵将从输入图像和变换图像中的相应坐标中获得。最后,将 M 矩阵传递给 cv2.warpAffine()

pts_1 = np.float32([[135, 45], [385, 45], [135, 230]])
pts_2 = np.float32([[135, 45], [385, 45], [150, 230]])
M = cv2.getAffineTransform(pts_1, pts_2)
dst_image = cv2.warpAffine(image_points, M, (width, height))
复制代码

仿射变换是保留点、直线和平面的变换。此外,平行线在此变换后将保持平行。但是,仿射变换不会同时保留像素点之间的距离和角度。

可以通过以下图像观察仿射变换的结果:

仿射变换

2.5 图像的透视变换

为了进行透视变换,首先需要使用 cv2.getPerspectiveTransform() 函数创建 3 x 3 变换矩阵。该函数需要四对点(源图像和输出图像中四边形的坐标),函数会根据这些点计算透视变换矩阵。然后,将 M 矩阵传递给 cv2.warpPerspective()

pts_1 = np.float32([[450, 65], [517, 65], [431, 164], [552, 164]])
pts_2 = np.float32([[0, 0], [300, 0], [0, 300], [300, 300]])
M = cv2.getPerspectiveTransform(pts_1, pts_2)
dst_image = cv2.warpPerspective(image, M, (300, 300)
复制代码

透视变换效果如下所示:

透视变换

2.6 裁剪图像

可以使用 NumPy 切片裁剪图像:

dst_image = image[80:200, 230:330]
复制代码

裁剪结果如下所示:

图像裁剪

猜你喜欢

转载自juejin.im/post/7035931986191450119