scipy 创建相机图像的无扭曲自上而下视图

u0njafvf  于 2022-11-10  发布在  其他
关注(0)|答案(2)|浏览(118)

我在墙上安装了一个固定的摄像机,从一个Angular 观察一个矩形的草坪,我的目标是获得一个不失真的,自上而下的草坪视图。
我有一个图像从相机作为一个python numpy阵列看起来像这样:
raw camera image
我使用skimage.transform.warp的逆矩阵将图像校正为自顶向下的视图:
top down distorted
这是完美的工作,但相机透镜引入桶形失真。
另外,我可以使用skimage.transform.warp_coords生成一个查找表,并根据here描述的算法传递一个简单的可调用函数来校正失真。然后使用scipy.ndimage.map_coordinates生成图像:
undistorted camera view
这两个过程分别工作,但我如何将它们结合起来,以创建一个不失真的自上而下的视图,而不创建一个中间图像?
我可以通过矩阵运行查找表中的每个点来创建一个新表,但该表很大,内存也很紧张(Raspberry Pi Zero)。
我想把无失真定义为一个矩阵,然后把两个矩阵组合起来,但据我所知,投影单应矩阵是线性的,而无失真是非线性的,所以这是不可能的。由于资源限制,我不能使用OpenCV,而且涉及多个棋盘图像的校准过程是不切实际的。目前,我通过取4个草坪角点进行校准,并从中生成矩阵。其工作良好。
我早就预料到这是计算机视觉中的一个常见问题,但却找不到任何合适的解决方案。

wqlqzqxt

wqlqzqxt1#

桶形失真是非线性的,但它也是平滑的。这意味着它可以通过一组分段线性近似来很好地近似。因此,您不需要一个大的、每个像素的未失真位移查找表。相反,您可以对其进行二次采样(或只是缩小),并对中间的像素使用双线性插值。

4smxwvx5

4smxwvx52#

我已经找到了一个解决方案,通过创建单独的函数来进行反扭曲和变换,然后将它们链接在一起。
这里的skimage源代码有一个_apply_mat方法,用于从一个矩阵生成一个Map,我的unwarp函数就是基于这个方法:

def unwarp(coords, matrix):
    coords = np.array(coords, copy=False, ndmin=2)
    x, y = np.transpose(coords)
    src = np.vstack((x, y, np.ones_like(x)))
    dst = src.T @ matrix.T

    # below, we will divide by the last dimension of the homogeneous
    # coordinate matrix. In order to avoid division by zero,
    # we replace exact zeros in this column with a very small number.
    dst[dst[:, 2] == 0, 2] = np.finfo(float).eps
    # rescale to homogeneous coordinates
    dst[:, :2] /= dst[:, 2:3]

    return dst[:, :2]

我基于Tanner Hellands algorithm创建了一个类似的函数来消除失真:

def undistort(coords, cols, rows, correction_radius, zoom):
    half_width = cols / 2
    half_height = rows / 2
    new_x = coords[:, 0] - half_width
    new_y = coords[:, 1] - half_height
    distance = np.hypot(new_x, new_y)
    r = distance / correction_radius
    theta = np.ones_like(r)
    # only process non-zero values
    np.divide(np.arctan(r), r, out=theta, where=r!=0)
    source_x = half_width + theta * new_x * zoom
    source_y = half_height + theta * new_y * zoom
    result = np.column_stack([source_x, source_y])       
    return result

这里唯一需要注意的是除法,我们需要防止除以零。
一旦我们有了每个查找表,我们就可以将它们链接在一起:

def undistort_unwarp(coords):
    undistorted = undistort(coords)
    both = unwarp(undistorted)
    return both

请注意,以下是传递给skimage.transform.warp_coords的可调用函数:

mymap = tf.warp_coords(undistort_unwarp, shape=(rows, cols), dtype=np.int16)

然后可以将贴图传递给skimage.transform.warp函数。
Francesco的回答很有帮助,但是我需要全像素分辨率来进行转换,所以我也用它来进行反扭曲,并寻找其他方法来减少内存消耗。
每个Map消耗
行 * 列 * 每项字节数 * 2(x和y)
默认的数据类型是float64,它要求每项8个字节,文档建议明智的选择是默认的或者每项4个字节的float32。我能够使用int16将其减少到每项2个字节,没有明显的不良影响,但是我怀疑样条插值没有被充分使用(根本没有?)。
彩色RGB图像的每个通道的Map都是一样的。但是,当我用shape=(rows,cols,3)调用warp_coords时,我得到了3个重复的Map,所以我创建了一个函数,通过分别处理每个通道来处理彩色图像:

def warp_colour(img_arr, coord_map):
    if img_arr.ndim == 3:
        # colour
        rows, cols, _chans = img_arr.shape
        r_arr = tf.warp(img_arr[:, :, 0], inverse_map=coord_map, output_shape=(rows, cols))
        g_arr = tf.warp(img_arr[:, :, 1], inverse_map=coord_map, output_shape=(rows, cols))
        b_arr = tf.warp(img_arr[:, :, 2], inverse_map=coord_map, output_shape=(rows, cols))
        rgb_arr = np.dstack([r_arr, g_arr, b_arr])
    else:
        # grayscale
        rows, cols = img_arr.shape
        rgb_arr = tf.warp(img_arr, inverse_map=coord_map, output_shape=(rows, cols))
    return rgb_arr

skimage.transform.warp_coords的一个问题是它没有skimage.transform.warp所具有的map_args字典参数。我不得不通过一个中间函数调用我的unwarp和undistort函数来添加这些参数。

相关问题