Spaces:
Running
on
Zero
Running
on
Zero
Update app.py
Browse files
app.py
CHANGED
@@ -17,10 +17,8 @@ os.environ["PIXEL3DMM_TRACKING_OUTPUT"] = f"{os.getcwd()}/tracking_results"
|
|
17 |
|
18 |
def sh(cmd): subprocess.check_call(cmd, shell=True)
|
19 |
|
20 |
-
os.system(f"pip install -e {os.getcwd()}")
|
21 |
-
|
22 |
-
from pixel3dmm import env_paths
|
23 |
|
|
|
24 |
sh("cd src/pixel3dmm/preprocessing/facer && pip install -e .")
|
25 |
sh("cd src/pixel3dmm/preprocessing/PIPNet/FaceBoxesV2/utils && sh make.sh")
|
26 |
|
@@ -44,194 +42,4 @@ def install_cuda_toolkit():
|
|
44 |
|
45 |
install_cuda_toolkit()
|
46 |
|
47 |
-
|
48 |
-
import torch
|
49 |
-
import numpy as np
|
50 |
-
import trimesh
|
51 |
-
from pytorch3d.io import load_obj
|
52 |
-
from pixel3dmm.tracking.renderer_nvdiffrast import NVDRenderer
|
53 |
-
from pixel3dmm.tracking.flame.FLAME import FLAME
|
54 |
-
from pixel3dmm.tracking.tracker import Tracker
|
55 |
-
from omegaconf import OmegaConf
|
56 |
-
|
57 |
-
|
58 |
-
DEVICE = "cuda"
|
59 |
-
|
60 |
-
base_conf = OmegaConf.load(f'{env_paths.CODE_BASE}/configs/tracking.yaml')
|
61 |
-
|
62 |
-
_mesh_file = env_paths.head_template
|
63 |
-
flame_model = FLAME(base_conf).to(DEVICE)
|
64 |
-
|
65 |
-
_obj_faces = load_obj(_mesh_file)[1]
|
66 |
-
|
67 |
-
diff_renderer = NVDRenderer(
|
68 |
-
image_size=base_conf.size,
|
69 |
-
obj_filename=_mesh_file,
|
70 |
-
no_sh=False,
|
71 |
-
white_bg=True
|
72 |
-
).to(DEVICE)
|
73 |
-
|
74 |
-
|
75 |
-
# Utility to select first image from a folder
|
76 |
-
def first_image_from_dir(directory):
|
77 |
-
patterns = ["*.jpg", "*.png", "*.jpeg"]
|
78 |
-
files = []
|
79 |
-
for p in patterns:
|
80 |
-
files.extend(glob.glob(os.path.join(directory, p)))
|
81 |
-
if not files:
|
82 |
-
return None
|
83 |
-
return sorted(files)[0]
|
84 |
-
|
85 |
-
# Function to reset the UI and state
|
86 |
-
def reset_all():
|
87 |
-
return (
|
88 |
-
None, # crop_img
|
89 |
-
None, # normals_img
|
90 |
-
None, # uv_img
|
91 |
-
None, # track_img
|
92 |
-
"Awaiting new image upload...", # status
|
93 |
-
{}, # state
|
94 |
-
gr.update(interactive=True), # preprocess_btn
|
95 |
-
gr.update(interactive=False), # normals_btn
|
96 |
-
gr.update(interactive=False), # uv_map_btn
|
97 |
-
gr.update(interactive=False) # track_btn
|
98 |
-
)
|
99 |
-
|
100 |
-
# Step 1: Preprocess the input image (Save and Crop)
|
101 |
-
# @spaces.GPU()
|
102 |
-
def preprocess_image(image_array, state):
|
103 |
-
if image_array is None:
|
104 |
-
return "❌ Please upload an image first.", None, state, gr.update(interactive=True), gr.update(interactive=False)
|
105 |
-
|
106 |
-
session_id = str(uuid.uuid4())
|
107 |
-
base_dir = os.path.join(os.environ["PIXEL3DMM_PREPROCESSED_DATA"], session_id)
|
108 |
-
os.makedirs(base_dir, exist_ok=True)
|
109 |
-
state.update({"session_id": session_id, "base_dir": base_dir})
|
110 |
-
|
111 |
-
img = Image.fromarray(image_array)
|
112 |
-
saved_image_path = os.path.join(base_dir, f"{session_id}.png")
|
113 |
-
img.save(saved_image_path)
|
114 |
-
state["image_path"] = saved_image_path
|
115 |
-
|
116 |
-
try:
|
117 |
-
p = subprocess.run([
|
118 |
-
"python", "scripts/run_preprocessing.py", "--video_or_images_path", saved_image_path
|
119 |
-
], check=True, capture_output=True, text=True)
|
120 |
-
except subprocess.CalledProcessError as e:
|
121 |
-
err = f"❌ Preprocess failed (exit {e.returncode}).\n\n{e.stdout}\n{e.stderr}"
|
122 |
-
shutil.rmtree(base_dir)
|
123 |
-
return err, None, {}, gr.update(interactive=True), gr.update(interactive=False)
|
124 |
-
|
125 |
-
crop_dir = os.path.join(base_dir, "cropped")
|
126 |
-
image = first_image_from_dir(crop_dir)
|
127 |
-
return "✅ Step 1 complete. Ready for Normals.", image, state, gr.update(interactive=False), gr.update(interactive=True)
|
128 |
-
|
129 |
-
# Step 2: Normals inference → normals image
|
130 |
-
@spaces.GPU()
|
131 |
-
def step2_normals(state):
|
132 |
-
session_id = state.get("session_id")
|
133 |
-
if not session_id:
|
134 |
-
return "❌ State lost. Please start from Step 1.", None, state, gr.update(interactive=False), gr.update(interactive=False)
|
135 |
-
|
136 |
-
try:
|
137 |
-
p = subprocess.run([
|
138 |
-
"python", "scripts/network_inference.py", "model.prediction_type=normals", f"video_name={session_id}"
|
139 |
-
], check=True, capture_output=True, text=True)
|
140 |
-
except subprocess.CalledProcessError as e:
|
141 |
-
err = f"❌ Normal map failed (exit {e.returncode}).\n\n{e.stdout}\n{e.stderr}"
|
142 |
-
return err, None, state, gr.update(interactive=True), gr.update(interactive=False)
|
143 |
-
|
144 |
-
normals_dir = os.path.join(state["base_dir"], "p3dmm", "normals")
|
145 |
-
image = first_image_from_dir(normals_dir)
|
146 |
-
return "✅ Step 2 complete. Ready for UV Map.", image, state, gr.update(interactive=False), gr.update(interactive=True)
|
147 |
-
|
148 |
-
# Step 3: UV map inference → uv map image
|
149 |
-
@spaces.GPU()
|
150 |
-
def step3_uv_map(state):
|
151 |
-
session_id = state.get("session_id")
|
152 |
-
if not session_id:
|
153 |
-
return "❌ State lost. Please start from Step 1.", None, state, gr.update(interactive=False), gr.update(interactive=False)
|
154 |
-
|
155 |
-
try:
|
156 |
-
p = subprocess.run([
|
157 |
-
"python", "scripts/network_inference.py", "model.prediction_type=uv_map", f"video_name={session_id}"
|
158 |
-
], check=True, capture_output=True, text=True)
|
159 |
-
except subprocess.CalledProcessError as e:
|
160 |
-
err = f"❌ UV map failed (exit {e.returncode}).\n\n{e.stdout}\n{e.stderr}"
|
161 |
-
return err, None, state, gr.update(interactive=True), gr.update(interactive=False)
|
162 |
-
|
163 |
-
uv_dir = os.path.join(state["base_dir"], "p3dmm", "uv_map")
|
164 |
-
image = first_image_from_dir(uv_dir)
|
165 |
-
return "✅ Step 3 complete. Ready for Tracking.", image, state, gr.update(interactive=False), gr.update(interactive=True)
|
166 |
-
|
167 |
-
# Step 4: Tracking → final tracking image
|
168 |
-
@spaces.GPU()
|
169 |
-
def step4_track(state):
|
170 |
-
session_id = state.get("session_id")
|
171 |
-
base_conf.video_name = f'{session_id}'
|
172 |
-
tracker = Tracker(base_conf, flame_model, diff_renderer)
|
173 |
-
tracker.run()
|
174 |
-
|
175 |
-
tracking_dir = os.path.join(os.environ["PIXEL3DMM_TRACKING_OUTPUT"], session_id, "frames")
|
176 |
-
image = first_image_from_dir(tracking_dir)
|
177 |
-
|
178 |
-
return "✅ Pipeline complete!", image, state, gr.update(interactive=False)
|
179 |
-
|
180 |
-
# Build Gradio UI
|
181 |
-
demo = gr.Blocks()
|
182 |
-
|
183 |
-
with demo:
|
184 |
-
gr.Markdown("## Image Processing Pipeline")
|
185 |
-
gr.Markdown("Upload an image, then click the buttons in order. Uploading a new image will reset the process.")
|
186 |
-
with gr.Row():
|
187 |
-
with gr.Column():
|
188 |
-
image_in = gr.Image(label="Upload Image", type="numpy", height=512)
|
189 |
-
status = gr.Textbox(label="Status", lines=2, interactive=False, value="Upload an image to start.")
|
190 |
-
state = gr.State({})
|
191 |
-
with gr.Column():
|
192 |
-
with gr.Row():
|
193 |
-
crop_img = gr.Image(label="Preprocessed", height=256)
|
194 |
-
normals_img = gr.Image(label="Normals", height=256)
|
195 |
-
with gr.Row():
|
196 |
-
uv_img = gr.Image(label="UV Map", height=256)
|
197 |
-
track_img = gr.Image(label="Tracking", height=256)
|
198 |
-
|
199 |
-
with gr.Row():
|
200 |
-
preprocess_btn = gr.Button("Step 1: Preprocess", interactive=True)
|
201 |
-
normals_btn = gr.Button("Step 2: Normals", interactive=False)
|
202 |
-
uv_map_btn = gr.Button("Step 3: UV Map", interactive=False)
|
203 |
-
track_btn = gr.Button("Step 4: Track", interactive=False)
|
204 |
-
|
205 |
-
# Define component list for reset
|
206 |
-
outputs_for_reset = [crop_img, normals_img, uv_img, track_img, status, state, preprocess_btn, normals_btn, uv_map_btn, track_btn]
|
207 |
-
|
208 |
-
# Pipeline execution logic
|
209 |
-
preprocess_btn.click(
|
210 |
-
fn=preprocess_image,
|
211 |
-
inputs=[image_in, state],
|
212 |
-
outputs=[status, crop_img, state, preprocess_btn, normals_btn]
|
213 |
-
)
|
214 |
-
normals_btn.click(
|
215 |
-
fn=step2_normals,
|
216 |
-
inputs=[state],
|
217 |
-
outputs=[status, normals_img, state, normals_btn, uv_map_btn]
|
218 |
-
)
|
219 |
-
uv_map_btn.click(
|
220 |
-
fn=step3_uv_map,
|
221 |
-
inputs=[state],
|
222 |
-
outputs=[status, uv_img, state, uv_map_btn, track_btn]
|
223 |
-
)
|
224 |
-
track_btn.click(
|
225 |
-
fn=step4_track,
|
226 |
-
inputs=[state],
|
227 |
-
outputs=[status, track_img, state, track_btn]
|
228 |
-
)
|
229 |
-
|
230 |
-
# Event to reset everything when a new image is uploaded
|
231 |
-
image_in.upload(fn=reset_all, inputs=None, outputs=outputs_for_reset)
|
232 |
-
|
233 |
-
# ------------------------------------------------------------------
|
234 |
-
# START THE GRADIO SERVER
|
235 |
-
# ------------------------------------------------------------------
|
236 |
-
demo.queue()
|
237 |
-
demo.launch(share=True, ssr_mode=False)
|
|
|
17 |
|
18 |
def sh(cmd): subprocess.check_call(cmd, shell=True)
|
19 |
|
|
|
|
|
|
|
20 |
|
21 |
+
sh("pip install -e .")
|
22 |
sh("cd src/pixel3dmm/preprocessing/facer && pip install -e .")
|
23 |
sh("cd src/pixel3dmm/preprocessing/PIPNet/FaceBoxesV2/utils && sh make.sh")
|
24 |
|
|
|
42 |
|
43 |
install_cuda_toolkit()
|
44 |
|
45 |
+
sh("python app_photo.py")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|