Refactor image mesh processing and background removal functionality

- Updated NODE_GROUP_NAME from "O Refine DA3 Surface" to "O DA3 Surface".
- Introduced `remove_surface_background` function to filter out background faces and compact geometry.
- Enhanced `build_image_mesh` to conditionally remove background faces based on a new property.
- Added `snapform_remove_background` and `snapform_keep_original` properties to operators for better control over background handling.
- Updated UI to reflect new properties and improved resolution scaling logic.
- Refactored tests to cover new background removal functionality and ensure proper geometry handling.
- Adjusted geometry nodes and validation scripts to remove references to background handling.
This commit is contained in:
2026-08-12 16:15:14 +08:00
parent 7cd5681a8b
commit c3a151ad2c
13 changed files with 477 additions and 271 deletions

View File

@@ -473,6 +473,12 @@ def export_prediction_attributes(prediction, output_directory):
return output_path
def remove_background_faces(faces, foreground_mask):
"""Return only triangles whose vertices all belong to the foreground."""
foreground_points = foreground_mask.reshape(-1)
return faces[foreground_points[faces].all(axis=1)]
def export_image_mesh_data(
prediction,
image_path,
@@ -505,9 +511,9 @@ def export_image_mesh_data(
raw_depth = depth_frames[0]
confidence = confidence_frames[0]
intrinsics = np.asarray(prediction.intrinsics[0], dtype=np.float32)
progress(2, "Unprojecting camera geometry")
progress(2, "Unprojecting the depth grid")
points = unproject_camera_depth(raw_depth, intrinsics)
progress(3, "Preparing DA3 and BEN2 attributes")
progress(3, "Preparing confidence and background data")
foreground_mask = np.ones(raw_depth.shape, dtype=bool)
if foreground_path is not None:
foreground_mask = foreground_mesh_mask(
@@ -518,7 +524,7 @@ def export_image_mesh_data(
raw_depth,
confidence,
)
progress(4, "Building editable source topology")
progress(4, "Building source triangles")
faces = mesh_triangle_faces(
points,
raw_depth,
@@ -527,11 +533,13 @@ def export_image_mesh_data(
False,
0.0,
)
if foreground_path is not None:
faces = remove_background_faces(faces, foreground_mask)
if not len(faces):
raise ValueError(
"DA3 output does not contain any valid surface faces"
)
progress(5, "Projecting UV and structured attributes")
progress(5, "Preparing source UV and attributes")
vertices, faces = compact_mesh(
points.reshape(-1, 3),
faces,
@@ -551,7 +559,7 @@ def export_image_mesh_data(
reference_depth = float(np.median(valid_depth))
output_directory = Path(output_directory)
output_directory.mkdir(parents=True, exist_ok=True)
progress(6, "Writing surface data")
progress(6, "Writing source surface data")
result_path = output_directory / "mesh.npz"
np.savez_compressed(
result_path,
@@ -564,7 +572,8 @@ def export_image_mesh_data(
**attributes,
)
texture_path = output_directory / "texture.png"
with Image.open(image_path) as image:
texture_source = foreground_path or image_path
with Image.open(texture_source) as image:
image.convert("RGBA").save(texture_path, format="PNG", compress_level=1)
return result_path, texture_path
@@ -1650,7 +1659,7 @@ def reconstruct(args, model_cache=None, cancel_check=None):
write_status(
args.status,
0.88 + 0.09 * step / total,
f"Post-processing {step}/{total}: {label}",
f"Preparing surface data {step}/{total}: {label}",
)
export_image_mesh_data(