CoolFace
Apppublic

tohid4n/PartCrafter

sourceHugging Facemitupdated 1y agoView on Hugging Face
0likes
inference_utils.py508 linesDownload Raw Back to utils
1from src.utils.typing_utils import *2 3import numpy as np4import torch5import torch.nn as nn6import scipy.ndimage7from skimage import measure8from einops import repeat9from diso import DiffDMC10import torch.nn.functional as F11 12def generate_dense_grid_points(13    bbox_min: np.ndarray, bbox_max: np.ndarray, octree_depth: int, indexing: str = "ij"14):15    length = bbox_max - bbox_min16    num_cells = np.exp2(octree_depth)17    x = np.linspace(bbox_min[0], bbox_max[0], int(num_cells) + 1, dtype=np.float32)18    y = np.linspace(bbox_min[1], bbox_max[1], int(num_cells) + 1, dtype=np.float32)19    z = np.linspace(bbox_min[2], bbox_max[2], int(num_cells) + 1, dtype=np.float32)20    [xs, ys, zs] = np.meshgrid(x, y, z, indexing=indexing)21    xyz = np.stack((xs, ys, zs), axis=-1)22    xyz = xyz.reshape(-1, 3)23    grid_size = [int(num_cells) + 1, int(num_cells) + 1, int(num_cells) + 1]24 25    return xyz, grid_size, length26 27def generate_dense_grid_points_gpu(28    bbox_min: torch.Tensor,                        29    bbox_max: torch.Tensor,30    octree_depth: int,31    indexing: str = "ij", 32    dtype: torch.dtype = torch.float1633):34    length = bbox_max - bbox_min35    num_cells = 2 ** octree_depth36    device = bbox_min.device37    38    x = torch.linspace(bbox_min[0], bbox_max[0], int(num_cells), dtype=dtype, device=device)39    y = torch.linspace(bbox_min[1], bbox_max[1], int(num_cells), dtype=dtype, device=device)40    z = torch.linspace(bbox_min[2], bbox_max[2], int(num_cells), dtype=dtype, device=device)41    42    xs, ys, zs = torch.meshgrid(x, y, z, indexing=indexing)43    xyz = torch.stack((xs, ys, zs), dim=-1)44    xyz = xyz.view(-1, 3)45    grid_size = [int(num_cells), int(num_cells), int(num_cells)]46 47    return xyz, grid_size, length48 49def find_mesh_grid_coordinates_fast_gpu(50    occupancy_grid, 51    n_limits=-152):53    core_grid = occupancy_grid[1:-1, 1:-1, 1:-1]54    occupied = core_grid > 055 56    neighbors_unoccupied = (57        (occupancy_grid[:-2, :-2, :-2] < 0)58        | (occupancy_grid[:-2, :-2, 1:-1] < 0)59        | (occupancy_grid[:-2, :-2, 2:] < 0)  # x-1, y-1, z-1/0/160        | (occupancy_grid[:-2, 1:-1, :-2] < 0)61        | (occupancy_grid[:-2, 1:-1, 1:-1] < 0)62        | (occupancy_grid[:-2, 1:-1, 2:] < 0)  # x-1, y0, z-1/0/163        | (occupancy_grid[:-2, 2:, :-2] < 0)64        | (occupancy_grid[:-2, 2:, 1:-1] < 0)65        | (occupancy_grid[:-2, 2:, 2:] < 0)  # x-1, y+1, z-1/0/166        | (occupancy_grid[1:-1, :-2, :-2] < 0)67        | (occupancy_grid[1:-1, :-2, 1:-1] < 0)68        | (occupancy_grid[1:-1, :-2, 2:] < 0)  # x0, y-1, z-1/0/169        | (occupancy_grid[1:-1, 1:-1, :-2] < 0)70        | (occupancy_grid[1:-1, 1:-1, 2:] < 0)  # x0, y0, z-1/171        | (occupancy_grid[1:-1, 2:, :-2] < 0)72        | (occupancy_grid[1:-1, 2:, 1:-1] < 0)73        | (occupancy_grid[1:-1, 2:, 2:] < 0)  # x0, y+1, z-1/0/174        | (occupancy_grid[2:, :-2, :-2] < 0)75        | (occupancy_grid[2:, :-2, 1:-1] < 0)76        | (occupancy_grid[2:, :-2, 2:] < 0)  # x+1, y-1, z-1/0/177        | (occupancy_grid[2:, 1:-1, :-2] < 0)78        | (occupancy_grid[2:, 1:-1, 1:-1] < 0)79        | (occupancy_grid[2:, 1:-1, 2:] < 0)  # x+1, y0, z-1/0/180        | (occupancy_grid[2:, 2:, :-2] < 0)81        | (occupancy_grid[2:, 2:, 1:-1] < 0)82        | (occupancy_grid[2:, 2:, 2:] < 0)  # x+1, y+1, z-1/0/183    )84    core_mesh_coords = torch.nonzero(occupied & neighbors_unoccupied, as_tuple=False) + 185 86    if n_limits != -1 and core_mesh_coords.shape[0] > n_limits:87        print(f"core mesh coords {core_mesh_coords.shape[0]} is too large, limited to {n_limits}")88        ind = np.random.choice(core_mesh_coords.shape[0], n_limits, True)89        core_mesh_coords = core_mesh_coords[ind]90 91    return core_mesh_coords92 93def find_candidates_band(94    occupancy_grid: torch.Tensor, 95    band_threshold: float, 96    n_limits: int = -197) -> torch.Tensor:98    """99    Returns the coordinates of all voxels in the occupancy_grid where |value| < band_threshold.100 101    Args:102        occupancy_grid (torch.Tensor): A 3D tensor of SDF values.103        band_threshold (float): The threshold below which |SDF| must be to include the voxel.104        n_limits (int): Maximum number of points to return (-1 for no limit)105 106    Returns:107        torch.Tensor: A 2D tensor of coordinates (N x 3) where each row is [x, y, z].108    """109    core_grid = occupancy_grid[1:-1, 1:-1, 1:-1]  110    # logits to sdf111    core_grid = torch.sigmoid(core_grid) * 2 - 1  112    # Create a boolean mask for all cells in the band113    in_band = torch.abs(core_grid) < band_threshold114 115    # Get coordinates of all voxels in the band116    core_mesh_coords = torch.nonzero(in_band, as_tuple=False) + 1117 118    if n_limits != -1 and core_mesh_coords.shape[0] > n_limits:119        print(f"core mesh coords {core_mesh_coords.shape[0]} is too large, limited to {n_limits}")120        ind = np.random.choice(core_mesh_coords.shape[0], n_limits, True)121        core_mesh_coords = core_mesh_coords[ind]122 123    return core_mesh_coords 124 125def expand_edge_region_fast(edge_coords, grid_size, dtype):126    expanded_tensor = torch.zeros(grid_size, grid_size, grid_size, device='cuda', dtype=dtype, requires_grad=False)127    expanded_tensor[edge_coords[:, 0], edge_coords[:, 1], edge_coords[:, 2]] = 1128    if grid_size < 512:129        kernel_size = 5130        pooled_tensor = torch.nn.functional.max_pool3d(expanded_tensor.unsqueeze(0).unsqueeze(0), kernel_size=kernel_size, stride=1, padding=2).squeeze()131    else:132        kernel_size = 3133        pooled_tensor = torch.nn.functional.max_pool3d(expanded_tensor.unsqueeze(0).unsqueeze(0), kernel_size=kernel_size, stride=1, padding=1).squeeze()134    expanded_coords_low_res = torch.nonzero(pooled_tensor, as_tuple=False).to(torch.int16)135 136    expanded_coords_high_res = torch.stack([137        torch.cat((expanded_coords_low_res[:, 0] * 2, expanded_coords_low_res[:, 0] * 2, expanded_coords_low_res[:, 0] * 2, expanded_coords_low_res[:, 0] * 2, expanded_coords_low_res[:, 0] * 2 + 1, expanded_coords_low_res[:, 0] * 2 + 1, expanded_coords_low_res[:, 0] * 2 + 1, expanded_coords_low_res[:, 0] * 2 + 1)),138        torch.cat((expanded_coords_low_res[:, 1] * 2, expanded_coords_low_res[:, 1] * 2, expanded_coords_low_res[:, 1] * 2+1, expanded_coords_low_res[:, 1] * 2 + 1, expanded_coords_low_res[:, 1] * 2, expanded_coords_low_res[:, 1] * 2, expanded_coords_low_res[:, 1] * 2 + 1, expanded_coords_low_res[:, 1] * 2 + 1)),139        torch.cat((expanded_coords_low_res[:, 2] * 2, expanded_coords_low_res[:, 2] * 2+1, expanded_coords_low_res[:, 2] * 2, expanded_coords_low_res[:, 2] * 2 + 1, expanded_coords_low_res[:, 2] * 2, expanded_coords_low_res[:, 2] * 2+1, expanded_coords_low_res[:, 2] * 2, expanded_coords_low_res[:, 2] * 2 + 1))140    ], dim=1)141 142    return expanded_coords_high_res143 144def zoom_block(block, scale_factor, order=3):145    block = block.astype(np.float32)146    return scipy.ndimage.zoom(block, scale_factor, order=order)147 148def parallel_zoom(occupancy_grid, scale_factor):149    result = torch.nn.functional.interpolate(occupancy_grid.unsqueeze(0).unsqueeze(0), scale_factor=scale_factor)150    return result.squeeze(0).squeeze(0)151 152 153@torch.no_grad()154def hierarchical_extract_geometry(155    geometric_func: Callable,156    device: torch.device,157    dtype: torch.dtype,158    bounds: Union[Tuple[float], List[float], float] = (-1.25, -1.25, -1.25, 1.25, 1.25, 1.25),159    dense_octree_depth: int = 8,160    hierarchical_octree_depth: int = 9, 161    max_num_expanded_coords: int = 1e8, 162    verbose: bool = False,163):164    """165    Args:166        geometric_func:167        device:168        bounds:169        dense_octree_depth:170        hierarchical_octree_depth:171    Returns:172    """173    if isinstance(bounds, float):174        bounds = [-bounds, -bounds, -bounds, bounds, bounds, bounds]175 176    bbox_min = torch.tensor(bounds[0:3]).to(device)177    bbox_max = torch.tensor(bounds[3:6]).to(device)178    bbox_size = bbox_max - bbox_min179 180    xyz_samples, grid_size, length = generate_dense_grid_points_gpu(181        bbox_min=bbox_min,182        bbox_max=bbox_max,183        octree_depth=dense_octree_depth,184        indexing="ij",185        dtype=dtype186    )187    188    if verbose:189        print(f'step 1 query num: {xyz_samples.shape[0]}')190    grid_logits = geometric_func(xyz_samples.unsqueeze(0)).to(dtype).view(grid_size[0], grid_size[1], grid_size[2])191    # print(f'step 1 grid_logits shape: {grid_logits.shape}')192    for i in range(hierarchical_octree_depth - dense_octree_depth):193        curr_octree_depth = dense_octree_depth + i + 1194        # upsample195        grid_size = 2**curr_octree_depth196        normalize_offset = grid_size / 2197        high_res_occupancy = parallel_zoom(grid_logits, 2).to(dtype)198 199        band_threshold = 1.0200        edge_coords = find_candidates_band(grid_logits, band_threshold)201        expanded_coords = expand_edge_region_fast(edge_coords, grid_size=int(grid_size/2), dtype=dtype).to(dtype)202        if verbose:203            print(f'step {i+2} query num: {len(expanded_coords)}')204        if max_num_expanded_coords > 0 and len(expanded_coords) > max_num_expanded_coords:205            raise ValueError(f"expanded_coords is too large, {len(expanded_coords)} > {max_num_expanded_coords}")206        expanded_coords_norm = (expanded_coords - normalize_offset) * (abs(bounds[0]) / normalize_offset)207 208        all_logits = None209 210        all_logits = geometric_func(expanded_coords_norm.unsqueeze(0)).to(dtype)211        all_logits = torch.cat([expanded_coords_norm, all_logits[0]], dim=1)212        # print("all logits shape = ", all_logits.shape)213 214        indices = all_logits[..., :3]215        indices = indices * (normalize_offset / abs(bounds[0]))  + normalize_offset216        indices = indices.type(torch.IntTensor)217        values = all_logits[:, 3]218        # breakpoint()219        high_res_occupancy[indices[:, 0], indices[:, 1], indices[:, 2]] = values220        grid_logits = high_res_occupancy221        # torch.cuda.empty_cache()222 223    if verbose:224        print("final grids shape = ", grid_logits.shape)225    vertices, faces, normals, _ = measure.marching_cubes(grid_logits.float().cpu().numpy(), 0, method="lewiner")226    vertices = vertices / (2**hierarchical_octree_depth) * bbox_size.cpu().numpy() + bbox_min.cpu().numpy()227    mesh_v_f = (vertices.astype(np.float32), np.ascontiguousarray(faces))228 229    return mesh_v_f230 231def extract_near_surface_volume_fn(input_tensor: torch.Tensor, alpha: float):232    """233    Args:234        input_tensor: shape [D, D, D], torch.float16235        alpha: isosurface offset236    Returns:237        mask: shape [D, D, D], torch.int32238    """239    device = input_tensor.device240    D = input_tensor.shape[0]241    signed_val = 0.0242 243    # add isosurface offset and exclude invalid value244    val = input_tensor + alpha245    valid_mask = val > -9000246 247    # obtain neighbors248    def get_neighbor(t, shift, axis):249        if shift == 0:250            return t.clone()251 252        pad_dims = [0, 0, 0, 0, 0, 0]  # [x_front,x_back,y_front,y_back,z_front,z_back]253 254        if axis == 0:  # x axis255            pad_idx = 0 if shift > 0 else 1256            pad_dims[pad_idx] = abs(shift)257        elif axis == 1:  # y axis258            pad_idx = 2 if shift > 0 else 3259            pad_dims[pad_idx] = abs(shift)260        elif axis == 2:  # z axis261            pad_idx = 4 if shift > 0 else 5262            pad_dims[pad_idx] = abs(shift)263 264        # Apply padding with replication at boundaries265        padded = F.pad(t.unsqueeze(0).unsqueeze(0), pad_dims[::-1], mode='replicate')266 267        # Create dynamic slicing indices268        slice_dims = [slice(None)] * 3269        if axis == 0:  # x axis270            if shift > 0:271                slice_dims[0] = slice(shift, None)272            else:273                slice_dims[0] = slice(None, shift)274        elif axis == 1:  # y axis275            if shift > 0:276                slice_dims[1] = slice(shift, None)277            else:278                slice_dims[1] = slice(None, shift)279        elif axis == 2:  # z axis280            if shift > 0:281                slice_dims[2] = slice(shift, None)282            else:283                slice_dims[2] = slice(None, shift)284 285        # Apply slicing and restore dimensions286        padded = padded.squeeze(0).squeeze(0)287        sliced = padded[slice_dims]288        return sliced289 290    # Get neighbors in all directions291    left = get_neighbor(val, 1, axis=0)  # x axis292    right = get_neighbor(val, -1, axis=0)293    back = get_neighbor(val, 1, axis=1)  # y axis294    front = get_neighbor(val, -1, axis=1)295    down = get_neighbor(val, 1, axis=2)  # z axis296    up = get_neighbor(val, -1, axis=2)297 298    # Handle invalid boundary values299    def safe_where(neighbor):300        return torch.where(neighbor > -9000, neighbor, val)301 302    left = safe_where(left)303    right = safe_where(right)304    back = safe_where(back)305    front = safe_where(front)306    down = safe_where(down)307    up = safe_where(up)308 309    # Calculate sign consistency310    sign = torch.sign(val.to(torch.float32))311    neighbors_sign = torch.stack([312        torch.sign(left.to(torch.float32)),313        torch.sign(right.to(torch.float32)),314        torch.sign(back.to(torch.float32)),315        torch.sign(front.to(torch.float32)),316        torch.sign(down.to(torch.float32)),317        torch.sign(up.to(torch.float32))318    ], dim=0)319 320    # Check if all signs are consistent321    same_sign = torch.all(neighbors_sign == sign, dim=0)322 323    # Generate final mask324    mask = (~same_sign).to(torch.int32)325    return mask * valid_mask.to(torch.int32)326 327 328def generate_dense_grid_points_2(329    bbox_min: np.ndarray,330    bbox_max: np.ndarray,331    octree_resolution: int,332    indexing: str = "ij",333):334    length = bbox_max - bbox_min335    num_cells = octree_resolution336 337    x = np.linspace(bbox_min[0], bbox_max[0], int(num_cells) + 1, dtype=np.float32)338    y = np.linspace(bbox_min[1], bbox_max[1], int(num_cells) + 1, dtype=np.float32)339    z = np.linspace(bbox_min[2], bbox_max[2], int(num_cells) + 1, dtype=np.float32)340    [xs, ys, zs] = np.meshgrid(x, y, z, indexing=indexing)341    xyz = np.stack((xs, ys, zs), axis=-1)342    grid_size = [int(num_cells) + 1, int(num_cells) + 1, int(num_cells) + 1]343 344    return xyz, grid_size, length345 346@torch.no_grad()347def flash_extract_geometry(348    latents: torch.FloatTensor,349    vae: Callable,350    bounds: Union[Tuple[float], List[float], float] = 1.01,351    num_chunks: int = 10000,352    mc_level: float = 0.0,353    octree_depth: int = 9,354    min_resolution: int = 63,355    mini_grid_num: int = 4,356    **kwargs,357):358    geo_decoder = vae.decoder359    device = latents.device360    dtype = latents.dtype361    # resolution to depth362    octree_resolution = 2 ** octree_depth363    resolutions = []364    if octree_resolution < min_resolution:365        resolutions.append(octree_resolution)366    while octree_resolution >= min_resolution:367        resolutions.append(octree_resolution)368        octree_resolution = octree_resolution // 2369    resolutions.reverse()370    resolutions[0] = round(resolutions[0] / mini_grid_num) * mini_grid_num - 1371    for i, resolution in enumerate(resolutions[1:]):372        resolutions[i + 1] = resolutions[0] * 2 ** (i + 1)373 374 375    # 1. generate query points376    if isinstance(bounds, float):377        bounds = [-bounds, -bounds, -bounds, bounds, bounds, bounds]378    bbox_min = np.array(bounds[0:3])379    bbox_max = np.array(bounds[3:6])380    bbox_size = bbox_max - bbox_min381 382    xyz_samples, grid_size, length = generate_dense_grid_points_2(383        bbox_min=bbox_min,384        bbox_max=bbox_max,385        octree_resolution=resolutions[0],386        indexing="ij"387    )388 389    dilate = nn.Conv3d(1, 1, 3, padding=1, bias=False, device=device, dtype=dtype)390    dilate.weight = torch.nn.Parameter(torch.ones(dilate.weight.shape, dtype=dtype, device=device))391 392    grid_size = np.array(grid_size)393 394    # 2. latents to 3d volume395    xyz_samples = torch.from_numpy(xyz_samples).to(device, dtype=dtype)396    batch_size = latents.shape[0]397    mini_grid_size = xyz_samples.shape[0] // mini_grid_num398    xyz_samples = xyz_samples.view(399        mini_grid_num, mini_grid_size,400        mini_grid_num, mini_grid_size,401        mini_grid_num, mini_grid_size, 3402    ).permute(403        0, 2, 4, 1, 3, 5, 6404    ).reshape(405        -1, mini_grid_size * mini_grid_size * mini_grid_size, 3406    )407    batch_logits = []408    num_batchs = max(num_chunks // xyz_samples.shape[1], 1)409    for start in range(0, xyz_samples.shape[0], num_batchs):410        queries = xyz_samples[start: start + num_batchs, :]411        batch = queries.shape[0]412        batch_latents = repeat(latents.squeeze(0), "p c -> b p c", b=batch)413        # geo_decoder.set_topk(True)414        geo_decoder.set_topk(False)415        logits = vae.decode(batch_latents, queries).sample416        batch_logits.append(logits)417    grid_logits = torch.cat(batch_logits, dim=0).reshape(418        mini_grid_num, mini_grid_num, mini_grid_num,419        mini_grid_size, mini_grid_size,420        mini_grid_size421    ).permute(0, 3, 1, 4, 2, 5).contiguous().view(422        (batch_size, grid_size[0], grid_size[1], grid_size[2])423    )424 425    for octree_depth_now in resolutions[1:]:426        grid_size = np.array([octree_depth_now + 1] * 3)427        resolution = bbox_size / octree_depth_now428        next_index = torch.zeros(tuple(grid_size), dtype=dtype, device=device)429        next_logits = torch.full(next_index.shape, -10000., dtype=dtype, device=device)430        curr_points = extract_near_surface_volume_fn(grid_logits.squeeze(0), mc_level)431        curr_points += grid_logits.squeeze(0).abs() < 0.95432 433        if octree_depth_now == resolutions[-1]:434            expand_num = 0435        else:436            expand_num = 1437        for i in range(expand_num):438            curr_points = dilate(curr_points.unsqueeze(0).to(dtype)).squeeze(0)439            curr_points = dilate(curr_points.unsqueeze(0).to(dtype)).squeeze(0)440        (cidx_x, cidx_y, cidx_z) = torch.where(curr_points > 0)441 442        next_index[cidx_x * 2, cidx_y * 2, cidx_z * 2] = 1443        for i in range(2 - expand_num):444            next_index = dilate(next_index.unsqueeze(0)).squeeze(0)445        nidx = torch.where(next_index > 0)446 447        next_points = torch.stack(nidx, dim=1)448        next_points = (next_points * torch.tensor(resolution, dtype=torch.float32, device=device) +449                        torch.tensor(bbox_min, dtype=torch.float32, device=device))450 451        query_grid_num = 6452        min_val = next_points.min(axis=0).values453        max_val = next_points.max(axis=0).values454        vol_queries_index = (next_points - min_val) / (max_val - min_val) * (query_grid_num - 0.001)455        index = torch.floor(vol_queries_index).long()456        index = index[..., 0] * (query_grid_num ** 2) + index[..., 1] * query_grid_num + index[..., 2]457        index = index.sort()458        next_points = next_points[index.indices].unsqueeze(0).contiguous()459        unique_values = torch.unique(index.values, return_counts=True)460        grid_logits = torch.zeros((next_points.shape[1]), dtype=latents.dtype, device=latents.device)461        input_grid = [[], []]462        logits_grid_list = []463        start_num = 0464        sum_num = 0465        for grid_index, count in zip(unique_values[0].cpu().tolist(), unique_values[1].cpu().tolist()):466            if sum_num + count < num_chunks or sum_num == 0:467                sum_num += count468                input_grid[0].append(grid_index)469                input_grid[1].append(count)470            else:471                # geo_decoder.set_topk(input_grid)472                geo_decoder.set_topk(False)473                logits_grid = vae.decode(latents,next_points[:, start_num:start_num + sum_num]).sample474                start_num = start_num + sum_num475                logits_grid_list.append(logits_grid)476                input_grid = [[grid_index], [count]]477                sum_num = count478        if sum_num > 0:479            # geo_decoder.set_topk(input_grid)480            geo_decoder.set_topk(False)481            logits_grid = vae.decode(latents,next_points[:, start_num:start_num + sum_num]).sample482            logits_grid_list.append(logits_grid)483        logits_grid = torch.cat(logits_grid_list, dim=1)484        grid_logits[index.indices] = logits_grid.squeeze(0).squeeze(-1)485        next_logits[nidx] = grid_logits486        grid_logits = next_logits.unsqueeze(0)487    488    grid_logits[grid_logits == -10000.] = float('nan')489    torch.cuda.empty_cache()490    mesh_v_f = []491    grid_logits = grid_logits[0]492    try:493        print("final grids shape = ", grid_logits.shape)494        dmc = DiffDMC(dtype=torch.float32).to(grid_logits.device)495        sdf = -grid_logits / octree_resolution496        sdf = sdf.to(torch.float32).contiguous()497        vertices, faces = dmc(sdf, deform=None, return_quads=False, normalize=False)498        vertices = vertices.detach().cpu().numpy()499        faces = faces.detach().cpu().numpy()[:, ::-1]        500        vertices = vertices / (2 ** octree_depth) * bbox_size + bbox_min501        mesh_v_f = (vertices.astype(np.float32), np.ascontiguousarray(faces))502    except Exception as e:503        print(e)504        torch.cuda.empty_cache()505        mesh_v_f = (None, None)506 507    return [mesh_v_f]508