Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question about modeling a 3d shape using marching cube #24

Closed
vincenthesiyuan opened this issue Nov 3, 2021 · 5 comments
Closed

Question about modeling a 3d shape using marching cube #24

vincenthesiyuan opened this issue Nov 3, 2021 · 5 comments

Comments

@vincenthesiyuan
Copy link

Hi,
Thanks for your great work. I trained an OctreeSDF model on LOD5, and want to do marching cube that similar to SIREN. Unfortunately, it dosen't work. The output .ply model have no shape and all of noise in the space.

Sorry for the broken english and my stupid question.
Looking forward to your answer.

@joeylitalien
Copy link
Collaborator

joeylitalien commented Nov 3, 2021

Hi @vincenthesiyuan,

You can try adding the following app in sdf-net/app. This is untested with the current branch but should work, as long as you call it with the same options you used to train.

import sys
import os

sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

import numpy as np
import torch
import torch.nn as nn

import trimesh
import mcubes

from lib.models import *
from lib.options import parse_options


def extract_mesh(args):
    # Prepare directory
    ins_dir = os.path.join(args.mesh_dir, name)
    if not os.path.exists(ins_dir):
        os.makedirs(ins_dir)

    # Get SDFs
    with torch.no_grad():
        xx = torch.linspace(-1, 1, args.mc_resolution, device=device)
        pts = torch.stack(torch.meshgrid(xx, xx, xx), dim=-1).reshape(-1,3)
        chunks = torch.split(pts, args.batch_size)
        dists = []
        for chunk_pts in chunks:
            dists.append(net(chunk_pts).detach())

    # Convert to occupancy
    dists = torch.cat(dists, dim=0)
    grid = dists.reshape(args.mc_resolution, args.mc_resolution, args.mc_resolution)
    occupancy = torch.where(grid <= 0, 1, 0)

    # Meshify
    print('Fraction occupied: {:.5f}'.format((occupancy == 1).float().mean().item()))
    # vertices, triangles = mcubes.marching_cubes(occupancy.cpu().numpy(), 0.5) # Original post, small bug
    vertices, triangles = mcubes.marching_cubes(occupancy.cpu().numpy(), 0)

    # Resize + recenter
    b_min_np = np.array([-1., -1., -1.])
    b_max_np = np.array([ 1.,  1.,  1.])
    vertices = vertices / (args.mc_resolution - 1.0) * (b_max_np - b_min_np) + b_min_np

    # Save mesh
    mesh = trimesh.Trimesh(vertices, triangles)
    mesh_fname = os.path.join(ins_dir, f'mc_res{args.mc_resolution}.obj')
    print(f'Saving mesh to {mesh_fname}')
    mesh.export(mesh_fname)


if __name__ == '__main__':
    # Parse
    parser = parse_options(return_parser=True)
    app_group = parser.add_argument_group('app')
    app_group.add_argument('--mesh-dir', type=str, default='_results/render_app/meshes',
                           help='Directory to save the mesh')
    app_group.add_argument('--mc-resolution', type=int, default=256,
                           help='Marching cube grid resolution.')
    args = parser.parse_args()

    # Pick device
    use_cuda = torch.cuda.is_available()
    device = torch.device('cuda' if use_cuda else 'cpu')

    # Get model
    if args.pretrained is not None:
        name = args.pretrained.split('/')[-1].split('.')[0]
    else:
        raise ValueError('No network weights specified!')
    net = globals()[args.net](args)
    net.load_state_dict(torch.load(args.pretrained), strict=False)
    net.to(device)
    net.eval()

    # Run Marching Cubes
    extract_mesh(args)

@zhaoyuanyuan2011
Copy link

zhaoyuanyuan2011 commented May 5, 2022

Hi @joeylitalien ,

Thank you for providing the exporter! I used the script and export a mesh, which has layered effect (like a voxel instead of smooth mesh) and is probably because the resolution of marching cube is too low.

Screen Shot 2022-05-05 at 4 40 07 PM

However, I've used 256 and even 512 so res shouldn't be the cause. I noticed that the fraction occupied is only 0.04. Is it fraction too low? Is it possible that there are too many empty points (points that are not inside the mesh) so even a 512 res doesn't help smooth the mesh?
armadillo_rgb
As a comparison, the img rendered from sdf using sdf_renderer.py looks pretty smooth.

Thank you!

@YuanxunLu
Copy link

I met the same problem exactly using the code above. I found the keypoint here is that it uses occupancy values as marching cubes input instead of SDF values, which causes the striped mesh.

Therefore, changing the codes of marching cubes works. Specifically, replace the original code

vertices, triangles = mcubes.marching_cubes(occupancy.cpu().numpy(), 0.5)

with

vertices, triangles = mcubes.marching_cubes(grid.cpu().numpy(), 0)

The results of 256 resolution will be as follows:
image

Hope the above helps!

@zhaoyuanyuan2011
Copy link

Hi @YuanxunLu, thank you so much for the reply! I haven't noticed this 😂

@joeylitalien
Copy link
Collaborator

Welp, that teaches me to put untested code snippets! Thanks for the quick fix @YuanxunLu.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants