de99 commited on
Commit
d090a55
·
verified ·
1 Parent(s): 1ec5627

Upload datasets_v6.py

Browse files
Files changed (1) hide show
  1. datasets_v6.py +415 -0
datasets_v6.py ADDED
@@ -0,0 +1,415 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # All rights reserved.
3
+
4
+ # This source code is licensed under the license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+ # --------------------------------------------------------
7
+ # References:
8
+ # NoMaD, GNM, ViNT: https://github.com/robodhruv/visualnav-transformer
9
+ # --------------------------------------------------------
10
+ # Inherited from dataset v4, seq2seq general version with context num control
11
+
12
+ # ===================== 可配置:用于重投影的历史帧数 =====================
13
+ # 仅影响“重投影”所用的历史帧子集(取最后 num_cond_pro 帧);
14
+ # 不改变 obs_image 的长度(仍为 context_size 帧),以保持下游兼容。
15
+ num_cond_pro = 4
16
+ # =====================================================================
17
+
18
+ import cv2
19
+ import numpy as np
20
+ import torch
21
+ import os
22
+ from PIL import Image
23
+ from typing import Tuple
24
+ import yaml
25
+ import pickle
26
+ import tqdm
27
+ from torch.utils.data import Dataset
28
+ from misc import angle_difference, get_data_path, get_delta_np, normalize_data, to_local_coords
29
+ from project_functions import reproject_depth_to_other_pose_seq2seq, project_to_2d_image_seq2seq, resize_image_half
30
+
31
+ class BaseDataset(Dataset):
32
+ def __init__(
33
+ self,
34
+ data_folder: str,
35
+ data_split_folder: str,
36
+ dataset_name: str,
37
+ image_size: Tuple[int, int],
38
+ min_dist_cat: int,
39
+ max_dist_cat: int,
40
+ len_traj_pred: int,
41
+ traj_stride: int,
42
+ context_size: int,
43
+ transform: object,
44
+ traj_names: str,
45
+ normalize: bool = True,
46
+ predefined_index: list = None,
47
+ goals_per_obs: int = 1,
48
+ ):
49
+ self.data_folder = data_folder
50
+ self.data_split_folder = data_split_folder
51
+ self.dataset_name = dataset_name
52
+ self.goals_per_obs = goals_per_obs
53
+
54
+ traj_names_file = os.path.join(data_split_folder, traj_names)
55
+ with open(traj_names_file, "r") as f:
56
+ file_lines = f.read()
57
+ self.traj_names = file_lines.split("\n")
58
+ if "" in self.traj_names:
59
+ self.traj_names.remove("")
60
+
61
+ self.image_size = image_size
62
+ self.distance_categories = list(range(min_dist_cat, max_dist_cat + 1))
63
+ self.min_dist_cat = self.distance_categories[0]
64
+ self.max_dist_cat = self.distance_categories[-1]
65
+ self.len_traj_pred = len_traj_pred
66
+ self.traj_stride = traj_stride
67
+
68
+ self.context_size = context_size
69
+ self.normalize = normalize
70
+
71
+ # load data/config
72
+ with open("config/data_config.yaml", "r") as f:
73
+ all_data_config = yaml.safe_load(f)
74
+
75
+ dataset_names = list(all_data_config.keys())
76
+ dataset_names.sort()
77
+ # use this index to retrieve the dataset name from the data_config.yaml
78
+ self.data_config = all_data_config[self.dataset_name]
79
+ self.transform = transform
80
+
81
+ # ======= 仅影响重投影用的历史帧子集:num_cond_pro(取最后 num_cond_pro 帧) =======
82
+ # 这里做一次安全夹取,保证 1 <= self.num_cond_pro <= context_size
83
+ try:
84
+ _n = int(num_cond_pro)
85
+ except Exception:
86
+ _n = context_size
87
+ if _n < 1:
88
+ _n = 1
89
+ if _n > context_size:
90
+ _n = context_size
91
+ self.num_cond_pro = _n
92
+ print(self.num_cond_pro)
93
+ # ========================================================================
94
+
95
+ self._load_index(predefined_index)
96
+ self.ACTION_STATS = {}
97
+ for key in all_data_config['action_stats']:
98
+ self.ACTION_STATS[key] = np.expand_dims(all_data_config['action_stats'][key], axis=0)
99
+
100
+ def _load_index(self, predefined_index) -> None:
101
+ """
102
+ Generates a list of tuples of (obs_traj_name, goal_traj_name, obs_time, goal_time) for each observation in the dataset
103
+ """
104
+ if predefined_index:
105
+ print(f"****** Using a predefined evaluation index... {predefined_index}******")
106
+ with open(predefined_index, "rb") as f:
107
+ self.index_to_data = pickle.load(f)
108
+ return
109
+ else:
110
+ print("****** Evaluating from NON PREDEFINED index... ******")
111
+ index_to_data_path = os.path.join(
112
+ self.data_split_folder,
113
+ f"dataset_dist_{self.min_dist_cat}_to_{self.max_dist_cat}_n{self.context_size}_len_traj_pred_{self.len_traj_pred}.pkl",
114
+ )
115
+
116
+ self.index_to_data, self.goals_index = self._build_index()
117
+ with open(index_to_data_path, "wb") as f:
118
+ pickle.dump((self.index_to_data, self.goals_index), f)
119
+
120
+ def _build_index(self, use_tqdm: bool = False):
121
+ """
122
+ Build an index consisting of tuples (trajectory name, time, max goal distance)
123
+ """
124
+ samples_index = []
125
+ goals_index = []
126
+
127
+ for traj_name in tqdm.tqdm(self.traj_names, disable=not use_tqdm, dynamic_ncols=True):
128
+ traj_data = self._get_trajectory(traj_name)
129
+ traj_len = len(traj_data["position"])
130
+ for goal_time in range(0, traj_len):
131
+ goals_index.append((traj_name, goal_time))
132
+
133
+ begin_time = self.context_size - 1
134
+ end_time = traj_len - self.len_traj_pred
135
+ for curr_time in range(begin_time, end_time, self.traj_stride):
136
+ max_goal_distance = min(self.max_dist_cat, traj_len - curr_time - 1)
137
+ min_goal_distance = max(self.min_dist_cat, -curr_time)
138
+ samples_index.append((traj_name, curr_time, min_goal_distance, max_goal_distance))
139
+
140
+ return samples_index, goals_index
141
+
142
+ def _get_trajectory(self, trajectory_name):
143
+ with open(os.path.join(self.data_folder, trajectory_name, "traj_data.pkl"), "rb") as f:
144
+ traj_data = pickle.load(f)
145
+ for k,v in traj_data.items():
146
+ traj_data[k] = v.astype('float')
147
+ return traj_data
148
+
149
+ def __len__(self) -> int:
150
+ return len(self.index_to_data)
151
+
152
+ # ============ seq2seq ============
153
+ def _compute_projected_images(self, traj_data, context_times, rgb_seq, goal_times_np):
154
+ """
155
+ 使用多帧历史 (context_times) 的 depth/rgb/pose; 重投影到多个目标位姿 (goal_times_np)。
156
+ 返回: np.ndarray, 形状 (B, H, W, 3) ; B = len(goal_times_np)
157
+ """
158
+ K = traj_data["K"] # (3,3)
159
+ depth_seq = traj_data["depth"][context_times] # (T', H, W)
160
+ poses_src_seq = traj_data["pose"][context_times] # (T', 4, 4)
161
+ H, W = depth_seq.shape[-2:]
162
+ poses_dst_seq = traj_data["pose"][goal_times_np] # (B, 4, 4)
163
+
164
+ # 先用 seq2seq 得到每个目标位姿的点云/颜色
165
+ points_3d_all, colors_all = reproject_depth_to_other_pose_seq2seq(
166
+ K=K,
167
+ depth_maps=depth_seq, # (T',H,W)
168
+ rgb_imgs=rgb_seq, # (T',H,W,3)
169
+ poses_src=poses_src_seq, # (T',4,4)
170
+ poses_dst=poses_dst_seq # (B,4,4)
171
+ )
172
+ # 再做 z-buffer 投成图像
173
+ images = project_to_2d_image_seq2seq(
174
+ K=K,
175
+ points_3d=points_3d_all, # List[(Ni,3)], 长度 B
176
+ colors=colors_all, # List[(Ni,3)], 长度 B
177
+ image_size=(H, W)
178
+ ) # (B, H, W, 3)
179
+ return images
180
+ # ============================================================
181
+
182
+ def _compute_actions(self, traj_data, curr_time, goal_time):
183
+ start_index = curr_time
184
+ end_index = curr_time + self.len_traj_pred + 1
185
+ yaw = traj_data["yaw"][start_index:end_index]
186
+ positions = traj_data["point"][start_index:end_index]
187
+ goal_pos = traj_data["point"][goal_time]
188
+ goal_yaw = traj_data["yaw"][goal_time]
189
+
190
+ if len(yaw.shape) == 2:
191
+ yaw = yaw.squeeze(1)
192
+
193
+ if yaw.shape != (self.len_traj_pred + 1,):
194
+ raise ValueError("is used?")
195
+
196
+ waypoints_pos = to_local_coords(positions, positions[0], yaw[0])
197
+ waypoints_yaw = angle_difference(yaw[0], yaw)
198
+ actions = np.concatenate([waypoints_pos, waypoints_yaw.reshape(-1, 1)], axis=-1)
199
+ actions = actions[1:]
200
+
201
+ goal_pos = to_local_coords(goal_pos, positions[0], yaw[0])
202
+ goal_yaw = angle_difference(yaw[0], goal_yaw)
203
+
204
+ if self.normalize:
205
+ actions[:, :3] /= self.data_config["metric_waypoint_spacing"]
206
+ goal_pos[:, :3] /= self.data_config["metric_waypoint_spacing"]
207
+
208
+ goal_pos = np.concatenate([goal_pos, goal_yaw.reshape(-1, 1)], axis=-1)
209
+
210
+ return actions, goal_pos
211
+
212
+ class TrainingDataset(BaseDataset):
213
+ def __init__(
214
+ self,
215
+ data_folder: str,
216
+ data_split_folder: str,
217
+ dataset_name: str,
218
+ image_size: Tuple[int, int],
219
+ min_dist_cat: int,
220
+ max_dist_cat: int,
221
+ len_traj_pred: int,
222
+ traj_stride: int,
223
+ context_size: int,
224
+ transform: object,
225
+ traj_names: str = 'traj_names.txt',
226
+ normalize: bool = True,
227
+ predefined_index: list = None,
228
+ goals_per_obs: int = 1,
229
+ ):
230
+ super().__init__(data_folder, data_split_folder, dataset_name, image_size, min_dist_cat, max_dist_cat,
231
+ len_traj_pred, traj_stride, context_size, transform, traj_names, normalize, predefined_index, goals_per_obs)
232
+
233
+ def __getitem__(self, i: int) -> Tuple[torch.Tensor]:
234
+ try:
235
+ f_curr, curr_time, min_goal_dist, max_goal_dist = self.index_to_data[i]
236
+ goal_offset = np.random.randint(min_goal_dist, max_goal_dist + 1, size=(self.goals_per_obs))
237
+ goal_time = (curr_time + goal_offset).astype('int') # (B,)
238
+ rel_time = (goal_offset).astype('float')/(128.) # TODO: tune this const
239
+
240
+ # 历史帧(观测输入仍使用完整 context_size 帧)
241
+ context_times = list(range(curr_time - self.context_size + 1, curr_time + 1))
242
+ true_context = [(f_curr, t) for t in context_times]
243
+ goal_context = [(f_curr, t) for t in goal_time]
244
+ context = true_context + goal_context
245
+
246
+ obs_image = torch.stack([self.transform(Image.open(get_data_path(self.data_folder, f, t))) for f, t in context])
247
+
248
+ # ===== 仅用于重投影的历史帧子集:取最后 self.num_cond_pro 帧 =====
249
+ cond_times = context_times[-self.num_cond_pro:]
250
+ cond_context = [(f_curr, t) for t in cond_times]
251
+ rgb_imgs = [cv2.imread(get_data_path(self.data_folder, f_img, t_img)) for f_img, t_img in cond_context]
252
+ rgb_imgs = [cv2.cvtColor(rgb_img, cv2.COLOR_BGR2RGB) for rgb_img in rgb_imgs]
253
+ rgb_imgs = np.stack(rgb_imgs, axis=0)
254
+ # ==========================================================
255
+
256
+ # Load other trajectory data
257
+ curr_traj_data = self._get_trajectory(f_curr)
258
+
259
+ # 计算动作/目标
260
+ _, goal_pos = self._compute_actions(curr_traj_data, curr_time, goal_time)
261
+ goal_pos[:, :3] = normalize_data(goal_pos[:, :3], self.ACTION_STATS)
262
+
263
+ # 使用“后 num_cond_pro 帧”→ 多目标帧重投影
264
+ projected_images = self._compute_projected_images(curr_traj_data, cond_times, rgb_imgs, goal_time) # (B,H,W,3)
265
+
266
+ # 转成张量
267
+ projected_tensor_list = [self.transform(Image.fromarray(img)) for img in projected_images]
268
+ projected_tensor = torch.stack(projected_tensor_list, dim=0)
269
+
270
+ return (
271
+ torch.as_tensor(obs_image, dtype=torch.float32),
272
+ torch.as_tensor(goal_pos, dtype=torch.float32),
273
+ torch.as_tensor(rel_time, dtype=torch.float32),
274
+ torch.as_tensor(projected_tensor, dtype=torch.float32),
275
+ )
276
+ except Exception as e:
277
+ print(f"Exception in {self.dataset_name}", e)
278
+ raise Exception(e)
279
+
280
+
281
+ class EvalDataset(BaseDataset):
282
+ def __init__(
283
+ self,
284
+ data_folder: str,
285
+ data_split_folder: str,
286
+ dataset_name: str,
287
+ image_size: Tuple[int, int],
288
+ min_dist_cat: int,
289
+ max_dist_cat: int,
290
+ len_traj_pred: int,
291
+ traj_stride: int,
292
+ context_size: int,
293
+ transform: object,
294
+ traj_names: str,
295
+ normalize: bool = True,
296
+ predefined_index: list = None,
297
+ goals_per_obs: int = 1,
298
+ ):
299
+ super().__init__(data_folder, data_split_folder, dataset_name, image_size, min_dist_cat, max_dist_cat,
300
+ len_traj_pred, traj_stride, context_size, transform, traj_names, normalize, predefined_index, goals_per_obs)
301
+
302
+ def __getitem__(self, i: int) -> Tuple[torch.Tensor]:
303
+ try:
304
+ f_curr, curr_time, _, _ = self.index_to_data[i]
305
+ context_times = list(range(curr_time - self.context_size + 1, curr_time + 1))
306
+ pred_times = list(range(curr_time + 1, curr_time + self.len_traj_pred + 1)) # 未来 B 帧
307
+
308
+ context = [(f_curr, t) for t in context_times]
309
+ pred = [(f_curr, t) for t in pred_times]
310
+
311
+ obs_image = torch.stack([self.transform(Image.open(get_data_path(self.data_folder, f, t))) for f, t in context])
312
+ pred_image = torch.stack([self.transform(Image.open(get_data_path(self.data_folder, f, t))) for f, t in pred])
313
+
314
+ curr_traj_data = self._get_trajectory(f_curr)
315
+
316
+ # 动作/delta
317
+ actions, _ = self._compute_actions(curr_traj_data, curr_time, np.array(pred_times))
318
+ actions[:, :3] = normalize_data(actions[:, :3], self.ACTION_STATS)
319
+ delta = get_delta_np(actions)
320
+
321
+ # ===== 仅用于重投影的历史帧子集:取最后 self.num_cond_pro 帧 =====
322
+ cond_times = context_times[-self.num_cond_pro:]
323
+ cond_context = [(f_curr, t) for t in cond_times]
324
+ rgb_imgs = [cv2.imread(get_data_path(self.data_folder, f_img, t_img)) for f_img, t_img in cond_context]
325
+ rgb_imgs = [cv2.cvtColor(rgb_img, cv2.COLOR_BGR2RGB) for rgb_img in rgb_imgs]
326
+ rgb_imgs = np.stack(rgb_imgs, axis=0)
327
+ # ==========================================================
328
+
329
+ # 多历史帧(子集) → 多目标帧重投影(B 张投影图)
330
+ projected_images = self._compute_projected_images(curr_traj_data, cond_times, rgb_imgs, np.array(pred_times))
331
+ projected_tensor_list = [self.transform(Image.fromarray(img)) for img in projected_images]
332
+ projected_tensor = torch.stack(projected_tensor_list, dim=0)
333
+
334
+ print(f"Index {i}, projected_images shape: {projected_images.shape}, projected_tensor shape: {projected_tensor.size()}")
335
+
336
+ return (
337
+ torch.tensor([i], dtype=torch.float32), # for logging purposes
338
+ torch.as_tensor(obs_image, dtype=torch.float32),
339
+ torch.as_tensor(pred_image, dtype=torch.float32),
340
+ torch.as_tensor(delta, dtype=torch.float32),
341
+ torch.as_tensor(projected_tensor, dtype=torch.float32),
342
+ )
343
+ except Exception as e:
344
+ print(f"Exception in {self.dataset_name}", e)
345
+ raise Exception(e)
346
+
347
+ class TrajectoryEvalDataset(BaseDataset):
348
+ def __init__(
349
+ self,
350
+ data_folder: str,
351
+ data_split_folder: str,
352
+ dataset_name: str,
353
+ image_size: Tuple[int, int],
354
+ min_dist_cat: int,
355
+ max_dist_cat: int,
356
+ len_traj_pred: int,
357
+ traj_stride: int,
358
+ context_size: int,
359
+ transform: object,
360
+ traj_names: str,
361
+ normalize: bool = True,
362
+ predefined_index: list = None,
363
+ goals_per_obs: int = 1,
364
+ ):
365
+ super().__init__(data_folder, data_split_folder, dataset_name, image_size, min_dist_cat, max_dist_cat,
366
+ len_traj_pred, traj_stride, context_size, transform, traj_names, normalize, predefined_index, goals_per_obs)
367
+
368
+
369
+ def _sample_goal(self, trajectory_name, curr_time, min_goal_dist, max_goal_dist):
370
+ """
371
+ Sample a goal from the future in the same trajectory.
372
+ Returns: (trajectory_name, goal_time, goal_is_negative)
373
+ """
374
+ goal_offset = np.random.randint(min_goal_dist, max_goal_dist + 1)
375
+ goal_time = curr_time + int(goal_offset)
376
+ return trajectory_name, goal_time, False
377
+
378
+ def __getitem__(self, i: int) -> Tuple[torch.Tensor]:
379
+ try:
380
+ f_curr, curr_time, min_goal_dist, max_goal_dist = self.index_to_data[i]
381
+ f_goal, goal_time, _ = self._sample_goal(f_curr, curr_time, min_goal_dist, max_goal_dist)
382
+
383
+ context_times = list(range(curr_time - self.context_size + 1, curr_time + 1))
384
+ context = [(f_curr, t) for t in context_times]
385
+
386
+ obs_image = torch.stack([self.transform(Image.open(get_data_path(self.data_folder, f, t))) for f, t in context])
387
+ goal_image = self.transform(Image.open(get_data_path(self.data_folder, f_goal, goal_time))).unsqueeze(0)
388
+ curr_traj_data = self._get_trajectory(f_curr)
389
+
390
+ # Compute actions, goal_pos
391
+ actions, goal_pos = self._compute_actions(curr_traj_data, curr_time, np.array([goal_time]))
392
+
393
+ # ===== 仅用于重投影的历史帧子集:取最后 self.num_cond_pro 帧 =====
394
+ cond_times = context_times[-self.num_cond_pro:]
395
+ cond_context = [(f_curr, t) for t in cond_times]
396
+ rgb_imgs = [cv2.imread(get_data_path(self.data_folder, f_img, t_img)) for f_img, t_img in cond_context]
397
+ rgb_imgs = [cv2.cvtColor(rgb_img, cv2.COLOR_BGR2RGB) for rgb_img in rgb_imgs]
398
+ rgb_imgs = np.stack(rgb_imgs, axis=0)
399
+ # ==========================================================
400
+
401
+ projected_images = self._compute_projected_images(curr_traj_data, cond_times, rgb_imgs, np.array([goal_time]))
402
+ projected_tensor_list = [self.transform(Image.fromarray(img)) for img in projected_images]
403
+ projected_tensor = torch.stack(projected_tensor_list, dim=0)
404
+
405
+ return (
406
+ torch.tensor([i], dtype=torch.float32), # for logging purposes
407
+ torch.as_tensor(obs_image, dtype=torch.float32),
408
+ torch.as_tensor(goal_image, dtype=torch.float32),
409
+ torch.as_tensor(actions, dtype=torch.float32),
410
+ torch.as_tensor(goal_pos, dtype=torch.float32),
411
+ torch.as_tensor(projected_tensor, dtype=torch.float32),
412
+ )
413
+ except Exception as e:
414
+ print(f"Exception in {self.dataset_name}", e)
415
+ raise Exception(e)