generators.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. # Copyright (c) 2018-present, Facebook, Inc.
  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. from itertools import zip_longest
  8. import numpy as np
  9. class ChunkedGenerator:
  10. """
  11. Batched data generator, used for training.
  12. The sequences are split into equal-length chunks and padded as necessary.
  13. Arguments:
  14. batch_size -- the batch size to use for training
  15. cameras -- list of cameras, one element for each video (optional, used for semi-supervised training)
  16. poses_3d -- list of ground-truth 3D poses, one element for each video (optional, used for supervised training)
  17. poses_2d -- list of input 2D keypoints, one element for each video
  18. chunk_length -- number of output frames to predict for each training example (usually 1)
  19. pad -- 2D input padding to compensate for valid convolutions, per side (depends on the receptive field)
  20. causal_shift -- asymmetric padding offset when causal convolutions are used (usually 0 or "pad")
  21. shuffle -- randomly shuffle the dataset before each epoch
  22. random_seed -- initial seed to use for the random generator
  23. augment -- augment the dataset by flipping poses horizontally
  24. kps_left and kps_right -- list of left/right 2D keypoints if flipping is enabled
  25. joints_left and joints_right -- list of left/right 3D joints if flipping is enabled
  26. """
  27. def __init__(self, batch_size, cameras, poses_3d, poses_2d,
  28. chunk_length, pad=0, causal_shift=0,
  29. shuffle=True, random_seed=1234,
  30. augment=False, kps_left=None, kps_right=None, joints_left=None, joints_right=None,
  31. endless=False):
  32. assert poses_3d is None or len(poses_3d) == len(poses_2d), (len(poses_3d), len(poses_2d))
  33. assert cameras is None or len(cameras) == len(poses_2d)
  34. # Build lineage info
  35. pairs = [] # (seq_idx, start_frame, end_frame, flip) tuples
  36. for i in range(len(poses_2d)):
  37. assert poses_3d is None or poses_3d[i].shape[0] == poses_3d[i].shape[0]
  38. n_chunks = (poses_2d[i].shape[0] + chunk_length - 1) // chunk_length
  39. offset = (n_chunks * chunk_length - poses_2d[i].shape[0]) // 2
  40. bounds = np.arange(n_chunks+1)*chunk_length - offset
  41. augment_vector = np.full(len(bounds - 1), False, dtype=bool)
  42. pairs += zip(np.repeat(i, len(bounds - 1)), bounds[:-1], bounds[1:], augment_vector)
  43. if augment:
  44. pairs += zip(np.repeat(i, len(bounds - 1)), bounds[:-1], bounds[1:], ~augment_vector)
  45. # Initialize buffers
  46. if cameras is not None:
  47. self.batch_cam = np.empty((batch_size, cameras[0].shape[-1]))
  48. if poses_3d is not None:
  49. self.batch_3d = np.empty((batch_size, chunk_length, poses_3d[0].shape[-2], poses_3d[0].shape[-1]))
  50. self.batch_2d = np.empty((batch_size, chunk_length + 2*pad, poses_2d[0].shape[-2], poses_2d[0].shape[-1]))
  51. self.num_batches = (len(pairs) + batch_size - 1) // batch_size
  52. self.batch_size = batch_size
  53. self.random = np.random.RandomState(random_seed)
  54. self.pairs = pairs
  55. self.shuffle = shuffle
  56. self.pad = pad
  57. self.causal_shift = causal_shift
  58. self.endless = endless
  59. self.state = None
  60. self.cameras = cameras
  61. self.poses_3d = poses_3d
  62. self.poses_2d = poses_2d
  63. self.augment = augment
  64. self.kps_left = kps_left
  65. self.kps_right = kps_right
  66. self.joints_left = joints_left
  67. self.joints_right = joints_right
  68. def num_frames(self):
  69. return self.num_batches * self.batch_size
  70. def random_state(self):
  71. return self.random
  72. def set_random_state(self, random):
  73. self.random = random
  74. def augment_enabled(self):
  75. return self.augment
  76. def next_pairs(self):
  77. if self.state is None:
  78. if self.shuffle:
  79. pairs = self.random.permutation(self.pairs)
  80. else:
  81. pairs = self.pairs
  82. return 0, pairs
  83. else:
  84. return self.state
  85. def next_epoch(self):
  86. enabled = True
  87. while enabled:
  88. start_idx, pairs = self.next_pairs()
  89. for b_i in range(start_idx, self.num_batches):
  90. chunks = pairs[b_i*self.batch_size : (b_i+1)*self.batch_size]
  91. for i, (seq_i, start_3d, end_3d, flip) in enumerate(chunks):
  92. start_2d = start_3d - self.pad - self.causal_shift
  93. end_2d = end_3d + self.pad - self.causal_shift
  94. # 2D poses
  95. seq_2d = self.poses_2d[seq_i]
  96. low_2d = max(start_2d, 0)
  97. high_2d = min(end_2d, seq_2d.shape[0])
  98. pad_left_2d = low_2d - start_2d
  99. pad_right_2d = end_2d - high_2d
  100. if pad_left_2d != 0 or pad_right_2d != 0:
  101. self.batch_2d[i] = np.pad(seq_2d[low_2d:high_2d], ((pad_left_2d, pad_right_2d), (0, 0), (0, 0)), 'edge')
  102. else:
  103. self.batch_2d[i] = seq_2d[low_2d:high_2d]
  104. if flip:
  105. # Flip 2D keypoints
  106. self.batch_2d[i, :, :, 0] *= -1
  107. self.batch_2d[i, :, self.kps_left + self.kps_right] = self.batch_2d[i, :, self.kps_right + self.kps_left]
  108. # 3D poses
  109. if self.poses_3d is not None:
  110. seq_3d = self.poses_3d[seq_i]
  111. low_3d = max(start_3d, 0)
  112. high_3d = min(end_3d, seq_3d.shape[0])
  113. pad_left_3d = low_3d - start_3d
  114. pad_right_3d = end_3d - high_3d
  115. if pad_left_3d != 0 or pad_right_3d != 0:
  116. self.batch_3d[i] = np.pad(seq_3d[low_3d:high_3d], ((pad_left_3d, pad_right_3d), (0, 0), (0, 0)), 'edge')
  117. else:
  118. self.batch_3d[i] = seq_3d[low_3d:high_3d]
  119. if flip:
  120. # Flip 3D joints
  121. self.batch_3d[i, :, :, 0] *= -1
  122. self.batch_3d[i, :, self.joints_left + self.joints_right] = \
  123. self.batch_3d[i, :, self.joints_right + self.joints_left]
  124. # Cameras
  125. if self.cameras is not None:
  126. self.batch_cam[i] = self.cameras[seq_i]
  127. if flip:
  128. # Flip horizontal distortion coefficients
  129. self.batch_cam[i, 2] *= -1
  130. self.batch_cam[i, 7] *= -1
  131. if self.endless:
  132. self.state = (b_i + 1, pairs)
  133. if self.poses_3d is None and self.cameras is None:
  134. yield None, None, self.batch_2d[:len(chunks)]
  135. elif self.poses_3d is not None and self.cameras is None:
  136. yield None, self.batch_3d[:len(chunks)], self.batch_2d[:len(chunks)]
  137. elif self.poses_3d is None:
  138. yield self.batch_cam[:len(chunks)], None, self.batch_2d[:len(chunks)]
  139. else:
  140. yield self.batch_cam[:len(chunks)], self.batch_3d[:len(chunks)], self.batch_2d[:len(chunks)]
  141. if self.endless:
  142. self.state = None
  143. else:
  144. enabled = False
  145. class UnchunkedGenerator:
  146. """
  147. Non-batched data generator, used for testing.
  148. Sequences are returned one at a time (i.e. batch size = 1), without chunking.
  149. If data augmentation is enabled, the batches contain two sequences (i.e. batch size = 2),
  150. the second of which is a mirrored version of the first.
  151. Arguments:
  152. cameras -- list of cameras, one element for each video (optional, used for semi-supervised training)
  153. poses_3d -- list of ground-truth 3D poses, one element for each video (optional, used for supervised training)
  154. poses_2d -- list of input 2D keypoints, one element for each video
  155. pad -- 2D input padding to compensate for valid convolutions, per side (depends on the receptive field)
  156. causal_shift -- asymmetric padding offset when causal convolutions are used (usually 0 or "pad")
  157. augment -- augment the dataset by flipping poses horizontally
  158. kps_left and kps_right -- list of left/right 2D keypoints if flipping is enabled
  159. joints_left and joints_right -- list of left/right 3D joints if flipping is enabled
  160. """
  161. def __init__(self, cameras, poses_3d, poses_2d, pad=0, causal_shift=0,
  162. augment=False, kps_left=None, kps_right=None, joints_left=None, joints_right=None):
  163. assert poses_3d is None or len(poses_3d) == len(poses_2d)
  164. assert cameras is None or len(cameras) == len(poses_2d)
  165. self.augment = augment
  166. self.kps_left = kps_left
  167. self.kps_right = kps_right
  168. self.joints_left = joints_left
  169. self.joints_right = joints_right
  170. self.pad = pad
  171. self.causal_shift = causal_shift
  172. self.cameras = [] if cameras is None else cameras
  173. self.poses_3d = [] if poses_3d is None else poses_3d
  174. self.poses_2d = poses_2d
  175. def num_frames(self):
  176. count = 0
  177. for p in self.poses_2d:
  178. count += p.shape[0]
  179. return count
  180. def augment_enabled(self):
  181. return self.augment
  182. def set_augment(self, augment):
  183. self.augment = augment
  184. def next_epoch(self):
  185. for seq_cam, seq_3d, seq_2d in zip_longest(self.cameras, self.poses_3d, self.poses_2d):
  186. batch_cam = None if seq_cam is None else np.expand_dims(seq_cam, axis=0)
  187. batch_3d = None if seq_3d is None else np.expand_dims(seq_3d, axis=0)
  188. batch_2d = np.expand_dims(np.pad(seq_2d,
  189. ((self.pad + self.causal_shift, self.pad - self.causal_shift), (0, 0), (0, 0)),
  190. 'edge'), axis=0)
  191. if self.augment:
  192. # Append flipped version
  193. if batch_cam is not None:
  194. batch_cam = np.concatenate((batch_cam, batch_cam), axis=0)
  195. batch_cam[1, 2] *= -1
  196. batch_cam[1, 7] *= -1
  197. if batch_3d is not None:
  198. batch_3d = np.concatenate((batch_3d, batch_3d), axis=0)
  199. batch_3d[1, :, :, 0] *= -1
  200. batch_3d[1, :, self.joints_left + self.joints_right] = batch_3d[1, :, self.joints_right + self.joints_left]
  201. batch_2d = np.concatenate((batch_2d, batch_2d), axis=0)
  202. batch_2d[1, :, :, 0] *= -1
  203. batch_2d[1, :, self.kps_left + self.kps_right] = batch_2d[1, :, self.kps_right + self.kps_left]
  204. yield batch_cam, batch_3d, batch_2d