在子像素位置将图像块插入到另一个图像中

Insert an image patch into another image at a subpixel location

我有一个图像补丁,我想将其插入另一个图像的浮点位置。事实上,我需要的是与 opencv getRectSubPix 函数相反的东西。

我想我可以通过将补丁的子像素扭曲到另一个补丁并将这个另一个补丁插入目标图像的整数位置来实现它。但是,我不清楚如何处理变形补丁中的空像素部分,或者如何将新补丁的边界与目标图像混合。

我宁愿使用库函数也不愿自己实现这个操作。有人知道opencv或任何其他图像处理库中是否有任何库函数可以执行此类操作吗?

更新:

我发现 opencv warpPerspective 可以与 borderMode = BORDER_TRANSPARENT 一起使用,这意味着目标中的像素与源图像中的“异常值”相对应的图像未被该函数修改。所以我想我可以只用 warpPerspective 和一个足够的变换矩阵来实现这个子像素补丁插入。所以我在python中写了这个函数来执行操作:

def insert_patch_subpixel(im, patch, p):
    """
    im: numpy array with source image.
    patch: numpy array with patch to be inserted into the source image
    p: tuple with the center of the position (can be float) where the patch is to be inserted.
    """
    ths = patch.shape[0]/2
    xpmin = p[0] - ths
    ypmin = p[1] - ths
    Ho = np.array([[1, 0, xpmin],
                   [0, 1, ypmin],
                   [0, 0,     1]], dtype=float)

    h,w = im.shape
    im2 = cv2.warpPerspective(patch, Ho, (w,h), dst=im,
                        flags=cv2.INTER_LINEAR,
                        borderMode=cv2.BORDER_TRANSPARENT)
    return im2

不幸的是,如果使用 BORDER_TRANSPARENT,插值似乎不适用于离群像素。我用一个 10x10 的小图像(填充值为 30)测试了这个函数,并在 p=(5,5)(左图)和 p=(5.5,5.5)(中间图)处插入了一个 4x4 的补丁(填充了值 100)我们可以在下图中看到边框没有插值。但是,如果我将 boderMode 更改为 BORDER_CONSTANT,则插值有效(右图),但也会用 0 填充目标图像作为离群值。

遗憾的是插值不适用于 BORDER_TRANSPARENT。我建议这是对 opencv 项目的改进。

将补丁图像调整为目标中所需的大小。然后根据 1.0 沿边缘设置 alpha - 左边缘的分数,右边缘的分数。然后混合。

它不是很完美,因为您没有正确地重新采样所有像素,但这也会损坏分辨率。这可能是您最好的折衷方案。

实际上你应该使用 getRectSubPix()。 使用它从源图像中提取您所需偏移量的小数部分的补丁,然后只需使用简单的副本(或根据需要混合)将其设置到目标图像中。

您可能想在可以进行混合的补丁周围添加一个 1 像素的边框。

此函数本质上只进行平移(亚像素)变形。

我根据我在问题更新中找到的内容找到了解决方案。 正如我看到在 warpPerspective 函数中使用 boderMode = BORDER_CONSTANT 时发生的插值我想我可以将其用作加权掩码用于原始图像和黑色背景上的子像素插入补丁之间的混合。查看新功能和测试代码:

 import numpy as np
 import matplotlib.pyplot as plt

 def insert_patch_subpixel2(im, patch, p):
    """
    im: numpy array with source image.
    patch: numpy array with patch to be inserted into the source image
    p: tuple with the center of the position (can be float) where the patch is to be inserted.
    """
    ths = patch.shape[0]/2
    xpmin = p[0] - ths
    ypmin = p[1] - ths
    Ho = np.array([[1, 0, xpmin],
                   [0, 1, ypmin],
                   [0, 0,     1]], dtype=float)

    h,w = im.shape
    im2 = cv2.warpPerspective(patch, Ho, (w,h),
                        flags=cv2.INTER_LINEAR,
                        borderMode=cv2.BORDER_CONSTANT)

    patch_mask = np.ones_like(patch,dtype=float)
    blend_mask = cv2.warpPerspective(patch_mask, Ho, (w,h),
                        flags=cv2.INTER_LINEAR,
                        borderMode=cv2.BORDER_CONSTANT)

    #I don't multiply im2 by blend_mask because im2 has already
    #been interpolated with a zero background.
    im3 = im*(1-blend_mask)+im2
    im4 = cv2.convertScaleAbs(im3)
    return im4

if __name__ == "__main__":
    x,y = np.mgrid[0:10:1, 0:10:1]
    im =(x+y).astype('uint8')*5
    #im = np.ones((10,10), dtype='uint8')*30
    patch = np.ones((4,4), dtype='uint8')*100
    p=(5.5,5.5)
    im = insert_patch_subpixel2(im, patch, p)
    plt.gray()
    plt.imshow(im, interpolation='none',  extent = (0, 10, 10, 0))
    ax=plt.gca()
    ax.grid(color='r', linestyle='-', linewidth=1)
    ax.set_xticks(np.arange(0, 10, 1));
    ax.set_yticks(np.arange(0, 10, 1));
    def format_coord(x, y):
        col = int(x)
        row = int(y)
        z = im[row,col]
        return 'x=%1.4f, y=%1.4f %s'%(x, y, z)
    ax.format_coord = format_coord
    plt.show()

在下图中,我们可以看到使用 10x10 小图像(填充值为 30)并在 p=(5,5) 处插入 4x4 补丁(填充值为 100)的测试结果(左图) 和 p=(5.5,5.5) (中图),现在我们可以在下图中看到边界处有双线性插值。为了表明插值适用于任意背景,我还展示了一个具有渐变 10x10 图像背景的测试(右图)。测试脚本创建一个图形,让您检查像素值并验证是否在每个边界像素处进行了正确的插值。