赞
踩
在特征提取上可以参考单目方法,在特征匹配和聚合上可以参考双目方法
方法 | 缺点 | |
---|---|---|
直接点云重建 point cloud based | 一般采用点云传播的方式逐步让模型变得稠密 | 难以并行化,重建时间长 |
基于体素的方法 volumetric based | 将3D空间划分为体素,在全局坐标系下判断每个体素的占用,能很好的通过正则化并行化。一般采用divide-and-conquer或八叉树等进行高分辨率重建 | 由于内存消耗,一般只能处理小分辨率的场景 |
基于深度图融合 depth map fusion based | 将MVS问题解耦为逐视点的深度估计+最终融合所有视点图得到3D模型 | 对于大基线角度和遮挡区域由于几何一致性会导致较差的质量 |
大基线角度:拍摄角度变换太大导致两张图差别很大
SfM 从运动中恢复结构【从图片中恢复/得到稀疏的点云+相机的参数】
Plane Sweeping平面扫描【得到深度图】
将空间划分成不同的深度假设平面,选择最好的深度假设平面 -> 恢复丢失的深度
在物体表面的点,不同相机看到的应该是一样的(在物体上) -> 某种特征&某种度量
由于SfM求得了相机参数,因此可以相互投影(Homograpy) -> 深度统一
已经估计出深度图,进行滤波和融合
像素点P,像素点P对应的深度D§,将参考视角下的P投影到源视角得到P’ -D(P‘),投影到很多源视角下
这就是几何一致性滤波
另外常见的光度一致性滤波
得到过滤后的深度图就可以进行融合,目前关于融合方面的工作比较成熟
针对MVS专门拍摄处理的高精度室内物体数据集,利用可调节照明的ABB机械臂进行多视点拍摄
大型室外场景数据集
准确率
召回率/完整性
代码仓库
https://github.com/waisvid/Awesome-MVS
基于深度学习:https://github.com/XYZ-qiyh/Awesome-Learning-MVS
train.py中最重要的fun就是train_sample
def train_sample(sample, detailed_summary=False):
model.train()
optimizer.zero_grad()
sample_cuda = tocuda(sample)
depth_gt = sample_cuda["depth"] # 深度图GT
"""
depth_visual_00xx.png:还有49张深度图的png版本被用作mask
二值图,值为1的像素是深度可靠点,后续训练只计算这些点的loss
"""
mask = sample_cuda["mask"] # 指出哪些地方不用计算loss
outputs = model(sample_cuda["imgs"], sample_cuda["proj_matrices"], sample_cuda["depth_values"])
depth_est = outputs["depth"] # MVSNet得到的深度估计图
loss = model_loss(depth_est, depth_gt, mask) # 在mask控制下计算估计深度图和GT的loss
loss.backward()
optimizer.step()
scalar_outputs = {"loss": loss}
image_outputs = {"depth_est": visualize_depth(depth_est * mask), # 深度图估计
"depth_gt": sample["depth"],
"ref_img": sample["imgs"][:, 0],
"mask": sample["mask"]}
if detailed_summary:
image_outputs["errormap"] = (depth_est - depth_gt).abs() * mask
# 更关注2mm和4mm的误差
scalar_outputs["abs_depth_error"] = AbsDepthError_metrics(depth_est, depth_gt, mask > 0.5)
scalar_outputs["thres2mm_error"] = Thres_metrics(depth_est, depth_gt, mask > 0.5, 2)
scalar_outputs["thres4mm_error"] = Thres_metrics(depth_est, depth_gt, mask > 0.5, 4)
scalar_outputs["thres8mm_error"] = Thres_metrics(depth_est, depth_gt, mask > 0.5, 8)
return tensor2float(loss), tensor2float(scalar_outputs), image_outputs
class MVSNet(nn.Module):
def __init__(self, refine=True):
super(MVSNet, self).__init__()
self.refine = refine
self.feature = FeatureNet()
self.cost_regularization = CostRegNet()
if self.refine:
self.refine_network = RefineNet()
def forward(self, imgs, proj_matrices, depth_values):
imgs = torch.unbind(imgs, 1)
proj_matrices = torch.unbind(proj_matrices, 1)
assert len(imgs) == len(proj_matrices), "Different number of images and projection matrices"
img_height, img_width = imgs[0].shape[2], imgs[0].shape[3]
num_depth = depth_values.shape[1]
num_views = len(imgs)
# step 1. feature extraction
# in: images; out: 32-channel feature maps
features = [self.feature(img) for img in imgs] # 会跑3次, 1张ref,两张src
ref_feature, src_features = features[0], features[1:] # 每个特征图都是 [B, 32, H/4, W/4]
ref_proj, src_projs = proj_matrices[0], proj_matrices[1:]
# step 2. differentiable homograph, build cost volume
# 把上面三个特征都投影到ref视点的锥体上,同时做一个方差聚合
ref_volume = ref_feature.unsqueeze(2).repeat(1, 1, num_depth, 1, 1) # [B, 32, 192,H/4, W/4]
volume_sum = ref_volume # 便于后面计算平均
volume_sq_sum = ref_volume ** 2 # 便于后面计算方差
del ref_volume
for src_fea, src_proj in zip(src_features, src_projs):
# warpped features
warped_volume = homo_warping(src_fea, src_proj, ref_proj, depth_values)
if self.training:
volume_sum = volume_sum + warped_volume
volume_sq_sum = volume_sq_sum + warped_volume ** 2
else: # 测试模式【分辨率率很大,要节省内存】
# TODO: this is only a temporal solution to save memory, better way?
volume_sum += warped_volume
volume_sq_sum += warped_volume.pow_(2) # the memory of warped_volume has been modified
del warped_volume
# aggregate multiple feature volumes by variance 对应原文公式(2),计算方差
volume_variance = volume_sq_sum.div_(num_views).sub_(volume_sum.div_(num_views).pow_(2)) # [B, 32, 192, H/4, W/4]
# step 3. cost volume regularization
cost_reg = self.cost_regularization(volume_variance) # [B, 1, 192, H/4, W/4]
# cost_reg = F.upsample(cost_reg, [num_depth * 4, img_height, img_width], mode='trilinear')
cost_reg = cost_reg.squeeze(1) # [B, 192, H/4, W/4]
prob_volume = F.softmax(cost_reg, dim=1) # [B, 192, H/4, W/4]
depth = depth_regression(prob_volume, depth_values=depth_values) # [B, H/4, W/4] 加权平均选择最优的深度
with torch.no_grad():
# photometric confidence-从概率体中获得一个置信图,只用来做滤波
prob_volume_sum4 = 4 * F.avg_pool3d(F.pad(prob_volume.unsqueeze(1), pad=(0, 0, 0, 0, 1, 2)), (4, 1, 1), stride=1, padding=0).squeeze(1) # 和周围4个加一下得到一个聚合概率值
depth_index = depth_regression(prob_volume, depth_values=torch.arange(num_depth, device=prob_volume.device, dtype=torch.float)).long()
photometric_confidence = torch.gather(prob_volume_sum4, 1, depth_index.unsqueeze(1)).squeeze(1)
# step 4. depth map refinement
if not self.refine:
return {"depth": depth, "photometric_confidence": photometric_confidence}
else:
refined_depth = self.refine_network(torch.cat((imgs[0], depth), 1))
return {"depth": depth, "refined_depth": refined_depth, "photometric_confidence": photometric_confidence}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。