看起来你正在尝试实现最近邻重取样器。你正在遍历图像,并将每个输入像素映射到输出图像中的新位置。这可能会导致出现问题,例如像素不正确地覆盖,输出像素被留空等。
基于经验,我建议你从相反的角度考虑问题。而不是关注输入像素在输出中的位置,你应该考虑每个输出像素在输入中的来源。这样,你就没有关于最近邻的歧义,整个图像数组都将被填充。
你想绕中心旋转。你正在使用的当前旋转矩阵是以(0, 0)
为中心旋转的。为了补偿这一点,你需要将图像的中心平移到(0, 0)
,旋转后再平移回去。我会向你展示如何手动进行这些单独的操作,然后再将它们组合成变换矩阵,而不是开发完整的仿射矩阵。
手动计算
首先获取输入和输出图像:
im = cv2.imread("Samples\\baboon.jpg", cv2.IMREAD_GRAYSCALE)
new = np.zeros_like(im)
确定旋转中心。明确您的尺寸,x
通常是列(维度 1
),而不是行(维度 0
):
center_row = im.shape[0] // 2
center_col = im.shape[1] // 2
计算图像中每个像素的径向坐标,形状应与相应的维度相同:
row_coord = np.arange(im.shape[0])[:, None] - center_row
col_coord = np.arange(im.shape[1]) - center_col
row_coord
和 col_coord
是在输出图像中距离中心的距离。现在计算它们在输入中的来源位置。请注意,我们可以使用广播来避免需要循环。我在这里遵循您最初的角度定义约定,并找到反转来确定源位置。这里的大区别在于将以度为单位的输入转换为弧度,因为三角函数所需的单位是弧度:
angle = float(input('Enter Angle in Degrees: ')) * np.pi / 180.0
source_row = row_coord * np.cos(angle) - col_coord * np.sin(angle) + center_row
source_col = row_coord * np.sin(angle) + col_coord * np.cos(angle) + center_col
如果所有的索引保证在输入图像内,甚至不需要预先分配输出。你只需要简单地执行
new = im[source_row, source_col]
。然而,你需要掩盖这些索引:
mask = source_row >= 0 & source_row < im.shape[0] & source_col >= 0 & source_col < im.shape[1]
new[mask] = im[source_row[mask].round().astype(int), source_col[mask].round().astype(int)]
仿射变换
现在让我们来看看如何使用仿射变换。首先,您需要从坐标中减去中心点。假设您有一个列向量 [[r], [c], [1]]
。将其平移到原点的矩阵为:
[[r'] [[1 0 -rc] [[r]
[c'] = [0 1 -cc] . [c]
[1 ]] [0 0 1 ]] [1]]
接着应用(反向的)旋转:
[[r''] [[cos(a) -sin(a) 0] [[r']
[c''] = [sin(a) cos(a) 0] . [c']
[ 1 ]] [ 0 0 1]] [1 ]]
最后,您需要将其翻译回中心。
[[r'''] [[1 0 rc] [[r'']
[c'''] = [0 1 cc] . [c'']
[ 1 ]] [0 0 1]] [ 1 ]]
如果你按照从右到左的顺序将这三个矩阵相乘,你会得到:
[[cos(a) -sin(a) cc * sin(a) - rc * cos(a) + rc]
M = [sin(a) cos(a) -cc * cos(a) - rc * sin(a) + cc]
[ 0 0 1 ]]
如果您构建一个完整的输出坐标矩阵,而不是我们开始使用的子集数组,您可以使用np.matmul(又名@运算符)来为您执行乘法。但对于这样一个简单的情况,没有必要使用这种复杂程度。
matrix = np.array([[np.cos(angle), -np.sin(angle), col_center * np.sin(angle) - row_center * np.cos(angle) + row_center],
[np.sin(angle), np.cos(angle), -col_center * np.cos(angle) - row_center * np.sin(angle) + col_center],
[0, 0, 1]])
coord = np.ones((*im.shape, 3, 1))
coord[..., 0, :] = np.arange(im.shape[0]).reshape(-1, 1, 1, 1)
coord[..., 1, :] = np.arange(im.shape[1]).reshape(-1, 1, 1)
source = (matrix @ coord)[..., :2, 0]
处理的其余部分与手动计算非常相似。
mask = (source >= 0 & source_row < im.shape).all(axis=-1)
new[mask] = im[source[0, mask].round().astype(int), source_col[1, mask].round().astype(int)]
cos
和sin
中吗?它们需要弧度。 - Cris Luengo