|
import collections |
|
import itertools |
|
import math |
|
from typing import Callable, Dict, List, Optional, Set, Tuple, Union |
|
|
|
import numpy as np |
|
import nvdiffrast.torch as dr |
|
import torch |
|
import torch.nn as nn |
|
import torch.nn.functional as F |
|
import xatlas |
|
from diffusers import ConfigMixin, ModelMixin |
|
from transformers import PreTrainedModel, ViTConfig, ViTImageProcessor |
|
from transformers.activations import ACT2FN |
|
from transformers.modeling_outputs import BaseModelOutput, BaseModelOutputWithPooling |
|
from transformers.pytorch_utils import ( |
|
find_pruneable_heads_and_indices, |
|
prune_linear_layer, |
|
) |
|
|
|
|
|
def generate_planes(): |
|
""" |
|
Defines planes by the three vectors that form the "axes" of the |
|
plane. Should work with arbitrary number of planes and planes of |
|
arbitrary orientation. |
|
|
|
Bugfix reference: https://github.com/NVlabs/eg3d/issues/67 |
|
""" |
|
return torch.tensor( |
|
[ |
|
[[1, 0, 0], [0, 1, 0], [0, 0, 1]], |
|
[[1, 0, 0], [0, 0, 1], [0, 1, 0]], |
|
[[0, 0, 1], [0, 1, 0], [1, 0, 0]], |
|
], |
|
dtype=torch.float32, |
|
) |
|
|
|
|
|
def project_onto_planes(planes, coordinates): |
|
""" |
|
Does a projection of a 3D point onto a batch of 2D planes, |
|
returning 2D plane coordinates. |
|
|
|
Takes plane axes of shape n_planes, 3, 3 |
|
# Takes coordinates of shape N, M, 3 |
|
# returns projections of shape N*n_planes, M, 2 |
|
""" |
|
N, M, C = coordinates.shape |
|
n_planes, _, _ = planes.shape |
|
coordinates = ( |
|
coordinates.unsqueeze(1) |
|
.expand(-1, n_planes, -1, -1) |
|
.reshape(N * n_planes, M, 3) |
|
) |
|
inv_planes = ( |
|
torch.linalg.inv(planes) |
|
.unsqueeze(0) |
|
.expand(N, -1, -1, -1) |
|
.reshape(N * n_planes, 3, 3) |
|
) |
|
projections = torch.bmm(coordinates, inv_planes) |
|
return projections[..., :2] |
|
|
|
|
|
def sample_from_planes( |
|
plane_axes, |
|
plane_features, |
|
coordinates, |
|
mode="bilinear", |
|
padding_mode="zeros", |
|
box_warp=None, |
|
): |
|
assert padding_mode == "zeros" |
|
N, n_planes, C, H, W = plane_features.shape |
|
_, M, _ = coordinates.shape |
|
plane_features = plane_features.view(N * n_planes, C, H, W) |
|
dtype = plane_features.dtype |
|
|
|
coordinates = (2 / box_warp) * coordinates |
|
|
|
projected_coordinates = project_onto_planes(plane_axes, coordinates).unsqueeze(1) |
|
output_features = ( |
|
torch.nn.functional.grid_sample( |
|
plane_features, |
|
projected_coordinates.to(dtype), |
|
mode=mode, |
|
padding_mode=padding_mode, |
|
align_corners=False, |
|
) |
|
.permute(0, 3, 2, 1) |
|
.reshape(N, n_planes, M, C) |
|
) |
|
return output_features |
|
|
|
|
|
class OSGDecoder(nn.Module): |
|
""" |
|
Triplane decoder that gives RGB and sigma values from sampled features. |
|
Using ReLU here instead of Softplus in the original implementation. |
|
|
|
Reference: |
|
EG3D: https://github.com/NVlabs/eg3d/blob/main/eg3d/training/triplane.py#L112 |
|
""" |
|
|
|
def __init__( |
|
self, |
|
n_features: int, |
|
hidden_dim: int = 64, |
|
num_layers: int = 4, |
|
activation: nn.Module = nn.ReLU, |
|
): |
|
super().__init__() |
|
|
|
self.net_sdf = nn.Sequential( |
|
nn.Linear(3 * n_features, hidden_dim), |
|
activation(), |
|
*itertools.chain( |
|
*[ |
|
[ |
|
nn.Linear(hidden_dim, hidden_dim), |
|
activation(), |
|
] |
|
for _ in range(num_layers - 2) |
|
] |
|
), |
|
nn.Linear(hidden_dim, 1), |
|
) |
|
self.net_rgb = nn.Sequential( |
|
nn.Linear(3 * n_features, hidden_dim), |
|
activation(), |
|
*itertools.chain( |
|
*[ |
|
[ |
|
nn.Linear(hidden_dim, hidden_dim), |
|
activation(), |
|
] |
|
for _ in range(num_layers - 2) |
|
] |
|
), |
|
nn.Linear(hidden_dim, 3), |
|
) |
|
self.net_deformation = nn.Sequential( |
|
nn.Linear(3 * n_features, hidden_dim), |
|
activation(), |
|
*itertools.chain( |
|
*[ |
|
[ |
|
nn.Linear(hidden_dim, hidden_dim), |
|
activation(), |
|
] |
|
for _ in range(num_layers - 2) |
|
] |
|
), |
|
nn.Linear(hidden_dim, 3), |
|
) |
|
self.net_weight = nn.Sequential( |
|
nn.Linear(8 * 3 * n_features, hidden_dim), |
|
activation(), |
|
*itertools.chain( |
|
*[ |
|
[ |
|
nn.Linear(hidden_dim, hidden_dim), |
|
activation(), |
|
] |
|
for _ in range(num_layers - 2) |
|
] |
|
), |
|
nn.Linear(hidden_dim, 21), |
|
) |
|
|
|
|
|
for m in self.modules(): |
|
if isinstance(m, nn.Linear): |
|
nn.init.zeros_(m.bias) |
|
|
|
def get_geometry_prediction(self, sampled_features, flexicubes_indices): |
|
_N, n_planes, _M, _C = sampled_features.shape |
|
sampled_features = sampled_features.permute(0, 2, 1, 3).reshape( |
|
_N, _M, n_planes * _C |
|
) |
|
|
|
sdf = self.net_sdf(sampled_features) |
|
deformation = self.net_deformation(sampled_features) |
|
|
|
grid_features = torch.index_select( |
|
input=sampled_features, index=flexicubes_indices.reshape(-1), dim=1 |
|
) |
|
grid_features = grid_features.reshape( |
|
sampled_features.shape[0], |
|
flexicubes_indices.shape[0], |
|
flexicubes_indices.shape[1] * sampled_features.shape[-1], |
|
) |
|
weight = self.net_weight(grid_features) * 0.1 |
|
|
|
return sdf, deformation, weight |
|
|
|
def get_texture_prediction(self, sampled_features): |
|
_N, n_planes, _M, _C = sampled_features.shape |
|
sampled_features = sampled_features.permute(0, 2, 1, 3).reshape( |
|
_N, _M, n_planes * _C |
|
) |
|
|
|
rgb = self.net_rgb(sampled_features) |
|
rgb = ( |
|
torch.sigmoid(rgb) * (1 + 2 * 0.001) - 0.001 |
|
) |
|
|
|
return rgb |
|
|
|
|
|
class TriplaneSynthesizer(nn.Module): |
|
""" |
|
Synthesizer that renders a triplane volume with planes and a camera. |
|
|
|
Reference: |
|
EG3D: https://github.com/NVlabs/eg3d/blob/main/eg3d/training/triplane.py#L19 |
|
""" |
|
|
|
DEFAULT_RENDERING_KWARGS = { |
|
"ray_start": "auto", |
|
"ray_end": "auto", |
|
"box_warp": 2.0, |
|
"white_back": True, |
|
"disparity_space_sampling": False, |
|
"clamp_mode": "softplus", |
|
"sampler_bbox_min": -1.0, |
|
"sampler_bbox_max": 1.0, |
|
} |
|
|
|
def __init__(self, triplane_dim: int, samples_per_ray: int): |
|
super().__init__() |
|
|
|
|
|
self.triplane_dim = triplane_dim |
|
self.rendering_kwargs = { |
|
**self.DEFAULT_RENDERING_KWARGS, |
|
"depth_resolution": samples_per_ray // 2, |
|
"depth_resolution_importance": samples_per_ray // 2, |
|
} |
|
|
|
|
|
self.plane_axes = generate_planes() |
|
self.decoder = OSGDecoder(n_features=triplane_dim) |
|
|
|
def get_geometry_prediction(self, planes, sample_coordinates, flexicubes_indices): |
|
plane_axes = self.plane_axes.to(planes.device) |
|
sampled_features = sample_from_planes( |
|
plane_axes, |
|
planes, |
|
sample_coordinates, |
|
padding_mode="zeros", |
|
box_warp=self.rendering_kwargs["box_warp"], |
|
) |
|
|
|
sdf, deformation, weight = self.decoder.get_geometry_prediction( |
|
sampled_features, flexicubes_indices |
|
) |
|
return sdf, deformation, weight |
|
|
|
def get_texture_prediction(self, planes, sample_coordinates): |
|
plane_axes = self.plane_axes.to(planes.device) |
|
sampled_features = sample_from_planes( |
|
plane_axes, |
|
planes, |
|
sample_coordinates, |
|
padding_mode="zeros", |
|
box_warp=self.rendering_kwargs["box_warp"], |
|
) |
|
|
|
rgb = self.decoder.get_texture_prediction(sampled_features) |
|
return rgb |
|
|
|
|
|
dmc_table = [ |
|
[ |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 8, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 9, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 3, 8, 9, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[4, 7, 8, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 4, 7, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 9, -1, -1, -1, -1], |
|
[4, 7, 8, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 3, 4, 7, 9, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[4, 5, 9, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 8, -1, -1, -1, -1], |
|
[4, 5, 9, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 4, 5, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 3, 4, 5, 8, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[5, 7, 8, 9, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 5, 7, 9, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 5, 7, 8, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 3, 5, 7, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[2, 3, 11, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 2, 8, 11, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 9, -1, -1, -1, -1], |
|
[2, 3, 11, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 2, 8, 9, 11, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[4, 7, 8, -1, -1, -1, -1], |
|
[2, 3, 11, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 2, 4, 7, 11, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 9, -1, -1, -1, -1], |
|
[4, 7, 8, -1, -1, -1, -1], |
|
[2, 3, 11, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 2, 4, 7, 9, 11, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[4, 5, 9, -1, -1, -1, -1], |
|
[2, 3, 11, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 2, 8, 11, -1, -1, -1], |
|
[4, 5, 9, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 4, 5, -1, -1, -1], |
|
[2, 3, 11, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 2, 4, 5, 8, 11, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[5, 7, 8, 9, -1, -1, -1], |
|
[2, 3, 11, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 2, 5, 7, 9, 11, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 5, 7, 8, -1, -1], |
|
[2, 3, 11, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 2, 5, 7, 11, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 2, 10, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 8, -1, -1, -1, -1], |
|
[1, 2, 10, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 2, 9, 10, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[2, 3, 8, 9, 10, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[4, 7, 8, -1, -1, -1, -1], |
|
[1, 2, 10, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 4, 7, -1, -1, -1], |
|
[1, 2, 10, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 2, 9, 10, -1, -1, -1], |
|
[4, 7, 8, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[2, 3, 4, 7, 9, 10, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[4, 5, 9, -1, -1, -1, -1], |
|
[1, 2, 10, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 8, -1, -1, -1, -1], |
|
[4, 5, 9, -1, -1, -1, -1], |
|
[1, 2, 10, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 2, 4, 5, 10, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[2, 3, 4, 5, 8, 10, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[5, 7, 8, 9, -1, -1, -1], |
|
[1, 2, 10, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 5, 7, 9, -1, -1], |
|
[1, 2, 10, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 2, 5, 7, 8, 10, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[2, 3, 5, 7, 10, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 3, 10, 11, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 8, 10, 11, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 9, 10, 11, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[8, 9, 10, 11, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[4, 7, 8, -1, -1, -1, -1], |
|
[1, 3, 10, 11, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 4, 7, 10, 11, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 9, 10, 11, -1, -1], |
|
[4, 7, 8, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[4, 7, 9, 10, 11, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[4, 5, 9, -1, -1, -1, -1], |
|
[1, 3, 10, 11, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 8, 10, 11, -1, -1], |
|
[4, 5, 9, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 4, 5, 10, 11, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[4, 5, 8, 10, 11, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[5, 7, 8, 9, -1, -1, -1], |
|
[1, 3, 10, 11, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 5, 7, 9, 10, 11], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 5, 7, 8, 10, 11], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[5, 7, 10, 11, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[6, 7, 11, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 8, -1, -1, -1, -1], |
|
[6, 7, 11, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 9, -1, -1, -1, -1], |
|
[6, 7, 11, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 3, 8, 9, -1, -1, -1], |
|
[6, 7, 11, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[4, 6, 8, 11, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 4, 6, 11, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 9, -1, -1, -1, -1], |
|
[4, 6, 8, 11, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 3, 4, 6, 9, 11, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[4, 5, 9, -1, -1, -1, -1], |
|
[6, 7, 11, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 8, -1, -1, -1, -1], |
|
[4, 5, 9, -1, -1, -1, -1], |
|
[6, 7, 11, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 4, 5, -1, -1, -1], |
|
[6, 7, 11, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 3, 4, 5, 8, -1, -1], |
|
[6, 7, 11, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[5, 6, 8, 9, 11, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 5, 6, 9, 11, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 5, 6, 8, 11, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 3, 5, 6, 11, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[2, 3, 6, 7, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 2, 6, 7, 8, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 9, -1, -1, -1, -1], |
|
[2, 3, 6, 7, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 2, 6, 7, 8, 9, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[2, 3, 4, 6, 8, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 2, 4, 6, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 9, -1, -1, -1, -1], |
|
[2, 3, 4, 6, 8, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 2, 4, 6, 9, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[4, 5, 9, -1, -1, -1, -1], |
|
[2, 3, 6, 7, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 2, 6, 7, 8, -1, -1], |
|
[4, 5, 9, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 4, 5, -1, -1, -1], |
|
[2, 3, 6, 7, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 2, 4, 5, 6, 7, 8], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[2, 3, 5, 6, 8, 9, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 2, 5, 6, 9, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 2, 3, 5, 6, 8], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 2, 5, 6, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 2, 10, -1, -1, -1, -1], |
|
[6, 7, 11, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 8, -1, -1, -1, -1], |
|
[1, 2, 10, -1, -1, -1, -1], |
|
[6, 7, 11, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 2, 9, 10, -1, -1, -1], |
|
[6, 7, 11, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[2, 3, 8, 9, 10, -1, -1], |
|
[6, 7, 11, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[4, 6, 8, 11, -1, -1, -1], |
|
[1, 2, 10, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 4, 6, 11, -1, -1], |
|
[1, 2, 10, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 2, 9, 10, -1, -1, -1], |
|
[4, 6, 8, 11, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[2, 3, 4, 6, 9, 10, 11], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[4, 5, 9, -1, -1, -1, -1], |
|
[1, 2, 10, -1, -1, -1, -1], |
|
[6, 7, 11, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 8, -1, -1, -1, -1], |
|
[4, 5, 9, -1, -1, -1, -1], |
|
[1, 2, 10, -1, -1, -1, -1], |
|
[6, 7, 11, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 2, 4, 5, 10, -1, -1], |
|
[6, 7, 11, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[2, 3, 4, 5, 8, 10, -1], |
|
[6, 7, 11, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[5, 6, 8, 9, 11, -1, -1], |
|
[1, 2, 10, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 5, 6, 9, 11, -1], |
|
[1, 2, 10, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 2, 5, 6, 8, 10, 11], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[2, 3, 5, 6, 10, 11, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 3, 6, 7, 10, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 6, 7, 8, 10, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 6, 7, 9, 10, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[6, 7, 8, 9, 10, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 3, 4, 6, 8, 10, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 4, 6, 10, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 4, 6, 8, 9, 10], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[4, 6, 9, 10, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[4, 5, 9, -1, -1, -1, -1], |
|
[1, 3, 6, 7, 10, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 6, 7, 8, 10, -1], |
|
[4, 5, 9, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 4, 5, 6, 7, 10], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[4, 5, 6, 7, 8, 10, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 3, 5, 6, 8, 9, 10], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 5, 6, 9, 10, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 8, -1, -1, -1, -1], |
|
[5, 6, 10, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[5, 6, 10, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[5, 6, 10, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 8, -1, -1, -1, -1], |
|
[5, 6, 10, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 9, -1, -1, -1, -1], |
|
[5, 6, 10, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 3, 8, 9, -1, -1, -1], |
|
[5, 6, 10, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[4, 7, 8, -1, -1, -1, -1], |
|
[5, 6, 10, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 4, 7, -1, -1, -1], |
|
[5, 6, 10, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 9, -1, -1, -1, -1], |
|
[4, 7, 8, -1, -1, -1, -1], |
|
[5, 6, 10, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 3, 4, 7, 9, -1, -1], |
|
[5, 6, 10, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[4, 6, 9, 10, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 8, -1, -1, -1, -1], |
|
[4, 6, 9, 10, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 4, 6, 10, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 3, 4, 6, 8, 10, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[6, 7, 8, 9, 10, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 6, 7, 9, 10, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 6, 7, 8, 10, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 3, 6, 7, 10, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[2, 3, 11, -1, -1, -1, -1], |
|
[5, 6, 10, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 2, 8, 11, -1, -1, -1], |
|
[5, 6, 10, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 9, -1, -1, -1, -1], |
|
[2, 3, 11, -1, -1, -1, -1], |
|
[5, 6, 10, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 2, 8, 9, 11, -1, -1], |
|
[5, 6, 10, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[4, 7, 8, -1, -1, -1, -1], |
|
[2, 3, 11, -1, -1, -1, -1], |
|
[5, 6, 10, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 2, 4, 7, 11, -1, -1], |
|
[5, 6, 10, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 9, -1, -1, -1, -1], |
|
[4, 7, 8, -1, -1, -1, -1], |
|
[2, 3, 11, -1, -1, -1, -1], |
|
[5, 6, 10, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 2, 4, 7, 9, 11, -1], |
|
[5, 6, 10, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[4, 6, 9, 10, -1, -1, -1], |
|
[2, 3, 11, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 2, 8, 11, -1, -1, -1], |
|
[4, 6, 9, 10, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 4, 6, 10, -1, -1], |
|
[2, 3, 11, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 2, 4, 6, 8, 10, 11], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[6, 7, 8, 9, 10, -1, -1], |
|
[2, 3, 11, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 2, 6, 7, 9, 10, 11], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 6, 7, 8, 10, -1], |
|
[2, 3, 11, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 2, 6, 7, 10, 11, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 2, 5, 6, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 8, -1, -1, -1, -1], |
|
[1, 2, 5, 6, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 2, 5, 6, 9, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[2, 3, 5, 6, 8, 9, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[4, 7, 8, -1, -1, -1, -1], |
|
[1, 2, 5, 6, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 4, 7, -1, -1, -1], |
|
[1, 2, 5, 6, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 2, 5, 6, 9, -1, -1], |
|
[4, 7, 8, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[2, 3, 4, 5, 6, 7, 9], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 2, 4, 6, 9, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 8, -1, -1, -1, -1], |
|
[1, 2, 4, 6, 9, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 2, 4, 6, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[2, 3, 4, 6, 8, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 2, 6, 7, 8, 9, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 2, 3, 6, 7, 9], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 2, 6, 7, 8, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[2, 3, 6, 7, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 3, 5, 6, 11, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 5, 6, 8, 11, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 5, 6, 9, 11, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[5, 6, 8, 9, 11, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[4, 7, 8, -1, -1, -1, -1], |
|
[1, 3, 5, 6, 11, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 4, 5, 6, 7, 11], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 5, 6, 9, 11, -1], |
|
[4, 7, 8, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[4, 5, 6, 7, 9, 11, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 3, 4, 6, 9, 11, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 4, 6, 8, 9, 11], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 4, 6, 11, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[4, 6, 8, 11, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 3, 6, 7, 8, 9, 11], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 9, -1, -1, -1, -1], |
|
[6, 7, 11, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 6, 7, 8, 11, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[6, 7, 11, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[5, 7, 10, 11, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 8, -1, -1, -1, -1], |
|
[5, 7, 10, 11, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 9, -1, -1, -1, -1], |
|
[5, 7, 10, 11, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 3, 8, 9, -1, -1, -1], |
|
[5, 7, 10, 11, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[4, 5, 8, 10, 11, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 4, 5, 10, 11, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 9, -1, -1, -1, -1], |
|
[4, 5, 8, 10, 11, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 3, 4, 5, 9, 10, 11], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[4, 7, 9, 10, 11, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 8, -1, -1, -1, -1], |
|
[4, 7, 9, 10, 11, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 4, 7, 10, 11, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 3, 4, 7, 8, 10, 11], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[8, 9, 10, 11, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 9, 10, 11, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 8, 10, 11, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 3, 10, 11, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[2, 3, 5, 7, 10, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 2, 5, 7, 8, 10, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 9, -1, -1, -1, -1], |
|
[2, 3, 5, 7, 10, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 2, 5, 7, 8, 9, 10], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[2, 3, 4, 5, 8, 10, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 2, 4, 5, 10, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 9, -1, -1, -1, -1], |
|
[2, 3, 4, 5, 8, 10, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 2, 4, 5, 9, 10, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[2, 3, 4, 7, 9, 10, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 2, 4, 7, 8, 9, 10], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 2, 3, 4, 7, 10], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[4, 7, 8, -1, -1, -1, -1], |
|
[1, 2, 10, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[2, 3, 8, 9, 10, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 2, 9, 10, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 2, 3, 8, 10, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 2, 10, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 2, 5, 7, 11, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 8, -1, -1, -1, -1], |
|
[1, 2, 5, 7, 11, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 2, 5, 7, 9, 11, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[2, 3, 5, 7, 8, 9, 11], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 2, 4, 5, 8, 11, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 2, 3, 4, 5, 11], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 2, 4, 5, 8, 9, 11], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[4, 5, 9, -1, -1, -1, -1], |
|
[2, 3, 11, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 2, 4, 7, 9, 11, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 8, -1, -1, -1, -1], |
|
[1, 2, 4, 7, 9, 11, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 2, 4, 7, 11, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[2, 3, 4, 7, 8, 11, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 2, 8, 9, 11, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 2, 3, 9, 11, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 2, 8, 11, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[2, 3, 11, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 3, 5, 7, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 5, 7, 8, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 5, 7, 9, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[5, 7, 8, 9, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 3, 4, 5, 8, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 4, 5, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 4, 5, 8, 9, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[4, 5, 9, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 3, 4, 7, 9, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 4, 7, 8, 9, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 4, 7, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[4, 7, 8, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[1, 3, 8, 9, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 1, 9, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[0, 3, 8, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
[ |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
[-1, -1, -1, -1, -1, -1, -1], |
|
], |
|
] |
|
num_vd_table = [ |
|
0, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
2, |
|
1, |
|
1, |
|
2, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
2, |
|
1, |
|
2, |
|
1, |
|
3, |
|
1, |
|
2, |
|
2, |
|
2, |
|
1, |
|
2, |
|
1, |
|
2, |
|
1, |
|
1, |
|
2, |
|
1, |
|
1, |
|
2, |
|
2, |
|
2, |
|
1, |
|
2, |
|
3, |
|
1, |
|
1, |
|
2, |
|
2, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
2, |
|
1, |
|
2, |
|
1, |
|
2, |
|
2, |
|
1, |
|
1, |
|
2, |
|
1, |
|
1, |
|
1, |
|
1, |
|
2, |
|
2, |
|
2, |
|
1, |
|
1, |
|
2, |
|
1, |
|
2, |
|
3, |
|
2, |
|
2, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
2, |
|
1, |
|
1, |
|
1, |
|
2, |
|
1, |
|
2, |
|
2, |
|
2, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
2, |
|
3, |
|
2, |
|
2, |
|
2, |
|
2, |
|
2, |
|
1, |
|
3, |
|
4, |
|
2, |
|
2, |
|
2, |
|
2, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
2, |
|
2, |
|
1, |
|
1, |
|
1, |
|
1, |
|
2, |
|
1, |
|
1, |
|
2, |
|
2, |
|
2, |
|
2, |
|
2, |
|
3, |
|
2, |
|
1, |
|
2, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
2, |
|
2, |
|
3, |
|
2, |
|
3, |
|
2, |
|
4, |
|
2, |
|
2, |
|
2, |
|
2, |
|
1, |
|
2, |
|
1, |
|
2, |
|
1, |
|
1, |
|
2, |
|
1, |
|
1, |
|
2, |
|
2, |
|
2, |
|
1, |
|
1, |
|
2, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
2, |
|
1, |
|
2, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
2, |
|
1, |
|
1, |
|
1, |
|
2, |
|
2, |
|
2, |
|
1, |
|
1, |
|
2, |
|
1, |
|
1, |
|
2, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
2, |
|
1, |
|
1, |
|
1, |
|
2, |
|
1, |
|
1, |
|
1, |
|
1, |
|
2, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
2, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
2, |
|
1, |
|
2, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
0, |
|
] |
|
check_table = [ |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[1, 1, 0, 0, 194], |
|
[1, -1, 0, 0, 193], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[1, 0, 1, 0, 164], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[1, 0, -1, 0, 161], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[1, 0, 0, 1, 152], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[1, 0, 0, 1, 145], |
|
[1, 0, 0, 1, 144], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[1, 0, 0, -1, 137], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[1, 0, 1, 0, 133], |
|
[1, 0, 1, 0, 132], |
|
[1, 1, 0, 0, 131], |
|
[1, 1, 0, 0, 130], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[1, 0, 0, 1, 100], |
|
[0, 0, 0, 0, 0], |
|
[1, 0, 0, 1, 98], |
|
[0, 0, 0, 0, 0], |
|
[1, 0, 0, 1, 96], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[1, 0, 1, 0, 88], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[1, 0, -1, 0, 82], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[1, 0, 1, 0, 74], |
|
[0, 0, 0, 0, 0], |
|
[1, 0, 1, 0, 72], |
|
[0, 0, 0, 0, 0], |
|
[1, 0, 0, -1, 70], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[1, -1, 0, 0, 67], |
|
[0, 0, 0, 0, 0], |
|
[1, -1, 0, 0, 65], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[1, 1, 0, 0, 56], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[1, -1, 0, 0, 52], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[1, 1, 0, 0, 44], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[1, 1, 0, 0, 40], |
|
[0, 0, 0, 0, 0], |
|
[1, 0, 0, -1, 38], |
|
[1, 0, -1, 0, 37], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[1, 0, -1, 0, 33], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[1, -1, 0, 0, 28], |
|
[0, 0, 0, 0, 0], |
|
[1, 0, -1, 0, 26], |
|
[1, 0, 0, -1, 25], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[1, -1, 0, 0, 20], |
|
[0, 0, 0, 0, 0], |
|
[1, 0, -1, 0, 18], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[1, 0, 0, -1, 9], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[1, 0, 0, -1, 6], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0], |
|
] |
|
tet_table = [ |
|
[-1, -1, -1, -1, -1, -1], |
|
[0, 0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0, 0], |
|
[1, 1, 1, 1, 1, 1], |
|
[4, 4, 4, 4, 4, 4], |
|
[0, 0, 0, 0, 0, 0], |
|
[4, 0, 0, 4, 4, -1], |
|
[1, 1, 1, 1, 1, 1], |
|
[4, 4, 4, 4, 4, 4], |
|
[0, 4, 0, 4, 4, -1], |
|
[0, 0, 0, 0, 0, 0], |
|
[1, 1, 1, 1, 1, 1], |
|
[5, 5, 5, 5, 5, 5], |
|
[0, 0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0, 0], |
|
[1, 1, 1, 1, 1, 1], |
|
[2, 2, 2, 2, 2, 2], |
|
[0, 0, 0, 0, 0, 0], |
|
[2, 0, 2, -1, 0, 2], |
|
[1, 1, 1, 1, 1, 1], |
|
[2, -1, 2, 4, 4, 2], |
|
[0, 0, 0, 0, 0, 0], |
|
[2, 0, 2, 4, 4, 2], |
|
[1, 1, 1, 1, 1, 1], |
|
[2, 4, 2, 4, 4, 2], |
|
[0, 4, 0, 4, 4, 0], |
|
[2, 0, 2, 0, 0, 2], |
|
[1, 1, 1, 1, 1, 1], |
|
[2, 5, 2, 5, 5, 2], |
|
[0, 0, 0, 0, 0, 0], |
|
[2, 0, 2, 0, 0, 2], |
|
[1, 1, 1, 1, 1, 1], |
|
[1, 1, 1, 1, 1, 1], |
|
[0, 1, 1, -1, 0, 1], |
|
[0, 0, 0, 0, 0, 0], |
|
[2, 2, 2, 2, 2, 2], |
|
[4, 1, 1, 4, 4, 1], |
|
[0, 1, 1, 0, 0, 1], |
|
[4, 0, 0, 4, 4, 0], |
|
[2, 2, 2, 2, 2, 2], |
|
[-1, 1, 1, 4, 4, 1], |
|
[0, 1, 1, 4, 4, 1], |
|
[0, 0, 0, 0, 0, 0], |
|
[2, 2, 2, 2, 2, 2], |
|
[5, 1, 1, 5, 5, 1], |
|
[0, 1, 1, 0, 0, 1], |
|
[0, 0, 0, 0, 0, 0], |
|
[2, 2, 2, 2, 2, 2], |
|
[1, 1, 1, 1, 1, 1], |
|
[0, 0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0, 0], |
|
[8, 8, 8, 8, 8, 8], |
|
[1, 1, 1, 4, 4, 1], |
|
[0, 0, 0, 0, 0, 0], |
|
[4, 0, 0, 4, 4, 0], |
|
[4, 4, 4, 4, 4, 4], |
|
[1, 1, 1, 4, 4, 1], |
|
[0, 4, 0, 4, 4, 0], |
|
[0, 0, 0, 0, 0, 0], |
|
[4, 4, 4, 4, 4, 4], |
|
[1, 1, 1, 5, 5, 1], |
|
[0, 0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0, 0], |
|
[5, 5, 5, 5, 5, 5], |
|
[6, 6, 6, 6, 6, 6], |
|
[6, -1, 0, 6, 0, 6], |
|
[6, 0, 0, 6, 0, 6], |
|
[6, 1, 1, 6, 1, 6], |
|
[4, 4, 4, 4, 4, 4], |
|
[0, 0, 0, 0, 0, 0], |
|
[4, 0, 0, 4, 4, 4], |
|
[1, 1, 1, 1, 1, 1], |
|
[6, 4, -1, 6, 4, 6], |
|
[6, 4, 0, 6, 4, 6], |
|
[6, 0, 0, 6, 0, 6], |
|
[6, 1, 1, 6, 1, 6], |
|
[5, 5, 5, 5, 5, 5], |
|
[0, 0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0, 0], |
|
[1, 1, 1, 1, 1, 1], |
|
[2, 2, 2, 2, 2, 2], |
|
[0, 0, 0, 0, 0, 0], |
|
[2, 0, 2, 2, 0, 2], |
|
[1, 1, 1, 1, 1, 1], |
|
[2, 2, 2, 2, 2, 2], |
|
[0, 0, 0, 0, 0, 0], |
|
[2, 0, 2, 2, 2, 2], |
|
[1, 1, 1, 1, 1, 1], |
|
[2, 4, 2, 2, 4, 2], |
|
[0, 4, 0, 4, 4, 0], |
|
[2, 0, 2, 2, 0, 2], |
|
[1, 1, 1, 1, 1, 1], |
|
[2, 2, 2, 2, 2, 2], |
|
[0, 0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0, 0], |
|
[1, 1, 1, 1, 1, 1], |
|
[6, 1, 1, 6, -1, 6], |
|
[6, 1, 1, 6, 0, 6], |
|
[6, 0, 0, 6, 0, 6], |
|
[6, 2, 2, 6, 2, 6], |
|
[4, 1, 1, 4, 4, 1], |
|
[0, 1, 1, 0, 0, 1], |
|
[4, 0, 0, 4, 4, 4], |
|
[2, 2, 2, 2, 2, 2], |
|
[6, 1, 1, 6, 4, 6], |
|
[6, 1, 1, 6, 4, 6], |
|
[6, 0, 0, 6, 0, 6], |
|
[6, 2, 2, 6, 2, 6], |
|
[5, 1, 1, 5, 5, 1], |
|
[0, 1, 1, 0, 0, 1], |
|
[0, 0, 0, 0, 0, 0], |
|
[2, 2, 2, 2, 2, 2], |
|
[1, 1, 1, 1, 1, 1], |
|
[0, 0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0, 0], |
|
[6, 6, 6, 6, 6, 6], |
|
[1, 1, 1, 1, 1, 1], |
|
[0, 0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0, 0], |
|
[4, 4, 4, 4, 4, 4], |
|
[1, 1, 1, 1, 4, 1], |
|
[0, 4, 0, 4, 4, 0], |
|
[0, 0, 0, 0, 0, 0], |
|
[4, 4, 4, 4, 4, 4], |
|
[1, 1, 1, 1, 1, 1], |
|
[0, 0, 0, 0, 0, 0], |
|
[0, 5, 0, 5, 0, 5], |
|
[5, 5, 5, 5, 5, 5], |
|
[5, 5, 5, 5, 5, 5], |
|
[0, 5, 0, 5, 0, 5], |
|
[-1, 5, 0, 5, 0, 5], |
|
[1, 5, 1, 5, 1, 5], |
|
[4, 5, -1, 5, 4, 5], |
|
[0, 5, 0, 5, 0, 5], |
|
[4, 5, 0, 5, 4, 5], |
|
[1, 5, 1, 5, 1, 5], |
|
[4, 4, 4, 4, 4, 4], |
|
[0, 4, 0, 4, 4, 4], |
|
[0, 0, 0, 0, 0, 0], |
|
[1, 1, 1, 1, 1, 1], |
|
[6, 6, 6, 6, 6, 6], |
|
[0, 0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0, 0], |
|
[1, 1, 1, 1, 1, 1], |
|
[2, 5, 2, 5, -1, 5], |
|
[0, 5, 0, 5, 0, 5], |
|
[2, 5, 2, 5, 0, 5], |
|
[1, 5, 1, 5, 1, 5], |
|
[2, 5, 2, 5, 4, 5], |
|
[0, 5, 0, 5, 0, 5], |
|
[2, 5, 2, 5, 4, 5], |
|
[1, 5, 1, 5, 1, 5], |
|
[2, 4, 2, 4, 4, 2], |
|
[0, 4, 0, 4, 4, 4], |
|
[2, 0, 2, 0, 0, 2], |
|
[1, 1, 1, 1, 1, 1], |
|
[2, 6, 2, 6, 6, 2], |
|
[0, 0, 0, 0, 0, 0], |
|
[2, 0, 2, 0, 0, 2], |
|
[1, 1, 1, 1, 1, 1], |
|
[1, 1, 1, 1, 1, 1], |
|
[0, 1, 1, 1, 0, 1], |
|
[0, 0, 0, 0, 0, 0], |
|
[2, 2, 2, 2, 2, 2], |
|
[4, 1, 1, 1, 4, 1], |
|
[0, 1, 1, 1, 0, 1], |
|
[4, 0, 0, 4, 4, 0], |
|
[2, 2, 2, 2, 2, 2], |
|
[1, 1, 1, 1, 1, 1], |
|
[0, 1, 1, 1, 1, 1], |
|
[0, 0, 0, 0, 0, 0], |
|
[2, 2, 2, 2, 2, 2], |
|
[1, 1, 1, 1, 1, 1], |
|
[0, 0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0, 0], |
|
[2, 2, 2, 2, 2, 2], |
|
[1, 1, 1, 1, 1, 1], |
|
[0, 0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0, 0], |
|
[5, 5, 5, 5, 5, 5], |
|
[1, 1, 1, 1, 4, 1], |
|
[0, 0, 0, 0, 0, 0], |
|
[4, 0, 0, 4, 4, 0], |
|
[4, 4, 4, 4, 4, 4], |
|
[1, 1, 1, 1, 1, 1], |
|
[0, 0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0, 0], |
|
[4, 4, 4, 4, 4, 4], |
|
[1, 1, 1, 1, 1, 1], |
|
[6, 0, 0, 6, 0, 6], |
|
[0, 0, 0, 0, 0, 0], |
|
[6, 6, 6, 6, 6, 6], |
|
[5, 5, 5, 5, 5, 5], |
|
[5, 5, 0, 5, 0, 5], |
|
[5, 5, 0, 5, 0, 5], |
|
[5, 5, 1, 5, 1, 5], |
|
[4, 4, 4, 4, 4, 4], |
|
[0, 0, 0, 0, 0, 0], |
|
[4, 4, 0, 4, 4, 4], |
|
[1, 1, 1, 1, 1, 1], |
|
[4, 4, 4, 4, 4, 4], |
|
[4, 4, 0, 4, 4, 4], |
|
[0, 0, 0, 0, 0, 0], |
|
[1, 1, 1, 1, 1, 1], |
|
[8, 8, 8, 8, 8, 8], |
|
[0, 0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0, 0], |
|
[1, 1, 1, 1, 1, 1], |
|
[2, 2, 2, 2, 2, 2], |
|
[0, 0, 0, 0, 0, 0], |
|
[2, 2, 2, 2, 0, 2], |
|
[1, 1, 1, 1, 1, 1], |
|
[2, 2, 2, 2, 2, 2], |
|
[0, 0, 0, 0, 0, 0], |
|
[2, 2, 2, 2, 2, 2], |
|
[1, 1, 1, 1, 1, 1], |
|
[2, 2, 2, 2, 2, 2], |
|
[0, 0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0, 0], |
|
[4, 1, 1, 4, 4, 1], |
|
[2, 2, 2, 2, 2, 2], |
|
[0, 0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0, 0], |
|
[1, 1, 1, 1, 1, 1], |
|
[1, 1, 1, 1, 1, 1], |
|
[1, 1, 1, 1, 0, 1], |
|
[0, 0, 0, 0, 0, 0], |
|
[2, 2, 2, 2, 2, 2], |
|
[1, 1, 1, 1, 1, 1], |
|
[0, 0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0, 0], |
|
[2, 4, 2, 4, 4, 2], |
|
[1, 1, 1, 1, 1, 1], |
|
[1, 1, 1, 1, 1, 1], |
|
[0, 0, 0, 0, 0, 0], |
|
[2, 2, 2, 2, 2, 2], |
|
[1, 1, 1, 1, 1, 1], |
|
[0, 0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0, 0], |
|
[2, 2, 2, 2, 2, 2], |
|
[1, 1, 1, 1, 1, 1], |
|
[0, 0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0, 0], |
|
[5, 5, 5, 5, 5, 5], |
|
[1, 1, 1, 1, 1, 1], |
|
[0, 0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0, 0], |
|
[4, 4, 4, 4, 4, 4], |
|
[1, 1, 1, 1, 1, 1], |
|
[0, 0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0, 0], |
|
[4, 4, 4, 4, 4, 4], |
|
[1, 1, 1, 1, 1, 1], |
|
[0, 0, 0, 0, 0, 0], |
|
[0, 0, 0, 0, 0, 0], |
|
[12, 12, 12, 12, 12, 12], |
|
] |
|
|
|
|
|
class FlexiCubes: |
|
def __init__(self, device="cuda", qef_reg_scale=1e-3, weight_scale=0.99): |
|
self.device = device |
|
self.dmc_table = torch.tensor( |
|
dmc_table, dtype=torch.long, device=device, requires_grad=False |
|
) |
|
self.num_vd_table = torch.tensor( |
|
num_vd_table, dtype=torch.long, device=device, requires_grad=False |
|
) |
|
self.check_table = torch.tensor( |
|
check_table, dtype=torch.long, device=device, requires_grad=False |
|
) |
|
|
|
self.tet_table = torch.tensor( |
|
tet_table, dtype=torch.long, device=device, requires_grad=False |
|
) |
|
self.quad_split_1 = torch.tensor( |
|
[0, 1, 2, 0, 2, 3], dtype=torch.long, device=device, requires_grad=False |
|
) |
|
self.quad_split_2 = torch.tensor( |
|
[0, 1, 3, 3, 1, 2], dtype=torch.long, device=device, requires_grad=False |
|
) |
|
self.quad_split_train = torch.tensor( |
|
[0, 1, 1, 2, 2, 3, 3, 0], |
|
dtype=torch.long, |
|
device=device, |
|
requires_grad=False, |
|
) |
|
|
|
self.cube_corners = torch.tensor( |
|
[ |
|
[0, 0, 0], |
|
[1, 0, 0], |
|
[0, 1, 0], |
|
[1, 1, 0], |
|
[0, 0, 1], |
|
[1, 0, 1], |
|
[0, 1, 1], |
|
[1, 1, 1], |
|
], |
|
dtype=torch.float, |
|
device=device, |
|
) |
|
self.cube_corners_idx = torch.pow(2, torch.arange(8, requires_grad=False)) |
|
self.cube_edges = torch.tensor( |
|
[0, 1, 1, 5, 4, 5, 0, 4, 2, 3, 3, 7, 6, 7, 2, 6, 2, 0, 3, 1, 7, 5, 6, 4], |
|
dtype=torch.long, |
|
device=device, |
|
requires_grad=False, |
|
) |
|
|
|
self.edge_dir_table = torch.tensor( |
|
[0, 2, 0, 2, 0, 2, 0, 2, 1, 1, 1, 1], dtype=torch.long, device=device |
|
) |
|
self.dir_faces_table = torch.tensor( |
|
[ |
|
[[5, 4], [3, 2], [4, 5], [2, 3]], |
|
[[5, 4], [1, 0], [4, 5], [0, 1]], |
|
[[3, 2], [1, 0], [2, 3], [0, 1]], |
|
], |
|
dtype=torch.long, |
|
device=device, |
|
) |
|
self.adj_pairs = torch.tensor( |
|
[0, 1, 1, 3, 3, 2, 2, 0], dtype=torch.long, device=device |
|
) |
|
self.qef_reg_scale = qef_reg_scale |
|
self.weight_scale = weight_scale |
|
|
|
def construct_voxel_grid(self, res): |
|
""" |
|
Generates a voxel grid based on the specified resolution. |
|
|
|
Args: |
|
res (int or list[int]): The resolution of the voxel grid. If an integer |
|
is provided, it is used for all three dimensions. If a list or tuple |
|
of 3 integers is provided, they define the resolution for the x, |
|
y, and z dimensions respectively. |
|
|
|
Returns: |
|
(torch.Tensor, torch.Tensor): Returns the vertices and the indices of the |
|
cube corners (index into vertices) of the constructed voxel grid. |
|
The vertices are centered at the origin, with the length of each |
|
dimension in the grid being one. |
|
""" |
|
base_cube_f = torch.arange(8).to(self.device) |
|
if isinstance(res, int): |
|
res = (res, res, res) |
|
voxel_grid_template = torch.ones(res, device=self.device) |
|
|
|
res = torch.tensor([res], dtype=torch.float, device=self.device) |
|
coords = torch.nonzero(voxel_grid_template).float() / res |
|
verts = (self.cube_corners.unsqueeze(0) / res + coords.unsqueeze(1)).reshape( |
|
-1, 3 |
|
) |
|
cubes = ( |
|
base_cube_f.unsqueeze(0) |
|
+ torch.arange(coords.shape[0], device=self.device).unsqueeze(1) * 8 |
|
).reshape(-1) |
|
|
|
verts_rounded = torch.round(verts * 10**5) / (10**5) |
|
verts_unique, inverse_indices = torch.unique( |
|
verts_rounded, dim=0, return_inverse=True |
|
) |
|
cubes = inverse_indices[cubes.reshape(-1)].reshape(-1, 8) |
|
|
|
return verts_unique - 0.5, cubes |
|
|
|
def __call__( |
|
self, |
|
x_nx3, |
|
s_n, |
|
cube_fx8, |
|
res, |
|
beta_fx12=None, |
|
alpha_fx8=None, |
|
gamma_f=None, |
|
training=False, |
|
output_tetmesh=False, |
|
grad_func=None, |
|
): |
|
r""" |
|
Main function for mesh extraction from scalar field using FlexiCubes. This function converts |
|
discrete signed distance fields, encoded on voxel grids and additional per-cube parameters, |
|
to triangle or tetrahedral meshes using a differentiable operation as described in |
|
`Flexible Isosurface Extraction for Gradient-Based Mesh Optimization`_. FlexiCubes enhances |
|
mesh quality and geometric fidelity by adjusting the surface representation based on gradient |
|
optimization. The output surface is differentiable with respect to the input vertex positions, |
|
scalar field values, and weight parameters. |
|
|
|
If you intend to extract a surface mesh from a fixed Signed Distance Field without the |
|
optimization of parameters, it is suggested to provide the "grad_func" which should |
|
return the surface gradient at any given 3D position. When grad_func is provided, the process |
|
to determine the dual vertex position adapts to solve a Quadratic Error Function (QEF), as |
|
described in the `Manifold Dual Contouring`_ paper, and employs an smart splitting strategy. |
|
Please note, this approach is non-differentiable. |
|
|
|
For more details and example usage in optimization, refer to the |
|
`Flexible Isosurface Extraction for Gradient-Based Mesh Optimization`_ SIGGRAPH 2023 paper. |
|
|
|
Args: |
|
x_nx3 (torch.Tensor): Coordinates of the voxel grid vertices, can be deformed. |
|
s_n (torch.Tensor): Scalar field values at each vertex of the voxel grid. Negative values |
|
denote that the corresponding vertex resides inside the isosurface. This affects |
|
the directions of the extracted triangle faces and volume to be tetrahedralized. |
|
cube_fx8 (torch.Tensor): Indices of 8 vertices for each cube in the voxel grid. |
|
res (int or list[int]): The resolution of the voxel grid. If an integer is provided, it |
|
is used for all three dimensions. If a list or tuple of 3 integers is provided, they |
|
specify the resolution for the x, y, and z dimensions respectively. |
|
beta_fx12 (torch.Tensor, optional): Weight parameters for the cube edges to adjust dual |
|
vertices positioning. Defaults to uniform value for all edges. |
|
alpha_fx8 (torch.Tensor, optional): Weight parameters for the cube corners to adjust dual |
|
vertices positioning. Defaults to uniform value for all vertices. |
|
gamma_f (torch.Tensor, optional): Weight parameters to control the splitting of |
|
quadrilaterals into triangles. Defaults to uniform value for all cubes. |
|
training (bool, optional): If set to True, applies differentiable quad splitting for |
|
training. Defaults to False. |
|
output_tetmesh (bool, optional): If set to True, outputs a tetrahedral mesh, otherwise, |
|
outputs a triangular mesh. Defaults to False. |
|
grad_func (callable, optional): A function to compute the surface gradient at specified |
|
3D positions (input: Nx3 positions). The function should return gradients as an Nx3 |
|
tensor. If None, the original FlexiCubes algorithm is utilized. Defaults to None. |
|
|
|
Returns: |
|
(torch.Tensor, torch.LongTensor, torch.Tensor): Tuple containing: |
|
- Vertices for the extracted triangular/tetrahedral mesh. |
|
- Faces for the extracted triangular/tetrahedral mesh. |
|
- Regularizer L_dev, computed per dual vertex. |
|
|
|
.. _Flexible Isosurface Extraction for Gradient-Based Mesh Optimization: |
|
https://research.nvidia.com/labs/toronto-ai/flexicubes/ |
|
.. _Manifold Dual Contouring: |
|
https://people.engr.tamu.edu/schaefer/research/dualsimp_tvcg.pdf |
|
""" |
|
|
|
surf_cubes, occ_fx8 = self._identify_surf_cubes(s_n, cube_fx8) |
|
if surf_cubes.sum() == 0: |
|
return ( |
|
torch.zeros((0, 3), device=self.device), |
|
( |
|
torch.zeros((0, 4), dtype=torch.long, device=self.device) |
|
if output_tetmesh |
|
else torch.zeros((0, 3), dtype=torch.long, device=self.device) |
|
), |
|
torch.zeros((0), device=self.device), |
|
) |
|
beta_fx12, alpha_fx8, gamma_f = self._normalize_weights( |
|
beta_fx12, alpha_fx8, gamma_f, surf_cubes |
|
) |
|
|
|
case_ids = self._get_case_id(occ_fx8, surf_cubes, res) |
|
|
|
surf_edges, idx_map, edge_counts, surf_edges_mask = self._identify_surf_edges( |
|
s_n, cube_fx8, surf_cubes |
|
) |
|
|
|
vd, L_dev, vd_gamma, vd_idx_map = self._compute_vd( |
|
x_nx3, |
|
cube_fx8[surf_cubes], |
|
surf_edges, |
|
s_n, |
|
case_ids, |
|
beta_fx12, |
|
alpha_fx8, |
|
gamma_f, |
|
idx_map, |
|
grad_func, |
|
) |
|
vertices, faces, s_edges, edge_indices = self._triangulate( |
|
s_n, |
|
surf_edges, |
|
vd, |
|
vd_gamma, |
|
edge_counts, |
|
idx_map, |
|
vd_idx_map, |
|
surf_edges_mask, |
|
training, |
|
grad_func, |
|
) |
|
if not output_tetmesh: |
|
return vertices, faces, L_dev |
|
else: |
|
vertices, tets = self._tetrahedralize( |
|
x_nx3, |
|
s_n, |
|
cube_fx8, |
|
vertices, |
|
faces, |
|
surf_edges, |
|
s_edges, |
|
vd_idx_map, |
|
case_ids, |
|
edge_indices, |
|
surf_cubes, |
|
training, |
|
) |
|
return vertices, tets, L_dev |
|
|
|
def _compute_reg_loss(self, vd, ue, edge_group_to_vd, vd_num_edges): |
|
""" |
|
Regularizer L_dev as in Equation 8 |
|
""" |
|
dist = torch.norm( |
|
ue - torch.index_select(input=vd, index=edge_group_to_vd, dim=0), dim=-1 |
|
) |
|
mean_l2 = torch.zeros_like(vd[:, 0]) |
|
mean_l2 = (mean_l2).index_add_( |
|
0, edge_group_to_vd, dist |
|
) / vd_num_edges.squeeze(1).float() |
|
mad = ( |
|
dist - torch.index_select(input=mean_l2, index=edge_group_to_vd, dim=0) |
|
).abs() |
|
return mad |
|
|
|
def _normalize_weights(self, beta_fx12, alpha_fx8, gamma_f, surf_cubes): |
|
""" |
|
Normalizes the given weights to be non-negative. If input weights are None, it creates and returns a set of weights of ones. |
|
""" |
|
n_cubes = surf_cubes.shape[0] |
|
|
|
if beta_fx12 is not None: |
|
beta_fx12 = torch.tanh(beta_fx12) * self.weight_scale + 1 |
|
else: |
|
beta_fx12 = torch.ones((n_cubes, 12), dtype=torch.float, device=self.device) |
|
|
|
if alpha_fx8 is not None: |
|
alpha_fx8 = torch.tanh(alpha_fx8) * self.weight_scale + 1 |
|
else: |
|
alpha_fx8 = torch.ones((n_cubes, 8), dtype=torch.float, device=self.device) |
|
|
|
if gamma_f is not None: |
|
gamma_f = ( |
|
torch.sigmoid(gamma_f) * self.weight_scale + (1 - self.weight_scale) / 2 |
|
) |
|
else: |
|
gamma_f = torch.ones((n_cubes), dtype=torch.float, device=self.device) |
|
|
|
return beta_fx12[surf_cubes], alpha_fx8[surf_cubes], gamma_f[surf_cubes] |
|
|
|
@torch.no_grad() |
|
def _get_case_id(self, occ_fx8, surf_cubes, res): |
|
""" |
|
Obtains the ID of topology cases based on cell corner occupancy. This function resolves the |
|
ambiguity in the Dual Marching Cubes (DMC) configurations as described in Section 1.3 of the |
|
supplementary material. It should be noted that this function assumes a regular grid. |
|
""" |
|
case_ids = ( |
|
occ_fx8[surf_cubes] * self.cube_corners_idx.to(self.device).unsqueeze(0) |
|
).sum(-1) |
|
|
|
problem_config = self.check_table.to(self.device)[case_ids] |
|
to_check = problem_config[..., 0] == 1 |
|
problem_config = problem_config[to_check] |
|
if not isinstance(res, (list, tuple)): |
|
res = [res, res, res] |
|
|
|
|
|
|
|
|
|
problem_config_full = torch.zeros( |
|
list(res) + [5], device=self.device, dtype=torch.long |
|
) |
|
vol_idx = torch.nonzero(problem_config_full[..., 0] == 0) |
|
vol_idx_problem = vol_idx[surf_cubes][to_check] |
|
problem_config_full[ |
|
vol_idx_problem[..., 0], vol_idx_problem[..., 1], vol_idx_problem[..., 2] |
|
] = problem_config |
|
vol_idx_problem_adj = vol_idx_problem + problem_config[..., 1:4] |
|
|
|
within_range = ( |
|
(vol_idx_problem_adj[..., 0] >= 0) |
|
& (vol_idx_problem_adj[..., 0] < res[0]) |
|
& (vol_idx_problem_adj[..., 1] >= 0) |
|
& (vol_idx_problem_adj[..., 1] < res[1]) |
|
& (vol_idx_problem_adj[..., 2] >= 0) |
|
& (vol_idx_problem_adj[..., 2] < res[2]) |
|
) |
|
|
|
vol_idx_problem = vol_idx_problem[within_range] |
|
vol_idx_problem_adj = vol_idx_problem_adj[within_range] |
|
problem_config = problem_config[within_range] |
|
problem_config_adj = problem_config_full[ |
|
vol_idx_problem_adj[..., 0], |
|
vol_idx_problem_adj[..., 1], |
|
vol_idx_problem_adj[..., 2], |
|
] |
|
|
|
to_invert = problem_config_adj[..., 0] == 1 |
|
idx = torch.arange(case_ids.shape[0], device=self.device)[to_check][ |
|
within_range |
|
][to_invert] |
|
case_ids.index_put_((idx,), problem_config[to_invert][..., -1]) |
|
return case_ids |
|
|
|
@torch.no_grad() |
|
def _identify_surf_edges(self, s_n, cube_fx8, surf_cubes): |
|
""" |
|
Identifies grid edges that intersect with the underlying surface by checking for opposite signs. As each edge |
|
can be shared by multiple cubes, this function also assigns a unique index to each surface-intersecting edge |
|
and marks the cube edges with this index. |
|
""" |
|
occ_n = s_n < 0 |
|
all_edges = cube_fx8[surf_cubes][:, self.cube_edges].reshape(-1, 2) |
|
unique_edges, _idx_map, counts = torch.unique( |
|
all_edges, dim=0, return_inverse=True, return_counts=True |
|
) |
|
|
|
unique_edges = unique_edges.long() |
|
mask_edges = occ_n[unique_edges.reshape(-1)].reshape(-1, 2).sum(-1) == 1 |
|
|
|
surf_edges_mask = mask_edges[_idx_map] |
|
counts = counts[_idx_map] |
|
|
|
mapping = ( |
|
torch.ones( |
|
(unique_edges.shape[0]), dtype=torch.long, device=cube_fx8.device |
|
) |
|
* -1 |
|
) |
|
mapping[mask_edges] = torch.arange(mask_edges.sum(), device=cube_fx8.device) |
|
|
|
|
|
idx_map = mapping[_idx_map] |
|
surf_edges = unique_edges[mask_edges] |
|
return surf_edges, idx_map, counts, surf_edges_mask |
|
|
|
@torch.no_grad() |
|
def _identify_surf_cubes(self, s_n, cube_fx8): |
|
""" |
|
Identifies grid cubes that intersect with the underlying surface by checking if the signs at |
|
all corners are not identical. |
|
""" |
|
occ_n = s_n < 0 |
|
occ_fx8 = occ_n[cube_fx8.reshape(-1)].reshape(-1, 8) |
|
_occ_sum = torch.sum(occ_fx8, -1) |
|
surf_cubes = (_occ_sum > 0) & (_occ_sum < 8) |
|
return surf_cubes, occ_fx8 |
|
|
|
def _linear_interp(self, edges_weight, edges_x): |
|
""" |
|
Computes the location of zero-crossings on 'edges_x' using linear interpolation with 'edges_weight'. |
|
""" |
|
edge_dim = edges_weight.dim() - 2 |
|
assert edges_weight.shape[edge_dim] == 2 |
|
edges_weight = torch.cat( |
|
[ |
|
torch.index_select( |
|
input=edges_weight, |
|
index=torch.tensor(1, device=self.device), |
|
dim=edge_dim, |
|
), |
|
-torch.index_select( |
|
input=edges_weight, |
|
index=torch.tensor(0, device=self.device), |
|
dim=edge_dim, |
|
), |
|
], |
|
edge_dim, |
|
) |
|
denominator = edges_weight.sum(edge_dim) |
|
ue = (edges_x * edges_weight).sum(edge_dim) / denominator |
|
return ue |
|
|
|
def _solve_vd_QEF(self, p_bxnx3, norm_bxnx3, c_bx3=None): |
|
p_bxnx3 = p_bxnx3.reshape(-1, 7, 3) |
|
norm_bxnx3 = norm_bxnx3.reshape(-1, 7, 3) |
|
c_bx3 = c_bx3.reshape(-1, 3) |
|
A = norm_bxnx3 |
|
B = ((p_bxnx3) * norm_bxnx3).sum(-1, keepdims=True) |
|
|
|
A_reg = ( |
|
(torch.eye(3, device=p_bxnx3.device) * self.qef_reg_scale) |
|
.unsqueeze(0) |
|
.repeat(p_bxnx3.shape[0], 1, 1) |
|
) |
|
B_reg = (self.qef_reg_scale * c_bx3).unsqueeze(-1) |
|
A = torch.cat([A, A_reg], 1) |
|
B = torch.cat([B, B_reg], 1) |
|
dual_verts = torch.linalg.lstsq(A, B).solution.squeeze(-1) |
|
return dual_verts |
|
|
|
def _compute_vd( |
|
self, |
|
x_nx3, |
|
surf_cubes_fx8, |
|
surf_edges, |
|
s_n, |
|
case_ids, |
|
beta_fx12, |
|
alpha_fx8, |
|
gamma_f, |
|
idx_map, |
|
grad_func, |
|
): |
|
""" |
|
Computes the location of dual vertices as described in Section 4.2 |
|
""" |
|
alpha_nx12x2 = torch.index_select( |
|
input=alpha_fx8, index=self.cube_edges, dim=1 |
|
).reshape(-1, 12, 2) |
|
surf_edges_x = torch.index_select( |
|
input=x_nx3, index=surf_edges.reshape(-1), dim=0 |
|
).reshape(-1, 2, 3) |
|
surf_edges_s = torch.index_select( |
|
input=s_n, index=surf_edges.reshape(-1), dim=0 |
|
).reshape(-1, 2, 1) |
|
zero_crossing = self._linear_interp(surf_edges_s, surf_edges_x) |
|
|
|
idx_map = idx_map.reshape(-1, 12) |
|
num_vd = torch.index_select(input=self.num_vd_table, index=case_ids, dim=0) |
|
edge_group, edge_group_to_vd, edge_group_to_cube, vd_num_edges, vd_gamma = ( |
|
[], |
|
[], |
|
[], |
|
[], |
|
[], |
|
) |
|
|
|
total_num_vd = 0 |
|
vd_idx_map = torch.zeros( |
|
(case_ids.shape[0], 12), |
|
dtype=torch.long, |
|
device=self.device, |
|
requires_grad=False, |
|
) |
|
if grad_func is not None: |
|
normals = torch.nn.functional.normalize(grad_func(zero_crossing), dim=-1) |
|
vd = [] |
|
for num in torch.unique(num_vd): |
|
cur_cubes = ( |
|
num_vd == num |
|
) |
|
curr_num_vd = cur_cubes.sum() * num |
|
curr_edge_group = self.dmc_table[case_ids[cur_cubes], :num].reshape( |
|
-1, num * 7 |
|
) |
|
curr_edge_group_to_vd = ( |
|
torch.arange(curr_num_vd, device=self.device).unsqueeze(-1).repeat(1, 7) |
|
+ total_num_vd |
|
) |
|
total_num_vd += curr_num_vd |
|
curr_edge_group_to_cube = ( |
|
torch.arange(idx_map.shape[0], device=self.device)[cur_cubes] |
|
.unsqueeze(-1) |
|
.repeat(1, num * 7) |
|
.reshape_as(curr_edge_group) |
|
) |
|
|
|
curr_mask = curr_edge_group != -1 |
|
edge_group.append(torch.masked_select(curr_edge_group, curr_mask)) |
|
edge_group_to_vd.append( |
|
torch.masked_select( |
|
curr_edge_group_to_vd.reshape_as(curr_edge_group), curr_mask |
|
) |
|
) |
|
edge_group_to_cube.append( |
|
torch.masked_select(curr_edge_group_to_cube, curr_mask) |
|
) |
|
vd_num_edges.append(curr_mask.reshape(-1, 7).sum(-1, keepdims=True)) |
|
vd_gamma.append( |
|
torch.masked_select(gamma_f, cur_cubes) |
|
.unsqueeze(-1) |
|
.repeat(1, num) |
|
.reshape(-1) |
|
) |
|
|
|
if grad_func is not None: |
|
with torch.no_grad(): |
|
cube_e_verts_idx = idx_map[cur_cubes] |
|
curr_edge_group[~curr_mask] = 0 |
|
|
|
verts_group_idx = torch.gather( |
|
input=cube_e_verts_idx, dim=1, index=curr_edge_group |
|
) |
|
verts_group_idx[verts_group_idx == -1] = 0 |
|
verts_group_pos = torch.index_select( |
|
input=zero_crossing, index=verts_group_idx.reshape(-1), dim=0 |
|
).reshape(-1, num.item(), 7, 3) |
|
v0 = ( |
|
x_nx3[surf_cubes_fx8[cur_cubes][:, 0]] |
|
.reshape(-1, 1, 1, 3) |
|
.repeat(1, num.item(), 1, 1) |
|
) |
|
curr_mask = curr_mask.reshape(-1, num.item(), 7, 1) |
|
verts_centroid = (verts_group_pos * curr_mask).sum(2) / ( |
|
curr_mask.sum(2) |
|
) |
|
|
|
normals_bx7x3 = torch.index_select( |
|
input=normals, index=verts_group_idx.reshape(-1), dim=0 |
|
).reshape(-1, num.item(), 7, 3) |
|
curr_mask = curr_mask.squeeze(2) |
|
vd.append( |
|
self._solve_vd_QEF( |
|
(verts_group_pos - v0) * curr_mask, |
|
normals_bx7x3 * curr_mask, |
|
verts_centroid - v0.squeeze(2), |
|
) |
|
+ v0.reshape(-1, 3) |
|
) |
|
edge_group = torch.cat(edge_group) |
|
edge_group_to_vd = torch.cat(edge_group_to_vd) |
|
edge_group_to_cube = torch.cat(edge_group_to_cube) |
|
vd_num_edges = torch.cat(vd_num_edges) |
|
vd_gamma = torch.cat(vd_gamma) |
|
|
|
if grad_func is not None: |
|
vd = torch.cat(vd) |
|
L_dev = torch.zeros([1], device=self.device) |
|
else: |
|
vd = torch.zeros((total_num_vd, 3), device=self.device) |
|
beta_sum = torch.zeros((total_num_vd, 1), device=self.device) |
|
|
|
idx_group = torch.gather( |
|
input=idx_map.reshape(-1), |
|
dim=0, |
|
index=edge_group_to_cube * 12 + edge_group, |
|
) |
|
|
|
x_group = torch.index_select( |
|
input=surf_edges_x, index=idx_group.reshape(-1), dim=0 |
|
).reshape(-1, 2, 3) |
|
s_group = torch.index_select( |
|
input=surf_edges_s, index=idx_group.reshape(-1), dim=0 |
|
).reshape(-1, 2, 1) |
|
|
|
zero_crossing_group = torch.index_select( |
|
input=zero_crossing, index=idx_group.reshape(-1), dim=0 |
|
).reshape(-1, 3) |
|
|
|
alpha_group = torch.index_select( |
|
input=alpha_nx12x2.reshape(-1, 2), |
|
dim=0, |
|
index=edge_group_to_cube * 12 + edge_group, |
|
).reshape(-1, 2, 1) |
|
ue_group = self._linear_interp(s_group * alpha_group, x_group) |
|
|
|
beta_group = torch.gather( |
|
input=beta_fx12.reshape(-1), |
|
dim=0, |
|
index=edge_group_to_cube * 12 + edge_group, |
|
).reshape(-1, 1) |
|
beta_sum = beta_sum.index_add_(0, index=edge_group_to_vd, source=beta_group) |
|
vd = ( |
|
vd.index_add_(0, index=edge_group_to_vd, source=ue_group * beta_group) |
|
/ beta_sum |
|
) |
|
L_dev = self._compute_reg_loss( |
|
vd, zero_crossing_group, edge_group_to_vd, vd_num_edges |
|
) |
|
|
|
v_idx = torch.arange(vd.shape[0], device=self.device) |
|
|
|
vd_idx_map = (vd_idx_map.reshape(-1)).scatter( |
|
dim=0, |
|
index=edge_group_to_cube * 12 + edge_group, |
|
src=v_idx[edge_group_to_vd], |
|
) |
|
|
|
return vd, L_dev, vd_gamma, vd_idx_map |
|
|
|
def _triangulate( |
|
self, |
|
s_n, |
|
surf_edges, |
|
vd, |
|
vd_gamma, |
|
edge_counts, |
|
idx_map, |
|
vd_idx_map, |
|
surf_edges_mask, |
|
training, |
|
grad_func, |
|
): |
|
""" |
|
Connects four neighboring dual vertices to form a quadrilateral. The quadrilaterals are then split into |
|
triangles based on the gamma parameter, as described in Section 4.3. |
|
""" |
|
with torch.no_grad(): |
|
group_mask = ( |
|
edge_counts == 4 |
|
) & surf_edges_mask |
|
group = idx_map.reshape(-1)[group_mask] |
|
vd_idx = vd_idx_map[group_mask] |
|
edge_indices, indices = torch.sort(group, stable=True) |
|
quad_vd_idx = vd_idx[indices].reshape(-1, 4) |
|
|
|
|
|
s_edges = s_n[ |
|
surf_edges[edge_indices.reshape(-1, 4)[:, 0]].reshape(-1) |
|
].reshape(-1, 2) |
|
flip_mask = s_edges[:, 0] > 0 |
|
quad_vd_idx = torch.cat( |
|
( |
|
quad_vd_idx[flip_mask][:, [0, 1, 3, 2]], |
|
quad_vd_idx[~flip_mask][:, [2, 3, 1, 0]], |
|
) |
|
) |
|
if grad_func is not None: |
|
|
|
with torch.no_grad(): |
|
vd_gamma = torch.nn.functional.normalize(grad_func(vd), dim=-1) |
|
quad_gamma = torch.index_select( |
|
input=vd_gamma, index=quad_vd_idx.reshape(-1), dim=0 |
|
).reshape(-1, 4, 3) |
|
gamma_02 = (quad_gamma[:, 0] * quad_gamma[:, 2]).sum(-1, keepdims=True) |
|
gamma_13 = (quad_gamma[:, 1] * quad_gamma[:, 3]).sum(-1, keepdims=True) |
|
else: |
|
quad_gamma = torch.index_select( |
|
input=vd_gamma, index=quad_vd_idx.reshape(-1), dim=0 |
|
).reshape(-1, 4) |
|
gamma_02 = torch.index_select( |
|
input=quad_gamma, index=torch.tensor(0, device=self.device), dim=1 |
|
) * torch.index_select( |
|
input=quad_gamma, index=torch.tensor(2, device=self.device), dim=1 |
|
) |
|
gamma_13 = torch.index_select( |
|
input=quad_gamma, index=torch.tensor(1, device=self.device), dim=1 |
|
) * torch.index_select( |
|
input=quad_gamma, index=torch.tensor(3, device=self.device), dim=1 |
|
) |
|
if not training: |
|
mask = (gamma_02 > gamma_13).squeeze(1) |
|
faces = torch.zeros( |
|
(quad_gamma.shape[0], 6), dtype=torch.long, device=quad_vd_idx.device |
|
) |
|
faces[mask] = quad_vd_idx[mask][:, self.quad_split_1] |
|
faces[~mask] = quad_vd_idx[~mask][:, self.quad_split_2] |
|
faces = faces.reshape(-1, 3) |
|
else: |
|
vd_quad = torch.index_select( |
|
input=vd, index=quad_vd_idx.reshape(-1), dim=0 |
|
).reshape(-1, 4, 3) |
|
vd_02 = ( |
|
torch.index_select( |
|
input=vd_quad, index=torch.tensor(0, device=self.device), dim=1 |
|
) |
|
+ torch.index_select( |
|
input=vd_quad, index=torch.tensor(2, device=self.device), dim=1 |
|
) |
|
) / 2 |
|
vd_13 = ( |
|
torch.index_select( |
|
input=vd_quad, index=torch.tensor(1, device=self.device), dim=1 |
|
) |
|
+ torch.index_select( |
|
input=vd_quad, index=torch.tensor(3, device=self.device), dim=1 |
|
) |
|
) / 2 |
|
weight_sum = (gamma_02 + gamma_13) + 1e-8 |
|
vd_center = ( |
|
(vd_02 * gamma_02.unsqueeze(-1) + vd_13 * gamma_13.unsqueeze(-1)) |
|
/ weight_sum.unsqueeze(-1) |
|
).squeeze(1) |
|
vd_center_idx = ( |
|
torch.arange(vd_center.shape[0], device=self.device) + vd.shape[0] |
|
) |
|
vd = torch.cat([vd, vd_center]) |
|
faces = quad_vd_idx[:, self.quad_split_train].reshape(-1, 4, 2) |
|
faces = torch.cat( |
|
[faces, vd_center_idx.reshape(-1, 1, 1).repeat(1, 4, 1)], -1 |
|
).reshape(-1, 3) |
|
return vd, faces, s_edges, edge_indices |
|
|
|
def _tetrahedralize( |
|
self, |
|
x_nx3, |
|
s_n, |
|
cube_fx8, |
|
vertices, |
|
faces, |
|
surf_edges, |
|
s_edges, |
|
vd_idx_map, |
|
case_ids, |
|
edge_indices, |
|
surf_cubes, |
|
training, |
|
): |
|
""" |
|
Tetrahedralizes the interior volume to produce a tetrahedral mesh, as described in Section 4.5. |
|
""" |
|
occ_n = s_n < 0 |
|
occ_fx8 = occ_n[cube_fx8.reshape(-1)].reshape(-1, 8) |
|
occ_sum = torch.sum(occ_fx8, -1) |
|
|
|
inside_verts = x_nx3[occ_n] |
|
mapping_inside_verts = ( |
|
torch.ones((occ_n.shape[0]), dtype=torch.long, device=self.device) * -1 |
|
) |
|
mapping_inside_verts[occ_n] = ( |
|
torch.arange(occ_n.sum(), device=self.device) + vertices.shape[0] |
|
) |
|
""" |
|
For each grid edge connecting two grid vertices with different |
|
signs, we first form a four-sided pyramid by connecting one |
|
of the grid vertices with four mesh vertices that correspond |
|
to the grid edge and then subdivide the pyramid into two tetrahedra |
|
""" |
|
inside_verts_idx = mapping_inside_verts[ |
|
surf_edges[edge_indices.reshape(-1, 4)[:, 0]].reshape(-1, 2)[s_edges < 0] |
|
] |
|
if not training: |
|
inside_verts_idx = inside_verts_idx.unsqueeze(1).expand(-1, 2).reshape(-1) |
|
else: |
|
inside_verts_idx = inside_verts_idx.unsqueeze(1).expand(-1, 4).reshape(-1) |
|
|
|
tets_surface = torch.cat([faces, inside_verts_idx.unsqueeze(-1)], -1) |
|
""" |
|
For each grid edge connecting two grid vertices with the |
|
same sign, the tetrahedron is formed by the two grid vertices |
|
and two vertices in consecutive adjacent cells |
|
""" |
|
inside_cubes = occ_sum == 8 |
|
inside_cubes_center = ( |
|
x_nx3[cube_fx8[inside_cubes].reshape(-1)].reshape(-1, 8, 3).mean(1) |
|
) |
|
inside_cubes_center_idx = ( |
|
torch.arange(inside_cubes_center.shape[0], device=inside_cubes.device) |
|
+ vertices.shape[0] |
|
+ inside_verts.shape[0] |
|
) |
|
|
|
surface_n_inside_cubes = surf_cubes | inside_cubes |
|
edge_center_vertex_idx = ( |
|
torch.ones( |
|
((surface_n_inside_cubes).sum(), 13), |
|
dtype=torch.long, |
|
device=x_nx3.device, |
|
) |
|
* -1 |
|
) |
|
surf_cubes = surf_cubes[surface_n_inside_cubes] |
|
inside_cubes = inside_cubes[surface_n_inside_cubes] |
|
edge_center_vertex_idx[surf_cubes, :12] = vd_idx_map.reshape(-1, 12) |
|
edge_center_vertex_idx[inside_cubes, 12] = inside_cubes_center_idx |
|
|
|
all_edges = cube_fx8[surface_n_inside_cubes][:, self.cube_edges].reshape(-1, 2) |
|
unique_edges, _idx_map, counts = torch.unique( |
|
all_edges, dim=0, return_inverse=True, return_counts=True |
|
) |
|
unique_edges = unique_edges.long() |
|
mask_edges = occ_n[unique_edges.reshape(-1)].reshape(-1, 2).sum(-1) == 2 |
|
mask = mask_edges[_idx_map] |
|
counts = counts[_idx_map] |
|
mapping = ( |
|
torch.ones((unique_edges.shape[0]), dtype=torch.long, device=self.device) |
|
* -1 |
|
) |
|
mapping[mask_edges] = torch.arange(mask_edges.sum(), device=self.device) |
|
idx_map = mapping[_idx_map] |
|
|
|
group_mask = (counts == 4) & mask |
|
group = idx_map.reshape(-1)[group_mask] |
|
edge_indices, indices = torch.sort(group) |
|
cube_idx = ( |
|
torch.arange( |
|
(_idx_map.shape[0] // 12), dtype=torch.long, device=self.device |
|
) |
|
.unsqueeze(1) |
|
.expand(-1, 12) |
|
.reshape(-1)[group_mask] |
|
) |
|
edge_idx = ( |
|
torch.arange((12), dtype=torch.long, device=self.device) |
|
.unsqueeze(0) |
|
.expand(_idx_map.shape[0] // 12, -1) |
|
.reshape(-1)[group_mask] |
|
) |
|
|
|
cube_idx_4 = cube_idx[indices].reshape(-1, 4) |
|
edge_dir = self.edge_dir_table[edge_idx[indices]].reshape(-1, 4)[..., 0] |
|
shared_faces_4x2 = self.dir_faces_table[edge_dir].reshape(-1) |
|
cube_idx_4x2 = cube_idx_4[:, self.adj_pairs].reshape(-1) |
|
|
|
|
|
case_ids_expand = ( |
|
torch.ones( |
|
(surface_n_inside_cubes).sum(), dtype=torch.long, device=x_nx3.device |
|
) |
|
* 255 |
|
) |
|
case_ids_expand[surf_cubes] = case_ids |
|
cases = case_ids_expand[cube_idx_4x2] |
|
quad_edge = edge_center_vertex_idx[ |
|
cube_idx_4x2, self.tet_table[cases, shared_faces_4x2] |
|
].reshape(-1, 2) |
|
mask = (quad_edge == -1).sum(-1) == 0 |
|
inside_edge = mapping_inside_verts[ |
|
unique_edges[mask_edges][edge_indices].reshape(-1) |
|
].reshape(-1, 2) |
|
tets_inside = torch.cat([quad_edge, inside_edge], -1)[mask] |
|
|
|
tets = torch.cat([tets_surface, tets_inside]) |
|
vertices = torch.cat([vertices, inside_verts, inside_cubes_center]) |
|
return vertices, tets |
|
|
|
|
|
def get_center_boundary_index(grid_res, device): |
|
v = torch.zeros( |
|
(grid_res + 1, grid_res + 1, grid_res + 1), dtype=torch.bool, device=device |
|
) |
|
v[grid_res // 2 + 1, grid_res // 2 + 1, grid_res // 2 + 1] = True |
|
center_indices = torch.nonzero(v.reshape(-1)) |
|
|
|
v[grid_res // 2 + 1, grid_res // 2 + 1, grid_res // 2 + 1] = False |
|
v[:2, ...] = True |
|
v[-2:, ...] = True |
|
v[:, :2, ...] = True |
|
v[:, -2:, ...] = True |
|
v[:, :, :2] = True |
|
v[:, :, -2:] = True |
|
boundary_indices = torch.nonzero(v.reshape(-1)) |
|
return center_indices, boundary_indices |
|
|
|
|
|
class Geometry: |
|
def __init__(self): |
|
pass |
|
|
|
def forward(self): |
|
pass |
|
|
|
|
|
class FlexiCubesGeometry(Geometry): |
|
def __init__( |
|
self, |
|
grid_res=64, |
|
scale=2.0, |
|
device="cuda", |
|
renderer=None, |
|
render_type="neural_render", |
|
args=None, |
|
): |
|
super(FlexiCubesGeometry, self).__init__() |
|
self.grid_res = grid_res |
|
self.device = device |
|
self.args = args |
|
self.fc = FlexiCubes(device, weight_scale=0.5) |
|
self.verts, self.indices = self.fc.construct_voxel_grid(grid_res) |
|
if isinstance(scale, list): |
|
self.verts[:, 0] = self.verts[:, 0] * scale[0] |
|
self.verts[:, 1] = self.verts[:, 1] * scale[1] |
|
self.verts[:, 2] = self.verts[:, 2] * scale[1] |
|
else: |
|
self.verts = self.verts * scale |
|
|
|
all_edges = self.indices[:, self.fc.cube_edges].reshape(-1, 2) |
|
self.all_edges = torch.unique(all_edges, dim=0) |
|
|
|
|
|
self.center_indices, self.boundary_indices = get_center_boundary_index( |
|
self.grid_res, device |
|
) |
|
self.renderer = renderer |
|
self.render_type = render_type |
|
|
|
def getAABB(self): |
|
return torch.min(self.verts, dim=0).values, torch.max(self.verts, dim=0).values |
|
|
|
def get_mesh( |
|
self, |
|
v_deformed_nx3, |
|
sdf_n, |
|
weight_n=None, |
|
with_uv=False, |
|
indices=None, |
|
is_training=False, |
|
): |
|
if indices is None: |
|
indices = self.indices |
|
|
|
verts, faces, v_reg_loss = self.fc( |
|
v_deformed_nx3, |
|
sdf_n, |
|
indices, |
|
self.grid_res, |
|
beta_fx12=weight_n[:, :12], |
|
alpha_fx8=weight_n[:, 12:20], |
|
gamma_f=weight_n[:, 20], |
|
training=is_training, |
|
) |
|
return verts, faces, v_reg_loss |
|
|
|
def render_mesh( |
|
self, |
|
mesh_v_nx3, |
|
mesh_f_fx3, |
|
camera_mv_bx4x4, |
|
resolution=256, |
|
hierarchical_mask=False, |
|
): |
|
return_value = dict() |
|
if self.render_type == "neural_render": |
|
tex_pos, mask, hard_mask, rast, v_pos_clip, mask_pyramid, depth, normal = ( |
|
self.renderer.render_mesh( |
|
mesh_v_nx3.unsqueeze(dim=0), |
|
mesh_f_fx3.int(), |
|
camera_mv_bx4x4, |
|
mesh_v_nx3.unsqueeze(dim=0), |
|
resolution=resolution, |
|
device=self.device, |
|
hierarchical_mask=hierarchical_mask, |
|
) |
|
) |
|
|
|
return_value["tex_pos"] = tex_pos |
|
return_value["mask"] = mask |
|
return_value["hard_mask"] = hard_mask |
|
return_value["rast"] = rast |
|
return_value["v_pos_clip"] = v_pos_clip |
|
return_value["mask_pyramid"] = mask_pyramid |
|
return_value["depth"] = depth |
|
return_value["normal"] = normal |
|
else: |
|
raise NotImplementedError |
|
|
|
return return_value |
|
|
|
def render( |
|
self, |
|
v_deformed_bxnx3=None, |
|
sdf_bxn=None, |
|
camera_mv_bxnviewx4x4=None, |
|
resolution=256, |
|
): |
|
|
|
v_list = [] |
|
f_list = [] |
|
n_batch = v_deformed_bxnx3.shape[0] |
|
all_render_output = [] |
|
for i_batch in range(n_batch): |
|
verts_nx3, faces_fx3 = self.get_mesh( |
|
v_deformed_bxnx3[i_batch], sdf_bxn[i_batch] |
|
) |
|
v_list.append(verts_nx3) |
|
f_list.append(faces_fx3) |
|
render_output = self.render_mesh( |
|
verts_nx3, faces_fx3, camera_mv_bxnviewx4x4[i_batch], resolution |
|
) |
|
all_render_output.append(render_output) |
|
|
|
|
|
return_keys = all_render_output[0].keys() |
|
return_value = dict() |
|
for k in return_keys: |
|
value = [v[k] for v in all_render_output] |
|
return_value[k] = value |
|
|
|
return return_value |
|
|
|
|
|
def interpolate(attr, rast, attr_idx, rast_db=None): |
|
return dr.interpolate( |
|
attr.contiguous(), |
|
rast, |
|
attr_idx, |
|
rast_db=rast_db, |
|
diff_attrs=None if rast_db is None else "all", |
|
) |
|
|
|
|
|
def xfm_points(points, matrix, use_python=True): |
|
"""Transform points. |
|
Args: |
|
points: Tensor containing 3D points with shape [minibatch_size, num_vertices, 3] or [1, num_vertices, 3] |
|
matrix: A 4x4 transform matrix with shape [minibatch_size, 4, 4] |
|
use_python: Use PyTorch's torch.matmul (for validation) |
|
Returns: |
|
Transformed points in homogeneous 4D with shape [minibatch_size, num_vertices, 4]. |
|
""" |
|
out = torch.matmul( |
|
torch.nn.functional.pad(points, pad=(0, 1), mode="constant", value=1.0), |
|
torch.transpose(matrix, 1, 2), |
|
) |
|
if torch.is_anomaly_enabled(): |
|
assert torch.all( |
|
torch.isfinite(out) |
|
), "Output of xfm_points contains inf or NaN" |
|
return out |
|
|
|
|
|
def dot(x, y): |
|
return torch.sum(x * y, -1, keepdim=True) |
|
|
|
|
|
def compute_vertex_normal(v_pos, t_pos_idx): |
|
i0 = t_pos_idx[:, 0] |
|
i1 = t_pos_idx[:, 1] |
|
i2 = t_pos_idx[:, 2] |
|
|
|
v0 = v_pos[i0, :] |
|
v1 = v_pos[i1, :] |
|
v2 = v_pos[i2, :] |
|
|
|
face_normals = torch.cross(v1 - v0, v2 - v0) |
|
|
|
|
|
v_nrm = torch.zeros_like(v_pos) |
|
v_nrm.scatter_add_(0, i0[:, None].repeat(1, 3), face_normals) |
|
v_nrm.scatter_add_(0, i1[:, None].repeat(1, 3), face_normals) |
|
v_nrm.scatter_add_(0, i2[:, None].repeat(1, 3), face_normals) |
|
|
|
|
|
v_nrm = torch.where( |
|
dot(v_nrm, v_nrm) > 1e-20, v_nrm, torch.as_tensor([0.0, 0.0, 1.0]).to(v_nrm) |
|
) |
|
v_nrm = F.normalize(v_nrm, dim=1) |
|
assert torch.all(torch.isfinite(v_nrm)) |
|
|
|
return v_nrm |
|
|
|
|
|
class Renderer: |
|
def __init__(self): |
|
pass |
|
|
|
def forward(self): |
|
pass |
|
|
|
|
|
class NeuralRender(Renderer): |
|
def __init__(self, device="cuda", camera_model=None): |
|
super(NeuralRender, self).__init__() |
|
self.device = device |
|
self.ctx = dr.RasterizeCudaContext(device=device) |
|
self.projection_mtx = None |
|
self.camera = camera_model |
|
|
|
def render_mesh( |
|
self, |
|
mesh_v_pos_bxnx3, |
|
mesh_t_pos_idx_fx3, |
|
camera_mv_bx4x4, |
|
mesh_v_feat_bxnxd, |
|
resolution=256, |
|
spp=1, |
|
device="cuda", |
|
hierarchical_mask=False, |
|
): |
|
assert not hierarchical_mask |
|
|
|
mtx_in = ( |
|
torch.tensor(camera_mv_bx4x4, dtype=torch.float32, device=device) |
|
if not torch.is_tensor(camera_mv_bx4x4) |
|
else camera_mv_bx4x4 |
|
) |
|
v_pos = xfm_points(mesh_v_pos_bxnx3, mtx_in) |
|
v_pos_clip = self.camera.project(v_pos) |
|
|
|
v_nrm = compute_vertex_normal( |
|
mesh_v_pos_bxnx3[0], mesh_t_pos_idx_fx3.long() |
|
) |
|
|
|
|
|
|
|
num_layers = 1 |
|
mask_pyramid = None |
|
assert mesh_t_pos_idx_fx3.shape[0] > 0 |
|
mesh_v_feat_bxnxd = torch.cat( |
|
[mesh_v_feat_bxnxd.repeat(v_pos.shape[0], 1, 1), v_pos], dim=-1 |
|
) |
|
|
|
with dr.DepthPeeler( |
|
self.ctx, |
|
v_pos_clip, |
|
mesh_t_pos_idx_fx3, |
|
[resolution * spp, resolution * spp], |
|
) as peeler: |
|
for _ in range(num_layers): |
|
rast, db = peeler.rasterize_next_layer() |
|
gb_feat, _ = interpolate(mesh_v_feat_bxnxd, rast, mesh_t_pos_idx_fx3) |
|
|
|
hard_mask = torch.clamp(rast[..., -1:], 0, 1) |
|
antialias_mask = dr.antialias( |
|
hard_mask.clone().contiguous(), rast, v_pos_clip, mesh_t_pos_idx_fx3 |
|
) |
|
|
|
depth = gb_feat[..., -2:-1] |
|
ori_mesh_feature = gb_feat[..., :-4] |
|
|
|
normal, _ = interpolate(v_nrm[None, ...], rast, mesh_t_pos_idx_fx3) |
|
normal = dr.antialias( |
|
normal.clone().contiguous(), rast, v_pos_clip, mesh_t_pos_idx_fx3 |
|
) |
|
normal = F.normalize(normal, dim=-1) |
|
normal = torch.lerp( |
|
torch.zeros_like(normal), (normal + 1.0) / 2.0, hard_mask.float() |
|
) |
|
|
|
return ( |
|
ori_mesh_feature, |
|
antialias_mask, |
|
hard_mask, |
|
rast, |
|
v_pos_clip, |
|
mask_pyramid, |
|
depth, |
|
normal, |
|
) |
|
|
|
|
|
def projection(x=0.1, n=1.0, f=50.0, near_plane=None): |
|
if near_plane is None: |
|
near_plane = n |
|
return np.array( |
|
[ |
|
[n / x, 0, 0, 0], |
|
[0, n / -x, 0, 0], |
|
[ |
|
0, |
|
0, |
|
-(f + near_plane) / (f - near_plane), |
|
-(2 * f * near_plane) / (f - near_plane), |
|
], |
|
[0, 0, -1, 0], |
|
] |
|
).astype(np.float32) |
|
|
|
|
|
class Camera(nn.Module): |
|
def __init__(self): |
|
super(Camera, self).__init__() |
|
pass |
|
|
|
|
|
class PerspectiveCamera(Camera): |
|
def __init__(self, fovy=49.0, device="cuda"): |
|
super(PerspectiveCamera, self).__init__() |
|
self.device = device |
|
focal = np.tan(fovy / 180.0 * np.pi * 0.5) |
|
self.proj_mtx = ( |
|
torch.from_numpy(projection(x=focal, f=1000.0, n=1.0, near_plane=0.1)) |
|
.to(self.device) |
|
.unsqueeze(dim=0) |
|
) |
|
|
|
def project(self, points_bxnx4): |
|
out = torch.matmul(points_bxnx4, torch.transpose(self.proj_mtx, 1, 2)) |
|
return out |
|
|
|
|
|
class ViTEmbeddings(nn.Module): |
|
def __init__(self, config: ViTConfig, use_mask_token: bool = False) -> None: |
|
super().__init__() |
|
|
|
self.cls_token = nn.Parameter(torch.randn(1, 1, config.hidden_size)) |
|
self.mask_token = ( |
|
nn.Parameter(torch.zeros(1, 1, config.hidden_size)) |
|
if use_mask_token |
|
else None |
|
) |
|
self.patch_embeddings = ViTPatchEmbeddings(config) |
|
num_patches = self.patch_embeddings.num_patches |
|
self.position_embeddings = nn.Parameter( |
|
torch.randn(1, num_patches + 1, config.hidden_size) |
|
) |
|
self.dropout = nn.Dropout(config.hidden_dropout_prob) |
|
self.config = config |
|
|
|
def interpolate_pos_encoding( |
|
self, embeddings: torch.Tensor, height: int, width: int |
|
) -> torch.Tensor: |
|
""" |
|
This method allows to interpolate the pre-trained position encodings, to be able to use the model on higher |
|
resolution images. |
|
|
|
Source: |
|
https://github.com/facebookresearch/dino/blob/de9ee3df6cf39fac952ab558447af1fa1365362a/vision_transformer.py#L174 |
|
""" |
|
|
|
num_patches = embeddings.shape[1] - 1 |
|
num_positions = self.position_embeddings.shape[1] - 1 |
|
if num_patches == num_positions and height == width: |
|
return self.position_embeddings |
|
class_pos_embed = self.position_embeddings[:, 0] |
|
patch_pos_embed = self.position_embeddings[:, 1:] |
|
dim = embeddings.shape[-1] |
|
h0 = height // self.config.patch_size |
|
w0 = width // self.config.patch_size |
|
|
|
|
|
h0, w0 = h0 + 0.1, w0 + 0.1 |
|
patch_pos_embed = patch_pos_embed.reshape( |
|
1, int(math.sqrt(num_positions)), int(math.sqrt(num_positions)), dim |
|
) |
|
patch_pos_embed = patch_pos_embed.permute(0, 3, 1, 2) |
|
patch_pos_embed = nn.functional.interpolate( |
|
patch_pos_embed, |
|
scale_factor=(h0 / math.sqrt(num_positions), w0 / math.sqrt(num_positions)), |
|
mode="bicubic", |
|
align_corners=False, |
|
) |
|
assert ( |
|
int(h0) == patch_pos_embed.shape[-2] |
|
and int(w0) == patch_pos_embed.shape[-1] |
|
) |
|
patch_pos_embed = patch_pos_embed.permute(0, 2, 3, 1).view(1, -1, dim) |
|
return torch.cat((class_pos_embed.unsqueeze(0), patch_pos_embed), dim=1) |
|
|
|
def forward( |
|
self, |
|
pixel_values: torch.Tensor, |
|
bool_masked_pos: Optional[torch.BoolTensor] = None, |
|
interpolate_pos_encoding: bool = False, |
|
) -> torch.Tensor: |
|
batch_size, num_channels, height, width = pixel_values.shape |
|
embeddings = self.patch_embeddings( |
|
pixel_values, interpolate_pos_encoding=interpolate_pos_encoding |
|
) |
|
|
|
if bool_masked_pos is not None: |
|
seq_length = embeddings.shape[1] |
|
mask_tokens = self.mask_token.expand(batch_size, seq_length, -1) |
|
|
|
mask = bool_masked_pos.unsqueeze(-1).type_as(mask_tokens) |
|
embeddings = embeddings * (1.0 - mask) + mask_tokens * mask |
|
|
|
|
|
cls_tokens = self.cls_token.expand(batch_size, -1, -1) |
|
embeddings = torch.cat((cls_tokens, embeddings), dim=1) |
|
|
|
|
|
if interpolate_pos_encoding: |
|
embeddings = embeddings + self.interpolate_pos_encoding( |
|
embeddings, height, width |
|
) |
|
else: |
|
embeddings = embeddings + self.position_embeddings |
|
|
|
embeddings = self.dropout(embeddings) |
|
|
|
return embeddings |
|
|
|
|
|
class ViTPatchEmbeddings(nn.Module): |
|
""" |
|
This class turns `pixel_values` of shape `(batch_size, num_channels, height, width)` into the initial |
|
`hidden_states` (patch embeddings) of shape `(batch_size, seq_length, hidden_size)` to be consumed by a |
|
Transformer. |
|
""" |
|
|
|
def __init__(self, config): |
|
super().__init__() |
|
image_size, patch_size = config.image_size, config.patch_size |
|
num_channels, hidden_size = config.num_channels, config.hidden_size |
|
|
|
image_size = ( |
|
image_size |
|
if isinstance(image_size, collections.abc.Iterable) |
|
else (image_size, image_size) |
|
) |
|
patch_size = ( |
|
patch_size |
|
if isinstance(patch_size, collections.abc.Iterable) |
|
else (patch_size, patch_size) |
|
) |
|
num_patches = (image_size[1] // patch_size[1]) * ( |
|
image_size[0] // patch_size[0] |
|
) |
|
self.image_size = image_size |
|
self.patch_size = patch_size |
|
self.num_channels = num_channels |
|
self.num_patches = num_patches |
|
|
|
self.projection = nn.Conv2d( |
|
num_channels, hidden_size, kernel_size=patch_size, stride=patch_size |
|
) |
|
|
|
def forward( |
|
self, pixel_values: torch.Tensor, interpolate_pos_encoding: bool = False |
|
) -> torch.Tensor: |
|
batch_size, num_channels, height, width = pixel_values.shape |
|
if num_channels != self.num_channels: |
|
raise ValueError( |
|
"Make sure that the channel dimension of the pixel values match with the one set in the configuration." |
|
f" Expected {self.num_channels} but got {num_channels}." |
|
) |
|
if not interpolate_pos_encoding: |
|
if height != self.image_size[0] or width != self.image_size[1]: |
|
raise ValueError( |
|
f"Input image size ({height}*{width}) doesn't match model" |
|
f" ({self.image_size[0]}*{self.image_size[1]})." |
|
) |
|
embeddings = self.projection(pixel_values).flatten(2).transpose(1, 2) |
|
return embeddings |
|
|
|
|
|
class ViTSelfAttention(nn.Module): |
|
def __init__(self, config: ViTConfig) -> None: |
|
super().__init__() |
|
if config.hidden_size % config.num_attention_heads != 0 and not hasattr( |
|
config, "embedding_size" |
|
): |
|
raise ValueError( |
|
f"The hidden size {config.hidden_size,} is not a multiple of the number of attention " |
|
f"heads {config.num_attention_heads}." |
|
) |
|
|
|
self.num_attention_heads = config.num_attention_heads |
|
self.attention_head_size = int(config.hidden_size / config.num_attention_heads) |
|
self.all_head_size = self.num_attention_heads * self.attention_head_size |
|
|
|
self.query = nn.Linear( |
|
config.hidden_size, self.all_head_size, bias=config.qkv_bias |
|
) |
|
self.key = nn.Linear( |
|
config.hidden_size, self.all_head_size, bias=config.qkv_bias |
|
) |
|
self.value = nn.Linear( |
|
config.hidden_size, self.all_head_size, bias=config.qkv_bias |
|
) |
|
|
|
self.dropout = nn.Dropout(config.attention_probs_dropout_prob) |
|
|
|
def transpose_for_scores(self, x: torch.Tensor) -> torch.Tensor: |
|
new_x_shape = x.size()[:-1] + ( |
|
self.num_attention_heads, |
|
self.attention_head_size, |
|
) |
|
x = x.view(new_x_shape) |
|
return x.permute(0, 2, 1, 3) |
|
|
|
def forward( |
|
self, |
|
hidden_states, |
|
head_mask: Optional[torch.Tensor] = None, |
|
output_attentions: bool = False, |
|
) -> Union[Tuple[torch.Tensor, torch.Tensor], Tuple[torch.Tensor]]: |
|
mixed_query_layer = self.query(hidden_states) |
|
|
|
key_layer = self.transpose_for_scores(self.key(hidden_states)) |
|
value_layer = self.transpose_for_scores(self.value(hidden_states)) |
|
query_layer = self.transpose_for_scores(mixed_query_layer) |
|
|
|
|
|
attention_scores = torch.matmul(query_layer, key_layer.transpose(-1, -2)) |
|
|
|
attention_scores = attention_scores / math.sqrt(self.attention_head_size) |
|
|
|
|
|
attention_probs = nn.functional.softmax(attention_scores, dim=-1) |
|
|
|
|
|
|
|
attention_probs = self.dropout(attention_probs) |
|
|
|
|
|
if head_mask is not None: |
|
attention_probs = attention_probs * head_mask |
|
|
|
context_layer = torch.matmul(attention_probs, value_layer) |
|
|
|
context_layer = context_layer.permute(0, 2, 1, 3).contiguous() |
|
new_context_layer_shape = context_layer.size()[:-2] + (self.all_head_size,) |
|
context_layer = context_layer.view(new_context_layer_shape) |
|
|
|
outputs = ( |
|
(context_layer, attention_probs) if output_attentions else (context_layer,) |
|
) |
|
|
|
return outputs |
|
|
|
|
|
class ViTSelfOutput(nn.Module): |
|
""" |
|
The residual connection is defined in ViTLayer instead of here (as is the case with other models), due to the |
|
layernorm applied before each block. |
|
""" |
|
|
|
def __init__(self, config: ViTConfig) -> None: |
|
super().__init__() |
|
self.dense = nn.Linear(config.hidden_size, config.hidden_size) |
|
self.dropout = nn.Dropout(config.hidden_dropout_prob) |
|
|
|
def forward( |
|
self, hidden_states: torch.Tensor, input_tensor: torch.Tensor |
|
) -> torch.Tensor: |
|
hidden_states = self.dense(hidden_states) |
|
hidden_states = self.dropout(hidden_states) |
|
|
|
return hidden_states |
|
|
|
|
|
class ViTAttention(nn.Module): |
|
def __init__(self, config: ViTConfig) -> None: |
|
super().__init__() |
|
self.attention = ViTSelfAttention(config) |
|
self.output = ViTSelfOutput(config) |
|
self.pruned_heads = set() |
|
|
|
def prune_heads(self, heads: Set[int]) -> None: |
|
if len(heads) == 0: |
|
return |
|
heads, index = find_pruneable_heads_and_indices( |
|
heads, |
|
self.attention.num_attention_heads, |
|
self.attention.attention_head_size, |
|
self.pruned_heads, |
|
) |
|
|
|
|
|
self.attention.query = prune_linear_layer(self.attention.query, index) |
|
self.attention.key = prune_linear_layer(self.attention.key, index) |
|
self.attention.value = prune_linear_layer(self.attention.value, index) |
|
self.output.dense = prune_linear_layer(self.output.dense, index, dim=1) |
|
|
|
|
|
self.attention.num_attention_heads = self.attention.num_attention_heads - len( |
|
heads |
|
) |
|
self.attention.all_head_size = ( |
|
self.attention.attention_head_size * self.attention.num_attention_heads |
|
) |
|
self.pruned_heads = self.pruned_heads.union(heads) |
|
|
|
def forward( |
|
self, |
|
hidden_states: torch.Tensor, |
|
head_mask: Optional[torch.Tensor] = None, |
|
output_attentions: bool = False, |
|
) -> Union[Tuple[torch.Tensor, torch.Tensor], Tuple[torch.Tensor]]: |
|
self_outputs = self.attention(hidden_states, head_mask, output_attentions) |
|
|
|
attention_output = self.output(self_outputs[0], hidden_states) |
|
|
|
outputs = (attention_output,) + self_outputs[ |
|
1: |
|
] |
|
return outputs |
|
|
|
|
|
class ViTIntermediate(nn.Module): |
|
def __init__(self, config: ViTConfig) -> None: |
|
super().__init__() |
|
self.dense = nn.Linear(config.hidden_size, config.intermediate_size) |
|
if isinstance(config.hidden_act, str): |
|
self.intermediate_act_fn = ACT2FN[config.hidden_act] |
|
else: |
|
self.intermediate_act_fn = config.hidden_act |
|
|
|
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor: |
|
hidden_states = self.dense(hidden_states) |
|
hidden_states = self.intermediate_act_fn(hidden_states) |
|
|
|
return hidden_states |
|
|
|
|
|
class ViTOutput(nn.Module): |
|
def __init__(self, config: ViTConfig) -> None: |
|
super().__init__() |
|
self.dense = nn.Linear(config.intermediate_size, config.hidden_size) |
|
self.dropout = nn.Dropout(config.hidden_dropout_prob) |
|
|
|
def forward( |
|
self, hidden_states: torch.Tensor, input_tensor: torch.Tensor |
|
) -> torch.Tensor: |
|
hidden_states = self.dense(hidden_states) |
|
hidden_states = self.dropout(hidden_states) |
|
|
|
hidden_states = hidden_states + input_tensor |
|
|
|
return hidden_states |
|
|
|
|
|
def modulate(x, shift, scale): |
|
return x * (1 + scale.unsqueeze(1)) + shift.unsqueeze(1) |
|
|
|
|
|
class ViTLayer(nn.Module): |
|
"""This corresponds to the Block class in the timm implementation.""" |
|
|
|
def __init__(self, config: ViTConfig) -> None: |
|
super().__init__() |
|
self.chunk_size_feed_forward = config.chunk_size_feed_forward |
|
self.seq_len_dim = 1 |
|
self.attention = ViTAttention(config) |
|
self.intermediate = ViTIntermediate(config) |
|
self.output = ViTOutput(config) |
|
self.layernorm_before = nn.LayerNorm( |
|
config.hidden_size, eps=config.layer_norm_eps |
|
) |
|
self.layernorm_after = nn.LayerNorm( |
|
config.hidden_size, eps=config.layer_norm_eps |
|
) |
|
|
|
self.adaLN_modulation = nn.Sequential( |
|
nn.SiLU(), nn.Linear(config.hidden_size, 4 * config.hidden_size, bias=True) |
|
) |
|
nn.init.constant_(self.adaLN_modulation[-1].weight, 0) |
|
nn.init.constant_(self.adaLN_modulation[-1].bias, 0) |
|
|
|
def forward( |
|
self, |
|
hidden_states: torch.Tensor, |
|
adaln_input: torch.Tensor = None, |
|
head_mask: Optional[torch.Tensor] = None, |
|
output_attentions: bool = False, |
|
) -> Union[Tuple[torch.Tensor, torch.Tensor], Tuple[torch.Tensor]]: |
|
shift_msa, scale_msa, shift_mlp, scale_mlp = self.adaLN_modulation( |
|
adaln_input |
|
).chunk(4, dim=1) |
|
|
|
self_attention_outputs = self.attention( |
|
modulate( |
|
self.layernorm_before(hidden_states), shift_msa, scale_msa |
|
), |
|
head_mask, |
|
output_attentions=output_attentions, |
|
) |
|
attention_output = self_attention_outputs[0] |
|
outputs = self_attention_outputs[ |
|
1: |
|
] |
|
|
|
|
|
hidden_states = attention_output + hidden_states |
|
|
|
|
|
layer_output = modulate( |
|
self.layernorm_after(hidden_states), shift_mlp, scale_mlp |
|
) |
|
layer_output = self.intermediate(layer_output) |
|
|
|
|
|
layer_output = self.output(layer_output, hidden_states) |
|
|
|
outputs = (layer_output,) + outputs |
|
|
|
return outputs |
|
|
|
|
|
class ViTEncoder(nn.Module): |
|
def __init__(self, config: ViTConfig) -> None: |
|
super().__init__() |
|
self.config = config |
|
self.layer = nn.ModuleList( |
|
[ViTLayer(config) for _ in range(config.num_hidden_layers)] |
|
) |
|
self.gradient_checkpointing = False |
|
|
|
def forward( |
|
self, |
|
hidden_states: torch.Tensor, |
|
adaln_input: torch.Tensor = None, |
|
head_mask: Optional[torch.Tensor] = None, |
|
output_attentions: bool = False, |
|
output_hidden_states: bool = False, |
|
return_dict: bool = True, |
|
) -> Union[tuple, BaseModelOutput]: |
|
all_hidden_states = () if output_hidden_states else None |
|
all_self_attentions = () if output_attentions else None |
|
|
|
for i, layer_module in enumerate(self.layer): |
|
if output_hidden_states: |
|
all_hidden_states = all_hidden_states + (hidden_states,) |
|
|
|
layer_head_mask = head_mask[i] if head_mask is not None else None |
|
|
|
if self.gradient_checkpointing and self.training: |
|
layer_outputs = self._gradient_checkpointing_func( |
|
layer_module.__call__, |
|
hidden_states, |
|
adaln_input, |
|
layer_head_mask, |
|
output_attentions, |
|
) |
|
else: |
|
layer_outputs = layer_module( |
|
hidden_states, adaln_input, layer_head_mask, output_attentions |
|
) |
|
|
|
hidden_states = layer_outputs[0] |
|
|
|
if output_attentions: |
|
all_self_attentions = all_self_attentions + (layer_outputs[1],) |
|
|
|
if output_hidden_states: |
|
all_hidden_states = all_hidden_states + (hidden_states,) |
|
|
|
if not return_dict: |
|
return tuple( |
|
v |
|
for v in [hidden_states, all_hidden_states, all_self_attentions] |
|
if v is not None |
|
) |
|
return BaseModelOutput( |
|
last_hidden_state=hidden_states, |
|
hidden_states=all_hidden_states, |
|
attentions=all_self_attentions, |
|
) |
|
|
|
|
|
class ViTPreTrainedModel(PreTrainedModel): |
|
""" |
|
An abstract class to handle weights initialization and a simple interface for downloading and loading pretrained |
|
models. |
|
""" |
|
|
|
config_class = ViTConfig |
|
base_model_prefix = "vit" |
|
main_input_name = "pixel_values" |
|
supports_gradient_checkpointing = True |
|
_no_split_modules = ["ViTEmbeddings", "ViTLayer"] |
|
|
|
def _init_weights(self, module: Union[nn.Linear, nn.Conv2d, nn.LayerNorm]) -> None: |
|
"""Initialize the weights""" |
|
if isinstance(module, (nn.Linear, nn.Conv2d)): |
|
|
|
|
|
module.weight.data = nn.init.trunc_normal_( |
|
module.weight.data.to(torch.float32), |
|
mean=0.0, |
|
std=self.config.initializer_range, |
|
).to(module.weight.dtype) |
|
if module.bias is not None: |
|
module.bias.data.zero_() |
|
elif isinstance(module, nn.LayerNorm): |
|
module.bias.data.zero_() |
|
module.weight.data.fill_(1.0) |
|
elif isinstance(module, ViTEmbeddings): |
|
module.position_embeddings.data = nn.init.trunc_normal_( |
|
module.position_embeddings.data.to(torch.float32), |
|
mean=0.0, |
|
std=self.config.initializer_range, |
|
).to(module.position_embeddings.dtype) |
|
|
|
module.cls_token.data = nn.init.trunc_normal_( |
|
module.cls_token.data.to(torch.float32), |
|
mean=0.0, |
|
std=self.config.initializer_range, |
|
).to(module.cls_token.dtype) |
|
|
|
|
|
class ViTModel(ViTPreTrainedModel): |
|
def __init__( |
|
self, |
|
config: ViTConfig, |
|
add_pooling_layer: bool = True, |
|
use_mask_token: bool = False, |
|
): |
|
super().__init__(config) |
|
self.config = config |
|
|
|
self.embeddings = ViTEmbeddings(config, use_mask_token=use_mask_token) |
|
self.encoder = ViTEncoder(config) |
|
|
|
self.layernorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) |
|
self.pooler = ViTPooler(config) if add_pooling_layer else None |
|
|
|
|
|
self.post_init() |
|
|
|
def get_input_embeddings(self) -> ViTPatchEmbeddings: |
|
return self.embeddings.patch_embeddings |
|
|
|
def _prune_heads(self, heads_to_prune: Dict[int, List[int]]) -> None: |
|
""" |
|
Prunes heads of the model. heads_to_prune: dict of {layer_num: list of heads to prune in this layer} See base |
|
class PreTrainedModel |
|
""" |
|
for layer, heads in heads_to_prune.items(): |
|
self.encoder.layer[layer].attention.prune_heads(heads) |
|
|
|
def forward( |
|
self, |
|
pixel_values: Optional[torch.Tensor] = None, |
|
adaln_input: Optional[torch.Tensor] = None, |
|
bool_masked_pos: Optional[torch.BoolTensor] = None, |
|
head_mask: Optional[torch.Tensor] = None, |
|
output_attentions: Optional[bool] = None, |
|
output_hidden_states: Optional[bool] = None, |
|
interpolate_pos_encoding: Optional[bool] = None, |
|
return_dict: Optional[bool] = None, |
|
) -> Union[Tuple, BaseModelOutputWithPooling]: |
|
r""" |
|
bool_masked_pos (`torch.BoolTensor` of shape `(batch_size, num_patches)`, *optional*): |
|
Boolean masked positions. Indicates which patches are masked (1) and which aren't (0). |
|
""" |
|
output_attentions = ( |
|
output_attentions |
|
if output_attentions is not None |
|
else self.config.output_attentions |
|
) |
|
output_hidden_states = ( |
|
output_hidden_states |
|
if output_hidden_states is not None |
|
else self.config.output_hidden_states |
|
) |
|
return_dict = ( |
|
return_dict if return_dict is not None else self.config.use_return_dict |
|
) |
|
|
|
if pixel_values is None: |
|
raise ValueError("You have to specify pixel_values") |
|
|
|
|
|
|
|
|
|
|
|
|
|
head_mask = self.get_head_mask(head_mask, self.config.num_hidden_layers) |
|
|
|
|
|
expected_dtype = self.embeddings.patch_embeddings.projection.weight.dtype |
|
if pixel_values.dtype != expected_dtype: |
|
pixel_values = pixel_values.to(expected_dtype) |
|
|
|
embedding_output = self.embeddings( |
|
pixel_values, |
|
bool_masked_pos=bool_masked_pos, |
|
interpolate_pos_encoding=interpolate_pos_encoding, |
|
) |
|
|
|
encoder_outputs = self.encoder( |
|
embedding_output, |
|
adaln_input=adaln_input, |
|
head_mask=head_mask, |
|
output_attentions=output_attentions, |
|
output_hidden_states=output_hidden_states, |
|
return_dict=return_dict, |
|
) |
|
sequence_output = encoder_outputs[0] |
|
sequence_output = self.layernorm(sequence_output) |
|
pooled_output = ( |
|
self.pooler(sequence_output) if self.pooler is not None else None |
|
) |
|
|
|
if not return_dict: |
|
head_outputs = ( |
|
(sequence_output, pooled_output) |
|
if pooled_output is not None |
|
else (sequence_output,) |
|
) |
|
return head_outputs + encoder_outputs[1:] |
|
|
|
return BaseModelOutputWithPooling( |
|
last_hidden_state=sequence_output, |
|
pooler_output=pooled_output, |
|
hidden_states=encoder_outputs.hidden_states, |
|
attentions=encoder_outputs.attentions, |
|
) |
|
|
|
|
|
class ViTPooler(nn.Module): |
|
def __init__(self, config: ViTConfig): |
|
super().__init__() |
|
self.dense = nn.Linear(config.hidden_size, config.hidden_size) |
|
self.activation = nn.Tanh() |
|
|
|
def forward(self, hidden_states): |
|
|
|
|
|
first_token_tensor = hidden_states[:, 0] |
|
pooled_output = self.dense(first_token_tensor) |
|
pooled_output = self.activation(pooled_output) |
|
return pooled_output |
|
|
|
|
|
class DinoWrapper(nn.Module): |
|
def __init__(self, model_name: str, freeze: bool = True): |
|
super().__init__() |
|
self.model, self.processor = self._build_dino(model_name) |
|
self.camera_embedder = nn.Sequential( |
|
nn.Linear(16, self.model.config.hidden_size, bias=True), |
|
nn.SiLU(), |
|
nn.Linear( |
|
self.model.config.hidden_size, self.model.config.hidden_size, bias=True |
|
), |
|
) |
|
if freeze: |
|
self._freeze() |
|
|
|
def forward(self, image, camera): |
|
if image.ndim == 5: |
|
image = image.view(-1, *image.shape[2:]) |
|
dtype = image.dtype |
|
inputs = ( |
|
self.processor( |
|
images=image.float(), |
|
return_tensors="pt", |
|
do_rescale=False, |
|
do_resize=False, |
|
) |
|
.to(self.model.device) |
|
.to(dtype) |
|
) |
|
|
|
camera_embeddings = self.camera_embedder(camera) |
|
camera_embeddings = camera_embeddings.view(-1, camera_embeddings.shape[-1]) |
|
embeddings = camera_embeddings |
|
|
|
outputs = self.model( |
|
**inputs, adaln_input=embeddings, interpolate_pos_encoding=True |
|
) |
|
last_hidden_states = outputs.last_hidden_state |
|
return last_hidden_states |
|
|
|
def _freeze(self): |
|
self.model.eval() |
|
for name, param in self.model.named_parameters(): |
|
param.requires_grad = False |
|
|
|
@staticmethod |
|
def _build_dino( |
|
model_name: str, proxy_error_retries: int = 3, proxy_error_cooldown: int = 5 |
|
): |
|
import requests |
|
|
|
try: |
|
model = ViTModel.from_pretrained(model_name, add_pooling_layer=False) |
|
processor = ViTImageProcessor.from_pretrained(model_name) |
|
return model, processor |
|
except requests.exceptions.ProxyError as err: |
|
if proxy_error_retries > 0: |
|
print( |
|
f"Huggingface ProxyError: Retrying in {proxy_error_cooldown} seconds..." |
|
) |
|
import time |
|
|
|
time.sleep(proxy_error_cooldown) |
|
return DinoWrapper._build_dino( |
|
model_name, proxy_error_retries - 1, proxy_error_cooldown |
|
) |
|
else: |
|
raise err |
|
|
|
|
|
class BasicTransformerBlock(nn.Module): |
|
def __init__( |
|
self, |
|
inner_dim: int, |
|
cond_dim: int, |
|
num_heads: int, |
|
eps: float, |
|
attn_drop: float = 0.0, |
|
attn_bias: bool = False, |
|
mlp_ratio: float = 4.0, |
|
mlp_drop: float = 0.0, |
|
): |
|
super().__init__() |
|
|
|
self.norm1 = nn.LayerNorm(inner_dim) |
|
self.cross_attn = nn.MultiheadAttention( |
|
embed_dim=inner_dim, |
|
num_heads=num_heads, |
|
kdim=cond_dim, |
|
vdim=cond_dim, |
|
dropout=attn_drop, |
|
bias=attn_bias, |
|
batch_first=True, |
|
) |
|
self.norm2 = nn.LayerNorm(inner_dim) |
|
self.self_attn = nn.MultiheadAttention( |
|
embed_dim=inner_dim, |
|
num_heads=num_heads, |
|
dropout=attn_drop, |
|
bias=attn_bias, |
|
batch_first=True, |
|
) |
|
self.norm3 = nn.LayerNorm(inner_dim) |
|
self.mlp = nn.Sequential( |
|
nn.Linear(inner_dim, int(inner_dim * mlp_ratio)), |
|
nn.GELU(), |
|
nn.Dropout(mlp_drop), |
|
nn.Linear(int(inner_dim * mlp_ratio), inner_dim), |
|
nn.Dropout(mlp_drop), |
|
) |
|
|
|
def forward(self, x, cond): |
|
x = x + self.cross_attn(self.norm1(x), cond, cond)[0] |
|
before_sa = self.norm2(x) |
|
x = x + self.self_attn(before_sa, before_sa, before_sa)[0] |
|
x = x + self.mlp(self.norm3(x)) |
|
return x |
|
|
|
|
|
class TriplaneTransformer(nn.Module): |
|
def __init__( |
|
self, |
|
inner_dim: int, |
|
image_feat_dim: int, |
|
triplane_low_res: int, |
|
triplane_high_res: int, |
|
triplane_dim: int, |
|
num_layers: int, |
|
num_heads: int, |
|
eps: float = 1e-6, |
|
): |
|
super().__init__() |
|
|
|
self.triplane_low_res = triplane_low_res |
|
self.triplane_high_res = triplane_high_res |
|
self.triplane_dim = triplane_dim |
|
|
|
self.pos_embed = nn.Parameter( |
|
torch.randn(1, 3 * triplane_low_res**2, inner_dim) |
|
* (1.0 / inner_dim) ** 0.5 |
|
) |
|
self.layers = nn.ModuleList( |
|
[ |
|
BasicTransformerBlock( |
|
inner_dim=inner_dim, |
|
cond_dim=image_feat_dim, |
|
num_heads=num_heads, |
|
eps=eps, |
|
) |
|
for _ in range(num_layers) |
|
] |
|
) |
|
self.norm = nn.LayerNorm(inner_dim, eps=eps) |
|
self.deconv = nn.ConvTranspose2d( |
|
inner_dim, triplane_dim, kernel_size=2, stride=2, padding=0 |
|
) |
|
|
|
def forward(self, image_feats): |
|
|
|
N = image_feats.shape[0] |
|
H = W = self.triplane_low_res |
|
|
|
x = self.pos_embed.repeat(N, 1, 1) |
|
for layer in self.layers: |
|
x = layer(x, image_feats) |
|
x = self.norm(x) |
|
|
|
x = x.view(N, 3, H, W, -1) |
|
x = torch.einsum("nihwd->indhw", x) |
|
x = x.contiguous().view(3 * N, -1, H, W) |
|
x = self.deconv(x) |
|
x = x.view(3, N, *x.shape[-3:]) |
|
x = torch.einsum("indhw->nidhw", x) |
|
x = x.contiguous() |
|
|
|
return x |
|
|
|
|
|
def interpolate_atlas(attr, rast, attr_idx, rast_db=None): |
|
return dr.interpolate( |
|
attr.contiguous(), |
|
rast, |
|
attr_idx, |
|
rast_db=rast_db, |
|
diff_attrs=None if rast_db is None else "all", |
|
) |
|
|
|
|
|
def xatlas_uvmap(ctx, mesh_v, mesh_pos_idx, resolution): |
|
_, indices, uvs = xatlas.parametrize( |
|
mesh_v.detach().cpu().numpy(), mesh_pos_idx.detach().cpu().numpy() |
|
) |
|
|
|
indices_int64 = indices.astype(np.uint64, casting="same_kind").view(np.int64) |
|
|
|
uvs = torch.tensor(uvs, dtype=torch.float32, device=mesh_v.device) |
|
mesh_tex_idx = torch.tensor(indices_int64, dtype=torch.int64, device=mesh_v.device) |
|
uv_clip = uvs[None, ...] * 2.0 - 1.0 |
|
|
|
uv_clip4 = torch.cat( |
|
( |
|
uv_clip, |
|
torch.zeros_like(uv_clip[..., 0:1]), |
|
torch.ones_like(uv_clip[..., 0:1]), |
|
), |
|
dim=-1, |
|
) |
|
|
|
rast, _ = dr.rasterize(ctx, uv_clip4, mesh_tex_idx.int(), (resolution, resolution)) |
|
|
|
gb_pos, _ = interpolate_atlas(mesh_v[None, ...], rast, mesh_pos_idx.int()) |
|
mask = rast[..., 3:4] > 0 |
|
return uvs, mesh_tex_idx, gb_pos, mask |
|
|
|
|
|
class LRM(ModelMixin, ConfigMixin): |
|
def __init__( |
|
self, |
|
encoder_freeze: bool = False, |
|
encoder_model_name: str = "facebook/dino-vitb16", |
|
encoder_feat_dim: int = 768, |
|
transformer_dim: int = 1024, |
|
transformer_layers: int = 16, |
|
transformer_heads: int = 16, |
|
triplane_low_res: int = 32, |
|
triplane_high_res: int = 64, |
|
triplane_dim: int = 80, |
|
rendering_samples_per_ray: int = 128, |
|
grid_res: int = 128, |
|
grid_scale: float = 2.1, |
|
): |
|
super().__init__() |
|
|
|
self.grid_res = grid_res |
|
self.grid_scale = grid_scale |
|
self.deformation_multiplier = 4.0 |
|
|
|
self.encoder = DinoWrapper( |
|
model_name=encoder_model_name, |
|
freeze=encoder_freeze, |
|
) |
|
|
|
self.transformer = TriplaneTransformer( |
|
inner_dim=transformer_dim, |
|
num_layers=transformer_layers, |
|
num_heads=transformer_heads, |
|
image_feat_dim=encoder_feat_dim, |
|
triplane_low_res=triplane_low_res, |
|
triplane_high_res=triplane_high_res, |
|
triplane_dim=triplane_dim, |
|
) |
|
|
|
self.synthesizer = TriplaneSynthesizer( |
|
triplane_dim=triplane_dim, |
|
samples_per_ray=rendering_samples_per_ray, |
|
) |
|
|
|
def init_flexicubes_geometry(self, device, fovy=50.0): |
|
camera = PerspectiveCamera(fovy=fovy, device=device) |
|
renderer = NeuralRender(device, camera_model=camera) |
|
self.geometry = FlexiCubesGeometry( |
|
grid_res=self.grid_res, |
|
scale=self.grid_scale, |
|
renderer=renderer, |
|
render_type="neural_render", |
|
device=device, |
|
) |
|
|
|
def forward_planes(self, images, cameras): |
|
B = images.shape[0] |
|
|
|
image_feats = self.encoder(images, cameras) |
|
image_feats = image_feats.view(B, -1, image_feats.shape[-1]) |
|
|
|
planes = self.transformer(image_feats) |
|
|
|
return planes |
|
|
|
def get_sdf_deformation_prediction(self, planes): |
|
init_position = self.geometry.verts.unsqueeze(0).expand(planes.shape[0], -1, -1) |
|
|
|
sdf, deformation, weight = torch.utils.checkpoint.checkpoint( |
|
self.synthesizer.get_geometry_prediction, |
|
planes, |
|
init_position, |
|
self.geometry.indices, |
|
use_reentrant=False, |
|
) |
|
|
|
deformation = ( |
|
1.0 |
|
/ (self.grid_res * self.deformation_multiplier) |
|
* torch.tanh(deformation) |
|
) |
|
sdf_reg_loss = torch.zeros(sdf.shape[0], device=sdf.device, dtype=torch.float32) |
|
|
|
sdf_bxnxnxn = sdf.reshape( |
|
(sdf.shape[0], self.grid_res + 1, self.grid_res + 1, self.grid_res + 1) |
|
) |
|
sdf_less_boundary = sdf_bxnxnxn[:, 1:-1, 1:-1, 1:-1].reshape(sdf.shape[0], -1) |
|
pos_shape = torch.sum((sdf_less_boundary > 0).int(), dim=-1) |
|
neg_shape = torch.sum((sdf_less_boundary < 0).int(), dim=-1) |
|
zero_surface = torch.bitwise_or(pos_shape == 0, neg_shape == 0) |
|
if torch.sum(zero_surface).item() > 0: |
|
update_sdf = torch.zeros_like(sdf[0:1]) |
|
max_sdf = sdf.max() |
|
min_sdf = sdf.min() |
|
update_sdf[:, self.geometry.center_indices] += 1.0 - min_sdf |
|
update_sdf[:, self.geometry.boundary_indices] += -1 - max_sdf |
|
new_sdf = torch.zeros_like(sdf) |
|
for i_batch in range(zero_surface.shape[0]): |
|
if zero_surface[i_batch]: |
|
new_sdf[i_batch : i_batch + 1] += update_sdf |
|
update_mask = (new_sdf == 0).float() |
|
sdf_reg_loss = torch.abs(sdf).mean(dim=-1).mean(dim=-1) |
|
sdf_reg_loss = sdf_reg_loss * zero_surface.float() |
|
sdf = sdf * update_mask + new_sdf * (1 - update_mask) |
|
|
|
final_sdf = [] |
|
final_def = [] |
|
for i_batch in range(zero_surface.shape[0]): |
|
if zero_surface[i_batch]: |
|
final_sdf.append(sdf[i_batch : i_batch + 1].detach()) |
|
final_def.append(deformation[i_batch : i_batch + 1].detach()) |
|
else: |
|
final_sdf.append(sdf[i_batch : i_batch + 1]) |
|
final_def.append(deformation[i_batch : i_batch + 1]) |
|
sdf = torch.cat(final_sdf, dim=0) |
|
deformation = torch.cat(final_def, dim=0) |
|
return sdf, deformation, sdf_reg_loss, weight |
|
|
|
def get_geometry_prediction(self, planes=None): |
|
sdf, deformation, sdf_reg_loss, weight = self.get_sdf_deformation_prediction( |
|
planes |
|
) |
|
v_deformed = ( |
|
self.geometry.verts.unsqueeze(dim=0).expand(sdf.shape[0], -1, -1) |
|
+ deformation |
|
) |
|
tets = self.geometry.indices |
|
n_batch = planes.shape[0] |
|
v_list = [] |
|
f_list = [] |
|
flexicubes_surface_reg_list = [] |
|
|
|
for i_batch in range(n_batch): |
|
verts, faces, flexicubes_surface_reg = self.geometry.get_mesh( |
|
v_deformed[i_batch], |
|
sdf[i_batch].squeeze(dim=-1), |
|
with_uv=False, |
|
indices=tets, |
|
weight_n=weight[i_batch].squeeze(dim=-1), |
|
is_training=self.training, |
|
) |
|
flexicubes_surface_reg_list.append(flexicubes_surface_reg) |
|
v_list.append(verts) |
|
f_list.append(faces) |
|
|
|
flexicubes_surface_reg = torch.cat(flexicubes_surface_reg_list).mean() |
|
flexicubes_weight_reg = (weight**2).mean() |
|
|
|
return ( |
|
v_list, |
|
f_list, |
|
sdf, |
|
deformation, |
|
v_deformed, |
|
(sdf_reg_loss, flexicubes_surface_reg, flexicubes_weight_reg), |
|
) |
|
|
|
def get_texture_prediction(self, planes, tex_pos, hard_mask=None): |
|
tex_pos = torch.cat(tex_pos, dim=0) |
|
if hard_mask is not None: |
|
tex_pos = tex_pos * hard_mask.float() |
|
batch_size = tex_pos.shape[0] |
|
tex_pos = tex_pos.reshape(batch_size, -1, 3) |
|
if hard_mask is not None: |
|
n_point_list = torch.sum( |
|
hard_mask.long().reshape(hard_mask.shape[0], -1), dim=-1 |
|
) |
|
sample_tex_pose_list = [] |
|
max_point = n_point_list.max() |
|
expanded_hard_mask = ( |
|
hard_mask.reshape(batch_size, -1, 1).expand(-1, -1, 3) > 0.5 |
|
) |
|
for i in range(tex_pos.shape[0]): |
|
tex_pos_one_shape = tex_pos[i][expanded_hard_mask[i]].reshape(1, -1, 3) |
|
if tex_pos_one_shape.shape[1] < max_point: |
|
tex_pos_one_shape = torch.cat( |
|
[ |
|
tex_pos_one_shape, |
|
torch.zeros( |
|
1, |
|
max_point - tex_pos_one_shape.shape[1], |
|
3, |
|
device=tex_pos_one_shape.device, |
|
dtype=torch.float32, |
|
), |
|
], |
|
dim=1, |
|
) |
|
sample_tex_pose_list.append(tex_pos_one_shape) |
|
tex_pos = torch.cat(sample_tex_pose_list, dim=0) |
|
|
|
tex_feat = torch.utils.checkpoint.checkpoint( |
|
self.synthesizer.get_texture_prediction, |
|
planes, |
|
tex_pos, |
|
use_reentrant=False, |
|
) |
|
|
|
if hard_mask is not None: |
|
final_tex_feat = torch.zeros( |
|
planes.shape[0], |
|
hard_mask.shape[1] * hard_mask.shape[2], |
|
tex_feat.shape[-1], |
|
device=tex_feat.device, |
|
) |
|
expanded_hard_mask = ( |
|
hard_mask.reshape(hard_mask.shape[0], -1, 1).expand( |
|
-1, -1, final_tex_feat.shape[-1] |
|
) |
|
> 0.5 |
|
) |
|
for i in range(planes.shape[0]): |
|
final_tex_feat[i][expanded_hard_mask[i]] = tex_feat[i][ |
|
: n_point_list[i] |
|
].reshape(-1) |
|
tex_feat = final_tex_feat |
|
|
|
return tex_feat.reshape( |
|
planes.shape[0], hard_mask.shape[1], hard_mask.shape[2], tex_feat.shape[-1] |
|
) |
|
|
|
def render_mesh(self, mesh_v, mesh_f, cam_mv, render_size=256): |
|
return_value_list = [] |
|
for i_mesh in range(len(mesh_v)): |
|
return_value = self.geometry.render_mesh( |
|
mesh_v[i_mesh], |
|
mesh_f[i_mesh].int(), |
|
cam_mv[i_mesh], |
|
resolution=render_size, |
|
hierarchical_mask=False, |
|
) |
|
return_value_list.append(return_value) |
|
|
|
return_keys = return_value_list[0].keys() |
|
return_value = dict() |
|
for k in return_keys: |
|
value = [v[k] for v in return_value_list] |
|
return_value[k] = value |
|
|
|
mask = torch.cat(return_value["mask"], dim=0) |
|
hard_mask = torch.cat(return_value["hard_mask"], dim=0) |
|
tex_pos = return_value["tex_pos"] |
|
depth = torch.cat(return_value["depth"], dim=0) |
|
normal = torch.cat(return_value["normal"], dim=0) |
|
return mask, hard_mask, tex_pos, depth, normal |
|
|
|
def forward_geometry(self, planes, render_cameras, render_size=256): |
|
B, NV = render_cameras.shape[:2] |
|
|
|
mesh_v, mesh_f, sdf, _, _, sdf_reg_loss = self.get_geometry_prediction(planes) |
|
|
|
cam_mv = render_cameras |
|
run_n_view = cam_mv.shape[1] |
|
antilias_mask, hard_mask, tex_pos, depth, normal = self.render_mesh( |
|
mesh_v, mesh_f, cam_mv, render_size=render_size |
|
) |
|
|
|
tex_hard_mask = hard_mask |
|
tex_pos = [ |
|
torch.cat([pos[i_view : i_view + 1] for i_view in range(run_n_view)], dim=2) |
|
for pos in tex_pos |
|
] |
|
tex_hard_mask = torch.cat( |
|
[ |
|
torch.cat( |
|
[ |
|
tex_hard_mask[ |
|
i * run_n_view + i_view : i * run_n_view + i_view + 1 |
|
] |
|
for i_view in range(run_n_view) |
|
], |
|
dim=2, |
|
) |
|
for i in range(planes.shape[0]) |
|
], |
|
dim=0, |
|
) |
|
|
|
tex_feat = self.get_texture_prediction(planes, tex_pos, tex_hard_mask) |
|
background_feature = torch.ones_like(tex_feat) |
|
|
|
img_feat = tex_feat * tex_hard_mask + background_feature * (1 - tex_hard_mask) |
|
|
|
img_feat = torch.cat( |
|
[ |
|
torch.cat( |
|
[ |
|
img_feat[ |
|
i : i + 1, |
|
:, |
|
render_size * i_view : render_size * (i_view + 1), |
|
] |
|
for i_view in range(run_n_view) |
|
], |
|
dim=0, |
|
) |
|
for i in range(len(tex_pos)) |
|
], |
|
dim=0, |
|
) |
|
|
|
img = img_feat.clamp(0, 1).permute(0, 3, 1, 2).unflatten(0, (B, NV)) |
|
antilias_mask = antilias_mask.permute(0, 3, 1, 2).unflatten(0, (B, NV)) |
|
depth = -depth.permute(0, 3, 1, 2).unflatten(0, (B, NV)) |
|
normal = normal.permute(0, 3, 1, 2).unflatten(0, (B, NV)) |
|
|
|
out = { |
|
"img": img, |
|
"mask": antilias_mask, |
|
"depth": depth, |
|
"normal": normal, |
|
"sdf": sdf, |
|
"mesh_v": mesh_v, |
|
"mesh_f": mesh_f, |
|
"sdf_reg_loss": sdf_reg_loss, |
|
} |
|
return out |
|
|
|
def forward(self, images, cameras, render_cameras, render_size: int): |
|
planes = self.forward_planes(images, cameras) |
|
out = self.forward_geometry(planes, render_cameras, render_size=render_size) |
|
|
|
return {"planes": planes, **out} |
|
|
|
def extract_mesh( |
|
self, |
|
planes: torch.Tensor, |
|
use_texture_map: bool = False, |
|
texture_resolution: int = 1024, |
|
progress_callback: Optional[Callable[[float], None]] = None, |
|
**kwargs, |
|
): |
|
""" |
|
Extract a 3D mesh from FlexiCubes. Only support batch_size 1. |
|
:param planes: triplane features |
|
:param use_texture_map: use texture map or vertex color |
|
:param texture_resolution: the resolution of texure map |
|
""" |
|
assert planes.shape[0] == 1 |
|
|
|
if progress_callback is not None: |
|
progress_callback(0.0) |
|
|
|
mesh_v, mesh_f, _, _, _, _ = self.get_geometry_prediction(planes) |
|
vertices, faces = mesh_v[0], mesh_f[0] |
|
|
|
if progress_callback is not None: |
|
progress_callback(0.5) |
|
|
|
if not use_texture_map: |
|
vertices_tensor = vertices.unsqueeze(0) |
|
vertices_colors = ( |
|
self.synthesizer.get_texture_prediction(planes, vertices_tensor) |
|
.clamp(0, 1) |
|
.squeeze(0) |
|
.cpu() |
|
.numpy() |
|
) |
|
vertices_colors = (vertices_colors * 255).astype(np.uint8) |
|
|
|
if progress_callback is not None: |
|
progress_callback(1.0) |
|
|
|
return vertices, faces, vertices_colors |
|
|
|
uvs, mesh_tex_idx, gb_pos, tex_hard_mask = xatlas_uvmap( |
|
self.geometry.renderer.ctx, vertices, faces, resolution=texture_resolution |
|
) |
|
tex_hard_mask = tex_hard_mask.float() |
|
|
|
tex_feat = self.get_texture_prediction(planes, [gb_pos], tex_hard_mask) |
|
background_feature = torch.zeros_like(tex_feat) |
|
img_feat = torch.lerp(background_feature, tex_feat, tex_hard_mask) |
|
texture_map = img_feat.permute(0, 3, 1, 2).squeeze(0) |
|
|
|
if progress_callback is not None: |
|
progress_callback(1.0) |
|
|
|
return vertices, faces, uvs, mesh_tex_idx, texture_map |
|
|