ginipick commited on
Commit
5691c67
Β·
verified Β·
1 Parent(s): 73ae303

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +48 -33
app.py CHANGED
@@ -1,11 +1,15 @@
1
  """
2
- GLB μ• λ‹ˆλ©”μ΄μ…˜ 생성기
3
  ────────────────────────────────────────────────────────
4
- μ—…λ‘œλ“œν•œ GLB 3D λͺ¨λΈμ— νšŒμ „Β·λΆ€μœ Β·ν­λ°œ λ“± μ• λ‹ˆλ©”μ΄μ…˜μ„ μ μš©ν•΄
5
- β‘  λ³€ν˜•λœ GLB β‘‘ μ‹€μ œ λͺ¨λΈμ„ λ Œλ”λ§ν•œ GIF β‘’ 메타데이터 JSON 을 λŒλ €μ€λ‹ˆλ‹€.
 
 
 
 
6
  """
7
 
8
- # ──────────────────── 1. 곡톡 λͺ¨λ“ˆ ────────────────────
9
  import os, io, time, glob, json, math, shutil
10
  import numpy as np
11
  from PIL import Image
@@ -23,7 +27,7 @@ import spaces
23
  LOG_PATH = "./results/demo"
24
  os.makedirs(LOG_PATH, exist_ok=True)
25
 
26
- # ──────────────────── 2. λ Œλ” μœ ν‹Έ ────────────────────
27
  def _render_with_trimesh(scene: trimesh.Scene, res):
28
  png = scene.save_image(resolution=res, visible=True)
29
  if png is None:
@@ -48,7 +52,7 @@ def _render_with_pyrender(mesh_or_scene, res):
48
  r.delete()
49
  return Image.fromarray(color[..., :3])
50
 
51
- # ──────────────────── 3. GIF 생성 ────────────────────
52
  def create_model_animation_gif(
53
  output_path: str,
54
  input_glb_path: str,
@@ -67,26 +71,31 @@ def create_model_animation_gif(
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":
73
- M = tf.translation_matrix([0, 0.5 * math.sin(2 * math.pi * t), 0])
74
  elif animation_type == "pulse":
75
  M = tf.scale_matrix(0.8 + 0.4 * math.sin(2 * math.pi * t))
76
  elif animation_type == "explode":
77
- M = tf.translation_matrix([0.5 * t, 0, 0])
78
  elif animation_type == "assemble":
79
- M = tf.translation_matrix([0.5 * (1 - t), 0, 0])
80
  elif animation_type == "swing":
81
  M = tf.rotation_matrix(math.pi / 6 * math.sin(2 * math.pi * t), [0, 0, 1])
82
  else:
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:
89
- print("trimesh λ Œλ” μ‹€νŒ¨, pyrender 폴백:", e)
90
  frame = _render_with_pyrender(scene, resolution)
91
 
92
  frames.append(frame)
@@ -100,7 +109,7 @@ def create_model_animation_gif(
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)
@@ -126,23 +135,22 @@ def modify_glb_file(input_glb_path, output_glb_path, animation_type="rotate"):
126
  scn.export(output_glb_path)
127
  return output_glb_path
128
  except Exception as e:
129
- print("GLB λ³€ν˜• μ‹€νŒ¨, 원본 볡사:", e)
130
  shutil.copy(input_glb_path, output_glb_path)
131
  return output_glb_path
132
 
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")
139
- gif_out = os.path.join(LOG_PATH, f"preview_{base}.gif")
140
  json_out = os.path.join(LOG_PATH, f"metadata_{base}.json")
141
 
142
  modify_glb_file(input_3d, glb_out, animation_type)
143
- create_model_animation_gif(
144
- gif_out, input_3d, animation_type, animation_duration, fps
145
- )
146
 
147
  meta = dict(
148
  animation_type=animation_type,
@@ -157,29 +165,36 @@ def process_3d_model(input_3d, animation_type, animation_duration, fps):
157
  return glb_out, gif_out, json_out
158
 
159
  except Exception as e:
160
- print("process_3d_model μ‹€νŒ¨:", e)
161
  err_gif = os.path.join(LOG_PATH, "error.gif")
162
  Image.new("RGB", (640, 480), (255, 0, 0)).save(err_gif)
163
  return input_3d, err_gif, None
164
 
165
- # ──────────────────── 6. Gradio UI ────────────────────
166
- with gr.Blocks(title="GLB μ• λ‹ˆλ©”μ΄μ…˜ 생성기") as demo:
 
 
 
 
 
 
167
  gr.Markdown(
168
  """
169
- <h2><b>GLB μ• λ‹ˆλ©”μ΄μ…˜ 생성기 - 3D λͺ¨λΈ μ›€μ§μž„ 효과</b></h2>
170
- μ—…λ‘œλ“œν•œ GLB λͺ¨λΈμ— μ• λ‹ˆλ©”μ΄μ…˜μ„ μ μš©ν•΄ λ³€ν˜•λœ GLBΒ·GIFΒ·JSON을 μ œκ³΅ν•©λ‹ˆλ‹€.
 
171
  """
172
  )
173
  with gr.Row():
174
  with gr.Column():
175
- inp = gr.Model3D(label="3D λͺ¨λΈ μ—…λ‘œλ“œ (GLB)")
176
  typ = gr.Dropdown(
177
- label="μ• λ‹ˆλ©”μ΄μ…˜ μœ ν˜•",
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,
@@ -192,11 +207,11 @@ with gr.Blocks(title="GLB μ• λ‹ˆλ©”μ΄μ…˜ 생성기") as demo:
192
  value=30,
193
  step=1,
194
  )
195
- btn = gr.Button("μ• λ‹ˆλ©”μ΄μ…˜ 생성")
196
  with gr.Column():
197
- out_glb = gr.Model3D(label="μ• λ‹ˆλ©”μ΄μ…˜λœ GLB")
198
- out_gif = gr.Image(label="미리보기 GIF")
199
- out_json = gr.File(label="메타데이터 JSON")
200
 
201
  btn.click(
202
  fn=process_3d_model,
@@ -204,9 +219,9 @@ with gr.Blocks(title="GLB μ• λ‹ˆλ©”μ΄μ…˜ 생성기") as demo:
204
  outputs=[out_glb, out_gif, out_json],
205
  )
206
 
207
- ex = [[f] for f in glob.glob("./data/demo_glb/*.glb")]
208
- if ex:
209
- gr.Examples(examples=ex, inputs=[inp])
210
 
211
  if __name__ == "__main__":
212
  demo.launch(server_name="0.0.0.0", server_port=7860)
 
1
  """
2
+ 3D to Video
3
  ────────────────────────────────────────────────────────
4
+ Upload a GLB model and turn it into
5
+ β‘  a transformed GLB β‘‘ an animated GIF preview β‘’ a metadata JSON.
6
+
7
+ β€’ Headless-server friendly (EGL + pyglet-headless β†’ pyrender fallback)
8
+ β€’ Object in the GIF is now x3 larger (global scale Γ—3)
9
+ β€’ English-only UI with a pastel background
10
  """
11
 
12
+ # ──────────────────── 1. Common imports ────────────────────
13
  import os, io, time, glob, json, math, shutil
14
  import numpy as np
15
  from PIL import Image
 
27
  LOG_PATH = "./results/demo"
28
  os.makedirs(LOG_PATH, exist_ok=True)
29
 
30
+ # ──────────────────── 2. Rendering helpers ────────────────────
31
  def _render_with_trimesh(scene: trimesh.Scene, res):
32
  png = scene.save_image(resolution=res, visible=True)
33
  if png is None:
 
52
  r.delete()
53
  return Image.fromarray(color[..., :3])
54
 
55
+ # ──────────────────── 3. GIF generator (object Γ—3 larger) ────────────────────
56
  def create_model_animation_gif(
57
  output_path: str,
58
  input_glb_path: str,
 
71
  t = i / (num_frames - 1)
72
  scene = base.copy()
73
 
74
+ # -------- enlarge the whole scene by x3 --------
75
+ scene.apply_transform(tf.scale_matrix(3.0))
76
+
77
+ # -------- per-frame transform --------
78
  if animation_type == "rotate":
79
  M = tf.rotation_matrix(2 * math.pi * t, [0, 1, 0])
80
  elif animation_type == "float":
81
+ M = tf.translation_matrix([0, 1.5 * math.sin(2 * math.pi * t), 0]) # 0.5*3
82
  elif animation_type == "pulse":
83
  M = tf.scale_matrix(0.8 + 0.4 * math.sin(2 * math.pi * t))
84
  elif animation_type == "explode":
85
+ M = tf.translation_matrix([1.5 * t, 0, 0]) # 0.5*3
86
  elif animation_type == "assemble":
87
+ M = tf.translation_matrix([1.5 * (1 - t), 0, 0])
88
  elif animation_type == "swing":
89
  M = tf.rotation_matrix(math.pi / 6 * math.sin(2 * math.pi * t), [0, 0, 1])
90
  else:
91
  M = np.eye(4)
92
  scene.apply_transform(M)
93
 
94
+ # -------- render --------
95
  try:
96
  frame = _render_with_trimesh(scene, resolution)
97
  except Exception as e:
98
+ print("trimesh render failed, pyrender fallback:", e)
99
  frame = _render_with_pyrender(scene, resolution)
100
 
101
  frames.append(frame)
 
109
  )
110
  return output_path
111
 
112
+ # ──────────────────── 4. Simple GLB transform (unchanged) ────────────────────
113
  def modify_glb_file(input_glb_path, output_glb_path, animation_type="rotate"):
114
  try:
115
  scn = trimesh.load(input_glb_path)
 
135
  scn.export(output_glb_path)
136
  return output_glb_path
137
  except Exception as e:
138
+ print("GLB transform failed, copying original:", e)
139
  shutil.copy(input_glb_path, output_glb_path)
140
  return output_glb_path
141
 
142
+ # ──────────────────── 5. Gradio pipeline ────────────────────
143
  @spaces.GPU
144
  def process_3d_model(input_3d, animation_type, animation_duration, fps):
145
  try:
146
  base = os.path.splitext(os.path.basename(input_3d))[0]
147
+ glb_out = os.path.join(LOG_PATH, f"animated_{base}.glb")
148
+ gif_out = os.path.join(LOG_PATH, f"preview_{base}.gif")
149
  json_out = os.path.join(LOG_PATH, f"metadata_{base}.json")
150
 
151
  modify_glb_file(input_3d, glb_out, animation_type)
152
+ create_model_animation_gif(gif_out, input_3d, animation_type,
153
+ animation_duration, fps)
 
154
 
155
  meta = dict(
156
  animation_type=animation_type,
 
165
  return glb_out, gif_out, json_out
166
 
167
  except Exception as e:
168
+ print("process_3d_model failed:", e)
169
  err_gif = os.path.join(LOG_PATH, "error.gif")
170
  Image.new("RGB", (640, 480), (255, 0, 0)).save(err_gif)
171
  return input_3d, err_gif, None
172
 
173
+ # ──────────────────── 6. Gradio UI ────────────────────
174
+ PASTEL_CSS = """
175
+ body {background:#f9f6ff !important;}
176
+ .gradio-container {background:#f9f6ff !important;}
177
+ footer {display:none !important;}
178
+ """
179
+
180
+ with gr.Blocks(title="3D to Video", css=PASTEL_CSS) as demo:
181
  gr.Markdown(
182
  """
183
+ <h2><b>3D to Video</b></h2>
184
+ Transform a static GLB model into an animated clip.
185
+ Select an animation style, tune duration & FPS, then download the results.
186
  """
187
  )
188
  with gr.Row():
189
  with gr.Column():
190
+ inp = gr.Model3D(label="Upload GLB")
191
  typ = gr.Dropdown(
192
+ label="Animation Type",
193
  choices=["rotate", "float", "explode", "assemble", "pulse", "swing"],
194
  value="rotate",
195
  )
196
  dur = gr.Slider(
197
+ label="Length (seconds)",
198
  minimum=1.0,
199
  maximum=10.0,
200
  value=3.0,
 
207
  value=30,
208
  step=1,
209
  )
210
+ btn = gr.Button("Generate Animation")
211
  with gr.Column():
212
+ out_glb = gr.Model3D(label="Animated GLB")
213
+ out_gif = gr.Image( label="GIF Preview")
214
+ out_json = gr.File( label="Metadata JSON")
215
 
216
  btn.click(
217
  fn=process_3d_model,
 
219
  outputs=[out_glb, out_gif, out_json],
220
  )
221
 
222
+ examples = [[f] for f in glob.glob("./data/demo_glb/*.glb")]
223
+ if examples:
224
+ gr.Examples(examples=examples, inputs=[inp])
225
 
226
  if __name__ == "__main__":
227
  demo.launch(server_name="0.0.0.0", server_port=7860)