介绍它也可以应用于扭曲一个图像到另一个图像平面。例如,与其直视前方的场景,不如自上而下地看。在这个场景中应用透视图变换来实现这一点。 另一个应用是训练深层神经网络。训练深度模型需要大量的数据。在几乎所有的情况下,模型都受益于更高的泛化性能,因为有更多的训练图像。人工生成更多数据的一种方法是对输入数据随机应用仿射变换(增强)。 在本文中,我将向你介绍一些变换,以及如何在Numpy和OpenCV中执行这些变换。特别是,我将关注二维仿射变换。你需要的是一些基本的线性代数知识。 仿射变换的类型在不涉及太多数学细节的情况下,变换的行为由仿射A中的一些参数控制。 x’ = Ax 其中A是在齐次坐标系中的2x3矩阵或3x3,x是在齐次坐标系中的(x,y)或(x,y,1)形式的向量。这个公式表示A将任意向量x,映射到另一个向量x’。 一般来说,仿射变换有6个自由度。根据参数的值,它将在矩阵乘法后扭曲任何图像。变换后的图像保留了原始图像中的平行直线(考虑剪切)。本质上,满足这两个条件的任何变换都是仿射的。 但是,有一些特殊形式的A,这是我们将要讨论的。这包括旋转、平移和缩放矩阵,如下图所示。 上述仿射变换的一个非常有用的性质是它们是线性函数。它们保留了乘法和加法运算,并遵循叠加原理。 换言之,我们可以组合2个或更多的变换:向量加法表示平移,矩阵乘法表示线性映射,只要我们用齐次坐标表示它们。例如,我们可以将旋转和平移表示为 A = array([[cos(angle), -sin(angle), tx], [sin(angle), cos(angle), ty], [0, 0, 1]]) 图像表示在Python和OpenCV中,2D矩阵的原点位于左上角,从x,y=(0,0)开始。坐标系是左手的,X轴指向右,Y轴指向正下方。 但在教科书和文献中,如上面所示的3个矩阵,大多数变换矩阵都遵循右手坐标系。因此,必须进行一些小的调整来调整轴线方向。 欧氏空间中的公共变换在我们对图像进行变换实验之前,让我们看看如何在点坐标上进行变换。因为它们本质上与图像是网格中的二维坐标数组相同。 利用上面的知识,下面的代码可以用来变换(0,0),(0,1),(1,0),(1,1)处的点。此外,Python还提供了一个有用的速记运算符@来表示矩阵乘法。
需要注意的是,除少数例外情况外,矩阵通常不进行交换。即 A1 @ A2 != A2 @ A1 因此,对于变换
你将看到它们不会产生相同的映射,而且顺序很重要。从右到左可以理解函数是如何应用的。 Numpy中的变换现在对于图片,有几点需要注意。首先,如前所述,我们必须重新调整垂直轴。其次,变换后的点必须投影到图像平面上。 实质上,需要采取的步骤是:
示例:围绕图像中心旋转、缩放和平移让我们看一个变换,我们希望放大2倍,并围绕图像的中心位置旋转45度。 这可以通过应用以下复合矩阵来实现。 height, width = image.shape[:2]tx, ty = np.array((width // 2, height // 2))angle = np.radians(45)scale = 2.0R = np.array([ [np.cos(angle), np.sin(angle), 0], [-np.sin(angle), np.cos(angle), 0], [0, 0, 1]])T = np.array([ [1, 0, tx], [0, 1, ty], [0, 0, 1]])S = np.array([ [scale, 0, 0], [0, scale, 0], [0, 0, 1]])A = T @ R @ S @ np.linalg.inv(T) 应用于图像
在上面的两个代码片段中有几点需要注意。
如你所见,由于步骤4的原因,生成的图像将有几个锯齿和孔。为了消除这种情况,开源库使用插值技术来消除变换后的差异。 逆扭曲(Inverse Warping)另一种防止上面情况的方法是将扭曲表示为给定扭曲点x'的源图像I(x,y)的重采样。这可以通过X'乘以A的逆来实现。这里需要注意的是,变换必须是可逆的。
X = np.linalg.inv(A) @ X' 注:对于图像,X'的逆扭曲只是将I'(X,y)重新投影到I(X,y)上。所以我们只需对I’(x,y)像素坐标进行逆变换,如下所示。
运行上面的代码应该可以得到一个密集的、无孔的图像。 OpenCV中的变换现在你已经对几何变换有了更好的理解,大多数开发人员和研究人员通常省去了编写所有这些变换的麻烦,而只需依赖优化的库来执行任务。在OpenCV中进行仿射变换非常简单。 有几种方法可以做到。一种可能的方法是你可以自己编写仿射变换,并调用cv2.warfaffine(image,A,output_shape) 下面的代码显示了整个仿射矩阵,它将给出与上面相同的结果。一个很好的练习就是自己推导公式! def get_affine_cv(t, r, s): sin_theta = np.sin(r) cos_theta = np.cos(r) a_11 = s * cos_theta a_21 = -s * sin_theta a_12 = s * sin_theta a_22 = s * cos_theta a_13 = t[0] * (1 - s * cos_theta) - s * sin_theta * t[1] a_23 = t[1] * (1 - s * cos_theta) + s * sin_theta * t[0] return np.array([[a_11, a_12, a_13], [a_21, a_22, a_23]])A2 = get_affine_cv((tx, ty), angle, scale)warped = cv2.warpAffine(image, A2, (width, height)) 另一种方法是依赖OpenCV使用cv2.getRotationMatrix2D(center,angle,scale)返回仿射变换矩阵。此函数使用角度围绕点中心旋转图像,并使用比例缩放图像。
总结在本文中,我介绍了几何变换的基本概念以及如何将其应用于图像。许多先进的计算机视觉,如使用视觉里程计和多视图合成的slam,都依赖于最初的理解变换。我希望你能更好地理解这些公式是如何在库中编写和使用的。 |
|
来自: taotao_2016 > 《图像处理》