Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified gnm/shape/data/versions/v3_0/gnm_head.npz
Binary file not shown.
8 changes: 4 additions & 4 deletions gnm/shape/gnm_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,10 +191,10 @@ def axis_angle_to_rotation_matrix(
sin_angle, cos_angle = xnp.sin(angle), xnp.cos(angle)
sin_angle, cos_angle = sin_angle[..., None], cos_angle[..., None]

matrix = xnp.broadcast_to(
eye(3, dtype=angle.dtype, reference_array=axis_angle, xnp=xnp),
(*axis_angle.shape[:-1], 3, 3),
)
eyem = eye(3, dtype=angle.dtype, reference_array=axis_angle, xnp=xnp)
for _ in range(axis_angle.ndim - 1):
eyem = eyem[None, ...]
matrix = eyem + xnp.zeros_like(axis_angle[..., None])

skew_01 = -axis[..., 2]
skew_02 = axis[..., 1]
Expand Down
46 changes: 37 additions & 9 deletions gnm/shape/gnm_xnp.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,11 @@ def as_int_array(val):
def as_original(val):
return val

def as_string_tuple(val):
if val is None:
return ()
return tuple(str(item) for item in val)

field_converters = {
'version': as_original,
'variant': as_original,
Expand All @@ -188,16 +193,16 @@ def as_original(val):
'vertex_identity_basis': as_float_array,
'joint_identity_basis': as_float_array,
'expression_basis': as_float_array,
'identity_names': as_original,
'joint_names': as_original,
'expression_names': as_original,
'joint_parent_indices': as_original,
'identity_names': as_string_tuple,
'joint_names': as_string_tuple,
'expression_names': as_string_tuple,
'joint_parent_indices': as_int_array,
'skinning_weights': as_float_array,
'quads': as_int_array,
'triangles': as_int_array,
'quad_uvs': as_float_array,
'triangle_uvs': as_float_array,
'mesh_component_names': as_original,
'mesh_component_names': as_string_tuple,
'mirror_indices': as_int_array,
'joint_regressor': as_float_array,
'pose_correctives_regressor': as_float_array,
Expand All @@ -208,6 +213,18 @@ def as_original(val):

for field_name, converter in field_converters.items():
val = model_data.get(field_name, None)
if (
val is None
and field_name == 'bone_aligned_template_joint_orientations'
):
val = model_data.get('bone_aligned_orientations', None)
if val is None and 'joint_names' in model_data:
n_joints = len(model_data['joint_names'])
val = xnp.tile(xnp.eye(3, dtype=xnp.float32), (n_joints, 1, 1))
if val is None and field_name == 'mesh_component_names':
val = model_data.get('part_names', None)
if val is None:
val = ('skin',)
init_kwargs[field_name] = converter(val)

return init_kwargs
Expand All @@ -218,7 +235,7 @@ def _from_model_data_with_xnp(
model_data: Mapping[str, Any],
xnp: enp.NpModule,
) -> Self:
"""Creates a GNM instance from a model data dictionary and array module."""
"""Creates a GNM instance from a model data dictionary and array module."""
init_kwargs = cls._prepare_init_kwargs(model_data, xnp)
# pylint: disable=no-value-for-parameter
instance = super(GNM, cls).__new__(cls)
Expand Down Expand Up @@ -257,6 +274,11 @@ def to_numpy_data_dict(self) -> dict[str, Any]:
data_dict[field.name] = val
return data_dict

@property
def bone_aligned_orientations(self) -> enpt.FloatArray:
"""Alias for bone_aligned_template_joint_orientations."""
return self.bone_aligned_template_joint_orientations

def _check_parameter_shapes(
self,
identity: enpt.FloatArray['...'] | None = None,
Expand Down Expand Up @@ -390,6 +412,7 @@ def vertex_positions_world(
joints: enpt.FloatArray['A1 ... An J 3'],
rotations: enpt.FloatArray['A1 ... An J 3'],
translation: enpt.FloatArray['A1 ... An 3'],
**_kwargs,
) -> enpt.FloatArray['A1 ... An V 3']:
"""Applies linear blend skinning to GNM vertices."""
return gnm_common.linear_blend_skinning(
Expand Down Expand Up @@ -500,6 +523,7 @@ def vertex_positions_bind_pose(
self,
identity: enpt.FloatArray['A1 ... An I'],
expression: enpt.FloatArray['A1 ... An E'],
**_kwargs,
) -> enpt.FloatArray['A1 ... An V 3']:
return gnm_common.vertex_positions_bind_pose(
identity,
Expand All @@ -511,7 +535,7 @@ def vertex_positions_bind_pose(

@enp.check_and_normalize_arrays(strict=False)
def joint_positions_bind_pose(
self, identity: enpt.FloatArray['A1 ... An I']
self, identity: enpt.FloatArray['A1 ... An I'], **_kwargs
) -> enpt.FloatArray['A1 ... An J 3']:
return gnm_common.joint_positions_bind_pose(
identity,
Expand All @@ -521,7 +545,7 @@ def joint_positions_bind_pose(

@enp.check_and_normalize_arrays(strict=False)
def compute_pose_correctives(
self, rotations: enpt.FloatArray['A1 ... An J 3']
self, rotations: enpt.FloatArray['A1 ... An J 3'], **_kwargs
) -> enpt.FloatArray['A1 ... An V 3']:
return gnm_common.compute_pose_correctives(
rotations,
Expand All @@ -537,6 +561,7 @@ def joint_transforms_world(
joints: enpt.FloatArray['A1 ... An J 3'],
rotations: enpt.FloatArray['A1 ... An J 3'],
translation: enpt.FloatArray['A1 ... An 3'],
**_kwargs,
) -> enpt.FloatArray['A1 ... An J 4 4']:
return gnm_common.joint_transforms_world(
joints, rotations, translation, self.joint_parent_indices
Expand Down Expand Up @@ -579,7 +604,10 @@ def vertex_group_mask(
operator, name = name[0], name[1:]
if name[0] == '~':
inverse, name = True, name[1:]
group_mask = self.vertex_group(name) > threshold
try:
group_mask = self.vertex_group(name) > threshold
except KeyError:
continue
if inverse:
group_mask = ~group_mask
match operator:
Expand Down
4 changes: 2 additions & 2 deletions gnm/shape/visualization/render_gnm.py
Original file line number Diff line number Diff line change
Expand Up @@ -421,8 +421,8 @@ def get_look_at_world_to_camera(
share_camera: np.ndarray | bool = True,
y_up: np.ndarray | bool = False,
look_at_vertex_groups: Sequence[str] = ('hockey_mask',),
left_vertex_groups: Sequence[str] = ('ears', '&left'),
right_vertex_groups: Sequence[str] = ('ears', '&right'),
left_vertex_groups: Sequence[str] = ('left_ear', 'ears', '&left'),
right_vertex_groups: Sequence[str] = ('right_ear', 'ears', '&right'),
forward_vertex_groups: Sequence[str] = ('nose_region',),
) -> np.ndarray:
"""Compute world-to-camera matrices for a 'look-at' transform.
Expand Down
Loading