If you have great ideas,
Let's talk!

blog

LRM 代码精读 1-[Datasets]

3d相关export

截屏2024-11-15 下午2.16.27.png

base.py

定义BaseDataset Class(torch.utils.data.Dataset, ABC)

值得注意的 1. ABC作为parent class建立BaseDataset

分别有@abstractmethod 和 @staticmethod

abstractmethod 相当于java 中interface

只需要pass 而后subclass中具体根据用途定义

但是ABC class更强大在于可以同时有staticmethod 是实实在在的implemented method

@abstractmethod
def inner_get_item(self, idx):
    pass

def __getitem__(self, idx):
    try:
        return self.inner_get_item(idx)
    except Exception as e:
        print(f"[DEBUG-DATASET] Error when loading {self.uids[idx]}")
        # return self.__getitem__(idx+1)
        raise e

这里用了一个组合 使用内置indexing 会直接调用该subclass 的inner_get_item

使得每个sub class只需要重新写inner_get_item

  1. Static method部分

self.uids = json.load() 为dataset长度

_load_rgba_image converts Image.open()→np.array()→torch.form_numpy

rgba转换成rgb

rgba[:, :3, :, :] * rgba[:, 3:4, :, :]

rgb channel分别与 a(alpha 代表opacity透明度) channel相乘

之后**bg_color * (1 - rgba[:, 3:, :, :])** Computes the background color contribution where alpha is not fully opaque 如果本身全透明 (a channel为0) 则back_ground color 全部显现

若全不透明 (a channel为1) 则back_ground color 完全看不见

background与image作concat

还有一个查找dir的_locate_datadir

cam_utils.py

An extrinsic matrix represents the pose (position and orientation) of a camera or an object in a 3D space relative to a world coordinate system. It typically combines a rotation(缩放,旋转,shear剪切,reflect轴对称…) matrix and a translation vector(平移) to transform points from the world coordinate system to the camera coordinate system

总结一下: R是(3,3) T是(3,) RT是(3,4) Extrinsic Matrix是(4,4)

image.png

image.png

def compose_extrinsic_R_T(R: torch.Tensor, T: torch.Tensor):
    """
    Compose the standard form extrinsic matrix from R and T.
    Batched I/O.
    """
    # (B,3,3) + (B,3,1) 末端加上translation matrix -> (B,3,4)
    RT = torch.cat((R, T.unsqueeze(-1)), dim=-1)
    return compose_extrinsic_RT(RT)
def compose_extrinsic_RT(RT: torch.Tensor):
    """
    Compose the standard form extrinsic matrix from RT.
    Batched I/O.
    """
    
    #右下角默认为1 -> (B,4,4) 这里先建最后一个row 然后repeat使前面dim对其 
    #batch dim后的dim 通过broadcast可以相加
    return torch.cat([
        RT,
        torch.tensor([[[0, 0, 0, 1]]], dtype=RT.dtype, device=RT.device).repeat(RT.shape[0], 1, 1)
        ], dim=1)

后面两个decompose很简单就不贴了

normalize camera extrinsic matrices (poses) by applying a transformation that centers the cameras based on their distance to a pivotal point (often the center of the scene or object of interest)


def camera_normalization_objaverse(normed_dist_to_center, poses: torch.Tensor, ret_transform: bool = False):
    
    assert normed_dist_to_center is not None
    pivotal_pose = compose_extrinsic_RT(poses[:1])
    #得到translation vector的normailized 值 这里取第一个pose做整个Dataset的reference
    #因此b=1 用item取唯一值
    dist_to_center = pivotal_pose[:, :3, 3].norm(dim=-1, keepdim=True).item() \
        if normed_dist_to_center == 'auto' else normed_dist_to_center

    # compute camera norm (new version)
    canonical_camera_extrinsics = torch.tensor([[
        [1, 0, 0, 0],
        [0, 0, -1, -dist_to_center],
        [0, 1, 0, 0],
        [0, 0, 0, 1],
    ]], dtype=torch.float32)
    pivotal_pose_inv = torch.inverse(pivotal_pose)
    camera_norm_matrix = torch.bmm(canonical_camera_extrinsics, pivotal_pose_inv)

    # normalize all views
    poses = compose_extrinsic_RT(poses)
    poses = torch.bmm(camera_norm_matrix.repeat(poses.shape[0], 1, 1), poses)
    poses = decompose_extrinsic_RT(poses)

    if ret_transform:
        return poses, camera_norm_matrix.squeeze(dim=0)
    return poses

这里了解了另外一种一般calibration方法

Intrinsics * Extrinsics 构建calibration矩阵

将world point映射为image point 3d→2d 点

https://www.youtube.com/watch?v=qByYk6JggQU&ab_channel=FirstPrinciplesofComputerVision

https://www.youtube.com/watch?v=H5qbRTikxI4&t=704s&ab_channel=KevinWood%7CRobotics%26AI

截屏2024-11-14 下午2.38.00.png

这里是将所有所有poses建立在canonical frame之上

camera alignment

截屏2024-11-14 下午3.20.24.png

Inverse matrix reverses linear transformation

因为有一段时间没有接触线性代数了 这里写的很慢 比较重复

可以想象从pos1 开始 相机会一直变换角度

我们相当于把每个pose除以最开始的pos(做reverse transformation)

得到转变量 之后再transform到canonical frame上

这样pos0因为 自身就是起点 转变量就是I 所以最终pose就是frame量本身

linear transformation → 看align上去的值

reverse transformation → 看mis align的值 转变量

截屏2024-11-14 下午3.30.26.png

Normalize 一下Intrinsics 内参

def get_normalized_camera_intrinsics(intrinsics: torch.Tensor):
    """
    intrinsics: (N, 3, 2), [[fx, fy], [cx, cy], [width, height]]
    Return batched fx, fy, cx, cy
    """
    fx, fy = intrinsics[:, 0, 0], intrinsics[:, 0, 1]
    cx, cy = intrinsics[:, 1, 0], intrinsics[:, 1, 1]
    width, height = intrinsics[:, 2, 0], intrinsics[:, 2, 1]
    fx, fy = fx / width, fy / height
    cx, cy = cx / width, cy / height
    return fx, fy, cx, cy

截屏2024-11-14 下午4.00.14.png

def build_camera_principle(RT: torch.Tensor, intrinsics: torch.Tensor):
    """
    RT: (N, 3, 4)
    intrinsics: (N, 3, 2), [[fx, fy], [cx, cy], [width, height]]
    """
    fx, fy, cx, cy = get_normalized_camera_intrinsics(intrinsics)
    return torch.cat([
        RT.reshape(-1, 12),
        fx.unsqueeze(-1), fy.unsqueeze(-1), cx.unsqueeze(-1), cy.unsqueeze(-1),
    ], dim=-1)

这里的broadcast 将(b,12) 与 4个 (1,1) concat在一起

[(b,12),(1,1)…] 第一个dim broadcast到dim上

因为camera不变 所有RT poses concat相同量

看错了 从上一步来的fx, fy 都是(b,)

这里单纯前面batch dim一致 在dim=-1 多加4个数

torch.cat不能broadcast 只有加减乘除可以

截屏2024-11-14 下午4.25.27.png

截屏2024-11-14 下午4.28.39.png

截屏2024-11-14 下午4.29.36.png

有点跑题了 试了试concat和repeat的机制 回到下一个function

def build_camera_standard(RT: torch.Tensor, intrinsics: torch.Tensor):
    """
    RT: (N, 3, 4)
    intrinsics: (N, 3, 2), [[fx, fy], [cx, cy], [width, height]]
    """
    E = compose_extrinsic_RT(RT)
    fx, fy, cx, cy = get_normalized_camera_intrinsics(intrinsics)
    I = torch.stack([
        torch.stack([fx, torch.zeros_like(fx), cx], dim=-1),
        torch.stack([torch.zeros_like(fy), fy, cy], dim=-1),
        torch.tensor([[0, 0, 1]], dtype=torch.float32, device=RT.device).repeat(RT.shape[0], 1),
    ], dim=1)
    return torch.cat([
        E.reshape(-1, 16),
        I.reshape(-1, 9),
    ], dim=-1)

截屏2024-11-14 下午4.39.09.png

像之前提到的构建完整Intrinsic matrix(I)

和之前function差不多 只是现在包含更规范 dim

构建完整matrix用stack更合适 如果只是一个dim上合并 concat适合

def center_looking_at_camera_pose(
    camera_position: torch.Tensor, look_at: torch.Tensor = None, up_world: torch.Tensor = None,
    device: torch.device = torch.device('cpu'),
    ):
    """
    camera_position: (M, 3)
    look_at: (3)
    up_world: (3)
    return: (M, 3, 4)
    """
    # by default, looking at the origin and world up is pos-z
    if look_at is None:
        look_at = torch.tensor([0, 0, 0], dtype=torch.float32, device=device)
    if up_world is None:
        up_world = torch.tensor([0, 0, 1], dtype=torch.float32, device=device)
    look_at = look_at.unsqueeze(0).repeat(camera_position.shape[0], 1)
    up_world = up_world.unsqueeze(0).repeat(camera_position.shape[0], 1)

    z_axis = camera_position - look_at
    z_axis = z_axis / z_axis.norm(dim=-1, keepdim=True)
    x_axis = torch.cross(up_world, z_axis)
    x_axis = x_axis / x_axis.norm(dim=-1, keepdim=True)
    y_axis = torch.cross(z_axis, x_axis)
    y_axis = y_axis / y_axis.norm(dim=-1, keepdim=True)
    extrinsics = torch.stack([x_axis, y_axis, z_axis, camera_position], dim=-1)
    return extrinsics

定义look_at 和 up_world(0,0,1)(default z axis up)

z_axis = camera_position - look_at

normalize(individually through layer norm)

def surrounding_views_linspace(n_views: int, radius: float = 2.0, height: float = 0.8, device: torch.device = torch.device('cpu')):
    """
    n_views: number of surrounding views
    radius: camera dist to center
    height: height of the camera
    return: (M, 3, 4)
    """
    assert n_views > 0
    assert radius > 0

    theta = torch.linspace(-torch.pi / 2, 3 * torch.pi / 2, n_views, device=device)
    projected_radius = math.sqrt(radius ** 2 - height ** 2)
    x = torch.cos(theta) * projected_radius
    y = torch.sin(theta) * projected_radius
    z = torch.full((n_views,), height, device=device)

    camera_positions = torch.stack([x, y, z], dim=1)
    extrinsics = center_looking_at_camera_pose(camera_positions, device=device)

    return extrinsics