7 minute read

poses.shape: (N, 3, 5)

poses์˜ shape์€ N๊ฐœ์˜ ์ด๋ฏธ์ง€์— ๋Œ€ํ•œ pose๋“ค์˜ ๋ชจ์Œ์œผ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

bandicam 2024-09-24 19-57-38-321

  • camera extrinsics๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” rotation, translation 3x4
  • camera intrinsics๋ฅผ ๊ตฌํ•  ์ˆ˜ ์žˆ๋Š” H, W, focal length 3x1
  • ๊ฐ pose๋Š” camera extrinsics์™€ camera intrinsics์„ ๊ตฌํ•˜๋Š”๋ฐ ํ•„์š”ํ•œ parameter๋“ค์„ ํฌํ•จํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
def normalize(x):
    return x / np.linalg.norm(x)


def viewmatrix(lookdir, up, position, subtract_position=False):
  """Construct lookat view matrix."""
  vec2 = normalize((lookdir - position) if subtract_position else lookdir)
  vec0 = normalize(np.cross(up, vec2))
  vec1 = normalize(np.cross(vec2, vec0))
  m = np.stack([vec0, vec1, vec2, position], axis=1)
  return m


def poses_avg(poses):
  """New pose using average position, z-axis, and up vector of input poses."""
  position = poses[:, :3, 3].mean(0)
  z_axis = poses[:, :3, 2].mean(0)
  up = poses[:, :3, 1].mean(0)
  cam2world = viewmatrix(z_axis, up, position)
  return cam2world

poses[:, :3, 3] -> translation ๋ฒกํ„ฐ์ด๋ฏ€๋กœ viewmatrix์—์„œ position์œผ๋กœ ๋ณ€์ˆ˜๋ช…์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

bandicam 2024-09-24 19-51-59-684

poses[:, :3, 2] -> z๋ฐฉํ–ฅ ๋ฒกํ„ฐ์ด๋ฏ€๋กœ viewmatrix์—์„œ z_axis๋กœ ๋ณ€์ˆ˜๋ช…์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

bandicam 2024-09-24 19-52-03-684

poses[:, :3, 1] -> y๋ฐฉํ–ฅ ๋ฒกํ„ฐ๋Š” viewmatrix์—์„œ up์œผ๋กœ ๋ณ€์ˆ˜๋ช…์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

bandicam 2024-09-24 19-52-04-918

viewmatrix์—์„œ๋Š” ๊ฒฐ๋ก ์ ์œผ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

  • poses[:, :3, 3].mean(0)์ด๋ฏ€๋กœ ๋ชจ๋“  poses๋“ค์˜ position์˜ ํ‰๊ท ์„ translation ๋ฒกํ„ฐ๋กœ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.
  • poses[:, :3, 2].mean(0)์ด๋ฏ€๋กœ ๋ชจ๋“  poses๋“ค์˜ z_axis์˜ ํ‰๊ท ์„ z๋ฐฉํ–ฅ ๋ฒกํ„ฐ๋กœ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.
  • poses[:, :3, 1].mean(0)์ด๋ฏ€๋กœ ๋ชจ๋“  poses๋“ค์˜ up์˜ ํ‰๊ท ์„ up๋ฐฉํ–ฅ ๋ฒกํ„ฐ๋กœ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

viewmatrix(z_axis, up, position)์„ ๋ฐ›๋Š”๋ฐ, ์ผ๋ฐ˜์ ์œผ๋กœ z_axis๋Š” ์นด๋ฉ”๋ผ๊ฐ€ ๋ณด๋Š” ๋ฐฉํ–ฅ์„ ๊ฐ€๋ฆฌํ‚ค๋ฏ€๋กœ lookdir๋กœ ๋ฐ›์Šต๋‹ˆ๋‹ค.

def normalize(x):
    return x / np.linalg.norm(x)


def viewmatrix(lookdir, up, position, subtract_position=False):
  """Construct lookat view matrix."""
  vec2 = normalize((lookdir - position) if subtract_position else lookdir)
  vec0 = normalize(np.cross(up, vec2))
  vec1 = normalize(np.cross(vec2, vec0))
  m = np.stack([vec0, vec1, vec2, position], axis=1)
  return m


def poses_avg(poses):
  """New pose using average position, z-axis, and up vector of input poses."""
  position = poses[:, :3, 3].mean(0)
  z_axis = poses[:, :3, 2].mean(0)
  up = poses[:, :3, 1].mean(0)
  cam2world = viewmatrix(z_axis, up, position)
  return cam2world
  • z_axis๋Š” lookdir
  • up์€ up
  • position์€ position
  1. lookdir์„ ๋จผ์ € normalizeํ•˜๊ณ . normalizeํ•œ lookdir๊ณผ up์˜ cross product๋กœ right ๋ฒกํ„ฐ(=vec0)๋ฅผ ๊ตฌํ•ฉ๋‹ˆ๋‹ค.
  2. normalizeํ•œ right ๋ฒกํ„ฐ์™€ normalizeํ•œ lookdir๋ฒกํ„ฐ๋ฅผ cross productํ•˜์—ฌ up ๋ฒกํ„ฐ๋ฅผ ์ œ๋Œ€๋กœ ์ง๊ตํ•˜๊ฒŒ ์ •์˜ํ•ด์ค๋‹ˆ๋‹ค.
  3. ์ด๋ฅผ stackํ•˜์—ฌ x๋ฐฉํ–ฅ, y๋ฐฉํ–ฅ, z๋ฐฉํ–ฅ ๋ฒกํ„ฐ, translation์ธ position์„ ์Œ“์•„ cam2world 3x4 matrix๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

image

recenter_poses

def recenter_poses(poses: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
  """Recenter poses around the origin."""
  cam2world = poses_avg(poses)
  transform = np.linalg.inv(pad_poses(cam2world))
  poses = transform @ pad_poses(poses)
  return unpad_poses(poses), transform

def pad_poses(p):
    """Pad [..., 3, 4] pose matrices with a homogeneous bottom row [0,0,0,1]."""
    bottom = np.broadcast_to([0, 0, 0, 1.], p[..., :1, :4].shape)
    return np.concatenate([p[..., :3, :4], bottom], axis=-2)

def unpad_poses(p):
    """Remove the homogeneous bottom row from [..., 4, 4] pose matrices."""
    return p[..., :3, :4]
  • recenter_poses๋Š” ์œ„์—์„œ poses_avg๋กœ ๊ตฌํ•œ ํ•˜๋‚˜์˜ cam2world์— ์—ญ๋ณ€ํ™˜์„ ์ทจํ•˜์—ฌ world2cam์„ ๊ตฌํ•˜๊ณ 
  • ๋ชจ๋“  poses์— ๋Œ€ํ•ด ์ด world2cam ๋ณ€ํ™˜์„ matrix multiplicationํ•˜์—ฌ ๋ชจ๋“  poese๋“ค์ด world coordinate์— ๊ฐ€๊น๋„๋ก ์˜ฎ๊น๋‹ˆ๋‹ค.
  • pad_poses, unpad_poses๋Š” ๋‹จ์ˆœํžˆ 4x4 transformation์ธ homogeneous coordinate์—์„œ ์—ฐ์‚ฐํ•˜๊ณ  return ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

image

backcenter_poses

  • ์ด์ฒ˜๋Ÿผ ๋ชจ๋“  poses์— ๋Œ€ํ•œ ํ‰๊ท  pose์ธ cam2world๋ฅผ ๊ตฌํ•˜๊ณ , ์ด์˜ ์—ญ๋ณ€ํ™˜์ธ world2cam์„ ๋ชจ๋“  poses๋“ค์— ๋Œ€ํ•ด ๋งคํŠธ๋ฆญ์Šค ์—ฐ์‚ฐํ•˜์—ฌ, poses๋ฅผ ํ•œ๋ฒˆ์— ์›ํ•˜๋Š” ์œ„์น˜๋กœ ์˜ฎ๊ฒจ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์‘์šฉ์œผ๋กœ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด pose_ref์˜ cam2world๋ฅผ ๊ตฌํ•˜์—ฌ, ์ด์˜ ์—ญ๋ณ€ํ™˜์ธ world2cam์œผ๋กœ, poses๋ฅผ ๋ชจ๋‘ recenterํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
def backcenter_poses(poses, pose_ref):
    """Recenter poses around the origin."""
    cam2world = poses_avg(pose_ref)
    poses = pad_poses(cam2world) @ pad_poses(poses)
    return unpad_poses(poses)

poses_bounds.npy

  • poses_bounds.npy๋Š” (N, 17)๋กœ ์ด๋ฏธ์ง€ N ๊ฐœ์ˆ˜๋งŒํผ pose๊ฐ€ N๊ฐœ ์žˆ๊ณ , 3x5=15๊ฐœ์™€ near bound, far bound 2๊ฐœ๊ฐ€ ๋”ํ•ด์„œ 17์˜ shape์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค.
  • poses_bounds.npy๋ฅผ loadํ•˜๊ณ  near, far๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ธฐ ์œ„ํ•ด poses_arr[:, :-2]๋กœ (N,15)์˜ shape์„ ์ทจํ•˜๊ณ  reshapeํ•˜์—ฌ (N, 3, 5)์˜ poses๋กœ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
  • bounds๋Š” ๋”ฐ๋กœ poses[:, -2:]๋กœ ์ธ๋ฑ์‹ฑํ•˜์—ฌ (N, 2)๋กœ N๊ฐœ์˜ pose์— ๋Œ€ํ•ด near, far bound๋ฅผ ์ •์˜ํ•ด์ค๋‹ˆ๋‹ค.
  poses = poses_arr[:, :-2].reshape([-1, 3, 5])
  bounds = poses_arr[:, -2:]

generate_spiral_path

def generate_spiral_path(poses_arr,
                         n_frames: int = 180,
                         n_rots: int = 2,
                         zrate: float = .5) -> np.ndarray:
  """Calculates a forward facing spiral path for rendering."""
  poses = poses_arr[:, :-2].reshape([-1, 3, 5])
  bounds = poses_arr[:, -2:]
  fix_rotation = np.array([
      [0, -1, 0, 0],
      [1, 0, 0, 0],
      [0, 0, 1, 0],
      [0, 0, 0, 1],
  ], dtype=np.float32)
  poses = poses[:, :3, :4] @ fix_rotation

  scale = 1. / (bounds.min() * .75)
  poses[:, :3, 3] *= scale
  bounds *= scale
  poses, transform = recenter_poses(poses)

  close_depth, inf_depth = bounds.min() * .9, bounds.max() * 5.
  dt = .75
  focal = 1 / (((1 - dt) / close_depth + dt / inf_depth))

  # Get radii for spiral path using 90th percentile of camera positions.
  positions = poses[:, :3, 3]
  radii = np.percentile(np.abs(positions), 90, 0)
  radii = np.concatenate([radii, [1.]])

  # Generate poses for spiral path.
  render_poses = []
  cam2world = poses_avg(poses)
  up = poses[:, :3, 1].mean(0)
  for theta in np.linspace(0., 2. * np.pi * n_rots, n_frames, endpoint=False):
    t = radii * [np.cos(theta), -np.sin(theta), -np.sin(theta * zrate), 1.]
    position = cam2world @ t
    lookat = cam2world @ [0, 0, -focal, 1.]
    z_axis = position - lookat
    render_pose = np.eye(4)
    render_pose[:3] = viewmatrix(z_axis, up, position)
    render_pose = np.linalg.inv(transform) @ render_pose
    render_pose[:3, 1:3] *= -1
    render_pose[:3, 3] /= scale
    render_poses.append(np.linalg.inv(render_pose))
  render_poses = np.stack(render_poses, axis=0)
  return render_poses

transform_poses_pca

  • ์ด ์ฝ”๋“œ๋Š” ์นด๋ฉ”๋ผ์˜ ์œ„์น˜์™€ ๋ฐฉํ–ฅ์„ ํ‘œํ˜„ํ•˜๋Š” poses ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ์„ฑ๋ถ„ ๋ถ„์„(PCA, Principal Component Analysis)์„ ์‚ฌ์šฉํ•ด ์ขŒํ‘œ๊ณ„๋ฅผ XYZ ์ถ•์— ๋งž์ถฐ ๋ณ€ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.
  • poses๋Š” ๊ฐ ์นด๋ฉ”๋ผ์˜ ์›”๋“œ ์ขŒํ‘œ๊ณ„์—์„œ ์นด๋ฉ”๋ผ ์ขŒํ‘œ๊ณ„๋กœ์˜ ๋ณ€ํ™˜์„ ๋‚˜ํƒ€๋‚ด๋Š” ํ–‰๋ ฌ๋“ค๋กœ, ์ด ํ•จ์ˆ˜๋Š” poses์˜ ํ‰๊ท  ์œ„์น˜๋ฅผ ์›์ ์œผ๋กœ ๋งž์ถ˜ ๋’ค, ์ฃผ์„ฑ๋ถ„์„ ์ฐพ์•„์„œ ํšŒ์ „ ํ–‰๋ ฌ์„ ๊ณ„์‚ฐํ•˜๊ณ , ์นด๋ฉ”๋ผ์˜ ์ขŒํ‘œ๊ณ„๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค.
def transform_poses_pca(poses):
    """Transforms poses so principal components lie on XYZ axes.

  Args:
    poses: a (N, 3, 4) array containing the cameras' camera to world transforms.

  Returns:
    A tuple (poses, transform), with the transformed poses and the applied
    camera_to_world transforms.
  """
    t = poses[:, :3, 3]
    t_mean = t.mean(axis=0)
    t = t - t_mean

    eigval, eigvec = np.linalg.eig(t.T @ t)
    # Sort eigenvectors in order of largest to smallest eigenvalue.
    inds = np.argsort(eigval)[::-1]
    eigvec = eigvec[:, inds]
    rot = eigvec.T
    if np.linalg.det(rot) < 0:
        rot = np.diag(np.array([1, 1, -1])) @ rot

    transform = np.concatenate([rot, rot @ -t_mean[:, None]], -1)
    poses_recentered = unpad_poses(transform @ pad_poses(poses))
    transform = np.concatenate([transform, np.eye(4)[3:]], axis=0)

    # Flip coordinate system if z component of y-axis is negative
    if poses_recentered.mean(axis=0)[2, 1] < 0:
        poses_recentered = np.diag(np.array([1, -1, -1])) @ poses_recentered
        transform = np.diag(np.array([1, -1, -1, 1])) @ transform

    # Just make sure it's it in the [-1, 1]^3 cube
    scale_factor = 1. / np.max(np.abs(poses_recentered[:, :3, 3]))
    poses_recentered[:, :3, 3] *= scale_factor
    transform = np.diag(np.array([scale_factor] * 3 + [1])) @ transform
    return poses_recentered, transform
  1. ํ‰๊ท  ์œ„์น˜ ๋งž์ถ”๊ธฐ (t_mean): poses์˜ ์นด๋ฉ”๋ผ ์œ„์น˜๋ฅผ ํ‰๊ท  ๋‚ด์–ด ์›์ ์— ๋งž์ถฅ๋‹ˆ๋‹ค.
  2. ์ฃผ์„ฑ๋ถ„ ๊ณ„์‚ฐ (eigval, eigvec): ์นด๋ฉ”๋ผ ์œ„์น˜๋“ค์˜ ๊ณต๋ถ„์‚ฐ ํ–‰๋ ฌ์—์„œ ๊ณ ์œ ๊ฐ’ ๋ถ„ํ•ด๋ฅผ ํ•˜์—ฌ ์ฃผ์„ฑ๋ถ„ ์ถ•์„ ์ฐพ์Šต๋‹ˆ๋‹ค. ๊ณ ์œ ๊ฐ’์ด ํฐ ์ถ•์ด ๋” ์ค‘์š”ํ•œ ์ถ•์ด๋ฏ€๋กœ, ์ด๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ขŒํ‘œ๊ณ„๋ฅผ ํšŒ์ „์‹œํ‚ต๋‹ˆ๋‹ค.
  3. ์ขŒํ‘œ๊ณ„ ํšŒ์ „ (rot): ๊ฐ€์žฅ ์ค‘์š”ํ•œ ์ฃผ์„ฑ๋ถ„์ด X์ถ•์— ๋งž๋„๋ก ํ•˜๊ณ , ๊ทธ ๋‹ค์Œ ์ถ•๋“ค์ด Y์™€ Z์ถ•์— ๋งž๋„๋ก ํšŒ์ „ํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ํšŒ์ „ ํ–‰๋ ฌ์˜ ํ–‰๋ ฌ์‹์ด ์Œ์ˆ˜๋ผ๋ฉด, Z์ถ• ๋ฐฉํ–ฅ์„ ๋ฐ˜๋Œ€๋กœ ๋งž์ถฅ๋‹ˆ๋‹ค.
  4. ๋ณ€ํ™˜ ํ–‰๋ ฌ ์ ์šฉ (transform): ์›๋ž˜์˜ poses์— ๋ณ€ํ™˜ ํ–‰๋ ฌ์„ ์ ์šฉํ•˜์—ฌ ์นด๋ฉ”๋ผ์˜ ์ƒˆ๋กœ์šด ์ขŒํ‘œ๊ณ„๋กœ ๋ณ€ํ™˜๋œ poses๋ฅผ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.
  5. ์ขŒํ‘œ๊ณ„ ํ”Œ๋ฆฝ: ๋ณ€ํ™˜๋œ ์ขŒํ‘œ๊ณ„์˜ Y์ถ•์ด ์•„๋ž˜์ชฝ์„ ํ–ฅํ•˜๊ณ  ์žˆ๋‹ค๋ฉด, ์ขŒํ‘œ๊ณ„๋ฅผ ๋‹ค์‹œ ๋ฐ˜์ „์‹œํ‚ต๋‹ˆ๋‹ค.
  6. ํฌ๊ธฐ ์กฐ์ • (scale_factor): ๋ณ€ํ™˜๋œ ์ขŒํ‘œ๊ณ„๋ฅผ ํฌ๊ธฐ ์กฐ์ •ํ•˜์—ฌ [-1, 1] ๋ฒ”์œ„์— ๋งž์ถฅ๋‹ˆ๋‹ค.
    • ์ด ๊ณผ์ •์—์„œ PCA๋ฅผ ํ†ตํ•ด ์นด๋ฉ”๋ผ์˜ ์œ„์น˜๋ฅผ ๋ณด๋‹ค ์ง๊ด€์ ์œผ๋กœ ๋ณผ ์ˆ˜ ์žˆ๋Š” ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ, ์ฃผ์„ฑ๋ถ„ ์ถ•์ด XYZ ์ถ•์— ๋งž๊ฒŒ ์ขŒํ‘œ๊ณ„๋ฅผ ํšŒ์ „ํ•˜๊ณ  ์ •๋ ฌํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
    • ์ด ์ฝ”๋“œ๋Š” ์นด๋ฉ”๋ผ์˜ ๋ฐฐ์น˜๊ฐ€ ์–ด๋–ป๊ฒŒ ์ด๋ฃจ์–ด์ ธ ์žˆ๋Š”์ง€ ์‹œ๊ฐ์ ์œผ๋กœ ๋” ์ž˜ ์ดํ•ดํ•˜๊ณ ์ž ํ•  ๋•Œ ์œ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 3D ๊ณต๊ฐ„์—์„œ ์นด๋ฉ”๋ผ๋“ค์˜ ์œ„์น˜๊ฐ€ ์–ด๋–ป๊ฒŒ ๋ถ„ํฌํ•˜๋Š”์ง€ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋„์™€์ค๋‹ˆ๋‹ค.

focus_pt_fn

  • ์ด ์ฝ”๋“œ๋Š” poses๋ผ๋Š” ์นด๋ฉ”๋ผ์˜ ์œ„์น˜์™€ ๋ฐฉํ–ฅ ์ •๋ณด๋ฅผ ์ž…๋ ฅ๋ฐ›์•„, ๋ชจ๋“  ์นด๋ฉ”๋ผ์˜ ์ดˆ์  ์ถ•(focal axis)์— ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ์ ์„ ๊ณ„์‚ฐํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.
  • ์ด ํ•จ์ˆ˜๋Š” ์—ฌ๋Ÿฌ ์นด๋ฉ”๋ผ๊ฐ€ ์„œ๋กœ ๋‹ค๋ฅธ ๋ฐฉํ–ฅ์„ ๋ณด๊ณ  ์žˆ์„ ๋•Œ, ๊ฐ ์นด๋ฉ”๋ผ์˜ ์‹œ์„ (์ถ•)์— ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ๊ณตํ†ต์˜ ํ•œ ์ (์ง‘์ค‘์ , focus point)์„ ์ฐพ๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค.
def focus_pt_fn(poses):
  """Calculate nearest point to all focal axes in poses."""
  directions, origins = poses[:, :3, 2:3], poses[:, :3, 3:4]
  m = np.eye(3) - directions * np.transpose(directions, [0, 2, 1])
  mt_m = np.transpose(m, [0, 2, 1]) @ m
  focus_pt = np.linalg.inv(mt_m.mean(0)) @ (mt_m @ origins).mean(0)[:, 0]
  return focus_pt

์ž…๋ ฅ poses:

  • poses๋Š” ์—ฌ๋Ÿฌ ์นด๋ฉ”๋ผ์˜ ์œ„์น˜์™€ ๋ฐฉํ–ฅ์„ ํฌํ•จํ•˜๋Š” ํ–‰๋ ฌ์ž…๋‹ˆ๋‹ค. ๊ฐ pose๋Š” ์นด๋ฉ”๋ผ์˜ ์›”๋“œ ์ขŒํ‘œ๊ณ„์—์„œ ์นด๋ฉ”๋ผ ์ขŒํ‘œ๊ณ„๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์ •๋ณด๊ฐ€ ๋“ค์–ด ์žˆ์Šต๋‹ˆ๋‹ค.
  • poses[:, :3, 2:3]: ๊ฐ ์นด๋ฉ”๋ผ์˜ z์ถ•(์ดˆ์  ์ถ•)์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. ์ด ์ถ•์ด ์นด๋ฉ”๋ผ๊ฐ€ ๋ฐ”๋ผ๋ณด๋Š” ๋ฐฉํ–ฅ์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.
  • poses[:, :3, 3:4]: ๊ฐ ์นด๋ฉ”๋ผ์˜ ์œ„์น˜(์›์ )๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

์นด๋ฉ”๋ผ ์ถ• ๊ธฐ๋ฐ˜ ๊ณ„์‚ฐ:

  • directions: ๊ฐ ์นด๋ฉ”๋ผ์˜ z์ถ• ๋ฐฉํ–ฅ(์ฆ‰, ์ดˆ์  ๋ฐฉํ–ฅ)์„ ์ถ”์ถœํ•ฉ๋‹ˆ๋‹ค.
  • origins: ๊ฐ ์นด๋ฉ”๋ผ์˜ ์œ„์น˜(์›์ )๋ฅผ ์ถ”์ถœํ•ฉ๋‹ˆ๋‹ค.

๊ทผ์‚ฌ์ ์ธ ์นด๋ฉ”๋ผ ์ถ•๋“ค๋กœ๋ถ€ํ„ฐ ํ‰๋ฉด ๊ณ„์‚ฐ:

  • m = np.eye(3) - directions * np.transpose(directions, [0, 2, 1]): ๊ฐ ์นด๋ฉ”๋ผ์˜ ์ดˆ์  ์ถ•์„ ์ด์šฉํ•˜์—ฌ ํ‰๋ฉด์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.
  • ์—ฌ๊ธฐ์„œ np.eye(3)์€ ๋‹จ์œ„ ํ–‰๋ ฌ์ด๊ณ , ์ดˆ์  ์ถ•์„ ๊ธฐ์ค€์œผ๋กœ ์นด๋ฉ”๋ผ์˜ ์ถ•์— ์ˆ˜์งํ•œ ํ‰๋ฉด์„ ๊ณ„์‚ฐํ•˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค.

ํ‰๋ฉด์„ ์‚ฌ์šฉํ•ด ์ดˆ์ ์ (focus point) ๊ณ„์‚ฐ:

  • mt_m = np.transpose(m, [0, 2, 1]) @ m: ๊ฐ ์นด๋ฉ”๋ผ ํ‰๋ฉด์˜ ์ •๋ณด์—์„œ ๋ณ€ํ™˜๋œ ํ–‰๋ ฌ์„ ๊ตฌํ•ฉ๋‹ˆ๋‹ค.
  • focus_pt = np.linalg.inv(mt_m.mean(0)) @ (mt_m @ origins).mean(0)[:, 0]: ์ด ๊ณผ์ •์„ ํ†ตํ•ด, ๋ชจ๋“  ์นด๋ฉ”๋ผ๊ฐ€ ๊ณตํ†ต์œผ๋กœ ๋ฐ”๋ผ๋ณด๋Š” ์ , ์ฆ‰ ์ดˆ์ ์ (focus point)์„ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.
  • ๊ฐ ์นด๋ฉ”๋ผ์˜ ์ดˆ์  ์ถ•์ด ๊ฐ€๋ฆฌํ‚ค๋Š” ํ‰๋ฉด์—์„œ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ์ ์„ ์ฐพ๋Š” ๊ณผ์ •์ด๋ผ๊ณ  ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฃผ์š” ๊ฐœ๋…:

  • ์ดˆ์  ์ถ• (Focal Axis): ๊ฐ ์นด๋ฉ”๋ผ๊ฐ€ ๋ฐ”๋ผ๋ณด๋Š” ๋ฐฉํ–ฅ์ž…๋‹ˆ๋‹ค. ๊ฐ ์นด๋ฉ”๋ผ์˜ z์ถ•์„ ๊ธฐ์ค€์œผ๋กœ ํ•˜์—ฌ ์ด๋ฅผ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.
  • ํ‰๋ฉด ๊ณ„์‚ฐ: ๊ฐ ์นด๋ฉ”๋ผ์˜ z์ถ•์— ์ˆ˜์ง์ธ ํ‰๋ฉด์„ ์ •์˜ํ•˜์—ฌ, ์ด ํ‰๋ฉด๋“ค์ด ๊ต์ฐจํ•˜๋Š” ์ง€์  ๋˜๋Š” ๊ทผ์ ‘ํ•œ ์ง€์ ์„ ๊ตฌํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.
  • ์ดˆ์ ์  (Focus Point): ์—ฌ๋Ÿฌ ์นด๋ฉ”๋ผ์˜ ์‹œ์„ ์ด ๋ชจ์ด๋Š” ์ง€์  ๋˜๋Š” ๊ทธ ์ง€์ ์— ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ์ ์„ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.

์š”์•ฝ:

  • ์ด ํ•จ์ˆ˜๋Š” ์—ฌ๋Ÿฌ ์นด๋ฉ”๋ผ์˜ ๋ฐฉํ–ฅ๊ณผ ์œ„์น˜๋ฅผ ๊ณ ๋ คํ•˜์—ฌ, ๋ชจ๋“  ์นด๋ฉ”๋ผ์˜ ์‹œ์„ ์ด ๊ต์ฐจํ•˜๋Š” ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ๊ณตํ†ต์˜ ํ•œ ์ ์„ ๊ณ„์‚ฐํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.
  • ์ด ๊ณ„์‚ฐ์€ ์นด๋ฉ”๋ผ ๋ฐฐ์—ด์—์„œ ํŠน์ •ํ•œ ํ•œ ์ง€์ ์„ ๋™์‹œ์— ๋ฐ”๋ผ๋ณด๋„๋ก ์‹œ๊ฐ์  ์ค‘์‹ฌ์„ ์ฐพ๋Š” ๋ฐ ์œ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Leave a comment