直线镜像

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 询问来自左下角之外的右上角像素时,它会为我们提供图像边界处的像素。如果有背景,这看起来不错,但如果物体接触到图片的边缘,它看起来很奇怪,就像这里一样。使用它时要记住一些事情。