diff --git a/apps/calibration/camera_parameters.md b/apps/calibration/camera_parameters.md deleted file mode 100644 index dba1a57..0000000 --- a/apps/calibration/camera_parameters.md +++ /dev/null @@ -1,19 +0,0 @@ - -# Camera Parameters Format - -For example, if the name of a video is `1.mp4`, then there must exist `K_1`, `dist_1` in `intri.yml`, and `R_1((3, 1), rotation vector of camera)`, `T_1(3, 1)` in `extri.yml`. The file format is following [OpenCV format](https://docs.opencv.org/master/dd/d74/tutorial_file_input_output_with_xml_yml.html). - -## Write/Read - -See `easymocap/mytools/camera_utils.py`=>`write_camera`, `read_camera` functions. - -## Conversion between different format - -TODO - diff --git a/apps/mocap/run.py b/apps/mocap/run.py index a88460d..d2328e2 100644 --- a/apps/mocap/run.py +++ b/apps/mocap/run.py @@ -3,14 +3,27 @@ import os from easymocap.config import Config, load_object from tqdm import tqdm -def process(dataset, model): +def process(dataset, model, args): ret_all = [] print('[Run] dataset has {} samples'.format(len(dataset))) - for i in tqdm(range(len(dataset)), desc='[Run]'): - data = dataset[i] - ret = model.at_step(data, i) - ret_all.append(ret) - ret_all = model.at_final(ret_all) + if args.num_workers == -1: + for i in tqdm(range(len(dataset)), desc='[Run]'): + data = dataset[i] + ret = model.at_step(data, i) + ret_all.append(ret) + else: + import torch + dataloader = torch.utils.data.DataLoader(dataset, + batch_size=1, num_workers=args.num_workers, shuffle=False, collate_fn=lambda x:x, drop_last=False) + index = 0 + for data in tqdm(dataloader, desc='[Run]'): + data = data[0] + ret = model.at_step(data, index) + if not args.skip_final: + ret_all.append(ret) + index += 1 + if not args.skip_final: + ret_all = model.at_final(ret_all) def update_data_by_args(cfg_data, args): if args.root is not None: @@ -23,7 +36,7 @@ def update_data_by_args(cfg_data, args): cfg_data.args.ranges = args.ranges if args.cameras is not None: cfg_data.args.reader.cameras.root = args.cameras - if args.skip_vis: + if args.skip_vis or args.skip_vis_step: cfg_data.args.subs_vis = [] return cfg_data @@ -41,7 +54,7 @@ def update_exp_by_args(cfg_exp, args): val.skip = True if args.skip_vis or args.skip_vis_final: for key, val in cfg_exp.args.at_final.items(): - if key.startswith('vis'): + if key.startswith('vis') or key == 'make_video': val.skip = True def load_cfg_from_file(cfg, args): @@ -74,9 +87,11 @@ def main_entrypoint(): parser.add_argument('--ranges', type=int, default=None, nargs=3) parser.add_argument('--cameras', type=str, default=None, help='Camera file path') parser.add_argument('--out', type=str, default=None) + parser.add_argument('--num_workers', type=int, default=-1) parser.add_argument('--skip_vis', action='store_true') parser.add_argument('--skip_vis_step', action='store_true') parser.add_argument('--skip_vis_final', action='store_true') + parser.add_argument('--skip_final', action='store_true') parser.add_argument('--debug', action='store_true') args = parser.parse_args() @@ -96,7 +111,7 @@ def main_entrypoint(): print(dataset) model = load_object(cfg_exp.module, cfg_exp.args) - process(dataset, model) + process(dataset, model, args) if __name__ == '__main__': main_entrypoint() \ No newline at end of file diff --git a/config/model/flame.yml b/config/model/flame.yml new file mode 100644 index 0000000..a9eef1d --- /dev/null +++ b/config/model/flame.yml @@ -0,0 +1,4 @@ +module: easymocap.bodymodel.smpl.SMPLModel +args: + model_path: data/bodymodels/FLAME2020/FLAME_NEUTRAL.pkl + device: cuda \ No newline at end of file diff --git a/easymocap/bodymodel/smpl.py b/easymocap/bodymodel/smpl.py index a2ba1d3..716e35c 100644 --- a/easymocap/bodymodel/smpl.py +++ b/easymocap/bodymodel/smpl.py @@ -85,12 +85,13 @@ class SMPLModel(Model): self.lbs = lbs self.data = load_model_data(model_path) self.register_any_lbs(self.data) - # keypoints regressor if regressor_path is not None and use_joints: X_regressor = load_regressor(regressor_path) X_regressor = torch.cat((self.J_regressor, X_regressor), dim=0) self.register_any_keypoints(X_regressor) + elif regressor_path is None: + self.register_any_keypoints(self.J_regressor) if not self.use_root_rot: self.NUM_POSES -= 3 # remove first 3 dims self.to(self.device) @@ -169,7 +170,9 @@ class SMPLModel(Model): def forward(self, return_verts=True, return_tensor=True, return_smpl_joints=False, - only_shape=False, pose2rot=True, **params): + only_shape=False, pose2rot=True, + v_template=None, + **params): params = self.check_params(params) poses, shapes = params['poses'], params['shapes'] poses = self.extend_poses(pose2rot=pose2rot, **params) @@ -187,7 +190,8 @@ class SMPLModel(Model): Rh = batch_rodrigues(Rh) Th = Th.unsqueeze(dim=1) if return_verts or not self.use_joints: - v_template = self.v_template + if v_template is None: + v_template = self.v_template if 'scale' in params.keys(): v_template = v_template * params['scale'][0] vertices, joints, T_joints, T_vertices = self.lbs(shapes, poses, v_template, @@ -199,7 +203,8 @@ class SMPLModel(Model): vertices = joints else: # only forward joints - v_template = self.j_v_template + if v_template is None: + v_template = self.j_v_template if 'scale' in params.keys(): v_template = v_template * params['scale'][0] vertices, joints, _, _ = self.lbs(shapes, poses, v_template, @@ -236,6 +241,39 @@ class SMPLModel(Model): return Params.merge(params, **kwargs) + def convert_to_standard_smpl(self, params): + params = self.check_params(params) + poses, shapes = params['poses'], params['shapes'] + Th = params['Th'] + vertices, joints, _, _ = lbs(shapes, poses, self.v_template, + self.shapedirs, self.posedirs, + self.J_regressor, self.parents, + self.weights, pose2rot=True, dtype=self.dtype, only_shape=True) + # N x 3 + j0 = joints[:, 0, :] + Rh = params['Rh'] + # N x 3 x 3 + rot = batch_rodrigues(Rh) + # change the rotate center + min_xyz, _ = self.v_template.min(dim=0, keepdim=True) + # X' = X + delta_center + delta_center = torch.tensor([0., -min_xyz[0, 1], 0.]).reshape(1, 3).to(j0.device) + # J' = J + delta_center + j0new = j0 + delta_center + # Tnew = T - (R(d - J0) + J0) + Tnew = Th - (torch.einsum('bij,bj->bi', rot, delta_center-j0new) + j0new) + if poses.shape[1] == 69: + poses = torch.cat([Rh, poses], dim=1) + elif poses.shape[1] == 72: + poses[:, :3] = Rh + else: + import ipdb;ipdb.set_trace() + res = dict(poses=poses.detach().cpu().numpy(), + shapes=shapes.detach().cpu().numpy(), + Th=Tnew.detach().cpu().numpy() + ) + return res + def convert_from_standard_smpl(self, params): params = self.check_params(params) poses, shapes = params['poses'], params['shapes']