直线镜像
Mirror image in line
给定一个带有镜像线的图像(梯度 m
和截距 c
),如下所示,
我想镜像图像的左侧,即应该反射镜像线左侧的部分,并替换原来的右侧。结果输出当然不需要与输入图像具有相同的尺寸。在 Python 中是否有相当快速的方法来执行此操作?
考虑反射的一种简便方法是在正交基(简称 onb)中,其中一个基向量指向镜轴方向。见下图。如果在该基础镜像中有一个点表示,它会翻转对应于另一个轴的组件的符号。所以我们这里的做法是取每个像素点的坐标,在onb中表示出来,翻转符号然后回到标准基础上。
作为示例,我将使用 Wikipedia-logo-v2 by Nohat. It is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported 许可证。
首先我们将示例图片与我们的onb一起绘制。
image = cv2.imread('wikipedia.jpg')
image = np.swapaxes(image,0,1)/255
origin = np.array([48,55], dtype='float')
b1 = np.array([12,-2], dtype='float')
b2 = np.array([-2,-12], dtype='float')
b1 /= -np.linalg.norm(b1)
b2 /= np.linalg.norm(b2)
plt.imshow(np.swapaxes(image,0,1))
plt.scatter(origin[0], origin[1])
plt.arrow(*origin,*b1*20,head_length = 10, head_width = 5)
plt.arrow(*origin,*b2*20,head_length = 10, head_width = 5)
现在我们计算旧坐标,onb中所有坐标的系数和反射后的坐标。这里很方便,我们的基础不仅是正交的,而且是规范的,因此我们可以将系数计算为标量积。请注意,实际反射发生在我们取 c1
的绝对值的地方,即上图中指向右侧的第一个基向量的系数。这意味着以前为负的系数,即原点左侧的系数现在位于右侧。
points = np.moveaxis(np.indices(image.shape[:2]),0,-1).reshape(-1,2)
b1.shape,(points-origin.reshape(1,2)).shape, ((points-origin.reshape(1,2))@b1).shape
c1 = (points-origin.reshape(1,2))@b1
c2 = (points-origin.reshape(1,2))@b2
coordinates = ((b1.reshape(2,1)*np.abs(c1)+b2.reshape(2,1)*c2)+origin.reshape(2,1))
现在的问题是我们不能用新坐标索引图片,因为它们不是整数。我们可以将它们四舍五入,但这可能会产生不良影响。相反,我们采用类似 knn 的方法对探测像素进行加权平均。
class Image_knn():
def fit(self, image):
self.image = image.astype('float')
def predict(self, x, y):
image = self.image
weights_x = [(1-(x % 1)).reshape(*x.shape,1), (x % 1).reshape(*x.shape,1)]
weights_y = [(1-(y % 1)).reshape(*x.shape,1), (y % 1).reshape(*x.shape,1)]
start_x = np.floor(x)
start_y = np.floor(y)
return sum([image[np.clip(np.floor(start_x + x), 0, image.shape[0]-1).astype('int'),
np.clip(np.floor(start_y + y), 0, image.shape[1]-1).astype('int')] * weights_x[x]*weights_y[y]
for x,y in itertools.product(range(2),range(2))])
最后我们可以应用我们的坐标并得到镜像。
image_model = Image_knn()
image_model.fit(image)
transformed_image = image_model.predict(*coordinates).reshape(*image.shape)
plt.imshow(np.swapaxes(transformed_image,0,1))
问:我可以反方向镜像吗?
是的,当然只是在第一个基向量前面放一个减号,即替换
b1 = np.array([12,-2], dtype='float')
和
b1 = -np.array([12,-2], dtype='float')
可能的问题
当我在另一个方向镜像进行测试时,我得到了这个 little.artifact
右上角看起来需要清洁屏幕的瑕疵来自以下问题:我们现在将较小的 space 镜像到稍大的镜像,因此我们没有足够的像素来画右上角。我们在 image_knn
中默认做的是将坐标裁剪到我们有信息的区域。这意味着当我们向图像 knn
询问来自左下角之外的右上角像素时,它会为我们提供图像边界处的像素。如果有背景,这看起来不错,但如果物体接触到图片的边缘,它看起来很奇怪,就像这里一样。使用它时要记住一些事情。
给定一个带有镜像线的图像(梯度 m
和截距 c
),如下所示,
我想镜像图像的左侧,即应该反射镜像线左侧的部分,并替换原来的右侧。结果输出当然不需要与输入图像具有相同的尺寸。在 Python 中是否有相当快速的方法来执行此操作?
考虑反射的一种简便方法是在正交基(简称 onb)中,其中一个基向量指向镜轴方向。见下图。如果在该基础镜像中有一个点表示,它会翻转对应于另一个轴的组件的符号。所以我们这里的做法是取每个像素点的坐标,在onb中表示出来,翻转符号然后回到标准基础上。
作为示例,我将使用 Wikipedia-logo-v2 by Nohat. It is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported 许可证。
首先我们将示例图片与我们的onb一起绘制。
image = cv2.imread('wikipedia.jpg')
image = np.swapaxes(image,0,1)/255
origin = np.array([48,55], dtype='float')
b1 = np.array([12,-2], dtype='float')
b2 = np.array([-2,-12], dtype='float')
b1 /= -np.linalg.norm(b1)
b2 /= np.linalg.norm(b2)
plt.imshow(np.swapaxes(image,0,1))
plt.scatter(origin[0], origin[1])
plt.arrow(*origin,*b1*20,head_length = 10, head_width = 5)
plt.arrow(*origin,*b2*20,head_length = 10, head_width = 5)
现在我们计算旧坐标,onb中所有坐标的系数和反射后的坐标。这里很方便,我们的基础不仅是正交的,而且是规范的,因此我们可以将系数计算为标量积。请注意,实际反射发生在我们取 c1
的绝对值的地方,即上图中指向右侧的第一个基向量的系数。这意味着以前为负的系数,即原点左侧的系数现在位于右侧。
points = np.moveaxis(np.indices(image.shape[:2]),0,-1).reshape(-1,2)
b1.shape,(points-origin.reshape(1,2)).shape, ((points-origin.reshape(1,2))@b1).shape
c1 = (points-origin.reshape(1,2))@b1
c2 = (points-origin.reshape(1,2))@b2
coordinates = ((b1.reshape(2,1)*np.abs(c1)+b2.reshape(2,1)*c2)+origin.reshape(2,1))
现在的问题是我们不能用新坐标索引图片,因为它们不是整数。我们可以将它们四舍五入,但这可能会产生不良影响。相反,我们采用类似 knn 的方法对探测像素进行加权平均。
class Image_knn():
def fit(self, image):
self.image = image.astype('float')
def predict(self, x, y):
image = self.image
weights_x = [(1-(x % 1)).reshape(*x.shape,1), (x % 1).reshape(*x.shape,1)]
weights_y = [(1-(y % 1)).reshape(*x.shape,1), (y % 1).reshape(*x.shape,1)]
start_x = np.floor(x)
start_y = np.floor(y)
return sum([image[np.clip(np.floor(start_x + x), 0, image.shape[0]-1).astype('int'),
np.clip(np.floor(start_y + y), 0, image.shape[1]-1).astype('int')] * weights_x[x]*weights_y[y]
for x,y in itertools.product(range(2),range(2))])
最后我们可以应用我们的坐标并得到镜像。
image_model = Image_knn()
image_model.fit(image)
transformed_image = image_model.predict(*coordinates).reshape(*image.shape)
plt.imshow(np.swapaxes(transformed_image,0,1))
问:我可以反方向镜像吗?
是的,当然只是在第一个基向量前面放一个减号,即替换
b1 = np.array([12,-2], dtype='float')
和
b1 = -np.array([12,-2], dtype='float')
可能的问题
当我在另一个方向镜像进行测试时,我得到了这个 little.artifact
右上角看起来需要清洁屏幕的瑕疵来自以下问题:我们现在将较小的 space 镜像到稍大的镜像,因此我们没有足够的像素来画右上角。我们在 image_knn
中默认做的是将坐标裁剪到我们有信息的区域。这意味着当我们向图像 knn
询问来自左下角之外的右上角像素时,它会为我们提供图像边界处的像素。如果有背景,这看起来不错,但如果物体接触到图片的边缘,它看起来很奇怪,就像这里一样。使用它时要记住一些事情。