AI 让观众成为 3D 版《老友记》的导演了?

news2025/1/11 12:46:54

《老友记》上线 3D 版了?

允许用户旋转镜头,且从近景切换到全景观看故事?

今年出炉的 3D 方向 AI 项目 SitCom3D,能够自动补齐《老友记》原剧中的三维拍摄空间,用户可以选择主视图、侧视图等不同角度欣赏剧集。镜头的主导权在观众手中,仿佛亲临拍摄片场。

举个栗子,在原剧中只出现了以下两个画面。

看了《老友记》后,AI直接还原出画面的3D场景,构造不同角度下的镜头故事。

这流畅的运镜,仿佛就是新上线的 3D 版老友记,并且你就是导演!

项目介绍:情景喜剧的三维重构

这个项目由 UC 伯克利的 Georgios Pavlakos 等研究人员在 ECCV 2022 “The One Where They Reconstructed 3D Humans and Environments in TV Shows” 论文中提出,旨在借助 AI 完成影视剧集中的3D重建。

3D建模在很多领域都有广泛的应用,然而传统的重建方式耗时巨大,比如在制造业,一个工业级模型需要专业建模师花费数周时间。目前快速获得3D模型的方式有两种,一种是靠仪器扫描获得三维形状数据,例如点云;另一种则是基于深度学习,使用AI建模。后者更直观,且成本更低。想象一下用手机拍几张、甚至一张照片就能获得高精度的3D模型,既可以为元宇宙线上生活提供基础设施,又能在传统工业领域加速研发流程。

提到2D图像转3D模型,绕不开近两年大火的NeRF神经网络。自2020年发表以来众多高校和业界公司基于它研发了各自版本的NeRF:英伟达推出了极速版的instant NeRF;苹果的NeuMan框架等等。
在这里插入图片描述
NeRF为2D转3D提供了新的思路,其背后的机制,相关论文解读有很多,这里仅做简述:NeRF通过对数张2D照片的学习,使用神经辐射场的方式建立起像素点位置(x, y, z)和相机参数(θ, φ)对应图像的volume density体积密度和RGB颜色值的关系,训练完成后以此生成新的视角。通过这种方式,NeRF相较传统的3D建模方式能够生成更精细的还原。

回到TV Show的三维重建项目,如下图(fig2)所示,研究人员正是基于NeRF模型,通过分析整个剧集里的三维信息,精确感知和重建3D人体姿势和演员位置,并生成新的不存在剧集里的2D角度图像。
在这里插入图片描述
生成3D模型的原理如下图所示,首先从剧集输入中通过SfM(Structure-from-Motion)方法估计出摄像机的位置,并通过NeRF重建出精确的环境3D场景信息。接着根据多镜头和单镜头的情形,进行人体3D重建。最后基于这些信息进行更多的编辑和开发,比如在电视剧中删除一个人,或者插入一只兔子。
图3
在这里插入图片描述
这个项目目前已在github上开源。

项目地址:https://github.com/ethanweber/sitcoms3D
论文地址:https://ethanweber.me/sitcoms3D

感兴趣的小伙伴可以直接在矩池云上复现这个项目,具体操作方式如下:

项目复现:用矩池云快速实现三维重构

1、打开矩池云官网,进入主机市场,找到合适的机器

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
等待机器启动,启动完成界面如下,点击进入 Jupyter Notebook
在这里插入图片描述

2、进入命令行,进行代码下载与安装

在这里插入图片描述
依次输入以下命令

cd /mnt
git clone https://hub.fastgit.xyz/ethanweber/sitcoms3D.git

如果下载慢可以用进入 https://hub.fastgit.xyz/ethanweber/sitcoms3D 手动下载后上传

https://github.com/CMUAbstract/cote.git

进入文件夹

cd sitcoms3D/

安装依赖

pip install -r requirements.txt
pip install tqdm

解压数据
矩池云已经为大家准备好了 sitcoms3D 数据,大家直接将 /public/data/image/sitcoms3D_data.zip 解压到 sitcoms3D-master 项目文件夹中的 data 下即可

unzip /public/data/videos_and_music/sitcoms3D_data.zip -d /mnt/sitcoms3D/data

3、进入Jupyter notebook

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
打开demo,在Jupyter中“Run all cell”即可运行官方的例程。
在这里插入图片描述

4、调整版代码

官方 Demo 码有演示性质,直接运行有可能一些变量会受到干扰,因此我们对代码进行了一定的精简,可以根据需要用以下方式进一步进行使用。

这一模型导入的数据文件有以下七个目录,应该是不同的数据,比如默认的sitcom_location = “Friends-monica_apartment” 表示从老友记里面选取数据
在这里插入图片描述
在第2个cell中更改sitcom_location 可以改变数据。

sitcom_location = sit_locs[0] 
# sitcom_location = "Friends-monica_apartment"

下一步为是选择图像,原文中使用了romdom随机选择一个图像,我们可以加一行代码来指定自己的图像。

# choose a random image to work with
image_name = random.choice(list(nerf_image_name_to_info.keys()))
image_name = "ELR_S09E01_00007186.jpg"
print("Showing camera information for image:", image_name)
pprint(nerf_image_name_to_info[image_name])

更改后的代码如下:在不指定图像的情况下,每次run all cell 即可随机抽取图像。

# import various modules
%load_ext autoreload
%autoreload 2
import copy
import json
import os
import random
from pprint import pprint

import mediapy as media
import numpy as np
import smplx
import torch
import trimesh
from tqdm import tqdm
# some custom code
# gross import... maybe put into a python module later
import sys
sys.path.append("..")
from utils.dataloader import human_to_nerf_space, load_colmap_cameras_from_sitcom_location
from utils.render_utils import render_human
from utils.io import load_from_json

这一段定位了数据的路径并抽取出图像

sit_locs = sorted(os.listdir("../data/sparse_reconstruction_and_nerf_data"))
print(sit_locs)

sitcom_location = sit_locs[3]
print("load.....",sitcom_location)

cameras = load_from_json(f"../data/sparse_reconstruction_and_nerf_data/{sitcom_location}/cameras.json")

nerf_image_name_to_info = {}
for dict_ in cameras["frames"]:
    nerf_image_name_to_info[dict_["image_name"]] = {
        "intrinsics": np.array(dict_["intrinsics"]),
        "camtoworld": np.array(dict_["camtoworld"]),
    }
    
image_name = random.choice(list(nerf_image_name_to_info.keys()))
print("image name: ", image_name)
    
basedir = f"../data/sparse_reconstruction_and_nerf_data/{sitcom_location}"
colmap_image_name_to_info = load_colmap_cameras_from_sitcom_location(basedir)

point_cloud_transform = np.array(cameras["point_cloud_transform"])
scale_factor = np.array(cameras["scale_factor"])
# colmap_rescale = float(smpl_data["colmap_rescale"])

根据抽取的图像读取其中的人物数据

# the set of image names that we used for nerf
# these images are included in the sparse_reconstruction_and_nerf_data/ folder
nerf_image_names = set(nerf_image_name_to_info.keys())

# the set of image names that we have smpl parameters for
# this is wherever our method "calibrated multi-shot" was run
human_pairs = load_from_json(f"../data/human_pairs/{sitcom_location}.json")
image_name_to_shot_change_image_name = {}
calibrated_multishot_image_names = set()
for image_name_a, human_idx_a, image_name_b, human_idx_b in human_pairs:
    calibrated_multishot_image_names.add(image_name_a)
    calibrated_multishot_image_names.add(image_name_b)
    image_name_to_shot_change_image_name[image_name_a] = image_name_b
    image_name_to_shot_change_image_name[image_name_b] = image_name_a

image_names = nerf_image_names.intersection(calibrated_multishot_image_names)
print("Found {} images that are used for nerf and contain smpl parameters".format(len(image_names)))

# choose a random image name to work with and visualize
# image_name = random.choice(list(image_names))
# image_name = "Friends_S08E20_00001431.jpg"

# read data for the image and a human...
human_data = load_from_json(f"../data/human_data/{sitcom_location}.json")

image_human_data = human_data[image_name]
print("going to visualize image {} with {} humans".format(image_name, len(image_human_data)))

显示读取的图片

image = media.read_image(f"../data/sparse_reconstruction_and_nerf_data/{sitcom_location}/images/{image_name}")
media.show_image(image, height=200)

导入SMPL模型

model_folder = "../data/smpl_models"
model_type = "smpl"
gender = "neutral"

body_model = smplx.create(model_folder,
                     model_type=model_type,
                     gender=gender)

用于将人物数据加载到模型,提取出mesh的函数

def get_human_obj_mesh(image_name: str, human_idx: int):
    if "smpl" not in human_data[image_name][human_idx]:
        print(f"smpl values don't exist for {image_name} and human_idx {human_idx}")
        return None
    smpl_data = human_data[image_name][human_idx]["smpl"]
    print(smpl_data.keys())

    camera_translation = torch.tensor(smpl_data["camera_translation"])[None]
    betas = torch.tensor(smpl_data["betas"])[None]
    global_orient = torch.tensor(smpl_data["global_orient"])[None]
    body_pose = torch.tensor(smpl_data["body_pose"])[None]
    colmap_rescale = float(smpl_data["colmap_rescale"])

    output = body_model(
        betas=betas,
        global_orient=global_orient,
        body_pose=body_pose,
        return_verts=True)

    vertices = output.vertices + camera_translation
    pose_colmap = torch.from_numpy(colmap_image_name_to_info[image_name]["camtoworld"]).float()
    pose_colmap[:3,3] *= colmap_rescale
    # homogeneous coordinates
    vertices = torch.cat([vertices, torch.ones_like(vertices[..., 0:1])], dim=-1)
    vertices = vertices @ pose_colmap.T
    vertices = vertices[...,:3]

    out_mesh = trimesh.Trimesh(vertices[0].detach().numpy(), body_model.faces, process=False)
    human_obj_filename = "temp.obj"
    out_mesh.export(human_obj_filename);

    # specify the human to render
    obj_mesh_original = trimesh.load(human_obj_filename, process=False)
    obj_mesh = human_to_nerf_space(obj_mesh_original, point_cloud_transform, scale_factor, colmap_rescale)
    return obj_mesh

应用get_human_obj_mesh函数,获取图像的mesh数据

human_obj_meshes = []
for human_idx in range(len(image_human_data)):
    print("human_idx", human_idx)
    obj_mesh = get_human_obj_mesh(image_name, human_idx)
    if obj_mesh:
        human_obj_meshes.append(obj_mesh)

现实提取出模型的结果

def show_humans(human_obj_meshes, pose, K, image_name):
    image = media.read_image(f"../data/sparse_reconstruction_and_nerf_data/{sitcom_location}/images/{image_name}")
    color_h, depth_h, alpha_h = render_human(human_obj_meshes, pose, K)
    media.show_image(image, height=200, title="Image we use for camera pose and intrinsics")
    media.show_image(color_h, height=200, title="Image of humans rendered from this camera")
    composited = (color_h * alpha_h[...,None] + image * (1 - alpha_h[...,None])).astype("uint8")
    media.show_image(composited, height=200, title="Composited image")

pose = nerf_image_name_to_info[image_name]["camtoworld"]
K = nerf_image_name_to_info[image_name]["intrinsics"]
show_humans(human_obj_meshes, pose, K, image_name) # image_name to read the background image

模型的局限性

当然,Sitcoms 也受到训练模型的的一些局限性,在我们运行的案例中,有相对成功的结果,也有相对混乱的结果。
在这里插入图片描述
相对失败的图片可以完整地提取出三维信息,但有可能图片仅能显示出画面的一部分人,甚至会将一些物体误判为人物,Sitcom 仍存在一些鲁棒性的问题。

参考资料

论文地址:https://arxiv.org/abs/2207.14279
GitHub地址:https://github.com/ethanweber/sitcoms3D
项目主页:https://ethanweber.me/sitcoms3D/

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

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

相关文章

[ vulhub漏洞复现篇 ] solr 远程命令执行 (CVE-2019-17558)

🍬 博主介绍 👨‍🎓 博主介绍:大家好,我是 _PowerShell ,很高兴认识大家~ ✨主攻领域:【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 🎉点赞➕评论➕收藏 养成习…

优秀的内部知识库对企业的重要性

我们都知道在客户服务方面,选择正确的知识库软件的重要性。但我们经常忘记的是,我们的员工也是我们的客户。根据盖洛普公司最近的研究,世界正在经历一场员工参与危机。只有大约三分之一的美国员工在工作中具有参与感,而在全球范围…

一文读懂Docker、K8s

目标: docker原理以及在运维工作的地位和作用,运维工作进化论,docker、微服务、k8s的联系、devops和docker的关系,docker的前世今生容器、镜像和仓库、容器和虚拟化,优势和劣势,底层的核心容器除了docker还…

什么是项目管理软件,能带来哪些作用?

在这个信息化时代,企业的项目管理除了需要一位出色的项目管理者外,还需要借助项目管理软件来对项目进行全面管理。因为如今的项目需求多样化,内容也愈加丰富,传统的项目管理方式已经难以满足,所以很多项目管理软件也应…

[附源码]JAVA毕业设计小型医院药品及门诊管理(系统+LW)

[附源码]JAVA毕业设计小型医院药品及门诊管理(系统LW) 项目运行 环境项配置: Jdk1.8 Tomcat8.5 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项…

CMake中add_subdirectory的使用

CMake中的add_subdirectory命令用于将子目录添加到构建,其格式如下: add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL] [SYSTEM]) source_dir指定源CMakeLists.txt和代码文件所在的目录。如果它是相对路径,则将相对于当前目录(…

毕业设计 - java web 酒店管理系统的设计与实现【源码+论文】

文章目录前言一、项目设计1. 模块设计总体设计具体模块数据库部分设计2. 实现效果二、部分源码项目源码前言 今天学长向大家分享一个 优秀的毕业设计项目: 酒店管理系统的设计与实现 源码获取方式: https://gitee.com/sinonfin/L-javaWebSha/tree/master 一、项目设计 1. 模…

FreeRTOS任务切换过程深层解析

FreeRTOS 系统的任务切换最终都是在 PendSV 中断服务函数中完成的,uCOS 也是在 PendSV 中断中完成任务切换的。 【为什么用PendSV异常来做任务切换】 PendSV 可以像普通中断一样被 Pending(往 NVIC 的 PendSV 的 Pend 寄存器写 1)&#xff…

Spark零基础入门实战(五)使用Eclipse创建Scala项目

本节讲解在Windows中使用Scala for Eclipse IDE编写Scala程序。 安装Scala for Eclipse IDE Scala for Eclipse IDE为纯Scala和混合Scala与Java应用程序的开发提供了高级编辑功能,并且有非常好用的Scala调试器、语义突出显示、更可靠的JUnit测试查找器等。 Scala for Eclip…

重磅首发!腾讯前晚最新爆出的“JVM学习笔记”,GitHub已评“钻级”,看完我爱了!

前言 “JVM”,一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。有了JVM后,Java语言在不同平台上运行时不需要重新编译,为我们提供了极大的便利性,现在在面试当中“JVM”相关的知识是必问…

5分钟部署云计算|云原生监控平台Prometheus-尚文网络xUP楠哥

~~全文共1277字,阅读需约5分钟。 进Q群11372462,领取专属报名福利,包含云计算学习路线图代表性实战训练大厂云计算面试题资料! # Prometheus介绍 Prometheus是由Go编写的时间序列监控数据库,在目前云计算|云原生时代非常流行&am…

分析linux内核qspi驱动层次

【推荐阅读】 需要多久才能看完linux内核源码? 概述Linux内核驱动之GPIO子系统API接口 https://mp.csdn.net/mp_blog/creation/editor/127819883 一篇长文叙述Linux内核虚拟地址空间的基本概括 纯干货,linux内存管理——内存管理架构(建议收藏…

【LeetCode每日一题】——237.删除链表中的节点

文章目录一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【解题思路】七【题目提示】八【时间频度】九【代码实现】十【提交结果】一【题目类别】 链表 二【题目难度】 中等 三【题目编号】 237.删除链表中的节点 四【题目描述】 有一个单链…

[附源码]JAVA毕业设计小区失物招领网站(系统+LW)

[附源码]JAVA毕业设计小区失物招领网站(系统LW) 项目运行 环境项配置: Jdk1.8 Tomcat8.5 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术…

网红家电逐渐沉寂,家电企业如何利用APS排产调整生产?

随着生活水平的提高,近年来的消费行业逐渐呈现出消费升级、个性化、多元化趋势。在这些趋势下,一大批网红小家电产品迅速出现,以创新性的功能和设计,满足消费者新需求。 近年来,小家电领域已经成为网红爆款产品的集中地…

OpenAI ChatGPT注册步骤(超详细!!!)

最近,很火的OpenAI ChatGPT,大伙都跃跃欲试。 由于注册过程比较麻烦,我整理了一下注册步骤。 一、前期准备: 1、梯子(需要科学上网,准备墙外代理) 2、国外接码平台,推荐sms-activ…

java计算机毕业设计ssm学生课堂考勤小程序947n4(附源码、数据库)

java计算机毕业设计ssm学生课堂考勤小程序947n4(附源码、数据库) 项目运行 环境配置: Jdk1.8 Tomcat8.5 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xf…

sprites精灵图+字体图标

1、sprites精灵图 使用精灵图就是为了减少网页请求服务器发送图片的次数,把一些小图标都放到一张图片(称为精灵图)精确单位,就不会请求服务器多次了 使用精灵图核心: 精灵技术主要针对于背景图片使用,就是把多个小背景图片整合到…

Qt-数据库开发-外键使用(4)

Qt-数据库开发-使用QSqlRelationalTableModel(关系表模型)来可视化数据库中[外键] 文章目录Qt-数据库开发-使用QSqlRelationalTableModel(关系表模型)来可视化数据库中[外键]1、概述2、实现效果3、主要代码4、完整源代码更多精彩内…

Swift学习笔记笔记(八) 日期选择和表现视图组件的使用

一、实验目的: 1.掌握DatePicker组件的使用 2.掌握TableView组件的使用 3.掌握代码设置属性的方法 二、实验原理: 1.属性面板设置属性的缺点 2.DatePicker中Moder属性的设置方法 3.DatePicker中Locale属性的设置方法 4.随机数函数的原型 5. 运动检测函…