det_3d_reader.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. # copyright (c) 2024 PaddlePaddle Authors. All Rights Reserve.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. import numpy as np
  15. import cv2
  16. import os
  17. from typing import Generic, List, Optional, Any, Dict
  18. import pickle
  19. from ...utils.benchmark import benchmark
  20. class _EasyDict(dict):
  21. def __getattr__(self, key: str):
  22. if key in self:
  23. return self[key]
  24. return super().__getattr__(self, key)
  25. def __setattr__(self, key: str, value: Generic):
  26. self[key] = value
  27. class SampleMeta(_EasyDict):
  28. # yapf: disable
  29. __slots__ = [
  30. "camera_intrinsic",
  31. # bgr or rgb
  32. "image_format",
  33. # pillow or cv2
  34. "image_reader",
  35. # chw or hwc
  36. "channel_order",
  37. # Unique ID of the sample
  38. "id",
  39. "time_lag",
  40. "ref_from_curr"
  41. ]
  42. # yapf: enable
  43. def __init__(self, **kwargs):
  44. for key, value in kwargs.items():
  45. setattr(self, key, value)
  46. class Sample(_EasyDict):
  47. """Data structure containing sample data information"""
  48. _VALID_MODALITIES = ["image", "lidar", "radar", "multimodal", "multiview"]
  49. def __init__(self, path: str, modality: str):
  50. if modality not in self._VALID_MODALITIES:
  51. raise ValueError(
  52. "Only modality {} is supported, but got {}".format(
  53. self._VALID_MODALITIES, modality
  54. )
  55. )
  56. self.meta = SampleMeta()
  57. self.path = path
  58. self.data = None
  59. self.modality = modality.lower()
  60. self.bboxes_2d = None
  61. self.bboxes_3d = None
  62. self.labels = None
  63. self.sweeps = []
  64. self.attrs = None
  65. @benchmark.timeit_with_options(name=None, is_read_operation=True)
  66. class ReadNuscenesData:
  67. def __init__(
  68. self,
  69. dataset_root="",
  70. load_interval=1,
  71. noise_sensor_type="camera",
  72. drop_frames=False,
  73. drop_set=[0, "discrete"],
  74. modality="multimodal",
  75. extrinsics_noise=False,
  76. extrinsics_noise_type="single",
  77. ):
  78. self.load_interval = load_interval
  79. self.noise_data = None
  80. self.noise_sensor_type = noise_sensor_type
  81. self.drop_frames = drop_frames
  82. self.drop_ratio = drop_set[0]
  83. self.drop_type = drop_set[1]
  84. self.modality = modality
  85. self.extrinsics_noise = extrinsics_noise
  86. self.extrinsics_noise_type = extrinsics_noise_type
  87. self.dataset_root = dataset_root
  88. def get_data_info(self, info):
  89. """Get data info.
  90. Returns:
  91. dict: Data information that will be passed to the data \
  92. preprocessing pipelines. It includes the following keys:
  93. - sample_idx (str): Sample index.
  94. - pts_filename (str): Filename of point clouds.
  95. - sweeps (list[dict]): Infos of sweeps.
  96. - timestamp (float): Sample timestamp.
  97. - img_filename (str, optional): Image filename.
  98. - lidar2img (list[np.ndarray], optional): Transformations \
  99. from lidar to different cameras.
  100. - ann_info (dict): Annotation info.
  101. """
  102. sample = Sample(path=None, modality=self.modality)
  103. sample.sample_idx = info["token"]
  104. sample.meta.id = info["token"]
  105. sample.pts_filename = os.path.join(self.dataset_root, info["lidar_path"])
  106. sample.sweeps = info["sweeps"]
  107. sample.timestamp = info["timestamp"] / 1e6
  108. if self.noise_sensor_type == "lidar":
  109. if self.drop_frames:
  110. pts_filename = sample.pts_filename
  111. file_name = pts_filename.split("/")[-1]
  112. if self.noise_data[file_name]["noise"]["drop_frames"][self.drop_ratio][
  113. self.drop_type
  114. ]["stuck"]:
  115. replace_file = self.noise_data[file_name]["noise"]["drop_frames"][
  116. self.drop_ratio
  117. ][self.drop_type]["replace"]
  118. if replace_file != "":
  119. pts_filename = pts_filename.replace(file_name, replace_file)
  120. sample.pts_filename = pts_filename
  121. sample.sweeps = self.noise_data[replace_file]["mmdet_info"][
  122. "sweeps"
  123. ]
  124. sample.timestamp = (
  125. self.noise_data[replace_file]["mmdet_info"]["timestamp"]
  126. / 1e6
  127. )
  128. cam_orders = [
  129. "CAM_FRONT_LEFT",
  130. "CAM_FRONT",
  131. "CAM_FRONT_RIGHT",
  132. "CAM_BACK_RIGHT",
  133. "CAM_BACK",
  134. "CAM_BACK_LEFT",
  135. ]
  136. if self.modality == "multiview" or self.modality == "multimodal":
  137. image_paths = []
  138. lidar2img_rts = []
  139. caminfos = []
  140. for cam_type in cam_orders:
  141. cam_info = info["cams"][cam_type]
  142. cam_data_path = cam_info["data_path"]
  143. cam_data_path = os.path.join(self.dataset_root, cam_data_path)
  144. file_name = cam_data_path.split("/")[-1]
  145. if self.noise_sensor_type == "camera":
  146. if self.drop_frames:
  147. if self.noise_data[file_name]["noise"]["drop_frames"][
  148. self.drop_ratio
  149. ][self.drop_type]["stuck"]:
  150. replace_file = self.noise_data[file_name]["noise"][
  151. "drop_frames"
  152. ][self.drop_ratio][self.drop_type]["replace"]
  153. if replace_file != "":
  154. cam_data_path = cam_data_path.replace(
  155. file_name, replace_file
  156. )
  157. image_paths.append(cam_data_path)
  158. # obtain lidar to image transformation matrix
  159. if self.extrinsics_noise:
  160. sensor2lidar_rotation = self.noise_data[file_name]["noise"][
  161. "extrinsics_noise"
  162. ][f"{self.extrinsics_noise_type}_noise_sensor2lidar_rotation"]
  163. sensor2lidar_translation = self.noise_data[file_name]["noise"][
  164. "extrinsics_noise"
  165. ][f"{self.extrinsics_noise_type}_noise_sensor2lidar_translation"]
  166. else:
  167. sensor2lidar_rotation = cam_info["sensor2lidar_rotation"]
  168. sensor2lidar_translation = cam_info["sensor2lidar_translation"]
  169. lidar2cam_r = np.linalg.inv(sensor2lidar_rotation)
  170. lidar2cam_t = sensor2lidar_translation @ lidar2cam_r.T
  171. lidar2cam_rt = np.eye(4)
  172. lidar2cam_rt[:3, :3] = lidar2cam_r.T
  173. lidar2cam_rt[3, :3] = -lidar2cam_t
  174. intrinsic = cam_info["cam_intrinsic"]
  175. viewpad = np.eye(4)
  176. viewpad[: intrinsic.shape[0], : intrinsic.shape[1]] = intrinsic
  177. lidar2img_rt = viewpad @ lidar2cam_rt.T
  178. lidar2img_rts.append(lidar2img_rt)
  179. caminfos.append(
  180. {
  181. "sensor2lidar_translation": sensor2lidar_translation,
  182. "sensor2lidar_rotation": sensor2lidar_rotation,
  183. "cam_intrinsic": cam_info["cam_intrinsic"],
  184. }
  185. )
  186. sample.update(
  187. dict(
  188. img_filename=image_paths, lidar2img=lidar2img_rts, caminfo=caminfos
  189. )
  190. )
  191. return sample
  192. def prepare_test_data(self, info):
  193. sample = self.get_data_info(info)
  194. sample = self.add_new_fields(sample)
  195. return sample
  196. def add_new_fields(self, sample):
  197. sample["img_fields"] = []
  198. sample["bbox3d_fields"] = []
  199. sample["pts_mask_fields"] = []
  200. sample["pts_seg_fields"] = []
  201. sample["bbox_fields"] = []
  202. sample["mask_fields"] = []
  203. sample["seg_fields"] = []
  204. return sample
  205. def __call__(self, batch_data):
  206. return [self.prepare_test_data(data_info) for data_info in batch_data]