Update app.py
Browse files
app.py
CHANGED
@@ -3,8 +3,6 @@ GLB μ λλ©μ΄μ
μμ±κΈ°
|
|
3 |
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
4 |
μ
λ‘λν GLB 3D λͺ¨λΈμ νμ Β·λΆμ Β·νλ° λ± μ λλ©μ΄μ
μ μ μ©ν΄
|
5 |
β λ³νλ GLB β‘ μ€μ λͺ¨λΈμ λ λλ§ν GIF β’ λ©νλ°μ΄ν° JSON μ λλ €μ€λλ€.
|
6 |
-
β’ headless μλ² λμ: EGL + pyglet.headless + trimesh β μ€ν¨ μ pyrender ν΄λ°±
|
7 |
-
β’ μ΅λ 60 fpsΒ·60 νλ μλ‘ μ ν (λ°λͺ¨ λͺ©μ )
|
8 |
"""
|
9 |
|
10 |
# ββββββββββββββββββββ 1. κ³΅ν΅ λͺ¨λ ββββββββββββββββββββ
|
@@ -13,8 +11,8 @@ import numpy as np
|
|
13 |
from PIL import Image
|
14 |
|
15 |
import pyglet
|
16 |
-
pyglet.options["headless"] = True
|
17 |
-
os.environ["PYOPENGL_PLATFORM"] = "egl"
|
18 |
|
19 |
import trimesh
|
20 |
import trimesh.transformations as tf
|
@@ -33,7 +31,7 @@ def _render_with_trimesh(scene: trimesh.Scene, res):
|
|
33 |
return Image.open(io.BytesIO(png)).convert("RGB")
|
34 |
|
35 |
def _render_with_pyrender(mesh_or_scene, res):
|
36 |
-
import pyrender
|
37 |
if isinstance(mesh_or_scene, trimesh.Scene):
|
38 |
mesh = trimesh.util.concatenate(mesh_or_scene.dump())
|
39 |
else:
|
@@ -59,7 +57,6 @@ def create_model_animation_gif(
|
|
59 |
fps: int = 30,
|
60 |
resolution=(640, 480),
|
61 |
):
|
62 |
-
"""GLB λͺ¨λΈμ μ€μ λ λλ§νμ¬ μ λλ©μ΄μ
GIF μμ±"""
|
63 |
base = trimesh.load(input_glb_path)
|
64 |
if isinstance(base, trimesh.Trimesh):
|
65 |
base = trimesh.Scene(base)
|
@@ -70,7 +67,6 @@ def create_model_animation_gif(
|
|
70 |
t = i / (num_frames - 1)
|
71 |
scene = base.copy()
|
72 |
|
73 |
-
# λ³ν νλ ¬
|
74 |
if animation_type == "rotate":
|
75 |
M = tf.rotation_matrix(2 * math.pi * t, [0, 1, 0])
|
76 |
elif animation_type == "float":
|
@@ -87,7 +83,6 @@ def create_model_animation_gif(
|
|
87 |
M = np.eye(4)
|
88 |
scene.apply_transform(M)
|
89 |
|
90 |
-
# β trimesh β β‘ pyrender ν΄λ°±
|
91 |
try:
|
92 |
frame = _render_with_trimesh(scene, resolution)
|
93 |
except Exception as e:
|
@@ -103,12 +98,10 @@ def create_model_animation_gif(
|
|
103 |
duration=int(1000 / fps),
|
104 |
loop=0,
|
105 |
)
|
106 |
-
print("GIF saved:", output_path)
|
107 |
return output_path
|
108 |
|
109 |
# ββββββββββββββββββββ 4. GLB λ³ν ββββββββββββββββββββ
|
110 |
def modify_glb_file(input_glb_path, output_glb_path, animation_type="rotate"):
|
111 |
-
"""λ¨μΌ λ³νμ μ μ©ν μ GLB μ μ₯ (μ€μ μ λλ©μ΄μ
μ μλ)"""
|
112 |
try:
|
113 |
scn = trimesh.load(input_glb_path)
|
114 |
if not isinstance(scn, trimesh.Scene):
|
@@ -140,7 +133,6 @@ def modify_glb_file(input_glb_path, output_glb_path, animation_type="rotate"):
|
|
140 |
# ββββββββββββββββββββ 5. Gradio νμ΄νλΌμΈ ββββββββββββββββββββ
|
141 |
@spaces.GPU
|
142 |
def process_3d_model(input_3d, animation_type, animation_duration, fps):
|
143 |
-
"""μ 체 νμ΄νλΌμΈ: GLB λ³ν β GIF λ λ β JSON μμ±"""
|
144 |
try:
|
145 |
base = os.path.splitext(os.path.basename(input_3d))[0]
|
146 |
glb_out = os.path.join(LOG_PATH, f"animated_{base}.glb")
|
@@ -186,8 +178,20 @@ with gr.Blocks(title="GLB μ λλ©μ΄μ
μμ±κΈ°") as demo:
|
|
186 |
choices=["rotate", "float", "explode", "assemble", "pulse", "swing"],
|
187 |
value="rotate",
|
188 |
)
|
189 |
-
dur = gr.Slider(
|
190 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
191 |
btn = gr.Button("μ λλ©μ΄μ
μμ±")
|
192 |
with gr.Column():
|
193 |
out_glb = gr.Model3D(label="μ λλ©μ΄μ
λ GLB")
|
|
|
3 |
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
4 |
μ
λ‘λν GLB 3D λͺ¨λΈμ νμ Β·λΆμ Β·νλ° λ± μ λλ©μ΄μ
μ μ μ©ν΄
|
5 |
β λ³νλ GLB β‘ μ€μ λͺ¨λΈμ λ λλ§ν GIF β’ λ©νλ°μ΄ν° JSON μ λλ €μ€λλ€.
|
|
|
|
|
6 |
"""
|
7 |
|
8 |
# ββββββββββββββββββββ 1. κ³΅ν΅ λͺ¨λ ββββββββββββββββββββ
|
|
|
11 |
from PIL import Image
|
12 |
|
13 |
import pyglet
|
14 |
+
pyglet.options["headless"] = True
|
15 |
+
os.environ["PYOPENGL_PLATFORM"] = "egl"
|
16 |
|
17 |
import trimesh
|
18 |
import trimesh.transformations as tf
|
|
|
31 |
return Image.open(io.BytesIO(png)).convert("RGB")
|
32 |
|
33 |
def _render_with_pyrender(mesh_or_scene, res):
|
34 |
+
import pyrender
|
35 |
if isinstance(mesh_or_scene, trimesh.Scene):
|
36 |
mesh = trimesh.util.concatenate(mesh_or_scene.dump())
|
37 |
else:
|
|
|
57 |
fps: int = 30,
|
58 |
resolution=(640, 480),
|
59 |
):
|
|
|
60 |
base = trimesh.load(input_glb_path)
|
61 |
if isinstance(base, trimesh.Trimesh):
|
62 |
base = trimesh.Scene(base)
|
|
|
67 |
t = i / (num_frames - 1)
|
68 |
scene = base.copy()
|
69 |
|
|
|
70 |
if animation_type == "rotate":
|
71 |
M = tf.rotation_matrix(2 * math.pi * t, [0, 1, 0])
|
72 |
elif animation_type == "float":
|
|
|
83 |
M = np.eye(4)
|
84 |
scene.apply_transform(M)
|
85 |
|
|
|
86 |
try:
|
87 |
frame = _render_with_trimesh(scene, resolution)
|
88 |
except Exception as e:
|
|
|
98 |
duration=int(1000 / fps),
|
99 |
loop=0,
|
100 |
)
|
|
|
101 |
return output_path
|
102 |
|
103 |
# ββββββββββββββββββββ 4. GLB λ³ν ββββββββββββββββββββ
|
104 |
def modify_glb_file(input_glb_path, output_glb_path, animation_type="rotate"):
|
|
|
105 |
try:
|
106 |
scn = trimesh.load(input_glb_path)
|
107 |
if not isinstance(scn, trimesh.Scene):
|
|
|
133 |
# ββββββββββββββββββββ 5. Gradio νμ΄νλΌμΈ ββββββββββββββββββββ
|
134 |
@spaces.GPU
|
135 |
def process_3d_model(input_3d, animation_type, animation_duration, fps):
|
|
|
136 |
try:
|
137 |
base = os.path.splitext(os.path.basename(input_3d))[0]
|
138 |
glb_out = os.path.join(LOG_PATH, f"animated_{base}.glb")
|
|
|
178 |
choices=["rotate", "float", "explode", "assemble", "pulse", "swing"],
|
179 |
value="rotate",
|
180 |
)
|
181 |
+
dur = gr.Slider(
|
182 |
+
label="μ λλ©μ΄μ
κΈΈμ΄ (μ΄)",
|
183 |
+
minimum=1.0,
|
184 |
+
maximum=10.0,
|
185 |
+
value=3.0,
|
186 |
+
step=0.5,
|
187 |
+
)
|
188 |
+
fps = gr.Slider(
|
189 |
+
label="FPS",
|
190 |
+
minimum=15,
|
191 |
+
maximum=60,
|
192 |
+
value=30,
|
193 |
+
step=1,
|
194 |
+
)
|
195 |
btn = gr.Button("μ λλ©μ΄μ
μμ±")
|
196 |
with gr.Column():
|
197 |
out_glb = gr.Model3D(label="μ λλ©μ΄μ
λ GLB")
|