代码之家  ›  专栏  ›  技术社区  ›  SuperElectric

反转实值索引网格

  •  24
  • SuperElectric  · 技术社区  · 8 年前

    OpenCV的 remap() 使用实值索引网格使用双线性插值对图像中的值网格进行采样,并将采样网格作为新图像返回。

    准确地说,让:

    A = an image 
    X = a grid of real-valued X coords into the image. 
    Y = a grid of real-valued Y coords into the image.
    B = remap(A, X, Y)
    

    则对于所有像素坐标i、j,

    B[i, j] = A(X[i, j], Y[i, j]) 
    

    其中,圆括号表示法 A(x, y) 表示使用双线性插值来使用浮点值坐标求解图像A的像素值 x y .

    我的问题是:给定一个索引网格 X , Y ,如何生成“逆网格” X^-1 , Y^-1 使得:

    X(X^-1[i, j], Y^-1[i, j]) = i
    Y(X^-1[i, j], Y^-1[i, j]) = j
    

    X^-1(X[i, j], Y[i, j]) = i
    Y^-1(X[i, j], Y[i, j]) = j
    

    对于所有整数像素坐标 i, j ?

    图像和索引图X和Y的形状相同。然而,索引映射X和Y没有先验结构。例如,它们不一定是仿射变换或刚性变换。它们甚至可能是不可逆的,例如: X, Y 将多个像素映射到 A 对于B中相同的精确像素坐标,我正在寻找一种方法,如果存在的话,可以找到一个合理的逆映射。

    解决方案不需要基于OpenCV,因为我不使用OpenCV,而是另一个具有 重新映射() 实施尽管欢迎任何建议,但我特别热衷于“数学上正确”的东西,即如果我的地图m是完全可逆的,那么该方法应该在机器精度的某个小范围内找到完美的逆。

    11 回复  |  直到 8 年前
        1
  •  11
  •   wcochran    7 年前

    我只需要解决这个问题 重映射反演问题 我和我将概述我的解决方案。

    鉴于 X , Y 对于 remap() 执行以下操作的函数:

    B[i, j] = A(X[i, j], Y[i, j])   
    

    我计算 Xinv , Yinv 可以由 重新映射() 作用 倒转 过程:

    A[x, y] = B(Xinv[x,y],Yinv[x,y])
    

    首先,我构建了一个 KD-Tree 对于二维点集: {(X[i,j],Y[i,j]} 所以我可以有效地找到 N 到给定点的最近邻居 (x,y). 我使用欧几里得距离作为距离度量。我发现了一个伟大的 C++ header lib for KD-Trees 在GitHub上。

    然后我循环通过所有 (x,y) 价值观 A '的网格并找到 N = 5 近邻 {(X[i_k,j_k],Y[i_k,j_k]) | k = 0 .. N-1} 在我的观点集中。

    • 如果距离 d_k == 0 对一些人来说 k 然后 Xinv[x,y] = i_k Yinv[x,y] = j_k 否则

    • 使用 Inverse Distance Weighting (IDW) 要计算内插值:

      • 让重量 w_k = 1 / pow(d_k, p) (我使用 p = 2 )
      • Xinv[x,y] = (sum_k w_k * i_k)/(sum_k w_k)
      • Yinv[x,y] = (sum_k w_k * j_k)/(sum_k w_k)

    注意,如果 B W x H 图像 十、 Y 宽x高 浮点数组。如果 A. w x h 图像 辛亥 伊涅夫 宽x高 浮点数的数组。与图像和地图大小保持一致非常重要。

    像个魔咒!在我的第一个版本中,我尝试了暴力强制搜索,我甚至从未等待它完成。我切换到KD树,然后开始获得合理的运行时间。如果我有时间,我想把这个添加到OpenCV中。

    下面的第二幅图像是使用 重新映射() 以从第一图像中去除透镜失真。第三个图像是反转过程的结果。

    enter image description here enter image description here enter image description here

        2
  •  6
  •   Hannesh    3 年前

    迭代解

    上面的许多解决方案对我不起作用,当地图不可逆或速度不快时失败。

    我提出了另一种6行迭代解决方案。

    def invert_map(F):
        I = np.zeros_like(F)
        I[:,:,1], I[:,:,0] = np.indices(sh)
        P = np.copy(I)
        for i in range(10):
            P += I - cv.remap(F, P, None, interpolation=cv.INTER_LINEAR)
        return P
    

    它做得怎么样? 在我为航空摄影反转地形校正地图的用例中,该方法以10步轻松收敛到1/10像素。它也非常快,因为所有繁重的计算都隐藏在OpenCV中

    它是如何工作的?

    该方法使用的思想是: (x', y') = F(x, y) 是映射,则逆可以用 (x, y) = -F(x', y') ,只要 F 是小的。

    我们可以继续完善我们的映射,上面给出了我们的第一个预测(我是“身份映射”):

    G_1 = I - F

    我们的第二个预测可以从中进行调整:

    G_2 = G_1 + I - F(G_1)

    等等:

    G_n+1 = G_n + I - F(G_n)

    证明 G_n 收敛到相反方向 F^-1 很难,但我们可以很容易地证明,如果 G 已经收敛,它将保持收敛。

    假定 G_n = F^-1 然后,我们可以替换为:

    n+1=G_n+I-F(G_n)

    然后得到:

    G_n+1 = F^-1 + I - F(F^-1)
    G_n+1 = F^-1 + I - I
    G_n+1 = F^-1
    Q.E.D.
    

    测试脚本

    import cv2 as cv
    from scipy import ndimage as ndi
    import numpy as np
    from matplotlib import pyplot as plt
    
    # Simulate deformation field
    N = 500
    sh = (N, N)
    t = np.random.normal(size=sh)
    dx = ndi.gaussian_filter(t, 40, order=(0,1))
    dy = ndi.gaussian_filter(t, 40, order=(1,0))
    dx *= 10/dx.max()
    dy *= 10/dy.max()
    
    # Test image
    img = np.zeros(sh)
    img[::10, :] = 1
    img[:, ::10] = 1
    img = ndi.gaussian_filter(img, 0.5)
    
    # Apply forward mapping
    yy, xx = np.indices(sh)
    xmap = (xx-dx).astype(np.float32)
    ymap = (yy-dy).astype(np.float32)
    warped = cv.remap(img, xmap, ymap ,cv.INTER_LINEAR)
    plt.imshow(warped, cmap='gray')
    

    output 1

    def invert_map(F: np.ndarray):
        I = np.zeros_like(F)
        I[:,:,1], I[:,:,0] = np.indices(sh)
        P = np.copy(I)
        for i in range(10):
            P += I - cv.remap(F, P, None, interpolation=cv.INTER_LINEAR)
        return P
    
    # F: The function to invert
    F = np.zeros((sh[0], sh[1], 2), dtype=np.float32)
    F[:,:,0], F[:,:,1] = (xmap, ymap)
    
    # Test the prediction
    unwarped = cv.remap(warped, invert_map(F), None, cv.INTER_LINEAR)
    plt.imshow(unwarped, cmap='gray')
    

    enter image description here

        3
  •  5
  •   pthibault    4 年前

    这是一个重要的问题,我感到惊讶的是,任何标准库都没有更好地解决这个问题(至少据我所知)。

    我对接受的解决方案不满意,因为它没有使用转换的隐式平滑。我可能会错过重要的情况,但我无法想象在任何有用的意义上都是可逆的,并且在像素尺度上是非平滑的映射。

    平滑意味着不需要计算最近邻点:最近点是原始网格上已经接近的点。

    我的解决方案使用了以下事实:在原始映射中,正方形[(i,j),(i+1,j)、(i+1、j+1)、(i,j+1)]映射到四边形[(X[i,j]、Y[i,j]、X[i+1,j]、Y[i+2,j]、…],内部没有其他点。然后逆映射只需要在四边形内进行插值。为此,我使用逆双线性插值,这将在顶点和任何其他仿射变换处给出精确结果。

    该实现除了 numpy 逻辑是遍历所有四边形并逐步建立反向映射。我将代码复制到这里,希望有足够的注释使想法足够清晰。

    关于不太明显的东西的一些评论:

    • 逆双线性函数通常只返回[0,1]范围内的坐标。我删除了剪裁操作,因此超出范围的值意味着坐标在四边形之外(这是解决多边形中的点问题的扭曲方式!)。为了避免边缘上丢失点,我实际上允许[0,1]范围之外的点,这通常意味着一个索引可以由两个相邻四边形拾取。在这些罕见的情况下,我只是让结果是两个结果的平均值,相信超出范围的点是以合理的方式“外推”的。
    • 一般来说,所有四边形都有不同的形状,它们与规则网格的重叠可以从零到多个点。该例程一次求解所有四边形(以利用 bilinear_inverse ,但在每次迭代时,仅选择坐标(偏移到其边界框)有效的四边形。
    import numpy as np
    
    def bilinear_inverse(p, vertices, numiter=4):
        """
        Compute the inverse of the bilinear map from the unit square
        [(0,0), (1,0), (1,1), (0,1)]
        to the quadrilateral vertices = [p0, p1, p2, p4]
    
        Parameters:
        ----------
        p: array of shape (2, ...)
            Points on which the inverse transforms are applied.
        vertices: array of shape (4, 2, ...)
            Coordinates of the vertices mapped to the unit square corners
        numiter:
            Number of Newton interations
    
        Returns:
        --------
        s: array of shape (2, ...)
            Mapped points.
    
        This is a (more general) python implementation of the matlab implementation 
        suggested in https://stackoverflow.com/a/18332009/1560876
        """
    
        p = np.asarray(p)
        v = np.asarray(vertices)
        sh = p.shape[1:]
        if v.ndim == 2:
            v = np.expand_dims(v, axis=tuple(range(2, 2 + len(sh))))
    
        # Start in the center
        s = .5 * np.ones((2,) + sh)
        s0, s1 = s
        for k in range(numiter):
            # Residual
            r = v[0] * (1 - s0) * (1 - s1) + v[1] * s0 * (1 - s1) + v[2] * s0 * s1 + v[3] * (1 - s0) * s1 - p
    
            # Jacobian
            J11 = -v[0, 0] * (1 - s1) + v[1, 0] * (1 - s1) + v[2, 0] * s1 - v[3, 0] * s1
            J21 = -v[0, 1] * (1 - s1) + v[1, 1] * (1 - s1) + v[2, 1] * s1 - v[3, 1] * s1
            J12 = -v[0, 0] * (1 - s0) - v[1, 0] * s0 + v[2, 0] * s0 + v[3, 0] * (1 - s0)
            J22 = -v[0, 1] * (1 - s0) - v[1, 1] * s0 + v[2, 1] * s0 + v[3, 1] * (1 - s0)
    
            inv_detJ = 1. / (J11 * J22 - J12 * J21)
    
            s0 -= inv_detJ * (J22 * r[0] - J12 * r[1])
            s1 -= inv_detJ * (-J21 * r[0] + J11 * r[1])
    
        return s
    
    
    def invert_map(xmap, ymap, diagnostics=False):
        """
        Generate the inverse of deformation map defined by (xmap, ymap) using inverse bilinear interpolation.
        """
    
        # Generate quadrilaterals from mapped grid points.
        quads = np.array([[ymap[:-1, :-1], xmap[:-1, :-1]],
                          [ymap[1:, :-1], xmap[1:, :-1]],
                          [ymap[1:, 1:], xmap[1:, 1:]],
                          [ymap[:-1, 1:], xmap[:-1, 1:]]])
    
        # Range of indices possibly within each quadrilateral
        x0 = np.floor(quads[:, 1, ...].min(axis=0)).astype(int)
        x1 = np.ceil(quads[:, 1, ...].max(axis=0)).astype(int)
        y0 = np.floor(quads[:, 0, ...].min(axis=0)).astype(int)
        y1 = np.ceil(quads[:, 0, ...].max(axis=0)).astype(int)
    
        # Quad indices
        i0, j0 = np.indices(x0.shape)
    
        # Offset of destination map
        x0_offset = x0.min()
        y0_offset = y0.min()
    
        # Index range in x and y (per quad)
        xN = x1 - x0 + 1
        yN = y1 - y0 + 1
    
        # Shape of destination array
        sh_dest = (1 + x1.max() - x0_offset, 1 + y1.max() - y0_offset)
    
        # Coordinates of destination array
        yy_dest, xx_dest = np.indices(sh_dest)
    
        xmap1 = np.zeros(sh_dest)
        ymap1 = np.zeros(sh_dest)
        TN = np.zeros(sh_dest, dtype=int)
    
        # Smallish number to avoid missing point lying on edges
        epsilon = .01
    
        # Loop through indices possibly within quads
        for ix in range(xN.max()):
            for iy in range(yN.max()):
                # Work only with quads whose bounding box contain indices
                valid = (xN > ix) * (yN > iy)
    
                # Local points to check
                p = np.array([y0[valid] + ix, x0[valid] + iy])
    
                # Map the position of the point in the quad
                s = bilinear_inverse(p, quads[:, :, valid])
    
                # s out of unit square means p out of quad
                # Keep some epsilon around to avoid missing edges
                in_quad = np.all((s > -epsilon) * (s < (1 + epsilon)), axis=0)
    
                # Add found indices
                ii = p[0, in_quad] - y0_offset
                jj = p[1, in_quad] - x0_offset
    
                ymap1[ii, jj] += i0[valid][in_quad] + s[0][in_quad]
                xmap1[ii, jj] += j0[valid][in_quad] + s[1][in_quad]
    
                # Increment count
                TN[ii, jj] += 1
    
        ymap1 /= TN + (TN == 0)
        xmap1 /= TN + (TN == 0)
    
        if diagnostics:
            diag = {'x_offset': x0_offset,
                    'y_offset': y0_offset,
                    'mask': TN > 0}
            return xmap1, ymap1, diag
        else:
            return xmap1, ymap1
    

    下面是一个测试示例

    import cv2 as cv
    from scipy import ndimage as ndi
    
    # Simulate deformation field
    N = 500
    sh = (N, N)
    t = np.random.normal(size=sh)
    dx = ndi.gaussian_filter(t, 40, order=(0,1))
    dy = ndi.gaussian_filter(t, 40, order=(1,0))
    dx *= 30/dx.max()
    dy *= 30/dy.max()
    
    # Test image
    img = np.zeros(sh)
    img[::10, :] = 1
    img[:, ::10] = 1
    img = ndi.gaussian_filter(img, 0.5)
    
    # Apply forward mapping
    yy, xx = np.indices(sh)
    xmap = (xx-dx).astype(np.float32)
    ymap = (yy-dy).astype(np.float32)
    warped = cv.remap(img, xmap, ymap ,cv.INTER_LINEAR)
    plt.imshow(warped, cmap='gray')
    

    Warped image

    # Now invert the mapping
    xmap1, ymap1 = invert_map(xmap, ymap)
    
    unwarped = cv.remap(warped, xmap1.astype(np.float32), ymap1.astype(np.float32) ,cv.INTER_LINEAR)
    
    plt.imshow(unwarped, cmap='gray')
    

    Unwarpped image

        4
  •  3
  •   FelisRattus    5 年前

    您可以在已知点处反转贴图,并将其插值到新网格中。 它可以正常工作,但失真不是很大。

    下面是使用scipy.interpolate.griddata在Python中的一个非常简单的实现:

    map_x, map_y = cv2.initUndistortRectifyMap(K, D, None, new_K, image_size, cv2.CV_32FC1)
    
    points =  np.stack([map_x.flatten(), map_y.flatten()], axis=1)
    grid = np.mgrid[:map_x.shape[0], :map_y.shape[1]]
    values = grid.reshape(2, -1).T[..., ::-1] 
    
    from scipy.interpolate import griddata
    grid_y, grid_x = grid
    map_back = griddata(points, values, (grid_x, grid_y), method='cubic').astype(map_undistort.dtype)
    

    如果将CV_32FC2用于贴图,则可以简化点构造:

    map_undistort, _ = cv2.initUndistortRectifyMap(K, D, None, new_K, image_size, cv2.CV_32FC2)
    points = map_undistort.reshape(-1, 2)
    
        5
  •  2
  •   Georgy rassa45    6 年前

    如果映射是从单应性导出的 H 你可以倒过来 H 并使用直接创建逆映射 cv::initUndistortRectifyMap() .

    e、 g.在Python中:

    import numpy as np.
    map_size = () # fill in your map size
    H_inv = np.linalg.inv(H)
    map1, map2 = cv2.initUndistortRectifyMap(cameraMatrix=np.eye(3), distCoeffs=np.zeros(5), R=H_inv, newCameraMatrix=np.eye(3), size=map_size, m1type=cv2.CV_32FC1)
    

    OpenCV文档说明了 initUndistortRectifyMap() :

    该函数实际上为逆映射构建映射 使用的算法 remap() 即,对于 目标图像,函数计算相应的 源图像中的坐标。

    如果你刚刚给了地图,你必须自己做。 Hoewever说,新地图坐标的插值并不简单,因为一个像素的支持区域可能非常大。

    下面是一个简单的Python解决方案,它通过执行点到点映射来反转映射。这可能会使一些坐标未分配,而其他坐标将被更新数次。所以地图上可能有洞。

    下面是一个小型Python程序,演示了两种方法:

    import cv2
    import numpy as np
    
    
    def invert_maps(map_x, map_y):
        assert(map_x.shape == map_y.shape)
        rows = map_x.shape[0]
        cols = map_x.shape[1]
        m_x = np.ones(map_x.shape, dtype=map_x.dtype) * -1
        m_y = np.ones(map_y.shape, dtype=map_y.dtype) * -1
        for i in range(rows):
            for j in range(cols):
                i_ = round(map_y[i, j])
                j_ = round(map_x[i, j])
                if 0 <= i_ < rows and 0 <= j_ < cols:
                    m_x[i_, j_] = j
                    m_y[i_, j_] = i
        return m_x, m_y
    
    
    def main():
        img = cv2.imread("pigeon.png", cv2.IMREAD_GRAYSCALE)
    
        # a simply rotation by 45 degrees
        H = np.array([np.sin(np.pi/4), -np.cos(np.pi/4), 0, np.cos(np.pi/4), np.sin(np.pi/4), 0, 0, 0, 1]).reshape((3,3))
        H_inv = np.linalg.inv(H)
        map_size = (img.shape[1], img.shape[0])
    
        map1, map2 = cv2.initUndistortRectifyMap(cameraMatrix=np.eye(3), distCoeffs=np.zeros(5), R=H, newCameraMatrix=np.eye(3), size=map_size, m1type=cv2.CV_32FC1)
        map1_inv, map2_inv = cv2.initUndistortRectifyMap(cameraMatrix=np.eye(3), distCoeffs=np.zeros(5), R=H_inv, newCameraMatrix=np.eye(3), size=map_size, m1type=cv2.CV_32FC1)
        map1_simple_inv, map2_simple_inv = invert_maps(map1, map2)
    
        img1 = cv2.remap(src=img, map1=map1, map2=map2, interpolation=cv2.INTER_LINEAR)
        img2 = cv2.remap(src=img1, map1=map1_inv, map2=map2_inv, interpolation=cv2.INTER_LINEAR)
        img3 = cv2.remap(src=img1, map1=map1_simple_inv, map2=map2_simple_inv,
                                   interpolation=cv2.INTER_LINEAR)
    
        cv2.imshow("Original image", img)
        cv2.imshow("Mapped image", img1)
        cv2.imshow("Mapping forth and back with H_inv", img2)
        cv2.imshow("Mapping forth and back with invert_maps()", img3)
        cv2.waitKey(0)
    
    
    if __name__ == '__main__':
        main()
    
        6
  •  2
  •   SCaffrey    5 年前

    下面是@wcochran答案的实现。我正试图恢复lensfunpy导致的镜片矫正。

    mod = lensfunpy.Modifier(lens, cam.crop_factor, width, height)
    mod.initialize(focal_length, aperture, distance)
    
    undist_coords = mod.apply_geometry_distortion()
    
    ## the lens correction part
    # im_undistorted = cv2.remap(im, undist_coords, None, cv2.INTER_CUBIC)
    
    # im_undistorted = cv2.remap(im, undist_coords, None, cv2.INTER_LANCZOS4)
    # cv2.imwrite(undistorted_image_path, im_undistorted)
    undist_coords_f = undist_coords.reshape((-1, 2))
    tree = KDTree(undist_coords_f)
    def calc_val(point_pos):
        nearest_dist, nearest_ind = tree.query([point_pos], k=5)
        if nearest_dist[0][0] == 0:
            return undist_coords_f[nearest_ind[0][0]]
        # starts inverse distance weighting
        w = np.array([1.0 / pow(d, 2) for d in nearest_dist])
        sw = np.sum(w)
        # embed()
        x_arr = np.floor(nearest_ind[0] / 1080)
        y_arr = (nearest_ind[0] % 1080)
        xx = np.sum(w * x_arr) / sw
        yy = np.sum(w * y_arr) / sw
        return (xx, yy)
    
    un_correction_x = np.zeros((720, 1080))
    un_correction_y = np.zeros((720, 1080))
    
    ## reverse the lens correction
    for i in range(720):
        print("row %d operating" % i)
        for j in range(1080):
            un_correction_x[i][j], un_correction_y[i][j] = calc_val((i, j))
            # print((i, j), calc_val((j, i)))
    
    dstMap1, dstMap2 = cv2.convertMaps(un_correction_x.astype(np.float32), un_correction_y.astype(np.float32), cv2.CV_32FC2)
    im_un_undistorted = cv2.remap(im_undistorted, dstMap1, dstMap2, cv2.INTER_LANCZOS4)
    
        7
  •  1
  •   Roulbacha    3 年前

    KNN回归器具有反转网格映射所需的所有组件!

    干得好:

    from sklearn.neighbors import KNeighborsRegressor
    
    def get_inverse_maps(map1, map2):
        regressor = KNeighborsRegressor(3)
        X = np.concatenate((map2[..., None], map1[..., None]), axis=-1).reshape(-1, 2)
        y = np.indices(map1.shape).transpose((1, 2, 0)).reshape(-1, 2)
        regressor.fit(X, y)
        map_inv = regressor.predict(y).reshape(map1.shape + (2,)).astype(np.float32)
        map_inv2, map_inv1 = map_inv[..., 0], map_inv[..., 1]
        return map_inv1, map_inv2
    
        8
  •  0
  •   avtomaton    8 年前

    没有任何标准的方法可以做到这一点 OpenCV .

    如果您正在寻找一个完整的即用解决方案,我不确定我是否能提供帮助,但我至少可以描述几年前我用于完成此任务的一种方法。

    首先,您应该创建与源图像具有相同维度的重映射映射。为了简化插值,我创建了更大尺寸的地图,并在最后一步将其裁剪为适当大小。然后,您应该用以前重新映射映射映射中存在的值填充它们(不太困难:只需迭代它们,如果映射坐标x和y位于图像的范围内,则将它们的行和列作为新的y和x,并将其放入新映射的旧x和y列和行中)。这是一个相当简单的解决方案,但它给出了相当好的结果。为了达到完美的效果,您应该使用插值方法和相邻像素将旧的x和y插值为整数值。

    在此之后,您应该手动重新映射像素颜色,或者用像素坐标完全填充重新映射的地图,并使用OpenCV的版本。

    您将遇到一个相当具有挑战性的任务:您应该在空白区域中插入像素。换言之,您应该获取到最接近的非零像素坐标的距离,并根据这些距离混合颜色(如果您重新映射颜色)或坐标(如果您继续进行完整贴图计算)。实际上,对于线性插值来说也不是那么困难,您甚至可以研究 remap() 实施 OpenCV github page 对于NN插值,它将简单得多-只需获取最近邻的颜色/坐标。

    最后一个任务是将区域外推到重新映射的像素区域的边界之外。OpenCV的算法也可以作为参考。

        9
  •  0
  •   SuperElectric    8 年前

    在这里操作。我想我找到了答案。我还没有实现它,如果有人提出了一个不那么复杂的解决方案(或者发现这个方案有问题),我会选择他们的答案。

    问题陈述

    设A为源图像,B为目标图像,M为从A坐标到B坐标的映射,即:

    B[k, l, :] == A(M[k, l, 0], M[k, l, 1], :) 
    for all k, l in B's coords.
    

    …其中,方括号表示使用整数索引的数组查找,圆括号表示使用浮点索引的双线性插值查找。我们使用更经济的符号重申上述内容:

    B = A(M)
    

    我们希望找到一个逆映射N,它将B尽可能地映射回A:

    Find N s.t. A \approx B(N)
    

    可以在不参考A或B的情况下说明问题:

    Find N = argmin_N || M(N) - I_n ||
    

    哪里 ||*|| 表示Frobenius范数,以及 I_n 是具有与N相同维度的身份图,即其中:

    I_n[i, j, :] == [i, j]
    for all i, j
    

    朴素的解决方案

    如果M的值都是整数,并且M是同构,则可以直接将N构造为:

    N[M[k, l, 0], M[k, l, 1], :] = [k, l]
    for all k, l
    

    或者在我们的简化符号中:

    N[M] = I_m
    

    …其中I_m是具有与m相同维度的身份映射。

    存在两个问题:

    1. M不是同构,因此上面将在N[i,j,:]处的N中为不在M中的值中的任何[i,j]留下“洞”。
    2. M的值是浮点坐标[i,j],而不是整数坐标。对于浮点值i,j,我们不能简单地为双线性插值量N(i,j,:)赋值。为了实现等效效果,我们必须设置[i,j]的四个环绕角N[floor(i),floor(j),:]、N[flool(i),ceil(j),,:]、N[ceil(i),floor(j),:]、[ceil(i)、N[ceil(i),ceil(j),N]的值,使插值N(i、j,:]等于所需值[k,l],对于所有像素映射[i,j]-->[k,l]以M为单位。

    解决方案

    将空N构造为浮点的三维张量:

    N = zeros(size=(A.shape[0], A.shape[1], 2))
    

    对于A坐标空间中的每个坐标[i,j],执行以下操作:

    1. 找到[i,j]所在的M中A坐标的2x2网格。 计算将这些A坐标映射到其对应B坐标的单应矩阵H(由2x2网格的像素索引给出)。
    2. 集合N[i,j,:]=matmul(H[i,j])

    这里潜在昂贵的步骤是在步骤1中搜索包围[i,j]的M中的2x2坐标网格。暴力搜索将使整个算法成为O(n*m),其中n是A中的像素数,m是B中的像素数目。

    为了将其简化为O(n),可以在每个a坐标四边形内运行扫描线算法,以识别其包含的所有整数值坐标[i,j]。这可以预先计算为一个哈希映射,将整数值a坐标[i,j]映射到其包围四边形的B坐标[k,l]的左上角。

        10
  •  0
  •   Zgyz McFitzgyzson    3 年前

    一种方法是获取原始映射,遍历其条目,并获取x和y值的下限和上限。这给出了(x,y),(x)周围的四个最近整数 f y f ),(x) c y f ),(x) f y c )和(x) c y c )在原始源图像的坐标中。然后,您可以将这些数据作为包含像素值和权重的索引填充到一个结构中,并对这些数据使用您首选的不规则网格插值。

    这很容易用逆距离插值实现,因为该结构可以是图像阵列累加,并且权重是标量。F是原始源,G是扭曲图像,F'是恢复图像。地图是M。

    将F'初始化为0。创建一个0初始化的权重数组W,其大小与F'相同。

    迭代M。对于M中的每个,找到4个整数对及其与(x,y)的距离。从G中获取相应的像素值,按其倒数距离对其进行加权,并将其累加为F'like

    F'(xf|c,yf|c)+=G(i,j)/sqrt((x-xf|c)^2+(y-yf|c)^2)

    然后将该重量累积到

    W(xf|c,yf|c)+=1./sqrt((x-xf|c)^2+(y-yf|c)^2) .

    完成后,通过迭代将F'归一化,并将每个像素除以其在W中的相应条目(如果它不是零)。

    在这一点上,图像通常几乎完成,但在高下采样率下,F’中的某些像素可能无法填充。然后你在W中来回传递几次,找到0个权重条目,并从它们的非空邻居中插值这些像素。这一部分也可以通过KNN搜索和插值来完成,因为它们通常并不多。

    它很容易实现,比KNN方法的伸缩性好得多(尽管我认为这对小图像很好)。缺点是,反向距离不是最好的插值方案,但如果映射不是太笨拙,并且原始图像没有被多次下采样,它似乎工作得相当好。当然,如果下采样率很高,你就必须推断出大量丢失的信息,因此它必然会给出粗略的结果。

    如果您想从地图反演中挤出尽可能多的空间,可以尝试求解由原始插值方案定义的(可能未确定的)方程组;这并非不可能,但具有挑战性。

        11
  •  -1
  •   Malcolm McLean    8 年前

    据我所知,你有一个原始图像和一个变换的图像,你希望在不知道的情况下恢复已应用的变换的性质,但假设它是有意义的,比如旋转或鱼眼扭曲。

    我要尝试的是在索引图像和普通图像中对图像进行阈值处理,将其转换为二进制。然后尝试识别对象。大多数映射至少会保留连通性和欧拉数,大多数情况下,索引中最大的对象仍然是平原中的最大对象。

    然后为匹配的图像/索引对花点时间,看看是否可以删除平移、旋转和缩放。这将提供几个反向贴图,然后您可以尝试将其缝合在一起。(如果变换不简单,则很难,但无法解决重构任何变换的一般问题)。