prepare_data_h36m.py 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  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. from glob import glob
  13. from shutil import rmtree
  14. import sys
  15. sys.path.append('../')
  16. from common.h36m_dataset import Human36mDataset
  17. from common.camera import world_to_camera, project_to_2d, image_coordinates
  18. from common.utils import wrap
  19. output_filename = 'data_3d_h36m'
  20. output_filename_2d = 'data_2d_h36m_gt'
  21. subjects = ['S1', 'S5', 'S6', 'S7', 'S8', 'S9', 'S11']
  22. if __name__ == '__main__':
  23. if os.path.basename(os.getcwd()) != 'data':
  24. print('This script must be launched from the "data" directory')
  25. exit(0)
  26. parser = argparse.ArgumentParser(description='Human3.6M dataset downloader/converter')
  27. # Convert dataset preprocessed by Martinez et al. in https://github.com/una-dinosauria/3d-pose-baseline
  28. parser.add_argument('--from-archive', default='', type=str, metavar='PATH', help='convert preprocessed dataset')
  29. # Convert dataset from original source, using files converted to .mat (the Human3.6M dataset path must be specified manually)
  30. # This option requires MATLAB to convert files using the provided script
  31. parser.add_argument('--from-source', default='', type=str, metavar='PATH', help='convert original dataset')
  32. # Convert dataset from original source, using original .cdf files (the Human3.6M dataset path must be specified manually)
  33. # This option does not require MATLAB, but the Python library cdflib must be installed
  34. parser.add_argument('--from-source-cdf', default='', type=str, metavar='PATH', help='convert original dataset')
  35. args = parser.parse_args()
  36. if args.from_archive and args.from_source:
  37. print('Please specify only one argument')
  38. exit(0)
  39. if os.path.exists(output_filename + '.npz'):
  40. print('The dataset already exists at', output_filename + '.npz')
  41. exit(0)
  42. if args.from_archive:
  43. print('Extracting Human3.6M dataset from', args.from_archive)
  44. with zipfile.ZipFile(args.from_archive, 'r') as archive:
  45. archive.extractall()
  46. print('Converting...')
  47. output = {}
  48. for subject in subjects:
  49. output[subject] = {}
  50. file_list = glob('h36m/' + subject + '/MyPoses/3D_positions/*.h5')
  51. assert len(file_list) == 30, "Expected 30 files for subject " + subject + ", got " + str(len(file_list))
  52. for f in file_list:
  53. action = os.path.splitext(os.path.basename(f))[0]
  54. if subject == 'S11' and action == 'Directions':
  55. continue # Discard corrupted video
  56. with h5py.File(f) as hf:
  57. positions = hf['3D_positions'].value.reshape(32, 3, -1).transpose(2, 0, 1)
  58. positions /= 1000 # Meters instead of millimeters
  59. output[subject][action] = positions.astype('float32')
  60. print('Saving...')
  61. np.savez_compressed(output_filename, positions_3d=output)
  62. print('Cleaning up...')
  63. rmtree('h36m')
  64. print('Done.')
  65. elif args.from_source:
  66. print('Converting original Human3.6M dataset from', args.from_source)
  67. output = {}
  68. from scipy.io import loadmat
  69. for subject in subjects:
  70. output[subject] = {}
  71. file_list = glob(args.from_source + '/' + subject + '/MyPoseFeatures/D3_Positions/*.cdf.mat')
  72. assert len(file_list) == 30, "Expected 30 files for subject " + subject + ", got " + str(len(file_list))
  73. for f in file_list:
  74. action = os.path.splitext(os.path.splitext(os.path.basename(f))[0])[0]
  75. if subject == 'S11' and action == 'Directions':
  76. continue # Discard corrupted video
  77. # Use consistent naming convention
  78. canonical_name = action.replace('TakingPhoto', 'Photo') \
  79. .replace('WalkingDog', 'WalkDog')
  80. hf = loadmat(f)
  81. positions = hf['data'][0, 0].reshape(-1, 32, 3)
  82. positions /= 1000 # Meters instead of millimeters
  83. output[subject][canonical_name] = positions.astype('float32')
  84. print('Saving...')
  85. np.savez_compressed(output_filename, positions_3d=output)
  86. print('Done.')
  87. elif args.from_source_cdf:
  88. print('Converting original Human3.6M dataset from', args.from_source_cdf, '(CDF files)')
  89. output = {}
  90. import cdflib
  91. for subject in subjects:
  92. output[subject] = {}
  93. file_list = glob(args.from_source_cdf + '/' + subject + '/MyPoseFeatures/D3_Positions/*.cdf')
  94. assert len(file_list) == 30, "Expected 30 files for subject " + subject + ", got " + str(len(file_list))
  95. for f in file_list:
  96. action = os.path.splitext(os.path.basename(f))[0]
  97. if subject == 'S11' and action == 'Directions':
  98. continue # Discard corrupted video
  99. # Use consistent naming convention
  100. canonical_name = action.replace('TakingPhoto', 'Photo') \
  101. .replace('WalkingDog', 'WalkDog')
  102. hf = cdflib.CDF(f)
  103. positions = hf['Pose'].reshape(-1, 32, 3)
  104. positions /= 1000 # Meters instead of millimeters
  105. output[subject][canonical_name] = positions.astype('float32')
  106. print('Saving...')
  107. np.savez_compressed(output_filename, positions_3d=output)
  108. print('Done.')
  109. else:
  110. print('Please specify the dataset source')
  111. exit(0)
  112. # Create 2D pose file
  113. print('')
  114. print('Computing ground-truth 2D poses...')
  115. dataset = Human36mDataset(output_filename + '.npz')
  116. output_2d_poses = {}
  117. for subject in dataset.subjects():
  118. output_2d_poses[subject] = {}
  119. for action in dataset[subject].keys():
  120. anim = dataset[subject][action]
  121. positions_2d = []
  122. for cam in anim['cameras']:
  123. pos_3d = world_to_camera(anim['positions'], R=cam['orientation'], t=cam['translation'])
  124. pos_2d = wrap(project_to_2d, pos_3d, cam['intrinsic'], unsqueeze=True)
  125. pos_2d_pixel_space = image_coordinates(pos_2d, w=cam['res_w'], h=cam['res_h'])
  126. positions_2d.append(pos_2d_pixel_space.astype('float32'))
  127. output_2d_poses[subject][action] = positions_2d
  128. print('Saving...')
  129. metadata = {
  130. 'num_joints': dataset.skeleton().num_joints(),
  131. 'keypoints_symmetry': [dataset.skeleton().joints_left(), dataset.skeleton().joints_right()]
  132. }
  133. np.savez_compressed(output_filename_2d, positions_2d=output_2d_poses, metadata=metadata)
  134. print('Done.')