Skip to content

Seg Mesh Extracting and Seg Extracting from GarfVDB#61

Open
zlalena wants to merge 1 commit into
openvdb:mainfrom
zlalena:segment_meshing
Open

Seg Mesh Extracting and Seg Extracting from GarfVDB#61
zlalena wants to merge 1 commit into
openvdb:mainfrom
zlalena:segment_meshing

Conversation

@zlalena

@zlalena zlalena commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

Cleaned up versions of the code used for the GTC SJ lab

  1. File to extract mesh segemnets from a mesh made from teh full orginal GS scene\
  • Added a couple things to fill in the giant gaps at the bottom of meshes
  • tested on safety park (outdoor airborn imagery)
  1. Mass segment extraction into plys using garfvdb

Signed-off-by: Zoe <zlalena@nvidia.com>
from scipy.spatial import cKDTree

# Import directly to avoid fvdb_reality_capture.tools.__init__ pulling optional deps (e.g. DLNR).
from fvdb_reality_capture.tools._filter_splats import (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚩 issue: ‏We shouldn't import through the internal _ modules. These examples are meant to demonstrate best practices to users and if DLNR is causing an issue in fvdb_reality_capture, we should fix that and not try to work around it in the examples.

help="Clustering scale (absolute or fraction of max; see --scale-is-fraction-of-max)",
)
parser.add_argument(
"--scale-is-fraction-of-max",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎵 note (non-blocking): ‏This option is a bit confusing, I think this should just be expressed as an option that turns off this behaviour. My understanding is that by default, the value of this option is True and the scale value is a fraction of the max_grouping_scale (in 'world' units), but this option when set to false (--scale-is-fraction-of-max=False?), turns the interpretation of scale into a world-unit measurement.

I think it makes more sense to just have this option be something like --scale-is-world-unit (by default it would be off) to turn on interpreting the scale value as the world unit and not a fraction.

@swahtz swahtz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks pretty good, just a few organizational changes that are important and documentation/user experience changes to address.

"--min-splat-scale",
type=float,
default=0.1,
help="Drop Gaussians with scale below this value (0 disables)",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 suggestion: I think it's worth mentioning whether the minimum scale here is in world units or a similar fraction of the max scale as above.‏

"--min-cluster-gaussians",
type=int,
default=200,
help="Drop clusters smaller than this (0 disables)",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 suggestion: ‏'smaller' is a bit confusing because it can be read as a 3D size/scale. I think it probably makes more sense to say "Drop clusters with a number of member Gaussians less than this value"

clustering_gs_model = gs_model
subsample_mask: torch.Tensor | None = None
if max_gaussians_for_clustering > 0 and gs_model.num_gaussians > max_gaussians_for_clustering:
logger.warning(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤓 nitpick (non-blocking): ‏I think this should just be an 'info' message like the one below, I don't believe this is warning about anything


Loads a Gaussian splat reconstruction and segmentation checkpoint, clusters
per-Gaussian affinity features at a chosen scale, filters clusters, then writes
``n`` segment ``.ply`` files (one per selected cluster).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤓 nitpick (non-blocking): ‏I think it's worth mentioning these ply files are Gaussian splat scenes as well, so they're not going to be confused with ply meshes.

Suggested change
``n`` segment ``.ply`` files (one per selected cluster).
``n`` segment ``.ply`` Gaussian splat scenes (one per selected cluster).

logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")

parser = argparse.ArgumentParser(
description="Segment full scene mesh based on a PLY Gaussian Splat segment",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚩 issue: ‏I'm confused about what this script does based on this description and the documentation at the top of the file. The description should be improved so users understand what this script will do.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 suggestion: ‏If this mesh extraction is going to be directly related to the segment extraction in GARfVDB, then I think we should just move this file to the instance_segementation/garfvdb directory. Otherwise, we should include a README, environment files, pyproject.toml, etc. in this directory for a user to be able to run this project.

import logging
from pathlib import Path

import igl

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

☑️ todo: ‏We should include igl and point_cloud_utils in GARfVDB's environment file so these modules are available in the example.

return vertex_colors is not None and vertex_colors.size > 0 and vertex_colors.shape[0] > 0


def _normalize_vertex_colors(colors: np.ndarray) -> np.ndarray:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def _normalize_vertex_colors(colors: np.ndarray) -> np.ndarray:
def _as_float_vertex_colors(colors: np.ndarray) -> np.ndarray:

This isn't really normalization in the statistical/vector sense, I'd suggest we change the name to something like _as_float_vertex_colors or _as_unit_float_vertex_colors so it's clear we're not changing the meaning in any way, we're just scaling to change formats.

str(output_path),
extracted_vertices,
extracted_faces_reindexed,
_normalize_vertex_colors(extracted_colors),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
_normalize_vertex_colors(extracted_colors),
extracted_colors,

☑️ todo: ‏We already called this function on the colors at line 154, I think we can just avoid calling this again.

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

Successfully merging this pull request may close these issues.

2 participants