STATICAI commited on
Commit
ed7d799
·
verified ·
1 Parent(s): 34af0d5

Restore docker_app.py from Make-It-Animatable

Browse files
Files changed (1) hide show
  1. docker_app.py +142 -0
docker_app.py ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import logging
3
+ import shutil
4
+ import time
5
+ import uuid
6
+ from pathlib import Path
7
+
8
+ from runtime import ROOT, MODEL_ROOT, configure_storage, startup_checks
9
+ configure_storage()
10
+ logging.basicConfig(filename=ROOT/"logs/workflow.log",level=logging.INFO,
11
+ format="%(asctime)s %(levelname)s %(message)s")
12
+ runtime_report = startup_checks()
13
+
14
+ import gradio as gr
15
+ import torch
16
+ import trimesh
17
+ from preflight import inspect_glb
18
+ import upstream.app as mia
19
+
20
+ mia.init_models()
21
+ # Initialize existing upstream component references used by its workflow.
22
+ # The original UI is not launched.
23
+ upstream_blocks = mia.init_blocks()
24
+ print("MODELS_READY dedicated CUDA inference",flush=True)
25
+
26
+ def preserve_and_inspect(path):
27
+ if not path:
28
+ raise gr.Error("Upload a GLB first.")
29
+ folder = ROOT/"inputs"/uuid.uuid4().hex
30
+ folder.mkdir()
31
+ original = folder/"original.glb"
32
+ shutil.copyfile(path,original)
33
+ try:
34
+ report = inspect_glb(original)
35
+ except (ValueError,KeyError,IndexError,TypeError) as exc:
36
+ report = {"inference_allowed":False,"warning":"Invalid or unsupported GLB: "+str(exc)}
37
+ report.update(original=str(original),cuda_available=torch.cuda.is_available(),gpu=runtime_report["gpu"])
38
+ (folder/"preflight.json").write_text(json.dumps(report,indent=2))
39
+ return str(original),report,str(original)
40
+
41
+ def execute(original,report):
42
+ if not original or not report or not report.get("inference_allowed"):
43
+ raise gr.Error((report or {}).get("warning") or "Inspect an eligible GLB before inference.")
44
+ if not torch.cuda.is_available():
45
+ raise gr.Error("CUDA is unavailable; CPU inference is disabled.")
46
+ # Recheck the preserved file before expensive processing.
47
+ checked=inspect_glb(original)
48
+ if not checked["inference_allowed"]:
49
+ raise gr.Error(checked["warning"])
50
+ job=ROOT/"outputs"/uuid.uuid4().hex
51
+ job.mkdir()
52
+ working=job/"character.glb"
53
+ shutil.copyfile(original,working)
54
+ db=mia.DB()
55
+ started=time.time()
56
+ stages=("Loading mesh","Preparing coordinates","Predicting skeleton and weights",
57
+ "Preparing rest pose","Exporting rigged asset","Finishing")
58
+ try:
59
+ for stage,_ in zip(stages,mia._pipeline(input_path=str(working),db=db,
60
+ reset_to_rest=True,rest_pose_type=None)):
61
+ yield stage,None,None,None
62
+ for p in (db.anim_path,db.anim_vis_path):
63
+ if not p or not Path(p).is_file() or Path(p).stat().st_size==0:
64
+ raise RuntimeError("Upstream export did not produce a nonempty asset")
65
+ # Validate export parsing without changing it.
66
+ result=trimesh.load(db.anim_vis_path,force="scene",process=False)
67
+ if not result.geometry:
68
+ raise RuntimeError("Exported GLB contains no geometry")
69
+ with open(db.anim_vis_path,"rb") as stream:
70
+ import struct
71
+ stream.seek(12)
72
+ length,kind=struct.unpack("<II",stream.read(8))
73
+ doc=json.loads(stream.read(length))
74
+ if not doc.get("skins") or not any("skin" in n for n in doc.get("nodes",[])):
75
+ raise RuntimeError("Exported GLB has no skin binding")
76
+ evidence=dict(status="PASS",original=original,glb=db.anim_vis_path,fbx=db.anim_path,
77
+ elapsed_seconds=time.time()-started,export_meshes=len(result.geometry),
78
+ skin_count=len(doc["skins"]),runtime=runtime_report["startup_id"])
79
+ (job/"result.json").write_text(json.dumps(evidence,indent=2))
80
+ logging.info("WORKFLOW_PASS %s",json.dumps(evidence))
81
+ print("WORKFLOW_PASS "+json.dumps(evidence),flush=True)
82
+ yield "Complete: rigged GLB and FBX exported.",db.anim_vis_path,db.anim_vis_path,db.anim_path
83
+ except Exception as exc:
84
+ logging.exception("Workflow failed in %s",job)
85
+ (job/"error.txt").write_text(str(exc))
86
+ raise gr.Error("Workflow failed: "+str(exc)+". Original preserved; details in /data/logs/workflow.log.")
87
+ finally:
88
+ mia.clear(db)
89
+
90
+ def example():
91
+ return preserve_and_inspect(MODEL_ROOT/"data/examples/ninja.glb")
92
+
93
+ def dense_character_test():
94
+ # Densify an existing character only for preflight; never modify its source.
95
+ source=MODEL_ROOT/"data/examples/ninja.glb"
96
+ mesh=trimesh.load(source,force="mesh",process=False)
97
+ while len(mesh.faces)<=500_000:
98
+ mesh=mesh.subdivide()
99
+ folder=ROOT/"inputs"/("dense-test-"+uuid.uuid4().hex)
100
+ folder.mkdir()
101
+ path=folder/"dense-character.glb"
102
+ mesh.export(path)
103
+ report=inspect_glb(path)
104
+ assert not report["inference_allowed"],"Dense character must be blocked before inference"
105
+ report["test"]="PASS: dense character preprocessing warning"
106
+ report["source"]=str(source)
107
+ report["original"]=str(path)
108
+ (folder/"preflight.json").write_text(json.dumps(report,indent=2))
109
+ print("DENSE_CHARACTER_TEST "+json.dumps(report),flush=True)
110
+ return report
111
+
112
+ with gr.Blocks(title="Make-It-Animatable") as demo:
113
+ gr.Markdown("# Motion Rig\nUpload a character GLB, inspect it, then prepare and export its rig.")
114
+ original=gr.State()
115
+ report_state=gr.State()
116
+ upload=gr.File(label="Upload GLB",file_types=[".glb"],type="filepath")
117
+ inspect_button=gr.Button("Inspect / preflight")
118
+ report_view=gr.JSON(label="Mesh preflight")
119
+ original_download=gr.File(label="Preserved original",interactive=False)
120
+ run=gr.Button("Prepare and export rig",variant="primary")
121
+ status=gr.Textbox(label="Status",interactive=False)
122
+ preview=gr.Model3D(label="Export preview",interactive=False)
123
+ with gr.Row():
124
+ glb=gr.File(label="Download rigged GLB",interactive=False)
125
+ fbx=gr.File(label="Download rigged FBX",interactive=False)
126
+ with gr.Accordion("Runtime and acceptance checks",open=False):
127
+ gr.JSON(value=runtime_report,label="Startup GPU and dependency checks")
128
+ normal_test=gr.Button("Load bundled normal character for testing")
129
+ dense_test=gr.Button("Test dense character preflight")
130
+ dense_result=gr.JSON(label="Dense test result")
131
+ inspect_button.click(preserve_and_inspect,inputs=upload,
132
+ outputs=[original,report_view,original_download]).success(
133
+ lambda x:x,inputs=report_view,outputs=report_state)
134
+ normal_test.click(example,outputs=[original,report_view,original_download]).success(
135
+ lambda x:x,inputs=report_view,outputs=report_state)
136
+ run.click(execute,inputs=[original,report_state],outputs=[status,preview,glb,fbx],
137
+ concurrency_limit=1,concurrency_id="workflow")
138
+ dense_test.click(dense_character_test,outputs=dense_result,
139
+ concurrency_limit=1,concurrency_id="workflow")
140
+ demo.queue(max_size=4,default_concurrency_limit=1).launch(
141
+ server_name="0.0.0.0",server_port=7860,show_error=True,ssr_mode=False,
142
+ allowed_paths=[str(ROOT/"inputs"),str(ROOT/"outputs")])