【视点合成】代码解读:生成demo视频

2023-12-15 12:12

本文主要是介绍【视点合成】代码解读:生成demo视频,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

变换工具

def render_3dphoto(src_imgs,          # 输入的源图像,维度为 [batch_size, 3, height, width]mpi_all_src,       # 输入的所有源图像的MPI,维度为 [batch_size, num_planes, 4, height, width]disparity_all_src, # 所有源图像的视差信息,维度为 [batch_size, num_planes]k_src,             # 源相机的内参矩阵,维度为 [batch_size, 3, 3]k_tgt,             # 目标相机的内参矩阵,维度为 [batch_size, 3, 3]save_path,         # 渲染视频的保存路径
):

disparity 也就是深度。

单应性变换

使用HomographySample变换。utils.mpi.homography_sampler.HomographySample


class HomographySample:"""处理与单应性变换相关的操作,它提供了生成二维网格坐标的功能,计算平面法线,以及将欧拉角转换为旋转矩阵"""def __init__(self, H_tgt, W_tgt, device=None):if device is None:self.device = torch.device("cpu")else:self.device = deviceself.Height_tgt = H_tgtself.Width_tgt = W_tgtself.meshgrid = self.grid_generation(self.Height_tgt, self.Width_tgt, self.device)  # 生成目标图像的二维网格坐标self.meshgrid = self.meshgrid.permute(2, 0, 1).contiguous()  # 3xHxW   对生成的网格进行维度重排,以匹配 3xHxW 的形状self.n = self.plane_normal_generation(self.device)  # 生成平面法线

其中方法:

def euler_to_rotation_matrix(x_angle, y_angle, z_angle, seq='xyz', degrees=False):"""将欧拉角转换为旋转矩阵Note that here we want to return a rotation matrix rot_mtx, which transform the tgt points into src frame,i.e, rot_mtx * p_tgt = p_srcTherefore we need to add negative to x/y/z_angle  由于在欧拉角中,旋转是绕坐标轴的正方向进行的,而我们希望进行的是相反方向的旋转,因此需要对 x/y/z 分量的欧拉角添加负号,以实现从目标点到源坐标系的变换。注意 **这里的欧拉角需要取反,因为我们希望得到的旋转矩阵是将目标点变换到源坐标系的旋转**  https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.transform.Rotation.html:param roll::param pitch::param yaw::return:"""r = Rotation.from_euler(seq,[-x_angle, -y_angle, -z_angle],degrees=degrees)rot_mtx = r.as_matrix().astype(np.float32)# 用于图像的单应性变换,其中 meshgrid 用于定义原始图像中每个像素的坐标,plane_normal_generation 用于定义平面法线(对于单应性变换而言通常是不必要的,但可能用于其他3D变换),# euler_to_rotation_matrix 则用于根据给定的欧拉角生成对应的旋转矩阵。这些操作都是在准备实施单应性变换或其他相关图像几何变换的数据和参数。return rot_mtx

映射前定义网格

@staticmethoddef grid_generation(H, W, device):""":生成网格坐标,用于后续的映射计算。"""# 使用 numpy 生成x和y方向的线性等分点,并通过 np.meshgrid 生成一个二维网格x = np.linspace(0, W - 1, W)y = np.linspace(0, H - 1, H)xv, yv = np.meshgrid(x, y)  # HxWxv = torch.from_numpy(xv.astype(np.float32)).to(dtype=torch.float32, device=device)yv = torch.from_numpy(yv.astype(np.float32)).to(dtype=torch.float32, device=device)# 生成一个全为1的张量,与x和y坐标一起堆叠(通道维度)成3通道的网格坐标张量ones = torch.ones_like(xv)meshgrid = torch.stack((xv, yv, ones), dim=2)  # HxWx3return meshgrid@staticmethoddef plane_normal_generation(device):"""创建一个表示垂直于xy平面(即z轴方向)的法线向量的张量"""n = torch.tensor([0, 0, 1], dtype=torch.float32, device=device)return n

使用

utils.utils.render_novel_view中:

  1. 计算深度
  2. 计算视点变换
  3. 执行变换
  4. 帧序列到视频
def render_novel_view(mpi_all_rgb_src,mpi_all_sigma_src,disparity_all_src,G_tgt_src,K_src_inv,K_tgt,homography_sampler,
):"""mpi_all_rgb_src,        # 来源视角的RGB图像集合mpi_all_sigma_src,      # 来源视角的不透明度(sigma)集合disparity_all_src,      # 来源视角的视差图集合G_tgt_src,              # 目标视角到来源视角的变换矩阵K_src_inv,              # 来源视角的相机内参矩阵的逆矩阵K_tgt,                  # 目标视角的相机内参矩阵homography_sampler,     # 用于生成和采样单应性(Homographies)的工具"""# 从源视角的视差图计算出每个平面上点的3D坐标xyz_src_BS3HW = mpi_rendering.get_src_xyz_from_plane_disparity(homography_sampler.meshgrid,disparity_all_src,K_src_inv)# 使用源视角的3D坐标和从目标视角到源视角的变换矩阵 G_tgt_src 来计算目标视角的3D坐标xyz_tgt_BS3HW = mpi_rendering.get_tgt_xyz_from_plane_disparity(xyz_src_BS3HW,G_tgt_src)# 调用 render_tgt_rgb_depth 函数,将源视角的RGB图像、不透明度、视差图和3D坐标映射到目标视角,生成合成的目标视角图像 tgt_imgs_syntgt_imgs_syn, _, _ = mpi_rendering.render_tgt_rgb_depth(homography_sampler,mpi_all_rgb_src,mpi_all_sigma_src,disparity_all_src,xyz_tgt_BS3HW,G_tgt_src,K_src_inv,K_tgt,use_alpha=False,is_bg_depth_inf=False,)return tgt_imgs_syn

生成

函数入口

def render_3dphoto(src_imgs,  # [b,3,h,w]mpi_all_src,  # [b,s,4,h,w]disparity_all_src,  # [b,s]k_src,  # [b,3,3]k_tgt,  # [b,3,3]save_path,
):"""src_imgs,          # 输入的源图像,维度为 [batch_size, 3, height, width]mpi_all_src,       # 输入的所有源图像的MPI,维度为 [batch_size, num_planes, 4, height, width]disparity_all_src, # 所有源图像的视差信息,维度为 [batch_size, num_planes]k_src,             # 源相机的内参矩阵,维度为 [batch_size, 3, 3]k_tgt,             # 目标相机的内参矩阵,维度为 [batch_size, 3, 3]save_path,         # 渲染视频的保存路径"""h, w = mpi_all_src.shape[-2:]device = mpi_all_src.devicehomography_sampler = HomographySample(h, w, device)  # 生成单应性变换k_src_inv = torch.inverse(k_src)    # 计算源相机内参矩阵的逆矩阵,用于从像素坐标到相机坐标系的转换。# preprocess the predict MPI# 将视差信息转换为3D空间坐标xyz_src_BS3HW = mpi_rendering.get_src_xyz_from_plane_disparity(     homography_sampler.meshgrid,disparity_all_src,k_src_inv,)# 分离通道mpi_all_rgb_src = mpi_all_src[:, :, 0:3, :, :]  # BxSx3xHxWmpi_all_sigma_src = mpi_all_src[:, :, 3:, :, :]  # BxSx1xHxW# 调用一个渲染函数,用于计算从MPI合成图像时使用的混合权重_, _, blend_weights, _ = mpi_rendering.render(mpi_all_rgb_src,mpi_all_sigma_src,xyz_src_BS3HW,use_alpha=False,is_bg_depth_inf=False,)# 混合权重来更新源图像的MPI RGB部分,这里使用的是alpha blending算法mpi_all_rgb_src = blend_weights * src_imgs.unsqueeze(1) + (1 - blend_weights) * mpi_all_rgb_src# render novel views# 定义通过改变相机外参来模拟相机移动的参数。同时初始化一个空列表用于存储渲染出的帧。swing_path_list = gen_swing_path()frames = []# for cam_ext in tqdm(swing_path_list):# 利用render_novel_view函数来渲染从新视角看到的图像。这个函数需要当前的MPI RGB数据、透明度数据、视差数据、目标相机的外参(这里假设cam_ext是外参# ,cam_ext.cuda()将外参移动到GPU上)、源相机的内参矩阵的逆以及目标相机的内参矩阵。homography_sampler可能用于计算在新视角下图像的单应性变换。for cam_ext in swing_path_list:frame = render_novel_view(mpi_all_rgb_src,mpi_all_sigma_src,disparity_all_src,cam_ext.cuda(), # 目标视角到来源视角的变换矩阵k_src_inv,k_tgt,          #  目标视角的相机内参矩阵homography_sampler,)# 张量转换为NumPy数组,并进行必要的排列以匹配图像的形状要求。然后它将像素值缩放到0-255范围,并转换为uint8类型frame_np = frame[0].permute(1, 2, 0).contiguous().cpu().numpy()  # [b,h,w,3]frame_np = np.clip(np.round(frame_np * 255), a_min=0, a_max=255).astype(np.uint8)frames.append(frame_np)rgb_clip = ImageSequenceClip(frames, fps=30)# moviepy库的ImageSequenceClip类将帧列表转换成视频剪辑,设置每秒帧数为30。然后调用write_videofile方法将视频保存到指定的save_path路径# 使用mpeg4编解码器,不显示详细信息,设置比特率为3000kbps。rgb_clip.write_videofile(save_path, verbose=False, codec='mpeg4', logger=None, bitrate='3000k')

获取移动路径

def gen_swing_path(num_frames=90, r_x=0.14, r_y=0.0, r_z=0.10):"""生成一个摆动路径.生成一系列的4x4变换矩阵,每个矩阵代表了不同时间点的位置num_frames 表示要生成的帧数,默认为90帧;r_x、r_y 在x、y方向上的摆动半径r_z 表示z轴方向上的摆动半径"""# 创建了一个从01等间距的一维张量 t。这个张量表示从摆动开始到结束的时间线索引t = torch.arange(num_frames) / (num_frames - 1)# 创建了一个4x4的单位矩阵(identity matrix),然后将其重复 num_frames 次,用来存储最终生成的每一帧的变换矩阵。poses = torch.eye(4).repeat(num_frames, 1, 1)# 计算了在x轴上的摆动,使用正弦函数来生成x轴的平移部分,摆动的幅度由 r_x 确定# 正余弦: 需要一个物体的运动从最大幅度开始并且回到最大幅度poses[:, 0, 3] = r_x * torch.sin(2. * math.pi * t)poses[:, 1, 3] = r_y * (torch.cos(2. * math.pi * t)  )poses[:, 2, 3] = r_z * (torch.cos(2. * math.pi * t) - 1.0)  # 减去了1,这样摆动的起始点会更低return poses.unbind()   # 张量 poses 拆分成一个元组,元组中的每个元素是一个4x4矩阵,代表了每一帧的变换矩阵

这篇关于【视点合成】代码解读:生成demo视频的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/496413

相关文章

流媒体平台/视频监控/安防视频汇聚EasyCVR播放暂停后视频画面黑屏是什么原因?

视频智能分析/视频监控/安防监控综合管理系统EasyCVR视频汇聚融合平台,是TSINGSEE青犀视频垂直深耕音视频流媒体技术、AI智能技术领域的杰出成果。该平台以其强大的视频处理、汇聚与融合能力,在构建全栈视频监控系统中展现出了独特的优势。视频监控管理系统EasyCVR平台内置了强大的视频解码、转码、压缩等技术,能够处理多种视频流格式,并以多种格式(RTMP、RTSP、HTTP-FLV、WebS

AI一键生成 PPT

AI一键生成 PPT 操作步骤 作为一名打工人,是不是经常需要制作各种PPT来分享我的生活和想法。但是,你们知道,有时候灵感来了,时间却不够用了!😩直到我发现了Kimi AI——一个能够自动生成PPT的神奇助手!🌟 什么是Kimi? 一款月之暗面科技有限公司开发的AI办公工具,帮助用户快速生成高质量的演示文稿。 无论你是职场人士、学生还是教师,Kimi都能够为你的办公文

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

pdfmake生成pdf的使用

实际项目中有时会有根据填写的表单数据或者其他格式的数据,将数据自动填充到pdf文件中根据固定模板生成pdf文件的需求 文章目录 利用pdfmake生成pdf文件1.下载安装pdfmake第三方包2.封装生成pdf文件的共用配置3.生成pdf文件的文件模板内容4.调用方法生成pdf 利用pdfmake生成pdf文件 1.下载安装pdfmake第三方包 npm i pdfma

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

poj 1287 Networking(prim or kruscal最小生成树)

题意给你点与点间距离,求最小生成树。 注意点是,两点之间可能有不同的路,输入的时候选择最小的,和之前有道最短路WA的题目类似。 prim代码: #include<stdio.h>const int MaxN = 51;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int P;int prim(){bool vis[MaxN];

poj 2349 Arctic Network uva 10369(prim or kruscal最小生成树)

题目很麻烦,因为不熟悉最小生成树的算法调试了好久。 感觉网上的题目解释都没说得很清楚,不适合新手。自己写一个。 题意:给你点的坐标,然后两点间可以有两种方式来通信:第一种是卫星通信,第二种是无线电通信。 卫星通信:任何两个有卫星频道的点间都可以直接建立连接,与点间的距离无关; 无线电通信:两个点之间的距离不能超过D,无线电收发器的功率越大,D越大,越昂贵。 计算无线电收发器D

hdu 1102 uva 10397(最小生成树prim)

hdu 1102: 题意: 给一个邻接矩阵,给一些村庄间已经修的路,问最小生成树。 解析: 把已经修的路的权值改为0,套个prim()。 注意prim 最外层循坏为n-1。 代码: #include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstri

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言