import chumpy as ch import numpy as np import sys import pickle as pkl import scipy.sparse as sp from chumpy.ch import Ch from .vendor.smpl.posemapper import posemap, Rodrigues from .vendor.smpl.serialization import backwards_compatibility_replacements VERT_NOSE = 331 VERT_EAR_L = 3485 VERT_EAR_R = 6880 VERT_EYE_L = 2802 VERT_EYE_R = 6262 class Smpl(Ch): """ Class to store SMPL object with slightly improved code and access to more matrices """ terms = 'model', dterms = 'trans', 'betas', 'pose', 'v_personal' def __init__(self, *args, **kwargs): self.on_changed(self._dirty_vars) def on_changed(self, which): if not hasattr(self, 'trans'): self.trans = ch.zeros(3) if not hasattr(self, 'betas'): self.betas = ch.zeros(10) if not hasattr(self, 'pose'): self.pose = ch.zeros(72) if 'model' in which: if not isinstance(self.model, dict): dd = pkl.load(open(self.model)) else: dd = self.model backwards_compatibility_replacements(dd) for s in ['posedirs', 'shapedirs']: if (s in dd) and not hasattr(dd[s], 'dterms'): dd[s] = ch.array(dd[s]) self.f = dd['f'] self.v_template = dd['v_template'] if not hasattr(self, 'v_personal'): self.v_personal = ch.zeros_like(self.v_template) self.shapedirs = dd['shapedirs'] self.J_regressor = dd['J_regressor'] if 'J_regressor_prior' in dd: self.J_regressor_prior = dd['J_regressor_prior'] if sp.issparse(self.J_regressor): self.J_regressor = self.J_regressor.toarray() self.bs_type = dd['bs_type'] self.weights = dd['weights'] if 'vert_sym_idxs' in dd: self.vert_sym_idxs = dd['vert_sym_idxs'] if 'weights_prior' in dd: self.weights_prior = dd['weights_prior'] self.kintree_table = dd['kintree_table'] self.posedirs = dd['posedirs'] self._set_up() def _set_up(self): self.v_shaped = self.shapedirs.dot(self.betas) + self.v_template self.v_shaped_personal = self.v_shaped + self.v_personal self.J = ch.sum(self.J_regressor.T.reshape(-1, 1, 24) * self.v_shaped.reshape(-1, 3, 1), axis=0).T self.v_posevariation = self.posedirs.dot(posemap(self.bs_type)(self.pose)) self.v_poseshaped = self.v_shaped_personal + self.v_posevariation self.A, A_global = self._global_rigid_transformation() self.Jtr = ch.vstack([g[:3, 3] for g in A_global]) self.J_transformed = self.Jtr + self.trans.reshape((1, 3)) self.V = self.A.dot(self.weights.T) rest_shape_h = ch.hstack((self.v_poseshaped, ch.ones((self.v_poseshaped.shape[0], 1)))) self.v_posed = ch.sum(self.V.T * rest_shape_h.reshape(-1, 4, 1), axis=1)[:, :3] self.v = self.v_posed + self.trans def _global_rigid_transformation(self): results = {} pose = self.pose.reshape((-1, 3)) parent = {i: self.kintree_table[0, i] for i in range(1, self.kintree_table.shape[1])} with_zeros = lambda x: ch.vstack((x, ch.array([[0.0, 0.0, 0.0, 1.0]]))) pack = lambda x: ch.hstack([ch.zeros((4, 3)), x.reshape((4, 1))]) results[0] = with_zeros(ch.hstack((Rodrigues(pose[0, :]), self.J[0, :].reshape((3, 1))))) for i in range(1, self.kintree_table.shape[1]): results[i] = results[parent[i]].dot(with_zeros(ch.hstack(( Rodrigues(pose[i, :]), # rotation around bone endpoint (self.J[i, :] - self.J[parent[i], :]).reshape((3, 1)) # bone )))) results = [results[i] for i in sorted(results.keys())] results_global = results # subtract rotated J position results2 = [results[i] - (pack( results[i].dot(ch.concatenate((self.J[i, :], [0])))) ) for i in range(len(results))] result = ch.dstack(results2) return result, results_global def compute_r(self): return self.v.r def compute_dr_wrt(self, wrt): if wrt is not self.trans and wrt is not self.betas and wrt is not self.pose and wrt is not self.v_personal: return None return self.v.dr_wrt(wrt) def copy_smpl(smpl, model): new = Smpl(model, betas=smpl.betas) new.pose[:] = smpl.pose.r new.trans[:] = smpl.trans.r return new def joints_coco(smpl): J = smpl.J_transformed nose = smpl[VERT_NOSE] ear_l = smpl[VERT_EAR_L] ear_r = smpl[VERT_EAR_R] eye_l = smpl[VERT_EYE_L] eye_r = smpl[VERT_EYE_R] shoulders_m = ch.sum(J[[14, 13]], axis=0) / 2. neck = J[12] - 0.55 * (J[12] - shoulders_m) return ch.vstack(( nose, neck, 2.1 * (J[14] - shoulders_m) + neck, J[[19, 21]], 2.1 * (J[13] - shoulders_m) + neck, J[[18, 20]], J[2] + 0.38 * (J[2] - J[1]), J[[5, 8]], J[1] + 0.38 * (J[1] - J[2]), J[[4, 7]], eye_r, eye_l, ear_r, ear_l, )) def model_params_in_camera_coords(trans, pose, J0, camera_t, camera_rt): root = Rodrigues(np.matmul(Rodrigues(camera_rt).r, Rodrigues(pose[:3]).r)).r.reshape(-1) pose[:3] = root trans = (Rodrigues(camera_rt).dot(J0 + trans) - J0 + camera_t).r return trans, pose if __name__ == '__main__': smpl = Smpl(model='../vendor/smpl/models/basicModel_f_lbs_10_207_0_v1.0.0.pkl') smpl.pose[:] = np.random.randn(72) * .2 smpl.pose[0] = np.pi # smpl.v_personal[:] = np.random.randn(*smpl.shape) / 500. # render test from opendr.renderer import ColoredRenderer from opendr.camera import ProjectPoints from opendr.lighting import LambertianPointLight rn = ColoredRenderer() # Assign attributes to renderer w, h = (640, 480) rn.camera = ProjectPoints(v=smpl, rt=np.zeros(3), t=np.array([0, 0, 3.]), f=np.array([w, w]), c=np.array([w, h]) / 2., k=np.zeros(5)) rn.frustum = {'near': 1., 'far': 10., 'width': w, 'height': h} rn.set(v=smpl, f=smpl.f, bgcolor=np.zeros(3)) # Construct point light source rn.vc = LambertianPointLight( f=smpl.f, v=rn.v, num_verts=len(smpl), light_pos=np.array([-1000, -1000, -2000]), vc=np.ones_like(smpl) * .9, light_color=np.array([1., 1., 1.])) # Show it using OpenCV import cv2 cv2.imshow('render_SMPL', rn.r) print ('..Print any key while on the display window') cv2.waitKey(0) cv2.destroyAllWindows()