prepare_data_humaneva.py 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  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. import argparse
  8. import os
  9. import zipfile
  10. import numpy as np
  11. import h5py
  12. import re
  13. from glob import glob
  14. from shutil import rmtree
  15. from data_utils import suggest_metadata, suggest_pose_importer
  16. import sys
  17. sys.path.append('../')
  18. from common.utils import wrap
  19. from itertools import groupby
  20. subjects = ['Train/S1', 'Train/S2', 'Train/S3', 'Validate/S1', 'Validate/S2', 'Validate/S3']
  21. cam_map = {
  22. 'C1': 0,
  23. 'C2': 1,
  24. 'C3': 2,
  25. }
  26. # Frame numbers for train/test split
  27. # format: [start_frame, end_frame[ (inclusive, exclusive)
  28. index = {
  29. 'Train/S1': {
  30. 'Walking 1': (590, 1203),
  31. 'Jog 1': (367, 740),
  32. 'ThrowCatch 1': (473, 945),
  33. 'Gestures 1': (395, 801),
  34. 'Box 1': (385, 789),
  35. },
  36. 'Train/S2': {
  37. 'Walking 1': (438, 876),
  38. 'Jog 1': (398, 795),
  39. 'ThrowCatch 1': (550, 1128),
  40. 'Gestures 1': (500, 901),
  41. 'Box 1': (382, 734),
  42. },
  43. 'Train/S3': {
  44. 'Walking 1': (448, 939),
  45. 'Jog 1': (401, 842),
  46. 'ThrowCatch 1': (493, 1027),
  47. 'Gestures 1': (533, 1102),
  48. 'Box 1': (512, 1021),
  49. },
  50. 'Validate/S1': {
  51. 'Walking 1': (5, 590),
  52. 'Jog 1': (5, 367),
  53. 'ThrowCatch 1': (5, 473),
  54. 'Gestures 1': (5, 395),
  55. 'Box 1': (5, 385),
  56. },
  57. 'Validate/S2': {
  58. 'Walking 1': (5, 438),
  59. 'Jog 1': (5, 398),
  60. 'ThrowCatch 1': (5, 550),
  61. 'Gestures 1': (5, 500),
  62. 'Box 1': (5, 382),
  63. },
  64. 'Validate/S3': {
  65. 'Walking 1': (5, 448),
  66. 'Jog 1': (5, 401),
  67. 'ThrowCatch 1': (5, 493),
  68. 'Gestures 1': (5, 533),
  69. 'Box 1': (5, 512),
  70. },
  71. }
  72. # Frames to skip for each video (synchronization)
  73. sync_data = {
  74. 'S1': {
  75. 'Walking 1': (82, 81, 82),
  76. 'Jog 1': (51, 51, 50),
  77. 'ThrowCatch 1': (61, 61, 60),
  78. 'Gestures 1': (45, 45, 44),
  79. 'Box 1': (57, 57, 56),
  80. },
  81. 'S2': {
  82. 'Walking 1': (115, 115, 114),
  83. 'Jog 1': (100, 100, 99),
  84. 'ThrowCatch 1': (127, 127, 127),
  85. 'Gestures 1': (122, 122, 121),
  86. 'Box 1': (119, 119, 117),
  87. },
  88. 'S3': {
  89. 'Walking 1': (80, 80, 80),
  90. 'Jog 1': (65, 65, 65),
  91. 'ThrowCatch 1': (79, 79, 79),
  92. 'Gestures 1': (83, 83, 82),
  93. 'Box 1': (1, 1, 1),
  94. },
  95. 'S4': {}
  96. }
  97. if __name__ == '__main__':
  98. if os.path.basename(os.getcwd()) != 'data':
  99. print('This script must be launched from the "data" directory')
  100. exit(0)
  101. parser = argparse.ArgumentParser(description='HumanEva dataset converter')
  102. parser.add_argument('-p', '--path', default='', type=str, metavar='PATH', help='path to the processed HumanEva dataset')
  103. parser.add_argument('--convert-3d', action='store_true', help='convert 3D mocap data')
  104. parser.add_argument('--convert-2d', default='', type=str, metavar='PATH', help='convert user-supplied 2D detections')
  105. parser.add_argument('-o', '--output', default='', type=str, metavar='PATH', help='output suffix for 2D detections (e.g. detectron_pt_coco)')
  106. args = parser.parse_args()
  107. if not args.convert_2d and not args.convert_3d:
  108. print('Please specify one conversion mode')
  109. exit(0)
  110. if args.path:
  111. print('Parsing HumanEva dataset from', args.path)
  112. output = {}
  113. output_2d = {}
  114. frame_mapping = {}
  115. from scipy.io import loadmat
  116. num_joints = None
  117. for subject in subjects:
  118. output[subject] = {}
  119. output_2d[subject] = {}
  120. split, subject_name = subject.split('/')
  121. if subject_name not in frame_mapping:
  122. frame_mapping[subject_name] = {}
  123. file_list = glob(args.path + '/' + subject + '/*.mat')
  124. for f in file_list:
  125. action = os.path.splitext(os.path.basename(f))[0]
  126. # Use consistent naming convention
  127. canonical_name = action.replace('_', ' ')
  128. hf = loadmat(f)
  129. positions = hf['poses_3d']
  130. positions_2d = hf['poses_2d'].transpose(1, 0, 2, 3) # Ground-truth 2D poses
  131. assert positions.shape[0] == positions_2d.shape[0] and positions.shape[1] == positions_2d.shape[2]
  132. assert num_joints is None or num_joints == positions.shape[1], "Joint number inconsistency among files"
  133. num_joints = positions.shape[1]
  134. # Sanity check for the sequence length
  135. assert positions.shape[0] == index[subject][canonical_name][1] - index[subject][canonical_name][0]
  136. # Split corrupted motion capture streams into contiguous chunks
  137. # e.g. 012XX567X9 is split into "012", "567", and "9".
  138. all_chunks = [list(v) for k, v in groupby(positions, lambda x: np.isfinite(x).all())]
  139. all_chunks_2d = [list(v) for k, v in groupby(positions_2d, lambda x: np.isfinite(x).all())]
  140. assert len(all_chunks) == len(all_chunks_2d)
  141. current_index = index[subject][canonical_name][0]
  142. chunk_indices = []
  143. for i, chunk in enumerate(all_chunks):
  144. next_index = current_index + len(chunk)
  145. name = canonical_name + ' chunk' + str(i)
  146. if np.isfinite(chunk).all():
  147. output[subject][name] = np.array(chunk, dtype='float32') / 1000
  148. output_2d[subject][name] = list(np.array(all_chunks_2d[i], dtype='float32').transpose(1, 0, 2, 3))
  149. chunk_indices.append((current_index, next_index, np.isfinite(chunk).all(), split, name))
  150. current_index = next_index
  151. assert current_index == index[subject][canonical_name][1]
  152. if canonical_name not in frame_mapping[subject_name]:
  153. frame_mapping[subject_name][canonical_name] = []
  154. frame_mapping[subject_name][canonical_name] += chunk_indices
  155. metadata = suggest_metadata('humaneva' + str(num_joints))
  156. output_filename = 'data_3d_' + metadata['layout_name']
  157. output_prefix_2d = 'data_2d_' + metadata['layout_name'] + '_'
  158. if args.convert_3d:
  159. print('Saving...')
  160. np.savez_compressed(output_filename, positions_3d=output)
  161. np.savez_compressed(output_prefix_2d + 'gt', positions_2d=output_2d, metadata=metadata)
  162. print('Done.')
  163. else:
  164. print('Please specify the dataset source')
  165. exit(0)
  166. if args.convert_2d:
  167. if not args.output:
  168. print('Please specify an output suffix (e.g. detectron_pt_coco)')
  169. exit(0)
  170. import_func = suggest_pose_importer(args.output)
  171. metadata = suggest_metadata(args.output)
  172. print('Parsing 2D detections from', args.convert_2d)
  173. output = {}
  174. file_list = glob(args.convert_2d + '/S*/*.avi.npz')
  175. for f in file_list:
  176. path, fname = os.path.split(f)
  177. subject = os.path.basename(path)
  178. assert subject.startswith('S'), subject + ' does not look like a subject directory'
  179. m = re.search('(.*) \\((.*)\\)', fname.replace('_', ' '))
  180. action = m.group(1)
  181. camera = m.group(2)
  182. camera_idx = cam_map[camera]
  183. keypoints = import_func(f)
  184. assert keypoints.shape[1] == metadata['num_joints']
  185. if action in sync_data[subject]:
  186. sync_offset = sync_data[subject][action][camera_idx] - 1
  187. else:
  188. sync_offset = 0
  189. if subject in frame_mapping and action in frame_mapping[subject]:
  190. chunks = frame_mapping[subject][action]
  191. for (start_idx, end_idx, labeled, split, name) in chunks:
  192. canonical_subject = split + '/' + subject
  193. if not labeled:
  194. canonical_subject = 'Unlabeled/' + canonical_subject
  195. if canonical_subject not in output:
  196. output[canonical_subject] = {}
  197. kps = keypoints[start_idx+sync_offset:end_idx+sync_offset]
  198. assert len(kps) == end_idx - start_idx, "Got len {}, expected {}".format(len(kps), end_idx - start_idx)
  199. if name not in output[canonical_subject]:
  200. output[canonical_subject][name] = [None, None, None]
  201. output[canonical_subject][name][camera_idx] = kps.astype('float32')
  202. else:
  203. canonical_subject = 'Unlabeled/' + subject
  204. if canonical_subject not in output:
  205. output[canonical_subject] = {}
  206. if action not in output[canonical_subject]:
  207. output[canonical_subject][action] = [None, None, None]
  208. output[canonical_subject][action][camera_idx] = keypoints.astype('float32')
  209. print('Saving...')
  210. np.savez_compressed(output_prefix_2d + args.output, positions_2d=output, metadata=metadata)
  211. print('Done.')