| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | import os |
| | import os.path as osp |
| |
|
| | import cv2 |
| | import numpy as np |
| | import torch |
| | import torch.nn.functional as F |
| | import torchvision.transforms as transforms |
| | import trimesh |
| | from PIL import Image |
| |
|
| | from lib.common.render import Render |
| | from lib.dataset.mesh_util import SMPLX, HoppeMesh, projection, rescale_smpl |
| |
|
| | cape_gender = { |
| | "male": |
| | ['00032', '00096', '00122', '00127', '00145', '00215', '02474', '03284', '03375', |
| | '03394'], "female": ['00134', '00159', '03223', '03331', '03383'] |
| | } |
| |
|
| |
|
| | class EvalDataset: |
| | def __init__(self, cfg, device): |
| |
|
| | self.root = cfg.root |
| | self.bsize = cfg.batch_size |
| |
|
| | self.opt = cfg.dataset |
| | self.datasets = self.opt.types |
| | self.input_size = self.opt.input_size |
| | self.scales = self.opt.scales |
| | self.vol_res = cfg.vol_res |
| |
|
| | |
| | self.in_geo = [item[0] for item in cfg.net.in_geo] |
| | self.in_nml = [item[0] for item in cfg.net.in_nml] |
| |
|
| | self.in_geo_dim = [item[1] for item in cfg.net.in_geo] |
| | self.in_nml_dim = [item[1] for item in cfg.net.in_nml] |
| |
|
| | self.in_total = self.in_geo + self.in_nml |
| | self.in_total_dim = self.in_geo_dim + self.in_nml_dim |
| |
|
| | self.rotations = range(0, 360, 120) |
| |
|
| | self.datasets_dict = {} |
| |
|
| | for dataset_id, dataset in enumerate(self.datasets): |
| |
|
| | dataset_dir = osp.join(self.root, dataset) |
| |
|
| | mesh_dir = osp.join(dataset_dir, "scans") |
| | smplx_dir = osp.join(dataset_dir, "smplx") |
| | smpl_dir = osp.join(dataset_dir, "smpl") |
| |
|
| | self.datasets_dict[dataset] = { |
| | "smplx_dir": smplx_dir, |
| | "smpl_dir": smpl_dir, |
| | "mesh_dir": mesh_dir, |
| | "scale": self.scales[dataset_id], |
| | } |
| |
|
| | self.datasets_dict[dataset].update({ |
| | "subjects": |
| | np.loadtxt(osp.join(dataset_dir, "all.txt"), dtype=str) |
| | }) |
| |
|
| | self.subject_list = self.get_subject_list() |
| | self.smplx = SMPLX() |
| |
|
| | |
| | self.image_to_tensor = transforms.Compose([ |
| | transforms.Resize(self.input_size), |
| | transforms.ToTensor(), |
| | transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)), |
| | ]) |
| |
|
| | |
| | self.mask_to_tensor = transforms.Compose([ |
| | transforms.Resize(self.input_size), |
| | transforms.ToTensor(), |
| | transforms.Normalize((0.0, ), (1.0, )), |
| | ]) |
| |
|
| | self.device = device |
| | self.render = Render(size=512, device=self.device) |
| |
|
| | def render_normal(self, verts, faces): |
| |
|
| | |
| | self.render.load_meshes(verts, faces) |
| | return self.render.get_image() |
| |
|
| | def get_subject_list(self): |
| |
|
| | subject_list = [] |
| |
|
| | for dataset in self.datasets: |
| |
|
| | split_txt = "" |
| |
|
| | if dataset == 'renderpeople': |
| | split_txt = osp.join(self.root, dataset, "loose.txt") |
| | elif dataset == 'cape': |
| | split_txt = osp.join(self.root, dataset, "pose.txt") |
| |
|
| | if osp.exists(split_txt) and osp.getsize(split_txt) > 0: |
| | print(f"load from {split_txt}") |
| | subject_list += np.loadtxt(split_txt, dtype=str).tolist() |
| |
|
| | return subject_list |
| |
|
| | def __len__(self): |
| | return len(self.subject_list) * len(self.rotations) |
| |
|
| | def __getitem__(self, index): |
| |
|
| | rid = index % len(self.rotations) |
| | mid = index // len(self.rotations) |
| |
|
| | rotation = self.rotations[rid] |
| | subject = self.subject_list[mid].split("/")[1] |
| | dataset = self.subject_list[mid].split("/")[0] |
| | render_folder = "/".join([dataset + f"_{self.opt.rotation_num}views", subject]) |
| |
|
| | if not osp.exists(osp.join(self.root, render_folder)): |
| | render_folder = "/".join([dataset + "_36views", subject]) |
| |
|
| | |
| | data_dict = { |
| | "dataset": dataset, |
| | "subject": subject, |
| | "rotation": rotation, |
| | "scale": self.datasets_dict[dataset]["scale"], |
| | "calib_path": osp.join(self.root, render_folder, "calib", f"{rotation:03d}.txt"), |
| | "image_path": osp.join(self.root, render_folder, "render", f"{rotation:03d}.png"), |
| | } |
| |
|
| | if dataset == "cape": |
| | data_dict.update({ |
| | "mesh_path": |
| | osp.join(self.datasets_dict[dataset]["mesh_dir"], f"{subject}.obj"), |
| | "smpl_path": |
| | osp.join(self.datasets_dict[dataset]["smpl_dir"], f"{subject}.obj"), |
| | }) |
| | else: |
| |
|
| | data_dict.update({ |
| | "mesh_path": |
| | osp.join( |
| | self.datasets_dict[dataset]["mesh_dir"], |
| | f"{subject}.obj", |
| | ), |
| | "smplx_path": |
| | osp.join(self.datasets_dict[dataset]["smplx_dir"], f"{subject}.obj"), |
| | }) |
| |
|
| | |
| | data_dict.update(self.load_calib(data_dict)) |
| |
|
| | |
| | for name, channel in zip(self.in_total, self.in_total_dim): |
| |
|
| | if f"{name}_path" not in data_dict.keys(): |
| | data_dict.update({ |
| | f"{name}_path": |
| | osp.join(self.root, render_folder, name, f"{rotation:03d}.png") |
| | }) |
| |
|
| | |
| | if os.path.exists(data_dict[f"{name}_path"]): |
| | data_dict.update({ |
| | name: |
| | self.imagepath2tensor(data_dict[f"{name}_path"], channel, inv=False) |
| | }) |
| |
|
| | data_dict.update(self.load_mesh(data_dict)) |
| | data_dict.update(self.load_smpl(data_dict)) |
| |
|
| | del data_dict["mesh"] |
| |
|
| | return data_dict |
| |
|
| | def imagepath2tensor(self, path, channel=3, inv=False): |
| |
|
| | rgba = Image.open(path).convert("RGBA") |
| |
|
| | |
| | if "cape" in path and "T_" not in path: |
| | mask = cv2.imread(path.replace(path.split("/")[-2], "mask"), 0) > 1 |
| | img = np.asarray(rgba)[:, :, :3] |
| | fill_mask = ((mask & (img.sum(axis=2) == 0))).astype(np.uint8) |
| | image = Image.fromarray( |
| | cv2.inpaint(img * mask[..., None], fill_mask, 3, cv2.INPAINT_TELEA) |
| | ) |
| | mask = Image.fromarray(mask) |
| | else: |
| | mask = rgba.split()[-1] |
| | image = rgba.convert("RGB") |
| |
|
| | image = self.image_to_tensor(image) |
| | mask = self.mask_to_tensor(mask) |
| | image = (image * mask)[:channel] |
| |
|
| | return (image * (0.5 - inv) * 2.0).float() |
| |
|
| | def load_calib(self, data_dict): |
| | calib_data = np.loadtxt(data_dict["calib_path"], dtype=float) |
| | extrinsic = calib_data[:4, :4] |
| | intrinsic = calib_data[4:8, :4] |
| | calib_mat = np.matmul(intrinsic, extrinsic) |
| | calib_mat = torch.from_numpy(calib_mat).float() |
| | return {"calib": calib_mat} |
| |
|
| | def load_mesh(self, data_dict): |
| |
|
| | mesh_path = data_dict["mesh_path"] |
| | scale = data_dict["scale"] |
| |
|
| | scan_mesh = trimesh.load(mesh_path) |
| | verts = scan_mesh.vertices |
| | faces = scan_mesh.faces |
| |
|
| | mesh = HoppeMesh(verts * scale, faces) |
| |
|
| | return { |
| | "mesh": mesh, |
| | "verts": torch.as_tensor(verts * scale).float(), |
| | "faces": torch.as_tensor(faces).long(), |
| | } |
| |
|
| | def load_smpl(self, data_dict): |
| |
|
| | smpl_type = ("smplx" if ("smplx_path" in data_dict.keys()) else "smpl") |
| |
|
| | smplx_verts = rescale_smpl(data_dict[f"{smpl_type}_path"], scale=100.0) |
| | smplx_faces = torch.as_tensor(getattr(self.smplx, f"{smpl_type}_faces")).long() |
| | smplx_verts = projection(smplx_verts, data_dict["calib"]).float() |
| |
|
| | return_dict = { |
| | "smpl_verts": smplx_verts, |
| | "smpl_faces": smplx_faces, |
| | } |
| |
|
| | return return_dict |
| |
|
| | def depth_to_voxel(self, data_dict): |
| |
|
| | data_dict["depth_F"] = transforms.Resize(self.vol_res)(data_dict["depth_F"]) |
| | data_dict["depth_B"] = transforms.Resize(self.vol_res)(data_dict["depth_B"]) |
| |
|
| | depth_mask = (~torch.isnan(data_dict['depth_F'])) |
| | depth_FB = torch.cat([data_dict['depth_F'], data_dict['depth_B']], dim=0) |
| | depth_FB[:, ~depth_mask[0]] = 0. |
| |
|
| | |
| | index_z = (((depth_FB + 1.) * 0.5 * self.vol_res) - 1).clip(0, self.vol_res - |
| | 1).permute(1, 2, 0) |
| | index_z_ceil = torch.ceil(index_z).long() |
| | index_z_floor = torch.floor(index_z).long() |
| | index_z_frac = torch.frac(index_z) |
| |
|
| | index_mask = index_z[..., 0] == torch.tensor(self.vol_res * 0.5 - 1).long() |
| | voxels = F.one_hot(index_z_ceil[..., 0], self.vol_res) * index_z_frac[..., 0] + \ |
| | F.one_hot(index_z_floor[..., 0], self.vol_res) * (1.0-index_z_frac[..., 0]) + \ |
| | F.one_hot(index_z_ceil[..., 1], self.vol_res) * index_z_frac[..., 1]+ \ |
| | F.one_hot(index_z_floor[..., 1], self.vol_res) * (1.0 - index_z_frac[..., 1]) |
| |
|
| | voxels[index_mask] *= 0 |
| | voxels = torch.flip(voxels, [2]).permute(2, 0, 1).float() |
| |
|
| | return { |
| | "depth_voxels": voxels.flip([ |
| | 0, |
| | ]).unsqueeze(0).to(self.device), |
| | } |
| |
|
| | def render_depth(self, verts, faces): |
| |
|
| | |
| | self.render.load_meshes(verts, faces) |
| | return self.render.get_image(type="depth") |
| |
|