我有一个固定的摄像机安装在墙上,观看长方形草坪的角度。我的目标是获得一个不扭曲的,自上而下的草坪景观.
我有一个来自摄像机的图片,它是python数组,如下所示:
我使用带有skimage.transform.warp的逆矩阵将图像更正为自顶向下视图:
这是完美的工作,但相机镜头引入桶形畸变。
单独地,我可以使用和弦生成的查找表纠正失真,并根据描述的这里算法传递一个简单的、不失真的可调用函数。然后使用坐标生成图像。
这两个过程分别工作,但我如何组合它们来创建一个不失真的自上而下视图,而不创建一个中间图像?
我可以通过矩阵运行查找表中的每个点来创建一个新表,但是这个表很大,内存也很紧(Raspberry Pi Zero)。
我想把不失真定义为一个矩阵,只组合两个矩阵,但据我所知,射影单形矩阵是线性的,而非失真矩阵是非线性的,所以这是不可能的。由于资源的限制,我不能使用OpenCV,涉及多个棋盘图像的校准过程是不切实际的。目前,我用4个草坪角点进行标定,并从中生成矩阵,效果很好。
我本以为这是计算机视觉中常见的问题,但却找不到合适的解决方案。
发布于 2022-07-26 15:42:57
桶形变形是非线性的,但也是光滑的。这意味着它可以很好地通过一组分段线性逼近来逼近。所以你不需要一个大的,每像素查表的无失真位移.相反,您可以对其进行子采样(或将其缩小),并对中间像素使用双线性插值。
发布于 2022-08-11 07:07:42
我找到了一个解决方案,它似乎可以通过创建独立的函数来实现非扭曲和转换,然后将它们链接在一起。
skimage源代码这里具有从矩阵生成映射的_apply_mat方法。我的解曲功能的基础是:
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]我创建了一个类似的基于坦纳Hellands算法的非失真函数
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的答案是有帮助的,但是我需要整个像素的分辨率来进行转换,所以我也将它用于非失真的转换,并寻找其他方法来减少内存消耗。
每一张地图
行* cols *字节-每项*2 (x和y)
字节。默认的数据类型是float64,它要求每项8字节,文档表明,正常的选择将是默认的,或者float32为每项4字节。我能够减少到每项使用int16的两个字节,没有明显的不良影响,但我怀疑样条插值没有被充分使用(?)
对于彩色RGB图像的每个通道,地图是相同的。然而,当我用warp_coords (行、科尔、3)调用shape=时,我得到了3个重复的映射,因此我创建了一个函数,通过分别处理每个通道来处理彩色图像:
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_arrskimage.transform.warp_coords的一个问题是它没有skimage.transform.warp具有的map_args字典参数。我必须通过一个中间函数调用我的反扭曲和非扭曲函数来添加参数。
https://stackoverflow.com/questions/73121247
复制相似问题