BonanDing commited on
Commit
915a235
·
1 Parent(s): 17a7880

Hardcode FOV sampler constants

Browse files
datasets/video/memory_selection.py CHANGED
@@ -9,6 +9,10 @@ import torch
9
 
10
 
11
  SEGMENT_KEYS = ("anchor", "dynamic", "revisit")
 
 
 
 
12
 
13
 
14
  def cfg_get(cfg, key: str, default=None):
@@ -54,19 +58,14 @@ def _pose_directions(poses: torch.Tensor) -> torch.Tensor:
54
  return directions / torch.linalg.vector_norm(directions, dim=-1, keepdim=True).clamp_min(1e-6)
55
 
56
 
57
- def _sample_points_in_sphere(center: torch.Tensor, cfg=None) -> torch.Tensor:
58
- num_points = max(1, int(cfg_get(cfg, "fov_num_points", 10000)))
59
- radius = float(cfg_get(cfg, "fov_radius", cfg_get(cfg, "fov_spatial_radius", 30.0)))
60
- seed = int(cfg_get(cfg, "fov_sample_seed", 0))
61
  device = center.device
62
  dtype = center.dtype
63
- generator = torch.Generator(device=device)
64
- generator.manual_seed(seed)
65
 
66
- samples_r = torch.rand(num_points, device=device, dtype=dtype, generator=generator)
67
- samples_phi = torch.rand(num_points, device=device, dtype=dtype, generator=generator)
68
- samples_u = torch.rand(num_points, device=device, dtype=dtype, generator=generator)
69
- r = radius * torch.pow(samples_r, 1.0 / 3.0)
70
  phi = 2.0 * torch.pi * samples_phi
71
  theta = torch.acos(1.0 - 2.0 * samples_u)
72
 
@@ -81,14 +80,12 @@ def _sample_points_in_sphere(center: torch.Tensor, cfg=None) -> torch.Tensor:
81
  return center[None, :3] + offsets
82
 
83
 
84
- def _inside_fov_points(points: torch.Tensor, poses: torch.Tensor, cfg=None) -> torch.Tensor:
85
  points = points.to(dtype=torch.float32)
86
  poses = _as_pose_tensor(poses).to(device=points.device, dtype=torch.float32)
87
  if points.numel() == 0 or poses.numel() == 0:
88
  return torch.zeros((poses.shape[0], points.shape[0]), device=points.device, dtype=torch.bool)
89
 
90
- fov_half_h = float(cfg_get(cfg, "fov_half_h", 105.0 / 2.0))
91
- fov_half_v = float(cfg_get(cfg, "fov_half_v", 75.0 / 2.0))
92
  vectors = points[None, :, :] - poses[:, None, :3]
93
  x = vectors[..., 0]
94
  y = vectors[..., 1]
@@ -97,18 +94,18 @@ def _inside_fov_points(points: torch.Tensor, poses: torch.Tensor, cfg=None) -> t
97
  elevation = torch.rad2deg(torch.atan2(y, torch.sqrt(x * x + z * z)))
98
  yaw_diff = _wrap_degrees(azimuth - poses[:, None, 4])
99
  pitch_diff = _wrap_degrees(elevation - poses[:, None, 3])
100
- return (yaw_diff < fov_half_h) & (pitch_diff < fov_half_v)
101
 
102
 
103
- def _target_fov_points(target_poses: torch.Tensor, cfg=None) -> torch.Tensor:
104
- """Sample a WorldMem-style sphere and keep points visible in the target FOV."""
105
 
106
  target_poses = _as_pose_tensor(target_poses)
107
  if target_poses.shape[0] == 0:
108
  return torch.zeros((0, 3), device=target_poses.device, dtype=target_poses.dtype)
109
 
110
- points = _sample_points_in_sphere(target_poses[0], cfg)
111
- target_inside = _inside_fov_points(points, target_poses, cfg).any(dim=0)
112
  return points[target_inside]
113
 
114
 
@@ -122,12 +119,11 @@ def _pose_preselect(candidates: np.ndarray, poses_t: torch.Tensor, target_positi
122
 
123
  candidates_t = torch.as_tensor(candidates, device=poses_t.device, dtype=torch.long)
124
  targets_t = torch.as_tensor(target_positions, device=poses_t.device, dtype=torch.long)
125
- radius = float(cfg_get(cfg, "fov_radius", cfg_get(cfg, "fov_spatial_radius", 30.0)))
126
  candidate_poses = poses_t.index_select(0, candidates_t)
127
  target_poses = poses_t.index_select(0, targets_t)
128
  candidate_forward = _pose_directions(candidate_poses)
129
  target_forward = _pose_directions(target_poses)
130
- translation_norm = torch.linalg.vector_norm(candidate_poses[:, None, :3] - target_poses[None, :, :3], dim=-1) / max(radius, 1e-6)
131
  dot = (candidate_forward[:, None, :] * target_forward[None, :, :]).sum(dim=-1).clamp(-1.0, 1.0)
132
  angular = torch.acos(dot) / torch.pi
133
  pose_distance = (translation_norm + angular).min(dim=1).values
@@ -138,7 +134,7 @@ def _pose_preselect(candidates: np.ndarray, poses_t: torch.Tensor, target_positi
138
 
139
  def _candidate_fov_masks(poses_t: torch.Tensor, candidates: np.ndarray, target_positions: np.ndarray, cfg=None) -> Tuple[torch.Tensor, torch.Tensor]:
140
  targets_t = torch.as_tensor(target_positions, device=poses_t.device, dtype=torch.long)
141
- points = _target_fov_points(poses_t.index_select(0, targets_t), cfg)
142
  if len(candidates) == 0 or points.shape[0] == 0:
143
  return (
144
  torch.zeros((len(candidates), 0), device=poses_t.device, dtype=torch.bool),
@@ -152,7 +148,7 @@ def _candidate_fov_masks(poses_t: torch.Tensor, candidates: np.ndarray, target_p
152
  score_parts = []
153
  for start in range(0, len(candidates), chunk_size):
154
  chunk = candidates_t[start : start + chunk_size]
155
- inside = _inside_fov_points(points, poses_t.index_select(0, chunk), cfg)
156
  inside_parts.append(inside)
157
  score_parts.append(inside.float().mean(dim=1))
158
  return torch.cat(inside_parts, dim=0), torch.cat(score_parts, dim=0)
@@ -223,8 +219,8 @@ def _select_by_pose_similarity(
223
  candidate_poses = poses_t.index_select(0, candidates_t)
224
  target_poses = poses_t.index_select(0, targets_t)
225
 
226
- # Training follows WorldMem's cheap pose-similarity sampling, not the 10k-point FOV selector.
227
- spatial_scale = max(float(cfg_get(cfg, "pose_similarity_radius", cfg_get(cfg, "fov_radius", 30.0))), 1e-6)
228
  angle_scale = max(float(cfg_get(cfg, "pose_similarity_angle_scale", 180.0)), 1e-6)
229
  spatial_delta = torch.linalg.vector_norm(candidate_poses[:, None, :3] - target_poses[None, :, :3], dim=-1)
230
  angle_delta = _wrap_degrees(candidate_poses[:, None, 3:] - target_poses[None, :, 3:]).mean(dim=-1)
 
9
 
10
 
11
  SEGMENT_KEYS = ("anchor", "dynamic", "revisit")
12
+ _FOV_NUM_POINTS = 10000
13
+ _FOV_RADIUS = 30.0
14
+ _FOV_HALF_H = 105.0 / 2.0
15
+ _FOV_HALF_V = 75.0 / 2.0
16
 
17
 
18
  def cfg_get(cfg, key: str, default=None):
 
58
  return directions / torch.linalg.vector_norm(directions, dim=-1, keepdim=True).clamp_min(1e-6)
59
 
60
 
61
+ def _sample_points_in_sphere(center: torch.Tensor) -> torch.Tensor:
 
 
 
62
  device = center.device
63
  dtype = center.dtype
 
 
64
 
65
+ samples_r = torch.rand(_FOV_NUM_POINTS, device=device, dtype=dtype)
66
+ samples_phi = torch.rand(_FOV_NUM_POINTS, device=device, dtype=dtype)
67
+ samples_u = torch.rand(_FOV_NUM_POINTS, device=device, dtype=dtype)
68
+ r = _FOV_RADIUS * torch.pow(samples_r, 1.0 / 3.0)
69
  phi = 2.0 * torch.pi * samples_phi
70
  theta = torch.acos(1.0 - 2.0 * samples_u)
71
 
 
80
  return center[None, :3] + offsets
81
 
82
 
83
+ def _inside_fov_points(points: torch.Tensor, poses: torch.Tensor) -> torch.Tensor:
84
  points = points.to(dtype=torch.float32)
85
  poses = _as_pose_tensor(poses).to(device=points.device, dtype=torch.float32)
86
  if points.numel() == 0 or poses.numel() == 0:
87
  return torch.zeros((poses.shape[0], points.shape[0]), device=points.device, dtype=torch.bool)
88
 
 
 
89
  vectors = points[None, :, :] - poses[:, None, :3]
90
  x = vectors[..., 0]
91
  y = vectors[..., 1]
 
94
  elevation = torch.rad2deg(torch.atan2(y, torch.sqrt(x * x + z * z)))
95
  yaw_diff = _wrap_degrees(azimuth - poses[:, None, 4])
96
  pitch_diff = _wrap_degrees(elevation - poses[:, None, 3])
97
+ return (yaw_diff < _FOV_HALF_H) & (pitch_diff < _FOV_HALF_V)
98
 
99
 
100
+ def _target_fov_points(target_poses: torch.Tensor) -> torch.Tensor:
101
+ """Sample a fixed-radius sphere and keep points visible in the target FOV."""
102
 
103
  target_poses = _as_pose_tensor(target_poses)
104
  if target_poses.shape[0] == 0:
105
  return torch.zeros((0, 3), device=target_poses.device, dtype=target_poses.dtype)
106
 
107
+ points = _sample_points_in_sphere(target_poses[0])
108
+ target_inside = _inside_fov_points(points, target_poses).any(dim=0)
109
  return points[target_inside]
110
 
111
 
 
119
 
120
  candidates_t = torch.as_tensor(candidates, device=poses_t.device, dtype=torch.long)
121
  targets_t = torch.as_tensor(target_positions, device=poses_t.device, dtype=torch.long)
 
122
  candidate_poses = poses_t.index_select(0, candidates_t)
123
  target_poses = poses_t.index_select(0, targets_t)
124
  candidate_forward = _pose_directions(candidate_poses)
125
  target_forward = _pose_directions(target_poses)
126
+ translation_norm = torch.linalg.vector_norm(candidate_poses[:, None, :3] - target_poses[None, :, :3], dim=-1) / _FOV_RADIUS
127
  dot = (candidate_forward[:, None, :] * target_forward[None, :, :]).sum(dim=-1).clamp(-1.0, 1.0)
128
  angular = torch.acos(dot) / torch.pi
129
  pose_distance = (translation_norm + angular).min(dim=1).values
 
134
 
135
  def _candidate_fov_masks(poses_t: torch.Tensor, candidates: np.ndarray, target_positions: np.ndarray, cfg=None) -> Tuple[torch.Tensor, torch.Tensor]:
136
  targets_t = torch.as_tensor(target_positions, device=poses_t.device, dtype=torch.long)
137
+ points = _target_fov_points(poses_t.index_select(0, targets_t))
138
  if len(candidates) == 0 or points.shape[0] == 0:
139
  return (
140
  torch.zeros((len(candidates), 0), device=poses_t.device, dtype=torch.bool),
 
148
  score_parts = []
149
  for start in range(0, len(candidates), chunk_size):
150
  chunk = candidates_t[start : start + chunk_size]
151
+ inside = _inside_fov_points(points, poses_t.index_select(0, chunk))
152
  inside_parts.append(inside)
153
  score_parts.append(inside.float().mean(dim=1))
154
  return torch.cat(inside_parts, dim=0), torch.cat(score_parts, dim=0)
 
219
  candidate_poses = poses_t.index_select(0, candidates_t)
220
  target_poses = poses_t.index_select(0, targets_t)
221
 
222
+ # Training uses cheap pose-similarity sampling, not the 10k-point FOV selector.
223
+ spatial_scale = max(float(cfg_get(cfg, "pose_similarity_radius", _FOV_RADIUS)), 1e-6)
224
  angle_scale = max(float(cfg_get(cfg, "pose_similarity_angle_scale", 180.0)), 1e-6)
225
  spatial_delta = torch.linalg.vector_norm(candidate_poses[:, None, :3] - target_poses[None, :, :3], dim=-1)
226
  angle_delta = _wrap_degrees(candidate_poses[:, None, 3:] - target_poses[None, :, 3:]).mean(dim=-1)
tests/test_dememwm_latent_dataset.py CHANGED
@@ -26,8 +26,6 @@ def _selection_cfg(**overrides):
26
  "local_context_exclusion_frames": 2,
27
  "plucker_moment_radius": 30.0,
28
  "anchor_diverse_selection": False,
29
- "fov_num_points": 512,
30
- "fov_sample_seed": 0,
31
  "pose_preselect_topk": 64,
32
  "candidate_chunk_size": 64,
33
  }
 
26
  "local_context_exclusion_frames": 2,
27
  "plucker_moment_radius": 30.0,
28
  "anchor_diverse_selection": False,
 
 
29
  "pose_preselect_topk": 64,
30
  "candidate_chunk_size": 64,
31
  }