def homo_warping(src_fea, src_proj, ref_proj, depth_values):
# src_fea: [B, C, H, W]
# src_proj: [B, 4, 4]
# ref_proj: [B, 4, 4]
# depth_values: [B, Ndepth]
# out: [B, C, Ndepth, H, W]
batch, channels = src_fea.shape[0], src_fea.shape[1]
num_depth = depth_values.shape[1]
height, width = src_fea.shape[2], src_fea.shape[3]
with torch.no_grad():
proj = torch.matmul(src_proj, torch.inverse(ref_proj)) # 将参考图像像素坐标映射到源图像 但是目的是要把源到参考?
rot = proj[:, :3, :3] # [B,3,3]
trans = proj[:, :3, 3:4] # [B,3,1]
# 生成网格坐标
y, x = torch.meshgrid([torch.arange(0, height, dtype=torch.float32, device=src_fea.device),
torch.arange(0, width, dtype=torch.float32, device=src_fea.device)])
y, x = y.contiguous(), x.contiguous()
y, x = y.view(height * width), x.view(height * width) # 展平为height * width个元素
xyz = torch.stack((x, y, torch.ones_like(x))) # [3, H*W] xyz是要得到像素坐标系(x,y,1) [[x1...xn],[y1...yn],[1...1]],1 是齐次坐标中的常数分量
xyz = torch.unsqueeze(xyz, 0).repeat(batch, 1, 1) # [B, 3, H*W] # 在指定维度 dim 插入一个新的维度
rot_xyz = torch.matmul(rot, xyz) # [B, 3, H*W]
rot_depth_xyz = rot_xyz.unsqueeze(2).repeat(1, 1, num_depth, 1) * depth_values.view(batch, 1, num_depth,
1) # [B, 3, Ndepth, H*W] 对于每个深度值的三维坐标。这个操作通过将深度值应用于旋转后的坐标,得到每个深度层的三维坐标
proj_xyz = rot_depth_xyz + trans.view(batch, 3, 1, 1) # [B, 3, Ndepth, H*W] 将旋转和位移应用到三维坐标上的结果,得到的是每个深度值的三维坐标
proj_xy = proj_xyz[:, :2, :, :] / proj_xyz[:, 2:3, :, :] # [B, 2, Ndepth, H*W] 将 proj_xyz 的前三维(x 和 y)除以第三维(z)来将三维坐标投影到二维平面上。
proj_x_normalized = proj_xy[:, 0, :, :] / ((width - 1) / 2) - 1
proj_y_normalized = proj_xy[:, 1, :, :] / ((height - 1) / 2) - 1
proj_xy = torch.stack((proj_x_normalized, proj_y_normalized), dim=3) # [B, Ndepth, H*W, 2]
grid = proj_xy
# grid 是一个采样网络,例如grid(0,0)表示输出图像的第一个像素是输入图像【1.5,1.5】位置的像素,实际上这个位置的像素值由插值算出来
# grid = [[[1.5, 1.5], [2.5, 2.5]], // 第一行输出的两个像素位置
# [[1.5, 1.5], [2.5, 2.5]]] // 第二行输出的两个像素位置
warped_src_fea = F.grid_sample(src_fea, grid.view(batch, num_depth * height, width, 2), mode='bilinear',
padding_mode='zeros')
warped_src_fea = warped_src_fea.view(batch, channels, num_depth, height, width)
return warped_src_fea`
这个函数 homo_warping
的主要作用是将源图像特征 src_fea
根据参考图像的视角和一系列深度值进行重投影,生成多视角几何中的特征对齐。具体来说,该函数通过利用投影矩阵和深度值,将源图像的特征图投影到参考图像的视角下,并进行双线性插值,得到在不同深度值下的重投影特征图。
函数输入
src_fea
: 源图像特征图,形状为[B, C, H, W]
,其中B
是批次大小,C
是通道数,H
和W
分别是高度和宽度。src_proj
: 源图像的投影矩阵,形状为[B, 4, 4]
。ref_proj
: 参考图像的投影矩阵,形状为[B, 4, 4]
。depth_values
: 深度值数组,形状为[B, Ndepth]
,其中Ndepth
是深度值的数量。
函数输出
warped_src_fea
: 重投影后的源图像特征图,形状为[B, C, Ndepth, H, W]
。
函数步骤
-
初始化和形状提取:
- 提取输入特征图的批次大小、通道数、深度值数量、高度和宽度。
-
计算相对投影矩阵:
- 通过矩阵乘法
torch.matmul(src_proj, torch.inverse(ref_proj))
计算源图像和参考图像之间的相对投影矩阵。 - 提取旋转矩阵
rot
和平移向量trans
。
- 通过矩阵乘法
-
生成像素网格坐标:
- 使用
torch.meshgrid
生成源图像平面的x
和y
坐标网格。 - 将坐标展平并堆叠为
[3, H*W]
的张量,表示齐次坐标系中的像素坐标[x, y, 1]
。 - 扩展批次维度并重复,以得到
[B, 3, H*W]
的坐标张量。
- 使用
-
应用旋转和深度:
- 将旋转矩阵
rot
应用于像素坐标,得到旋转后的坐标rot_xyz
。 - 将深度值应用于旋转后的坐标,得到每个深度层的三维坐标
rot_depth_xyz
。
- 将旋转矩阵
-
应用投影和归一化:
- 将平移向量
trans
应用于三维坐标,得到投影后的三维坐标proj_xyz
。 - 将三维坐标投影到二维平面,得到
proj_xy
。 - 归一化
proj_xy
,使其坐标范围在[-1, 1]
之间,以适应grid_sample
函数的要求。
- 将平移向量
-
生成网格并采样:
- 使用
proj_xy
生成采样网格grid
。 - 使用
F.grid_sample
函数根据采样网格从源图像特征图src_fea
中进行双线性插值,生成重投影后的特征图warped_src_fea
。
- 使用
-
调整输出形状:
- 将重投影后的特征图
warped_src_fea
的形状调整为[B, C, Ndepth, H, W]
。
- 将重投影后的特征图
总结
这个函数通过将源图像的特征图根据参考图像的视角和一系列深度值进行重投影,实现了多视角几何中的特征对齐。这样可以在不同的深度层上对源图像的特征进行采样,生成在参考视角下的特征图,用于后续的深度估计或三维重建等任务。