【三维重建】【深度学习】NeuS代码Pytorch实现--测试阶段代码解析(下)

news2024/11/26 0:56:57

【三维重建】【深度学习】NeuS代码Pytorch实现–测试阶段代码解析(下)

论文提出了一种新颖的神经表面重建方法,称为NeuS,用于从2D图像输入以高保真度重建对象和场景。在NeuS中建议将曲面表示为有符号距离函数(SDF)的零级集,并开发一种新的体绘制方法来训练神经SDF表示,因此即使没有掩模监督,也可以实现更准确的表面重建。NeuS在高质量的表面重建方面的性能优于现有技术,特别是对于具有复杂结构和自遮挡的对象和场景。本篇博文将根据代码执行流程解析测试阶段具体的功能模块代码。

文章目录

  • 【三维重建】【深度学习】NeuS代码Pytorch实现--测试阶段代码解析(下)
  • 前言
  • validate_mesh附加
  • interpolate_view
  • render_novel_image
  • gen_rays_between
  • 总结


前言

在详细解析NeuS网络之前,首要任务是搭建NeuS【win10下参考教程】所需的运行环境,并完成模型的训练和测试,展开后续工作才有意义。
本博文将对NeuS测试阶段涉及的剩余功能代码模块进行解析。

博主将各功能模块的代码在不同的博文中进行了详细的解析,点击【win10下参考教程】,博文的目录链接放在前言部分。

这里的代码段是exp_runner.py文件的__main__函数部分,在此前的博文中博主已经详细讲解了train模块和validate_mesh模块,本博文讲最后讲解的interpolate_view模块代码。

if args.mode == 'train':
    runner.train()
elif args.mode == 'validate_mesh':
    runner.validate_mesh(world_space=True, resolution=512, threshold=args.mcube_threshold)
elif args.mode.startswith('interpolate'):  # Interpolate views given two image indices
    _, img_idx_0, img_idx_1 = args.mode.split('_')
    img_idx_0 = int(img_idx_0)
    img_idx_1 = int(img_idx_1)
    runner.interpolate_view(img_idx_0, img_idx_1)

validate_mesh附加

在之前的博文中已经对代码进行了详细的讲解,这里只是补充说明resolution参数的作用。

runner.validate_mesh(world_space=True, resolution=512, threshold=args.mcube_threshold)

如下图所示,resolution的数值越大,提取点(体素)的数量越多,则重建效果越细致。


interpolate_view

属于exp_runner.py文件的Runner类中的成员方法,将渲染产生的图片合成视频(mp4格式)保存。

def interpolate_view(self, img_idx_0, img_idx_1):
    # 保存渲染的图片
    images = []
    # 渲染生成的图片帧数
    n_frames = 60
    for i in range(n_frames):
        print(i)
        # 根据图像序号,分别作为起点和终点,渲染出介意俩图片之间的图片
        images.append(self.render_novel_image(img_idx_0,
                                              img_idx_1,
                                              np.sin(((i / n_frames) - 0.5) * np.pi) * 0.5 + 0.5,
                      resolution_level=4))
    # 将图片倒放一遍
    for i in range(n_frames):
        images.append(images[n_frames - i - 1])

    # 图片合成mp4格式
    fourcc = cv.VideoWriter_fourcc(*'mp4v')
    # 指定视频保存路径
    video_dir = os.path.join(self.base_exp_dir, 'render')
    os.makedirs(video_dir, exist_ok=True)

    h, w, _ = images[0].shape

    # 合成视频
    writer = cv.VideoWriter(os.path.join(video_dir,
                                         '{:0>8d}_{}_{}.mp4'.format(self.iter_step, img_idx_0, img_idx_1)),
                            fourcc, 30, (w, h))
    for image in images:
        writer.write(image)
    writer.release()

这里还将渲染产生的图片反方向再加入列表里,所以合成的视频感觉从一点就是推过去而后又拉回来。


render_novel_image

完成NeuS模型训练后,根据俩个相机位姿渲染出从一个位姿运动到另一个位姿的过程中的多个图片,从而验证模型训练的效果。
首先需要gen_rays_between函数生成运动过程中某个位置的整张图片(下采样后)的光线rays,然后获取rays光线上采样点(前景)的最远点和最近点,最后通过renderer函数获取所需的结果。

def render_novel_image(self, idx_0, idx_1, ratio, resolution_level):
    """
    Interpolate view between two cameras.
    """
    rays_o, rays_d = self.dataset.gen_rays_between(idx_0, idx_1, ratio, resolution_level=resolution_level)
    H, W, _ = rays_o.shape
    rays_o = rays_o.reshape(-1, 3).split(self.batch_size)
    rays_d = rays_d.reshape(-1, 3).split(self.batch_size)

    out_rgb_fine = []
    for rays_o_batch, rays_d_batch in zip(rays_o, rays_d):
        # 最近点和最远点
        near, far = self.dataset.near_far_from_sphere(rays_o_batch, rays_d_batch)
        # 背景颜色
        background_rgb = torch.ones([1, 3]) if self.use_white_bkgd else None
        render_out = self.renderer.render(rays_o_batch,
                                          rays_d_batch,
                                          near,
                                          far,
                                          cos_anneal_ratio=self.get_cos_anneal_ratio(),
                                          background_rgb=background_rgb)
        
        out_rgb_fine.append(render_out['color_fine'].detach().cpu().numpy())
        del render_out
    # 渲染出图片
    img_fine = (np.concatenate(out_rgb_fine, axis=0).reshape([H, W, 3]) * 256).clip(0, 255).astype(np.uint8)
    return img_fine

gen_rays_between

Dataset数据管理器的定义的函数,在models/dataset.py文件下。博主【NeuS总览】的博文中,已经简单介绍过这个过程。

def gen_rays_between(self, idx_0, idx_1, ratio, resolution_level=1):
    """
    Interpolate pose between two cameras.
    在两个摄影机之间的插值,获得新的摄影机并在世界空间中生成光线
    """
    # idx_0是起点图像
    # idx_1是终点图像
    # 下采样倍数
    l = resolution_level
    # 理解为将图像尺寸缩放为原始输入图像尺寸的(1/l*1/l)倍
    tx = torch.linspace(0, self.W - 1, self.W // l)
    ty = torch.linspace(0, self.H - 1, self.H // l)
    pixels_x, pixels_y = torch.meshgrid(tx, ty)

    # 由像素坐标系转换到对应的空间坐标系
    p = torch.stack([pixels_x, pixels_y, torch.ones_like(pixels_y)], dim=-1)  # [w, h, 3]
    p = torch.matmul(self.intrinsics_all_inv[0, None, None, :3, :3], p[:, :, :, None]).squeeze()  # [w, h, 3]

    # 相机坐标下的单位方向向量
    rays_v = p / torch.linalg.norm(p, ord=2, dim=-1, keepdim=True)  # [w, h, 3]

    # 这行代码好像没啥作用
    trans = self.pose_all[idx_0, :3, 3] * (1.0 - ratio) + self.pose_all[idx_1, :3, 3] * ratio

    # idx_0图像的外参(逆)
    pose_0 = self.pose_all[idx_0].detach().cpu().to(self.device).numpy()
    # idx_1图像的外参(逆)
    pose_1 = self.pose_all[idx_1].detach().cpu().to(self.device).numpy()
    # idx_0图像的外参
    pose_0 = np.linalg.inv(pose_0)
    # idx_1图像的外参
    pose_1 = np.linalg.inv(pose_1)
    # idx_0旋转矩阵
    rot_0 = pose_0[:3, :3]
    # idx_1旋转矩阵
    rot_1 = pose_1[:3, :3]
    # 旋转矩阵表示的旋转的对象
    rots = Rot.from_matrix(np.stack([rot_0, rot_1]))    # [2,3,3]
    key_times = [0, 1]
    # 确定对应的时间和旋转量 2个pose对应俩个时间
    slerp = Slerp(key_times, rots)
    # 获得rot_0到rot_1的ratio的旋转矩阵rot_ratio(ratio在0~1之间)
    # 简单理解就是rot_0变换到rot_1的期间的中间过程rot_ratio
    rot = slerp(ratio)  # [3,3]
    # 4×4的单位矩阵
    pose = np.diag([1.0, 1.0, 1.0, 1.0])
    pose = pose.astype(np.float32)

    # 将旋转矩阵rot覆盖到pose对应位置上
    pose[:3, :3] = rot.as_matrix()
    # 将位移矩阵覆盖到pose对应位置上 (p1-p0)*r+p0=(1-r)*p0+p1*r
    pose[:3, 3] = ((1.0 - ratio) * pose_0 + ratio * pose_1)[:3, 3]

    # 对外参pose求逆
    pose = np.linalg.inv(pose)
    # 旋转矩阵
    rot = torch.from_numpy(pose[:3, :3]).cuda()
    # 平移矩阵
    trans = torch.from_numpy(pose[:3, 3]).cuda()
    # 世界坐标系下的单位方向向量
    rays_v = torch.matmul(rot[None, None, :3, :3], rays_v[:, :, :, None]).squeeze()  # [w, h, 3]
    # 相机光心在世界坐标系下的位置
    rays_o = trans[None, None, :3].expand(rays_v.shape)  # [w, h, 3]
    # [h, w, 3] [h, w, 3]
    return rays_o.transpose(0, 1), rays_v.transpose(0, 1)

代码的执行示意图如下图所示,函数返回了rays_o(光心)和rays_v(单位方向向量)。

根据俩个相机位姿算出第三个相机位姿,R和T的计算方式有点区别。R的计算借助cipy.spatial.transform.Rotation,具体原理博主是参考ChatGPT的。


总结

尽可能简单、详细的介绍NeuS测试阶段剩余代码:interpolate_view渲染合成视频。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/912608.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

springboot+vue实现的智慧学校云平台源码

智慧校园源码 智慧班牌云平台源码 系统架构:Javavue2springbootMySQL elmentuiQuartzjpajwt 智慧校园电子班牌云平台是出于校园考勤管理以及班级校园信息展示为目的的管理系统,电子班牌系统主要用于中小学教育的综合管理平台,融合了多媒体技…

SPSS中级统计--卡方独立性检验之n×c表资料

nc资料表检验 nc资料表分类: ①双向无序的RC表资料 ②单向有序的RC表资料 ③双向有序的RC表资料 1、双向无序RC表资料 多个样本率的比较(c2) 例1:比较不同污染地区的动物畸形率是否有差异? H0:不同污染…

Android中使用JT808协议进行车载终端通信的实现和优化

JT808是一种在中国广泛应用的车载终端通信协议,用于车辆与监控中心之间的数据通信。下面是关于Android平台上使用JT808协议进行通信的一般步骤和注意事项: 协议了解:首先,您需要详细了解JT808协议的规范和定义。该协议包含了通信消…

iServer通过服务实例动态化管理MongoDB万级瓦片集应用实践

作者:Carlo 文章目录 数据特点项目难点优化方案先了解“服务实例动态化管理”功能特性“服务实例动态化管理”应用场景优化1:开启服务实例动态化管理优化2:同时设置一个特定服务关闭动态管理持续存活优化3:将服务配置信息存储到Po…

React(8)

千锋学习视频https://www.bilibili.com/video/BV1dP4y1c7qd?p72&spm_id_frompageDriver&vd_sourcef07a5c4baae42e64ab4bebdd9f3cd1b3 1.React 路由 1.1 什么是路由? 路由是根据不同的 url 地址展示不同的内容或页面。 一个针对React而设计的路由解决方案…

python从入门到精通——完整教程

阅读全文点击《python从入门到精通——完整教程》 一、编程入门与进阶提高 Python编程入门 1、Python环境搭建( 下载、安装与版本选择)。 2、如何选择Python编辑器?(IDLE、Notepad、PyCharm、Jupyter…) 3、Pytho…

安装npm和react

下载react 下载node链接 或者: https://nodejs.org/zh-cn/download/ 下载以后重启电脑(刷新环境变量) 或者手工刷新环境变量 检查环境变量 node -v npm -v如果没有问题应该是这两个: 修改镜像源 npm config set registry ht…

基于swing的在线考试系统java jsp线上试卷问答mysql源代码

本项目为前几天收费帮学妹做的一个项目,Java EE JSP项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。 一、项目描述 基于swing的在线考试系统 系统有2权限:管…

第二篇:基础窗口部件 QWidget

基础窗口部件 QWidget QWidget 类是所有用户界面对象的基类,因此被称为基础窗口部件。QWidget 继承自 QObject 类和QPaintDevice 类,其中 QObject 类是所有支持 Qt 对象模型(Qt Object Model)的对象的基类,QPaintDevi…

LTD与杭州商务局系统签订战略合作协议:共同推动商贸企业数字化

LTD将继续发挥“营销SaaS系统场景应用”的优势,为做强做优做大我国数字经济贡献更多力量。 近日,由杭州市商务局指导,杭州市商务发展研究中心(杭州市中小商贸流通企业服务中心)主办,每日商报承办&#xff0…

【Vue3】 Vue-Router路由和路由导航守卫

路由 前后端分离阶段路由单页面富应用阶段前端路由如何做到URL和内容进行映射?URl的hash(哈希)URl的history Vue-Router基本使用1,安装Vue-Router2,新建页面router文件下的index.js,路由,导入页面,导入路由…

一定解决JavaFx运行时Application爆红色问题

文章目录 注意maven项目创建maven javafx项目配置 注意 以下的问题纯粹是因为新建的是普通项目,而不是Java FX项目,如果新建的是Java FX项目,那么idea会自动给你生成相应的需要的pom.xml文件,并且运行也是正常的 maven项目创建…

RPC和HTTP协议

RPC 全称(Remote Procedure Call),它是一种针对跨进程或者跨网络节点的应用之间的远程过程调用协议。 它的核心目标是,让开发人员在进行远程方法调用的时候,就像调用本地方法一样,不需要额外为了完成这个交…

Rspack 创建 vue2/3 项目接入 antdv(rspack.config.js 配置 less 主题)

一、简介 Rspack CLI 官方文档。 rspack.config.js 官方文档。 二、创建 vue 项目 创建项目(文档中还提供了 Rspack 内置 monorepo 框架 Nx 的创建方式,根据需求进行选择) # npm 方式 $ npm create rspacklatest# yarn 方式 $ yarn create…

SSD基本工作原理了解

SSD与RAM的原理有些类似,RAM使用晶体管和电容来表示0或1,晶体管用于将电荷转移到电容器或从电容器中吸取电荷,并且电荷必须每几微秒刷新一次。 而SSD相比于RAM的非易失性来自于其使用的浮栅晶体管。其创造了一个小笼子,不需要外界…

Sui生态项目|集隐私通信、移动钱包、链上朋友圈和红包功能一体的社交应用ComingChat

ComingChat是在Sui网络上构建的去中心化社交平台,功能众多,其中加密聊天功能为用户提供了安全的沟通方式。该功能利用了Signal加密协议,这是一种在Signal、WhatsApp和Skype等应用中广受欢迎的开源软件协议。 ComingChat在Sui上提供了全面的…

Echarts 柱状图-值为0时柱状图数据

需求是这样的:当数据为0时,鼠标悬浮也需要展示对应的数据,当值很小,也需进行占位,所以要加barMinHeight 刚开始以为没有对应方案呢,然后在官网死磕,最后还是找到解决方案了. 打开官网地址 解决方…

软件测试工程师的职业发展方向,别迷茫了,振作起来

软件测试在职业发展上,可以概括分为“管理”和“技术”两大类。另外,软件测试还可以在质量领域发展。 1. 软件测试在管理上的发展 软件测试管理是大家比较熟悉的软件测试职业发展路线之一,比较流行的职位包括测试组长、测试经理、测试代表、…

删除ubuntu开始菜单中的图标

背景 本来是很好看干净的界面 更新谷歌浏览器后出现了Gmail,幻灯片,谷歌硬盘等跟谷歌相关的乱七八糟东西搞得界面就很丑 解决问题 删掉那个图标 输入命令 sudo nautilus /usr/share/applicationssudo nautilus ~/.local/share/applications可以…

风险变化快,业务人员如何快速增加风控规则?

目录 什么是风控规则? 风控规则的来源 如何在风控引擎中配置规则? 今年暑假,博物馆和演出会门票被黄牛抢走。主办方、博物馆如果拥有风控系统,可以制订一系列规则来识别和拦截潜在的黄牛行为。 在制订规则时,需考虑…