You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

transformation.py 15 kB

2 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. """
  2. # -*- coding: utf-8 -*-
  3. -----------------------------------------------------------------------------------
  4. # Refer: https://github.com/ghimiredhikura/Complex-YOLOv3
  5. # Source : https://github.com/jeasinema/VoxelNet-tensorflow/blob/master/utils/utils.py
  6. """
  7. import os
  8. import sys
  9. import math
  10. import numpy as np
  11. import torch
  12. src_dir = os.path.dirname(os.path.realpath(__file__))
  13. # while not src_dir.endswith("sfa"):
  14. # src_dir = os.path.dirname(src_dir)
  15. if src_dir not in sys.path:
  16. sys.path.append(src_dir)
  17. from config import kitti_config as cnf
  18. def angle_in_limit(angle):
  19. # To limit the angle in -pi/2 - pi/2
  20. limit_degree = 5
  21. while angle >= np.pi / 2:
  22. angle -= np.pi
  23. while angle < -np.pi / 2:
  24. angle += np.pi
  25. if abs(angle + np.pi / 2) < limit_degree / 180 * np.pi:
  26. angle = np.pi / 2
  27. return angle
  28. def camera_to_lidar(x, y, z, V2C=None, R0=None, P2=None):
  29. p = np.array([x, y, z, 1])
  30. if V2C is None or R0 is None:
  31. p = np.matmul(cnf.R0_inv, p)
  32. p = np.matmul(cnf.Tr_velo_to_cam_inv, p)
  33. else:
  34. R0_i = np.zeros((4, 4))
  35. R0_i[:3, :3] = R0
  36. R0_i[3, 3] = 1
  37. p = np.matmul(np.linalg.inv(R0_i), p)
  38. p = np.matmul(inverse_rigid_trans(V2C), p)
  39. p = p[0:3]
  40. return tuple(p)
  41. def lidar_to_camera(x, y, z, V2C=None, R0=None, P2=None):
  42. p = np.array([x, y, z, 1])
  43. if V2C is None or R0 is None:
  44. p = np.matmul(cnf.Tr_velo_to_cam, p)
  45. p = np.matmul(cnf.R0, p)
  46. else:
  47. p = np.matmul(V2C, p)
  48. p = np.matmul(R0, p)
  49. p = p[0:3]
  50. return tuple(p)
  51. def camera_to_lidar_point(points):
  52. # (N, 3) -> (N, 3)
  53. N = points.shape[0]
  54. points = np.hstack([points, np.ones((N, 1))]).T # (N,4) -> (4,N)
  55. points = np.matmul(cnf.R0_inv, points)
  56. points = np.matmul(cnf.Tr_velo_to_cam_inv, points).T # (4, N) -> (N, 4)
  57. points = points[:, 0:3]
  58. return points.reshape(-1, 3)
  59. def lidar_to_camera_point(points, V2C=None, R0=None):
  60. # (N, 3) -> (N, 3)
  61. N = points.shape[0]
  62. points = np.hstack([points, np.ones((N, 1))]).T
  63. if V2C is None or R0 is None:
  64. points = np.matmul(cnf.Tr_velo_to_cam, points)
  65. points = np.matmul(cnf.R0, points).T
  66. else:
  67. points = np.matmul(V2C, points)
  68. points = np.matmul(R0, points).T
  69. points = points[:, 0:3]
  70. return points.reshape(-1, 3)
  71. def camera_to_lidar_box(boxes, V2C=None, R0=None, P2=None):
  72. # (N, 7) -> (N, 7) x,y,z,h,w,l,r
  73. ret = []
  74. for box in boxes:
  75. x, y, z, h, w, l, ry = box
  76. # print(x, y, z, h, w, l, ry)
  77. (x, y, z), h, w, l, rz = camera_to_lidar(x, y, z, V2C=V2C, R0=R0, P2=P2), h, w, l, -ry - np.pi / 2
  78. # print(x, y, z, h, w, l, ry)
  79. # print("camera_to_lidar")
  80. # rz = angle_in_limit(rz)
  81. ret.append([x, y, z, h, w, l, rz])
  82. return np.array(ret).reshape(-1, 7)
  83. def lidar_to_camera_box(boxes, V2C=None, R0=None, P2=None):
  84. # (N, 7) -> (N, 7) x,y,z,h,w,l,r
  85. ret = []
  86. for box in boxes:
  87. x, y, z, h, w, l, rz = box
  88. # (x, y, z), h, w, l, ry = lidar_to_camera(x, y, z, V2C=V2C, R0=R0, P2=P2), h, w, l, -rz - np.pi / 2
  89. # ry = angle_in_limit(ry)
  90. ry = -rz - np.pi / 2
  91. ret.append([x, y, z, h, w, l, ry])
  92. return np.array(ret).reshape(-1, 7)
  93. def center_to_corner_box2d(boxes_center, coordinate='lidar'):
  94. # (N, 5) -> (N, 4, 2)
  95. N = boxes_center.shape[0]
  96. boxes3d_center = np.zeros((N, 7))
  97. boxes3d_center[:, [0, 1, 4, 5, 6]] = boxes_center
  98. boxes3d_corner = center_to_corner_box3d(boxes3d_center, coordinate=coordinate)
  99. return boxes3d_corner[:, 0:4, 0:2]
  100. def center_to_corner_box3d(boxes_center, coordinate='lidar'):
  101. # (N, 7) -> (N, 8, 3)
  102. N = boxes_center.shape[0]
  103. ret = np.zeros((N, 8, 3), dtype=np.float32)
  104. if coordinate == 'camera':
  105. boxes_center = camera_to_lidar_box(boxes_center)
  106. for i in range(N):
  107. box = boxes_center[i]
  108. translation = box[0:3]
  109. size = box[3:6]
  110. rotation = [0, 0, box[-1]]
  111. h, w, l = size[0], size[1], size[2]
  112. trackletBox = np.array([ # in velodyne coordinates around zero point and without orientation yet
  113. [-l / 2, -l / 2, l / 2, l / 2, -l / 2, -l / 2, l / 2, l / 2], \
  114. [w / 2, -w / 2, -w / 2, w / 2, w / 2, -w / 2, -w / 2, w / 2], \
  115. [0, 0, 0, 0, h, h, h, h]])
  116. # re-create 3D bounding box in velodyne coordinate system
  117. yaw = rotation[2]
  118. rotMat = np.array([
  119. [np.cos(yaw), -np.sin(yaw), 0.0],
  120. [np.sin(yaw), np.cos(yaw), 0.0],
  121. [0.0, 0.0, 1.0]])
  122. cornerPosInVelo = np.dot(rotMat, trackletBox) + np.tile(translation, (8, 1)).T
  123. box3d = cornerPosInVelo.transpose()
  124. ret[i] = box3d
  125. if coordinate == 'camera':
  126. for idx in range(len(ret)):
  127. ret[idx] = lidar_to_camera_point(ret[idx])
  128. return ret
  129. CORNER2CENTER_AVG = True
  130. def corner_to_center_box3d(boxes_corner, coordinate='camera'):
  131. # (N, 8, 3) -> (N, 7) x,y,z,h,w,l,ry/z
  132. if coordinate == 'lidar':
  133. for idx in range(len(boxes_corner)):
  134. boxes_corner[idx] = lidar_to_camera_point(boxes_corner[idx])
  135. ret = []
  136. for roi in boxes_corner:
  137. if CORNER2CENTER_AVG: # average version
  138. roi = np.array(roi)
  139. h = abs(np.sum(roi[:4, 1] - roi[4:, 1]) / 4)
  140. w = np.sum(
  141. np.sqrt(np.sum((roi[0, [0, 2]] - roi[3, [0, 2]]) ** 2)) +
  142. np.sqrt(np.sum((roi[1, [0, 2]] - roi[2, [0, 2]]) ** 2)) +
  143. np.sqrt(np.sum((roi[4, [0, 2]] - roi[7, [0, 2]]) ** 2)) +
  144. np.sqrt(np.sum((roi[5, [0, 2]] - roi[6, [0, 2]]) ** 2))
  145. ) / 4
  146. l = np.sum(
  147. np.sqrt(np.sum((roi[0, [0, 2]] - roi[1, [0, 2]]) ** 2)) +
  148. np.sqrt(np.sum((roi[2, [0, 2]] - roi[3, [0, 2]]) ** 2)) +
  149. np.sqrt(np.sum((roi[4, [0, 2]] - roi[5, [0, 2]]) ** 2)) +
  150. np.sqrt(np.sum((roi[6, [0, 2]] - roi[7, [0, 2]]) ** 2))
  151. ) / 4
  152. x = np.sum(roi[:, 0], axis=0) / 8
  153. y = np.sum(roi[0:4, 1], axis=0) / 4
  154. z = np.sum(roi[:, 2], axis=0) / 8
  155. ry = np.sum(
  156. math.atan2(roi[2, 0] - roi[1, 0], roi[2, 2] - roi[1, 2]) +
  157. math.atan2(roi[6, 0] - roi[5, 0], roi[6, 2] - roi[5, 2]) +
  158. math.atan2(roi[3, 0] - roi[0, 0], roi[3, 2] - roi[0, 2]) +
  159. math.atan2(roi[7, 0] - roi[4, 0], roi[7, 2] - roi[4, 2]) +
  160. math.atan2(roi[0, 2] - roi[1, 2], roi[1, 0] - roi[0, 0]) +
  161. math.atan2(roi[4, 2] - roi[5, 2], roi[5, 0] - roi[4, 0]) +
  162. math.atan2(roi[3, 2] - roi[2, 2], roi[2, 0] - roi[3, 0]) +
  163. math.atan2(roi[7, 2] - roi[6, 2], roi[6, 0] - roi[7, 0])
  164. ) / 8
  165. if w > l:
  166. w, l = l, w
  167. ry = ry - np.pi / 2
  168. elif l > w:
  169. l, w = w, l
  170. ry = ry - np.pi / 2
  171. ret.append([x, y, z, h, w, l, ry])
  172. else: # max version
  173. h = max(abs(roi[:4, 1] - roi[4:, 1]))
  174. w = np.max(
  175. np.sqrt(np.sum((roi[0, [0, 2]] - roi[3, [0, 2]]) ** 2)) +
  176. np.sqrt(np.sum((roi[1, [0, 2]] - roi[2, [0, 2]]) ** 2)) +
  177. np.sqrt(np.sum((roi[4, [0, 2]] - roi[7, [0, 2]]) ** 2)) +
  178. np.sqrt(np.sum((roi[5, [0, 2]] - roi[6, [0, 2]]) ** 2))
  179. )
  180. l = np.max(
  181. np.sqrt(np.sum((roi[0, [0, 2]] - roi[1, [0, 2]]) ** 2)) +
  182. np.sqrt(np.sum((roi[2, [0, 2]] - roi[3, [0, 2]]) ** 2)) +
  183. np.sqrt(np.sum((roi[4, [0, 2]] - roi[5, [0, 2]]) ** 2)) +
  184. np.sqrt(np.sum((roi[6, [0, 2]] - roi[7, [0, 2]]) ** 2))
  185. )
  186. x = np.sum(roi[:, 0], axis=0) / 8
  187. y = np.sum(roi[0:4, 1], axis=0) / 4
  188. z = np.sum(roi[:, 2], axis=0) / 8
  189. ry = np.sum(
  190. math.atan2(roi[2, 0] - roi[1, 0], roi[2, 2] - roi[1, 2]) +
  191. math.atan2(roi[6, 0] - roi[5, 0], roi[6, 2] - roi[5, 2]) +
  192. math.atan2(roi[3, 0] - roi[0, 0], roi[3, 2] - roi[0, 2]) +
  193. math.atan2(roi[7, 0] - roi[4, 0], roi[7, 2] - roi[4, 2]) +
  194. math.atan2(roi[0, 2] - roi[1, 2], roi[1, 0] - roi[0, 0]) +
  195. math.atan2(roi[4, 2] - roi[5, 2], roi[5, 0] - roi[4, 0]) +
  196. math.atan2(roi[3, 2] - roi[2, 2], roi[2, 0] - roi[3, 0]) +
  197. math.atan2(roi[7, 2] - roi[6, 2], roi[6, 0] - roi[7, 0])
  198. ) / 8
  199. if w > l:
  200. w, l = l, w
  201. ry = angle_in_limit(ry + np.pi / 2)
  202. ret.append([x, y, z, h, w, l, ry])
  203. if coordinate == 'lidar':
  204. ret = camera_to_lidar_box(np.array(ret))
  205. return np.array(ret)
  206. def point_transform(points, tx, ty, tz, rx=0, ry=0, rz=0):
  207. # Input:
  208. # points: (N, 3)
  209. # rx/y/z: in radians
  210. # Output:
  211. # points: (N, 3)
  212. N = points.shape[0]
  213. points = np.hstack([points, np.ones((N, 1))])
  214. mat1 = np.eye(4)
  215. mat1[3, 0:3] = tx, ty, tz
  216. points = np.matmul(points, mat1)
  217. if rx != 0:
  218. mat = np.zeros((4, 4))
  219. mat[0, 0] = 1
  220. mat[3, 3] = 1
  221. mat[1, 1] = np.cos(rx)
  222. mat[1, 2] = -np.sin(rx)
  223. mat[2, 1] = np.sin(rx)
  224. mat[2, 2] = np.cos(rx)
  225. points = np.matmul(points, mat)
  226. if ry != 0:
  227. mat = np.zeros((4, 4))
  228. mat[1, 1] = 1
  229. mat[3, 3] = 1
  230. mat[0, 0] = np.cos(ry)
  231. mat[0, 2] = np.sin(ry)
  232. mat[2, 0] = -np.sin(ry)
  233. mat[2, 2] = np.cos(ry)
  234. points = np.matmul(points, mat)
  235. if rz != 0:
  236. mat = np.zeros((4, 4))
  237. mat[2, 2] = 1
  238. mat[3, 3] = 1
  239. mat[0, 0] = np.cos(rz)
  240. mat[0, 1] = -np.sin(rz)
  241. mat[1, 0] = np.sin(rz)
  242. mat[1, 1] = np.cos(rz)
  243. points = np.matmul(points, mat)
  244. return points[:, 0:3]
  245. def box_transform(boxes, tx, ty, tz, r=0, coordinate='lidar'):
  246. # Input:
  247. # boxes: (N, 7) x y z h w l rz/y
  248. # Output:
  249. # boxes: (N, 7) x y z h w l rz/y
  250. boxes_corner = center_to_corner_box3d(boxes, coordinate=coordinate) # (N, 8, 3)
  251. for idx in range(len(boxes_corner)):
  252. if coordinate == 'lidar':
  253. boxes_corner[idx] = point_transform(boxes_corner[idx], tx, ty, tz, rz=r)
  254. else:
  255. boxes_corner[idx] = point_transform(boxes_corner[idx], tx, ty, tz, ry=r)
  256. return corner_to_center_box3d(boxes_corner, coordinate=coordinate)
  257. def inverse_rigid_trans(Tr):
  258. ''' Inverse a rigid body transform matrix (3x4 as [R|t])
  259. [R'|-R't; 0|1]
  260. '''
  261. inv_Tr = np.zeros_like(Tr) # 3x4
  262. inv_Tr[0:3, 0:3] = np.transpose(Tr[0:3, 0:3])
  263. inv_Tr[0:3, 3] = np.dot(-np.transpose(Tr[0:3, 0:3]), Tr[0:3, 3])
  264. return inv_Tr
  265. class Compose(object):
  266. def __init__(self, transforms, p=1.0):
  267. self.transforms = transforms
  268. self.p = p
  269. def __call__(self, lidar, labels):
  270. if np.random.random() <= self.p:
  271. for t in self.transforms:
  272. lidar, labels = t(lidar, labels)
  273. return lidar, labels
  274. class OneOf(object):
  275. def __init__(self, transforms, p=1.0):
  276. self.transforms = transforms
  277. self.p = p
  278. def __call__(self, lidar, labels):
  279. if np.random.random() <= self.p:
  280. choice = np.random.randint(low=0, high=len(self.transforms))
  281. lidar, labels = self.transforms[choice](lidar, labels)
  282. return lidar, labels
  283. class Random_Rotation(object):
  284. def __init__(self, limit_angle=np.pi / 4, p=0.5):
  285. self.limit_angle = limit_angle
  286. self.p = p
  287. def __call__(self, lidar, labels):
  288. """
  289. :param labels: # (N', 7) x, y, z, h, w, l, r
  290. :return:
  291. """
  292. if np.random.random() <= self.p:
  293. angle = np.random.uniform(-self.limit_angle, self.limit_angle)
  294. lidar[:, 0:3] = point_transform(lidar[:, 0:3], 0, 0, 0, rz=angle)
  295. labels = box_transform(labels, 0, 0, 0, r=angle, coordinate='lidar')
  296. return lidar, labels
  297. class Random_Scaling(object):
  298. def __init__(self, scaling_range=(0.95, 1.05), p=0.5):
  299. self.scaling_range = scaling_range
  300. self.p = p
  301. def __call__(self, lidar, labels):
  302. """
  303. :param labels: # (N', 7) x, y, z, h, w, l, r
  304. :return:
  305. """
  306. if np.random.random() <= self.p:
  307. factor = np.random.uniform(self.scaling_range[0], self.scaling_range[0])
  308. lidar[:, 0:3] = lidar[:, 0:3] * factor
  309. labels[:, 0:6] = labels[:, 0:6] * factor
  310. return lidar, labels
  311. class Cutout(object):
  312. """Randomly mask out one or more patches from an image.
  313. Args:
  314. n_holes (int): Number of patches to cut out of each image.
  315. length (int): The length (in pixels) of each square patch.
  316. Refer from: https://github.com/uoguelph-mlrg/Cutout/blob/master/util/cutout.py
  317. """
  318. def __init__(self, n_holes, ratio, fill_value=0., p=1.0):
  319. self.n_holes = n_holes
  320. self.ratio = ratio
  321. assert 0. <= fill_value <= 1., "the fill value is in a range of 0 to 1"
  322. self.fill_value = fill_value
  323. self.p = p
  324. def __call__(self, img, targets):
  325. """
  326. Args:
  327. img (Tensor): Tensor image of size (C, H, W).
  328. Returns:
  329. Tensor: Image with n_holes of dimension length x length cut out of it.
  330. """
  331. if np.random.random() <= self.p:
  332. h = img.size(1)
  333. w = img.size(2)
  334. h_cutout = int(self.ratio * h)
  335. w_cutout = int(self.ratio * w)
  336. for n in range(self.n_holes):
  337. y = np.random.randint(h)
  338. x = np.random.randint(w)
  339. y1 = np.clip(y - h_cutout // 2, 0, h)
  340. y2 = np.clip(y + h_cutout // 2, 0, h)
  341. x1 = np.clip(x - w_cutout // 2, 0, w)
  342. x2 = np.clip(x + w_cutout // 2, 0, w)
  343. img[:, y1: y2, x1: x2] = self.fill_value # Zero out the selected area
  344. # Remove targets that are in the selected area
  345. keep_target = []
  346. for target_idx, target in enumerate(targets):
  347. _, _, target_x, target_y, target_w, target_l, _, _ = target
  348. if (x1 <= target_x * w <= x2) and (y1 <= target_y * h <= y2):
  349. continue
  350. keep_target.append(target_idx)
  351. targets = targets[keep_target]
  352. return img, targets

一站式算法开发平台、高性能分布式深度学习框架、先进算法模型库、视觉模型炼知平台、数据可视化分析平台等一系列平台及工具,在模型高效分布式训练、数据处理和可视分析、模型炼知和轻量化等技术上形成独特优势,目前已在产学研等各领域近千家单位及个人提供AI应用赋能