yongyeol commited on
Commit
cc80057
·
verified ·
1 Parent(s): 35cddec

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +42 -28
app.py CHANGED
@@ -1,11 +1,11 @@
1
  # ────────────────────────────────────────────────────────────────────────────
2
- # app.py – Text ➜ 2D (FLUX-mini Kontext) ➜ 3D (Hunyuan3D-2)
3
- # • Fits into 16 GB system RAM: 경량 모델 + lazy loading + offload
4
- # • Updated: use device_map="balanced" ("auto" not supported by Flux pipelines)
5
  # ────────────────────────────────────────────────────────────────────────────
6
  import os
7
  import tempfile
8
- from typing import List, Tuple
9
 
10
  import gradio as gr
11
  import torch
@@ -17,7 +17,7 @@ HF_TOKEN = os.getenv("HF_TOKEN")
17
  if not HF_TOKEN:
18
  raise RuntimeError(
19
  "HF_TOKEN이 설정되지 않았습니다. Space Settings → Secrets에서 "
20
- "HF_TOKEN=your_read_token 을 등록한 뒤 재시작하세요."
21
  )
22
  login(token=HF_TOKEN, add_to_git_credential=False)
23
 
@@ -29,46 +29,56 @@ DTYPE = torch.float16 if torch.cuda.is_available() else torch.float32
29
  from diffusers import FluxKontextPipeline, FluxPipeline
30
 
31
  # Global caches
32
- kontext_pipe = None # type: FluxKontextPipeline | None
33
- _text2img_pipe = None # type: FluxPipeline | None
34
  shape_pipe = None
35
  paint_pipe = None
36
 
37
- MINI_KONTEXT_REPO = "black-forest-labs/FLUX.1-Kontext-mini"
38
- MINI_T2I_REPO = "black-forest-labs/FLUX.1-mini"
39
- HUNYUAN_REPO = "tencent/Hunyuan3D-2"
40
- DEVICE_MAP_STRATEGY = "balanced" # "auto" unsupported for Flux pipelines
41
 
 
 
 
42
 
43
  def load_kontext() -> FluxKontextPipeline:
 
44
  global kontext_pipe
45
  if kontext_pipe is None:
46
- print("[+] Loading FLUX.1-Kontext-mini … (balanced offload)")
47
  kontext_pipe = FluxKontextPipeline.from_pretrained(
48
  MINI_KONTEXT_REPO,
49
  torch_dtype=DTYPE,
50
  device_map=DEVICE_MAP_STRATEGY,
51
  low_cpu_mem_usage=True,
 
 
52
  )
53
  kontext_pipe.set_progress_bar_config(disable=True)
54
  return kontext_pipe
55
 
56
 
57
  def load_text2img() -> FluxPipeline:
 
58
  global _text2img_pipe
59
  if _text2img_pipe is None:
60
- print("[+] Loading FLUX.1-mini (text→image)…")
61
  _text2img_pipe = FluxPipeline.from_pretrained(
62
  MINI_T2I_REPO,
63
  torch_dtype=DTYPE,
64
  device_map=DEVICE_MAP_STRATEGY,
65
  low_cpu_mem_usage=True,
 
 
66
  )
67
  _text2img_pipe.set_progress_bar_config(disable=True)
68
  return _text2img_pipe
69
 
70
 
71
- def load_hunyuan() -> tuple:
 
72
  global shape_pipe, paint_pipe
73
  if shape_pipe is None or paint_pipe is None:
74
  print("[+] Loading Hunyuan3D-2 (shape & texture)…")
@@ -80,6 +90,8 @@ def load_hunyuan() -> tuple:
80
  torch_dtype=DTYPE,
81
  device_map=DEVICE_MAP_STRATEGY,
82
  low_cpu_mem_usage=True,
 
 
83
  )
84
  shape_pipe.set_progress_bar_config(disable=True)
85
 
@@ -88,40 +100,42 @@ def load_hunyuan() -> tuple:
88
  torch_dtype=DTYPE,
89
  device_map=DEVICE_MAP_STRATEGY,
90
  low_cpu_mem_usage=True,
 
 
91
  )
92
  paint_pipe.set_progress_bar_config(disable=True)
93
  return shape_pipe, paint_pipe
94
 
95
- # ───────────────────────────────────────────────
96
- # Helper functions
97
- # ──────────────────��────────────────────────────
98
 
99
  def generate_single_2d(prompt: str, image: Image.Image | None, guidance_scale: float) -> Image.Image:
100
- kontext = load_kontext()
101
  if image is None:
102
  t2i = load_text2img()
103
- result = t2i(prompt=prompt, guidance_scale=guidance_scale).images[0]
104
- else:
105
- result = kontext(image=image, prompt=prompt, guidance_scale=guidance_scale).images[0]
106
- return result
107
 
108
 
109
  def generate_multiview(prompt: str, base_image: Image.Image, guidance_scale: float) -> List[Image.Image]:
 
110
  kontext = load_kontext()
111
- views = [
112
  base_image,
113
  kontext(image=base_image, prompt=f"{prompt}, left side view", guidance_scale=guidance_scale).images[0],
114
  kontext(image=base_image, prompt=f"{prompt}, right side view", guidance_scale=guidance_scale).images[0],
115
  kontext(image=base_image, prompt=f"{prompt}, back view", guidance_scale=guidance_scale).images[0],
116
  ]
117
- return views
118
 
119
 
120
  def build_3d_mesh(prompt: str, images: List[Image.Image]) -> str:
 
121
  shape, paint = load_hunyuan()
122
- single_or_multi = images if len(images) > 1 else images[0]
123
- mesh = shape(image=single_or_multi, prompt=prompt)[0]
124
- mesh = paint(mesh, image=single_or_multi)
 
125
 
126
  tmpdir = tempfile.mkdtemp()
127
  out_path = os.path.join(tmpdir, "mesh.glb")
@@ -146,7 +160,7 @@ def workflow(prompt: str, input_image: Image.Image | None, multiview: bool, guid
146
  def build_ui():
147
  with gr.Blocks(css=CSS, title="Text ➜ 2D ➜ 3D (mini)") as demo:
148
  gr.Markdown("# 🌀 텍스트 → 2D → 3D 생성기 (경량 버전)")
149
- gr.Markdown("Kontext-mini + Hunyuan3D-2. 16 GB RAM에서도 동작합니다.")
150
 
151
  with gr.Row():
152
  with gr.Column():
 
1
  # ────────────────────────────────────────────────────────────────────────────
2
+ # app.py – Text ➜ 2D (FLUX-mini Kontext-dev) ➜ 3D (Hunyuan3D-2)
3
+ # • Fits into 16 GB system RAM: lightweight models + lazy loading + offload
4
+ # • 2025-07-07: fixed repo names, added HF token + trust_remote_code, cleaned logs
5
  # ────────────────────────────────────────────────────────────────────────────
6
  import os
7
  import tempfile
8
+ from typing import List
9
 
10
  import gradio as gr
11
  import torch
 
17
  if not HF_TOKEN:
18
  raise RuntimeError(
19
  "HF_TOKEN이 설정되지 않았습니다. Space Settings → Secrets에서 "
20
+ "HF_TOKEN=<your_read_token> 을 등록한 뒤 재시작하세요."
21
  )
22
  login(token=HF_TOKEN, add_to_git_credential=False)
23
 
 
29
  from diffusers import FluxKontextPipeline, FluxPipeline
30
 
31
  # Global caches
32
+ kontext_pipe: FluxKontextPipeline | None = None
33
+ _text2img_pipe: FluxPipeline | None = None
34
  shape_pipe = None
35
  paint_pipe = None
36
 
37
+ # Repository names (공개 버전)
38
+ MINI_KONTEXT_REPO = "black-forest-labs/FLUX.1-Kontext-dev" # 이미지 편집/확장용
39
+ MINI_T2I_REPO = "black-forest-labs/FLUX.1-schnell" # 텍스트→이미지(4-step distilled)
40
+ HUNYUAN_REPO = "tencent/Hunyuan3D-2" # 3D shape & paint
41
 
42
+ DEVICE_MAP_STRATEGY = "balanced" # "auto"(offload) 미지원, so use "balanced"
43
+
44
+ # ──────────────────────────── Loaders ────────────────────────────
45
 
46
  def load_kontext() -> FluxKontextPipeline:
47
+ """Lazy-load FLUX.1-Kontext-dev (image-to-image editing)."""
48
  global kontext_pipe
49
  if kontext_pipe is None:
50
+ print("[+] Loading FLUX.1-Kontext-dev … (balanced offload)")
51
  kontext_pipe = FluxKontextPipeline.from_pretrained(
52
  MINI_KONTEXT_REPO,
53
  torch_dtype=DTYPE,
54
  device_map=DEVICE_MAP_STRATEGY,
55
  low_cpu_mem_usage=True,
56
+ token=HF_TOKEN,
57
+ trust_remote_code=True,
58
  )
59
  kontext_pipe.set_progress_bar_config(disable=True)
60
  return kontext_pipe
61
 
62
 
63
  def load_text2img() -> FluxPipeline:
64
+ """Lazy-load FLUX.1-schnell (text-to-image)."""
65
  global _text2img_pipe
66
  if _text2img_pipe is None:
67
+ print("[+] Loading FLUX.1-schnell (text→image)…")
68
  _text2img_pipe = FluxPipeline.from_pretrained(
69
  MINI_T2I_REPO,
70
  torch_dtype=DTYPE,
71
  device_map=DEVICE_MAP_STRATEGY,
72
  low_cpu_mem_usage=True,
73
+ token=HF_TOKEN,
74
+ trust_remote_code=True,
75
  )
76
  _text2img_pipe.set_progress_bar_config(disable=True)
77
  return _text2img_pipe
78
 
79
 
80
+ def load_hunyuan():
81
+ """Lazy-load Hunyuan3D-2 shape & texture pipelines."""
82
  global shape_pipe, paint_pipe
83
  if shape_pipe is None or paint_pipe is None:
84
  print("[+] Loading Hunyuan3D-2 (shape & texture)…")
 
90
  torch_dtype=DTYPE,
91
  device_map=DEVICE_MAP_STRATEGY,
92
  low_cpu_mem_usage=True,
93
+ token=HF_TOKEN,
94
+ trust_remote_code=True,
95
  )
96
  shape_pipe.set_progress_bar_config(disable=True)
97
 
 
100
  torch_dtype=DTYPE,
101
  device_map=DEVICE_MAP_STRATEGY,
102
  low_cpu_mem_usage=True,
103
+ token=HF_TOKEN,
104
+ trust_remote_code=True,
105
  )
106
  paint_pipe.set_progress_bar_config(disable=True)
107
  return shape_pipe, paint_pipe
108
 
109
+ # ───────────────────────────── Helpers ─────────────────────────────
 
 
110
 
111
  def generate_single_2d(prompt: str, image: Image.Image | None, guidance_scale: float) -> Image.Image:
112
+ """Generate a single 2D image (txt2img or img2img)."""
113
  if image is None:
114
  t2i = load_text2img()
115
+ return t2i(prompt=prompt, guidance_scale=guidance_scale).images[0]
116
+
117
+ kontext = load_kontext()
118
+ return kontext(image=image, prompt=prompt, guidance_scale=guidance_scale).images[0]
119
 
120
 
121
  def generate_multiview(prompt: str, base_image: Image.Image, guidance_scale: float) -> List[Image.Image]:
122
+ """Generate 4-view images for better 3D reconstruction."""
123
  kontext = load_kontext()
124
+ return [
125
  base_image,
126
  kontext(image=base_image, prompt=f"{prompt}, left side view", guidance_scale=guidance_scale).images[0],
127
  kontext(image=base_image, prompt=f"{prompt}, right side view", guidance_scale=guidance_scale).images[0],
128
  kontext(image=base_image, prompt=f"{prompt}, back view", guidance_scale=guidance_scale).images[0],
129
  ]
 
130
 
131
 
132
  def build_3d_mesh(prompt: str, images: List[Image.Image]) -> str:
133
+ """Create GLB mesh from single or multi-view images."""
134
  shape, paint = load_hunyuan()
135
+ source = images if len(images) > 1 else images[0]
136
+
137
+ mesh = shape(image=source, prompt=prompt)[0]
138
+ mesh = paint(mesh, image=source) # texture painting
139
 
140
  tmpdir = tempfile.mkdtemp()
141
  out_path = os.path.join(tmpdir, "mesh.glb")
 
160
  def build_ui():
161
  with gr.Blocks(css=CSS, title="Text ➜ 2D ➜ 3D (mini)") as demo:
162
  gr.Markdown("# 🌀 텍스트 → 2D → 3D 생성기 (경량 버전)")
163
+ gr.Markdown("Kontext-dev + Hunyuan3D-2. 16 GB RAM에서도 동작합니다.")
164
 
165
  with gr.Row():
166
  with gr.Column():