chore: auto-commit (14 archivos)
- docs/capabilities/comfyui.md - python/functions/ml/comfyui_build_image_to_3d_workflow.md - python/functions/ml/comfyui_build_image_to_3d_workflow.py - python/functions/ml/tests/test_comfyui_build_image_to_3d_workflow.py - python/functions/ml/comfyui_build_facedetailer_workflow.md - python/functions/ml/comfyui_build_facedetailer_workflow.py - python/functions/ml/comfyui_build_hires_fix_workflow.md - python/functions/ml/comfyui_build_hires_fix_workflow.py - python/functions/ml/tests/test_comfyui_build_facedetailer_workflow.py - python/functions/ml/tests/test_comfyui_build_hires_fix_workflow.py - ... Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
"""comfyui_mesh_cleanup_oneshot — limpia una malla 3D en una sola llamada.
|
||||
|
||||
Promocion de la secuencia repetida (issue 0087) del post-proceso de mallas
|
||||
Hunyuan3D: decimar a un presupuesto de caras razonable y, opcionalmente, hacerla
|
||||
estanca. Capitaliza el "Golden 3" del report 0088 (80k caras + estanca a la vez).
|
||||
Compone funciones del registry del grupo `comfyui`:
|
||||
|
||||
comfyui_simplify_mesh_py_ml (decima conservando apariencia)
|
||||
comfyui_make_watertight_py_ml (cierra la malla; solo si watertight=True)
|
||||
|
||||
Las mallas nativas de ComfyUI/Hunyuan3D salen densas (decenas de MB, >1M caras) y
|
||||
NO estancas (VoxelToMeshBasic produce "cube-soup"). Este pipeline las deja listas
|
||||
para web/impresion: ligeras y, si se pide, cerradas.
|
||||
|
||||
Pipeline impuro: lee y escribe archivos en disco. Requiere trimesh + pymeshlab +
|
||||
scipy (simplify) y, para method="voxel", scikit-image (make_watertight).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Importa las funciones del registry (mismo arbol python/functions).
|
||||
_FUNCTIONS_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
if _FUNCTIONS_ROOT not in sys.path:
|
||||
sys.path.insert(0, _FUNCTIONS_ROOT)
|
||||
|
||||
from ml.comfyui_make_watertight import comfyui_make_watertight
|
||||
from ml.comfyui_simplify_mesh import comfyui_simplify_mesh
|
||||
|
||||
|
||||
def comfyui_mesh_cleanup_oneshot(
|
||||
in_path: str,
|
||||
*,
|
||||
target_faces: int = 80000,
|
||||
watertight: bool = True,
|
||||
method: str = "repair",
|
||||
out_path: str | None = None,
|
||||
) -> dict:
|
||||
"""Decima una malla y, opcionalmente, la hace estanca, en una sola llamada.
|
||||
|
||||
Args:
|
||||
in_path: ruta de la malla de entrada (.glb/.obj/.ply/.gltf/.stl/.off).
|
||||
target_faces: caras objetivo del paso de decimacion (comfyui_simplify_mesh).
|
||||
keyword-only.
|
||||
watertight: si True (default) aplica comfyui_make_watertight tras decimar;
|
||||
si False solo decima. keyword-only.
|
||||
method: metodo de make_watertight cuando watertight=True. "repair" (default)
|
||||
conserva las caras decimadas (fill_holes + fix_normals) pero NO garantiza
|
||||
estanqueidad en mallas muy rotas; "voxel" GARANTIZA is_watertight=True via
|
||||
voxeliza+fill+marching cubes, a costa de re-mallar (cambia el conteo de
|
||||
caras y descarta apariencia). keyword-only.
|
||||
out_path: ruta de salida final. Si None, se deriva de in_path
|
||||
("<in>_cleaned.glb"). keyword-only.
|
||||
|
||||
Returns:
|
||||
dict {ok, in_path, out_path, in_faces, simplified_faces, final_faces,
|
||||
watertight_requested, is_watertight, method, steps, error}. `steps` es la
|
||||
lista de resultados crudos de cada funcion compuesta (para auditar). Si algun
|
||||
paso falla, ok=False y error explica en cual.
|
||||
"""
|
||||
base = {
|
||||
"ok": False, "in_path": in_path, "out_path": "",
|
||||
"in_faces": 0, "simplified_faces": 0, "final_faces": 0,
|
||||
"watertight_requested": watertight, "is_watertight": None,
|
||||
"method": method, "steps": [], "error": "",
|
||||
}
|
||||
if watertight and method not in ("repair", "voxel"):
|
||||
return {**base, "error": f"method '{method}' invalido (usa 'repair' o 'voxel')"}
|
||||
if out_path is None:
|
||||
out_path = os.path.splitext(in_path)[0] + "_cleaned.glb"
|
||||
|
||||
# Paso 1: decimar. Si no se pide watertight, este es el output final directo.
|
||||
simplify_out = out_path if not watertight else (
|
||||
os.path.splitext(out_path)[0] + "_simplified.glb"
|
||||
)
|
||||
s = comfyui_simplify_mesh(in_path, target_faces=target_faces, out_path=simplify_out)
|
||||
steps = [{"step": "simplify_mesh", **s}]
|
||||
if not s.get("ok"):
|
||||
return {**base, "steps": steps, "error": f"simplify_mesh fallo: {s.get('error')}"}
|
||||
|
||||
in_faces = s["in_faces"]
|
||||
simplified_faces = s["out_faces"]
|
||||
|
||||
if not watertight:
|
||||
return {
|
||||
**base, "ok": True, "out_path": s["out_path"],
|
||||
"in_faces": in_faces, "simplified_faces": simplified_faces,
|
||||
"final_faces": simplified_faces, "is_watertight": None,
|
||||
"steps": steps,
|
||||
}
|
||||
|
||||
# Paso 2: hacer estanca la malla decimada.
|
||||
w = comfyui_make_watertight(s["out_path"], method=method, out_path=out_path)
|
||||
steps.append({"step": "make_watertight", **w})
|
||||
if not w.get("ok"):
|
||||
return {
|
||||
**base, "out_path": s["out_path"],
|
||||
"in_faces": in_faces, "simplified_faces": simplified_faces,
|
||||
"final_faces": simplified_faces, "steps": steps,
|
||||
"error": f"make_watertight fallo: {w.get('error')}",
|
||||
}
|
||||
|
||||
return {
|
||||
**base, "ok": True, "out_path": w["out_path"],
|
||||
"in_faces": in_faces, "simplified_faces": simplified_faces,
|
||||
"final_faces": w["out_faces"], "is_watertight": w["is_watertight"],
|
||||
"steps": steps,
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import json
|
||||
|
||||
src = sys.argv[1] if len(sys.argv) > 1 else (
|
||||
os.path.expanduser("~/ComfyUI/output/3d_mesh_00002_.glb"))
|
||||
res = comfyui_mesh_cleanup_oneshot(src)
|
||||
print(json.dumps({k: v for k, v in res.items() if k != "steps"}, indent=2))
|
||||
Reference in New Issue
Block a user