K1Z3M1112 commited on
Commit
fa69362
·
verified ·
1 Parent(s): c8dc4d0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +326 -255
app.py CHANGED
@@ -40,126 +40,186 @@ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
40
  dtype = torch.float16 if torch.cuda.is_available() else torch.float32
41
 
42
  print("=" * 50)
43
- print("🎨 T2I-Adapter Photo Restoration")
44
  print("=" * 50)
45
  print("Using device:", device)
46
- print("Models: SD1.5 + SDXL with T2I-Adapters")
47
  print("=" * 50)
48
 
49
  from diffusers import (
50
- StableDiffusionAdapterPipeline,
51
- T2IAdapter,
52
  StableDiffusionXLAdapterPipeline,
 
53
  AutoencoderKL
54
  )
55
- from controlnet_aux import CannyDetector, LineartDetector
 
 
 
 
 
 
56
  import gc
57
 
58
- # Initialize models
59
- pipe_sd15 = None
60
- pipe_sdxl = None
61
- adapter_sd15_canny = None
62
- adapter_sdxl_canny = None
63
- canny_detector = CannyDetector()
 
 
 
 
64
 
65
- try:
66
- print("🔄 Loading T2I-Adapter for SD1.5 (Canny)...")
67
- adapter_sd15_canny = T2IAdapter.from_pretrained(
68
- "TencentARC/t2iadapter_canny_sd15v2",
69
- torch_dtype=dtype
70
- ).to(device)
71
-
72
- pipe_sd15 = StableDiffusionAdapterPipeline.from_pretrained(
73
- "runwayml/stable-diffusion-v1-5",
74
- adapter=adapter_sd15_canny,
75
- torch_dtype=dtype,
76
- safety_checker=None
77
- ).to(device)
78
-
79
- pipe_sd15.enable_attention_slicing()
80
- print("✅ SD1.5 T2I-Adapter loaded!")
81
-
82
- except Exception as e:
83
- print(f"❌ SD1.5 Error: {e}")
84
 
85
- try:
86
- print("🔄 Loading T2I-Adapter for SDXL (Canny)...")
87
- adapter_sdxl_canny = T2IAdapter.from_pretrained(
88
- "TencentARC/t2i-adapter-canny-sdxl-1.0",
89
- torch_dtype=dtype,
90
- varient="fp16"
91
- ).to(device)
92
-
93
- pipe_sdxl = StableDiffusionXLAdapterPipeline.from_pretrained(
94
- "stabilityai/stable-diffusion-xl-base-1.0",
95
- adapter=adapter_sdxl_canny,
96
- torch_dtype=dtype,
97
- variant="fp16"
98
- ).to(device)
99
-
100
- if device.type == "cuda":
101
- pipe_sdxl.enable_model_cpu_offload()
102
- pipe_sdxl.enable_attention_slicing()
103
-
104
- print("✅ SDXL T2I-Adapter loaded!")
105
-
106
- except Exception as e:
107
- print(f"❌ SDXL Error: {e}")
108
 
109
  MAX_SEED = np.iinfo(np.int32).max
110
 
111
- # Restoration presets
112
- RESTORATION_PRESETS = {
113
- "ลงสีภาพขาวดำ (Colorize)": "a colorized version of this photograph with realistic natural colors, professional photo restoration, high quality, detailed",
114
- "ซ่อมแซมเต็มรูปแบบ (Full Restore)": "a fully restored vintage photograph, damage removed, enhanced colors, sharp details, professional restoration, like new",
115
- "เพิ่มความคมชัด (Enhance)": "a professionally enhanced photograph with improved sharpness, vivid colors, better contrast, high quality",
116
- "สไตล์วินเทจ (Vintage)": "a beautifully restored vintage photograph with authentic period colors, nostalgic atmosphere",
117
- "สไตล์อนิเมะ (Anime)": "beautiful anime art style, vibrant colors, detailed anime illustration, high quality artwork",
118
- "ภาพวาดสีน้ำ (Watercolor)": "watercolor painting style, soft artistic brush strokes, painterly effect",
119
- "กำหนดเอง (Custom)": ""
120
  }
121
 
122
- def get_canny_edge(image, low_threshold=50, high_threshold=150):
123
- """Extract canny edges from image"""
124
  image = np.array(image)
125
- edges = cv2.Canny(image, low_threshold, high_threshold)
126
- edges = Image.fromarray(edges)
127
- return edges
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
 
129
- def restore_photo(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  input_image,
131
- instruction,
132
- model_choice,
 
133
  seed,
134
  randomize_seed,
135
  guidance_scale,
136
  adapter_strength,
137
  steps,
138
- canny_low,
139
- canny_high,
140
  progress=gr.Progress(track_tqdm=True)
141
  ):
142
- """Restore photo using T2I-Adapter"""
143
  if input_image is None:
144
  raise gr.Error("กรุณาอัพโหลดภาพ")
145
 
146
- # Select pipeline
147
- if model_choice == "SD 1.5" and pipe_sd15 is None:
148
- raise gr.Error("โมเดล SD1.5 ยังไม่พร้อมใช้งาน")
149
- elif model_choice == "SDXL" and pipe_sdxl is None:
150
- raise gr.Error("โมเดล SDXL ยังไม่พร้อมใช้งาน")
151
 
152
- pipe = pipe_sd15 if model_choice == "SD 1.5" else pipe_sdxl
153
-
154
  if randomize_seed:
155
  seed = random.randint(0, MAX_SEED)
156
-
157
  generator = torch.Generator(device=device).manual_seed(seed)
158
  original_image = input_image.convert("RGB")
159
 
160
  # Resize
161
  width, height = original_image.size
162
- max_size = 768 if model_choice == "SDXL" else 512
163
 
164
  if width > max_size or height > max_size:
165
  if width > height:
@@ -173,45 +233,44 @@ def restore_photo(
173
  new_height = (new_height // 8) * 8
174
  original_image = original_image.resize((new_width, new_height), Image.LANCZOS)
175
 
176
- # Extract canny edges
177
- canny_image = get_canny_edge(original_image, canny_low, canny_high)
178
 
179
  try:
180
  result = pipe(
181
- prompt=instruction,
182
- image=canny_image,
183
  num_inference_steps=steps,
184
  guidance_scale=guidance_scale,
185
  adapter_conditioning_scale=adapter_strength,
186
  generator=generator,
187
  ).images[0]
188
 
189
- # Clean up
190
  if device.type == "cuda":
191
  torch.cuda.empty_cache()
192
  gc.collect()
193
 
194
- return result, canny_image, seed
195
-
196
  except Exception as e:
197
  raise gr.Error(f"เกิดข้อผิดพลาด: {str(e)}")
198
 
199
  def load_preset(preset_name):
200
- """Load restoration preset"""
201
- return RESTORATION_PRESETS.get(preset_name, "")
202
 
203
  css="""
204
  #col-container {
205
  margin: 0 auto;
206
- max-width: 1600px;
207
  }
208
  #main-title h1 {
209
- font-size: 2.5em !important;
210
  text-align: center;
211
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
212
  -webkit-background-clip: text;
213
  -webkit-text-fill-color: transparent;
214
  background-clip: text;
 
215
  }
216
  .feature-box {
217
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
@@ -219,101 +278,116 @@ css="""
219
  border-radius: 12px;
220
  padding: 25px;
221
  margin: 20px 0;
222
- box-shadow: 0 4px 6px rgba(0,0,0,0.1);
 
 
 
 
 
223
  }
224
  """
225
 
226
  with gr.Blocks(css=css, theme=steel_blue_theme) as demo:
227
  with gr.Column(elem_id="col-container"):
228
- gr.Markdown("# 📸 T2I-Adapter Photo Restoration", elem_id="main-title")
229
- gr.Markdown("### Control Image Generation with Structure Guidance")
230
 
231
  gr.HTML("""
232
  <div class="feature-box">
233
- <h3>🎯 T2I-Adapter Features</h3>
234
- <table style="width:100%; color:white; font-size:1.05em;">
235
- <tr>
236
- <td style="padding:8px; width:30%;"><strong>🖼️ Structure Control</strong></td>
237
- <td>ควบคุมโครงสร้างภาพด้วย Canny Edge Detection</td>
238
- </tr>
239
- <tr>
240
- <td style="padding:8px;"><strong>🎨 Style Freedom</strong></td>
241
- <td>เปลี่ยนสไตล์ได้อย่างอิสระโดยรักษาโครงสร้าง</td>
242
- </tr>
243
- <tr>
244
- <td style="padding:8px;"><strong>⚡ Two Models</strong></td>
245
- <td>SD 1.5 (เร็ว) และ SDXL (คุณภาพสูง)</td>
246
- </tr>
247
- <tr>
248
- <td style="padding:8px;"><strong>💎 High Quality</strong></td>
249
- <td>ผลลัพธ์คุณภาพสูงพร้อมความยืดหยุ่น</td>
250
- </tr>
251
- </table>
 
 
 
 
 
 
 
252
  </div>
253
  """)
254
 
255
  with gr.Row():
256
  with gr.Column(scale=1):
257
  input_image = gr.Image(
258
- label="📤 อัพโหลดภาพต้นฉบับ",
259
- type="pil",
260
  height=400
261
  )
262
 
263
- model_choice = gr.Radio(
264
- choices=["SD 1.5", "SDXL"],
265
- label="🤖 เลือกโมเดล",
266
- value="SD 1.5",
267
- info="SD 1.5 = เร็ว | SDXL = คุณภาพสูง"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
  )
269
 
270
  preset = gr.Dropdown(
271
- choices=list(RESTORATION_PRESETS.keys()),
272
- label="🎯 เลือกพรีเซ็ต",
273
- value="ลงสีภาพขาวดำ (Colorize)"
274
  )
275
 
276
- instruction = gr.Textbox(
277
- label="💬 Prompt (คำอธิบายภาพที่ต้องการ)",
278
  lines=3,
279
- value=RESTORATION_PRESETS["ลงสีภาพขาวดำ (Colorize)"]
280
  )
281
 
282
  preset.change(
283
  fn=load_preset,
284
  inputs=[preset],
285
- outputs=[instruction]
286
  )
287
 
288
- with gr.Accordion("⚙️ Canny Edge Settings", open=False):
289
- canny_low = gr.Slider(
290
- label="Low Threshold",
291
- minimum=0,
292
- maximum=255,
293
- value=50,
294
- step=1
295
- )
296
- canny_high = gr.Slider(
297
- label="High Threshold",
298
- minimum=0,
299
- maximum=255,
300
- value=150,
301
- step=1
302
- )
303
-
304
  run_button = gr.Button("✨ Generate", variant="primary", size="lg")
305
-
306
  with gr.Column(scale=2):
307
  with gr.Row():
308
- canny_output = gr.Image(
309
- label="🔍 Canny Edge Detection",
310
  type="pil",
311
- height=350
312
  )
313
  output_image = gr.Image(
314
- label="✨ Generated Result",
315
  type="pil",
316
- height=350
317
  )
318
 
319
  with gr.Accordion("⚙️ Advanced Settings", open=True):
@@ -326,7 +400,7 @@ with gr.Blocks(css=css, theme=steel_blue_theme) as demo:
326
  value=42
327
  )
328
  randomize_seed = gr.Checkbox(
329
- label="🔀 Random Seed",
330
  value=True
331
  )
332
 
@@ -336,17 +410,14 @@ with gr.Blocks(css=css, theme=steel_blue_theme) as demo:
336
  minimum=1.0,
337
  maximum=20.0,
338
  step=0.5,
339
- value=7.5,
340
- info="ยิ่งสูง = ทำตาม prompt มากขึ้น"
341
  )
342
-
343
  adapter_strength = gr.Slider(
344
  label="🎨 Adapter Strength",
345
  minimum=0.0,
346
  maximum=1.0,
347
  step=0.05,
348
- value=0.75,
349
- info="ยิ่งสูง = รักษาโครงสร้างมากขึ้น"
350
  )
351
 
352
  steps = gr.Slider(
@@ -358,117 +429,117 @@ with gr.Blocks(css=css, theme=steel_blue_theme) as demo:
358
  )
359
 
360
  run_button.click(
361
- fn=restore_photo,
362
  inputs=[
363
- input_image, instruction, model_choice, seed, randomize_seed,
364
- guidance_scale, adapter_strength, steps, canny_low, canny_high
365
  ],
366
- outputs=[output_image, canny_output, seed]
367
  )
368
-
369
  gr.Markdown("""
370
  ---
371
- ### 📚 คู่มือการใช้งาน T2I-Adapter
372
-
373
- #### 🎯 **T2I-Adapter คืออะไร?**
374
- T2I-Adapter เป็นเทคนิคที่ช่วยควบคุมการสร้างภาพด้วย **condition เพิ่มเติม** เช่น:
375
- - **Canny Edge** - โครงร่างของวัตถุ
376
- - **Depth Map** - ความลึกของภาพ
377
- - **Sketch** - ภาพร่าง
378
-
379
- แอปนี้ใช้ **Canny Edge Detection** เพื่อรักษาโครงสร้างของภาพเดิม แต่เปลี่ยนสไตล์ สี และรายละเอียดตาม prompt
380
-
381
- #### 🤖 **เลือกโมเดล:**
382
-
383
- **📘 SD 1.5 (Stable Diffusion 1.5):**
384
- - เร็วกว่า (20-40 วินาที)
385
- - ใช้ VRAM น้อย (~4-6GB)
386
- - ✅ เหมาะสำหรับทดลอง
387
- - ⚠️ คุณภาพต่ำกว่า SDXL
388
-
389
- **📗 SDXL (Stable Diffusion XL):**
390
- - คุณภาพสูงกว่ามาก
391
- - ✅ รายละเอียดดีกว่า
392
- - ✅ สีสันสมจริงกว่า
393
- - ⚠️ ช้ากว่า (60-120 วินาที)
394
- - ⚠️ ใช้ VRAM มาก (~8-12GB)
395
-
396
- #### 🎯 **วิธีใช้งาน:**
397
-
398
- 1. **อัพโหลดภาพ** - ภาพถ่ายเก่า หรือภาพที่ต้องการแปลง
399
- 2. **เลือกโมเดล** - SD 1.5 (เร็ว) หรือ SDXL (คุณภาพสูง)
400
- 3. **เลือกพรีเซ็ต** - เลือกสไตล์ที่ต้องการ
401
- 4. **แก้ไข Prompt** - ปรับแต่งคำอธิบายตามต้องการ
402
- 5. **กด Generate** - รอผลลัพธ์
403
- 6. **ดู Canny Edge** - ตรวจสอบว่าโครงสร้างถูกต้อง
404
- 7. **ปรับค่า Adapter Strength** - ถ้าต้องการเปลี่ยนมากขึ้น/น้อยลง
405
-
406
- #### 💡 **เคล็ดลับ:**
407
-
408
- **สำหรับภาพขาวดำ:**
409
- - Adapter Strength: 0.7-0.8
410
- - Guidance: 7-9
411
- - Prompt: "colorized vintage photograph with realistic colors"
412
-
413
- **สำหรับการเปลี่ยนสไตล์:**
414
- - Adapter Strength: 0.6-0.75
415
- - Guidance: 8-12
416
- - Prompt: "anime art style" / "watercolor painting"
417
-
418
- **สำหรับการซ่อมแซม:**
419
- - Adapter Strength: 0.8-0.9 (รักษาโครงสร้างเดิมมาก)
420
- - Guidance: 7-9
421
- - Prompt: "restored photograph, enhanced colors"
422
-
423
- **Canny Edge Settings:**
424
- - **Low/High Threshold:** ปรับเพื่อควบคุมรายละเอียดของ edge
425
- - **Low = 50, High = 150** เหมาะสำหรับภาพทั่วไป
426
- - **Low = 30, High = 100** สำหรับภาพที่มีรายละเอียดน้อย
427
- - **Low = 100, High = 200** สำหรับภาพที่มีรายละเอียดเยอะ
428
-
429
- #### ⚙️ **พารามิเตอร์:**
430
-
431
- **Guidance Scale (1-20):**
432
- - 5-7: สร้างสรรค์มาก, อิสระ
433
- - 7-10: สมดุล
434
- - 10-15: ทำตาม prompt เยอะมาก
435
-
436
- **Adapter Strength (0-1):**
437
- - 0.5-0.6: เปลี่ยนแปลงมาก, อิสระ
438
- - 0.7-0.8: สมดุล
439
- - 0.85-1.0: รักษาโครงสร้างเดิมเกือบทั้งหมด
440
-
441
- **Steps:**
442
- - SD 1.5: 20-30 steps
443
- - SDXL: 25-40 steps
444
-
445
- #### ⚡ **ประสิทธิภาพ:**
446
-
447
- **SD 1.5:**
448
- - เวลา: 20-40 วินาที
449
- - VRAM: 4-6GB
450
- - ความละเอียด: 512x512px
451
-
452
- **SDXL:**
453
- - เวลา: 60-120 วินาที
454
- - VRAM: 8-12GB
455
- - ความละเอียด: 768x768px หรือ 1024x1024px
456
-
457
- #### 📦 **Dependencies:**
458
  ```bash
459
- pip install diffusers transformers
460
  pip install controlnet-aux
461
  pip install opencv-python
462
- pip install torch torchvision
463
- pip install gradio
464
  ```
465
 
 
 
 
 
 
 
 
 
 
 
 
 
466
  ---
467
 
468
  <div style="text-align:center; color:#666; padding:20px;">
469
- <strong>🌟 T2I-Adapter</strong><br>
470
- Structure-Guided Image Generation<br>
471
- <em>TencentARC Stable Diffusion</em>
472
  </div>
473
  """)
474
 
 
40
  dtype = torch.float16 if torch.cuda.is_available() else torch.float32
41
 
42
  print("=" * 50)
43
+ print("🎨 Complete T2I-Adapter Suite")
44
  print("=" * 50)
45
  print("Using device:", device)
46
+ print("All T2I-Adapters: SD1.5 + SDXL")
47
  print("=" * 50)
48
 
49
  from diffusers import (
50
+ StableDiffusionAdapterPipeline,
 
51
  StableDiffusionXLAdapterPipeline,
52
+ T2IAdapter,
53
  AutoencoderKL
54
  )
55
+ from controlnet_aux import (
56
+ CannyDetector,
57
+ LineartDetector,
58
+ OpenposeDetector,
59
+ MidasDetector,
60
+ PidiNetDetector
61
+ )
62
  import gc
63
 
64
+ # T2I-Adapter models for SD1.5
65
+ SD15_ADAPTERS = {
66
+ "Canny": "TencentARC/t2iadapter_canny_sd15v2",
67
+ "Sketch": "TencentARC/t2iadapter_sketch_sd15v2",
68
+ "Depth": "TencentARC/t2iadapter_depth_sd15v2",
69
+ "OpenPose": "TencentARC/t2iadapter_openpose_sd14v1",
70
+ "Seg": "TencentARC/t2iadapter_seg_sd14v1",
71
+ "Color": "TencentARC/t2iadapter_color_sd14v1",
72
+ "Style": "TencentARC/t2iadapter_style_sd14v1",
73
+ }
74
 
75
+ # T2I-Adapter models for SDXL
76
+ SDXL_ADAPTERS = {
77
+ "Canny": "TencentARC/t2i-adapter-canny-sdxl-1.0",
78
+ "Sketch": "TencentARC/t2i-adapter-sketch-sdxl-1.0",
79
+ "Lineart": "TencentARC/t2i-adapter-lineart-sdxl-1.0",
80
+ "Depth-Midas": "TencentARC/t2i-adapter-depth-midas-sdxl-1.0",
81
+ "Depth-Zoe": "TencentARC/t2i-adapter-depth-zoe-sdxl-1.0",
82
+ "OpenPose": "TencentARC/t2i-adapter-openpose-sdxl-1.0",
83
+ }
84
+
85
+ # Loaded pipelines cache
86
+ loaded_pipelines = {}
 
 
 
 
 
 
 
87
 
88
+ # Detectors
89
+ canny_detector = CannyDetector()
90
+ lineart_detector = LineartDetector.from_pretrained("lllyasviel/Annotators")
91
+ openpose_detector = OpenposeDetector.from_pretrained("lllyasviel/Annotators")
92
+ midas_detector = MidasDetector.from_pretrained("lllyasviel/Annotators")
93
+ pidinet_detector = PidiNetDetector.from_pretrained("lllyasviel/Annotators")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
 
95
  MAX_SEED = np.iinfo(np.int32).max
96
 
97
+ PRESETS = {
98
+ "ลงสีภาพขาวดำ": "colorized vintage photograph with realistic natural colors, professional restoration",
99
+ "ซ่อมแซมภาพเก่า": "fully restored old photograph, enhanced colors, sharp details, professional quality",
100
+ "สไตล์อนิเมะ": "beautiful anime art style, vibrant colors, detailed illustration, high quality",
101
+ "ภาพวาดสีน้ำ": "watercolor painting, soft artistic brushstrokes, painterly effect",
102
+ "ภาพวาดน้ำมัน": "oil painting, rich textures, artistic brushstrokes, classic art style",
103
+ "ถ่ายภาพมืออาชีพ": "professional photograph, high quality, detailed, 4k, sharp focus",
104
+ "สไตล์วินเทจ": "vintage style photograph, nostalgic atmosphere, retro colors",
 
105
  }
106
 
107
+ def get_control_image(image, adapter_type):
108
+ """Generate control image based on adapter type"""
109
  image = np.array(image)
110
+
111
+ if "Canny" in adapter_type:
112
+ return canny_detector(image)
113
+ elif "Sketch" in adapter_type:
114
+ return pidinet_detector(image, detect_resolution=512, image_resolution=512)
115
+ elif "Lineart" in adapter_type:
116
+ return lineart_detector(image, detect_resolution=512, image_resolution=512)
117
+ elif "Depth" in adapter_type:
118
+ return midas_detector(image)
119
+ elif "OpenPose" in adapter_type:
120
+ return openpose_detector(image)
121
+ elif "Color" in adapter_type:
122
+ # For color adapter, just return the image
123
+ return Image.fromarray(image)
124
+ elif "Seg" in adapter_type or "Style" in adapter_type:
125
+ # These need special preprocessing
126
+ return Image.fromarray(image)
127
+ else:
128
+ return Image.fromarray(image)
129
 
130
+ def load_pipeline(model_version, adapter_type):
131
+ """Load or get cached pipeline"""
132
+ cache_key = f"{model_version}_{adapter_type}"
133
+
134
+ if cache_key in loaded_pipelines:
135
+ return loaded_pipelines[cache_key]
136
+
137
+ try:
138
+ # Select adapter model
139
+ if model_version == "SD 1.5":
140
+ adapter_name = SD15_ADAPTERS.get(adapter_type)
141
+ base_model = "runwayml/stable-diffusion-v1-5"
142
+ else:
143
+ adapter_name = SDXL_ADAPTERS.get(adapter_type)
144
+ base_model = "stabilityai/stable-diffusion-xl-base-1.0"
145
+
146
+ if not adapter_name:
147
+ return None
148
+
149
+ print(f"Loading {adapter_name}...")
150
+
151
+ # Load adapter
152
+ adapter = T2IAdapter.from_pretrained(
153
+ adapter_name,
154
+ torch_dtype=dtype,
155
+ variant="fp16" if dtype == torch.float16 else None
156
+ ).to(device)
157
+
158
+ # Load pipeline
159
+ if model_version == "SD 1.5":
160
+ pipe = StableDiffusionAdapterPipeline.from_pretrained(
161
+ base_model,
162
+ adapter=adapter,
163
+ torch_dtype=dtype,
164
+ safety_checker=None
165
+ ).to(device)
166
+ else:
167
+ vae = AutoencoderKL.from_pretrained(
168
+ "madebyollin/sdxl-vae-fp16-fix",
169
+ torch_dtype=dtype
170
+ ).to(device)
171
+
172
+ pipe = StableDiffusionXLAdapterPipeline.from_pretrained(
173
+ base_model,
174
+ adapter=adapter,
175
+ vae=vae,
176
+ torch_dtype=dtype,
177
+ variant="fp16" if dtype == torch.float16 else None
178
+ ).to(device)
179
+
180
+ if device.type == "cuda":
181
+ pipe.enable_model_cpu_offload()
182
+ pipe.enable_attention_slicing()
183
+
184
+ loaded_pipelines[cache_key] = pipe
185
+ print(f"✅ Loaded {cache_key}")
186
+
187
+ return pipe
188
+
189
+ except Exception as e:
190
+ print(f"❌ Error loading {cache_key}: {e}")
191
+ return None
192
+
193
+ def generate_image(
194
  input_image,
195
+ prompt,
196
+ model_version,
197
+ adapter_type,
198
  seed,
199
  randomize_seed,
200
  guidance_scale,
201
  adapter_strength,
202
  steps,
 
 
203
  progress=gr.Progress(track_tqdm=True)
204
  ):
205
+ """Generate image with T2I-Adapter"""
206
  if input_image is None:
207
  raise gr.Error("กรุณาอัพโหลดภาพ")
208
 
209
+ # Load pipeline
210
+ pipe = load_pipeline(model_version, adapter_type)
211
+ if pipe is None:
212
+ raise gr.Error(f"ไม่สามารถโหลด {model_version} + {adapter_type}")
 
213
 
 
 
214
  if randomize_seed:
215
  seed = random.randint(0, MAX_SEED)
216
+
217
  generator = torch.Generator(device=device).manual_seed(seed)
218
  original_image = input_image.convert("RGB")
219
 
220
  # Resize
221
  width, height = original_image.size
222
+ max_size = 768 if model_version == "SDXL" else 512
223
 
224
  if width > max_size or height > max_size:
225
  if width > height:
 
233
  new_height = (new_height // 8) * 8
234
  original_image = original_image.resize((new_width, new_height), Image.LANCZOS)
235
 
236
+ # Generate control image
237
+ control_image = get_control_image(original_image, adapter_type)
238
 
239
  try:
240
  result = pipe(
241
+ prompt=prompt,
242
+ image=control_image,
243
  num_inference_steps=steps,
244
  guidance_scale=guidance_scale,
245
  adapter_conditioning_scale=adapter_strength,
246
  generator=generator,
247
  ).images[0]
248
 
 
249
  if device.type == "cuda":
250
  torch.cuda.empty_cache()
251
  gc.collect()
252
 
253
+ return result, control_image, seed
254
+
255
  except Exception as e:
256
  raise gr.Error(f"เกิดข้อผิดพลาด: {str(e)}")
257
 
258
  def load_preset(preset_name):
259
+ return PRESETS.get(preset_name, "")
 
260
 
261
  css="""
262
  #col-container {
263
  margin: 0 auto;
264
+ max-width: 1800px;
265
  }
266
  #main-title h1 {
267
+ font-size: 2.8em !important;
268
  text-align: center;
269
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
270
  -webkit-background-clip: text;
271
  -webkit-text-fill-color: transparent;
272
  background-clip: text;
273
+ margin-bottom: 10px;
274
  }
275
  .feature-box {
276
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
 
278
  border-radius: 12px;
279
  padding: 25px;
280
  margin: 20px 0;
281
+ }
282
+ .adapter-grid {
283
+ display: grid;
284
+ grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
285
+ gap: 10px;
286
+ margin: 15px 0;
287
  }
288
  """
289
 
290
  with gr.Blocks(css=css, theme=steel_blue_theme) as demo:
291
  with gr.Column(elem_id="col-container"):
292
+ gr.Markdown("# 🎨 Complete T2I-Adapter Suite", elem_id="main-title")
293
+ gr.Markdown("### 📸 All TencentARC T2I-Adapters for SD1.5 & SDXL")
294
 
295
  gr.HTML("""
296
  <div class="feature-box">
297
+ <h3>🎯 Available T2I-Adapters</h3>
298
+ <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px;">
299
+ <div>
300
+ <h4>📘 SD 1.5 Adapters:</h4>
301
+ <ul style="color:white;">
302
+ <li><strong>Canny</strong> - Edge detection</li>
303
+ <li><strong>Sketch</strong> - Sketch/scribble</li>
304
+ <li><strong>Depth</strong> - Depth map</li>
305
+ <li><strong>OpenPose</strong> - Human pose</li>
306
+ <li><strong>Seg</strong> - Segmentation map</li>
307
+ <li><strong>Color</strong> - Color palette</li>
308
+ <li><strong>Style</strong> - Style transfer</li>
309
+ </ul>
310
+ </div>
311
+ <div>
312
+ <h4>📗 SDXL Adapters:</h4>
313
+ <ul style="color:white;">
314
+ <li><strong>Canny</strong> - Edge detection (XL)</li>
315
+ <li><strong>Sketch</strong> - Sketch/scribble (XL)</li>
316
+ <li><strong>Lineart</strong> - Line art</li>
317
+ <li><strong>Depth-Midas</strong> - Depth (Midas)</li>
318
+ <li><strong>Depth-Zoe</strong> - Depth (ZoeDepth)</li>
319
+ <li><strong>OpenPose</strong> - Human pose (XL)</li>
320
+ </ul>
321
+ </div>
322
+ </div>
323
  </div>
324
  """)
325
 
326
  with gr.Row():
327
  with gr.Column(scale=1):
328
  input_image = gr.Image(
329
+ label="📤 Input Image",
330
+ type="pil",
331
  height=400
332
  )
333
 
334
+ with gr.Row():
335
+ model_version = gr.Radio(
336
+ choices=["SD 1.5", "SDXL"],
337
+ label="🤖 Model Version",
338
+ value="SD 1.5",
339
+ info="SD 1.5 = Fast | SDXL = High Quality"
340
+ )
341
+
342
+ adapter_type = gr.Dropdown(
343
+ choices=list(SD15_ADAPTERS.keys()),
344
+ label="🎨 Adapter Type",
345
+ value="Canny"
346
+ )
347
+
348
+ def update_adapter_choices(version):
349
+ if version == "SD 1.5":
350
+ return gr.Dropdown(choices=list(SD15_ADAPTERS.keys()), value="Canny")
351
+ else:
352
+ return gr.Dropdown(choices=list(SDXL_ADAPTERS.keys()), value="Canny")
353
+
354
+ model_version.change(
355
+ fn=update_adapter_choices,
356
+ inputs=[model_version],
357
+ outputs=[adapter_type]
358
  )
359
 
360
  preset = gr.Dropdown(
361
+ choices=list(PRESETS.keys()),
362
+ label="🎯 Preset",
363
+ value="ลงสีภาพขาวดำ"
364
  )
365
 
366
+ prompt = gr.Textbox(
367
+ label="💬 Prompt",
368
  lines=3,
369
+ value=PRESETS["ลงสีภาพขาวดำ"]
370
  )
371
 
372
  preset.change(
373
  fn=load_preset,
374
  inputs=[preset],
375
+ outputs=[prompt]
376
  )
377
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
378
  run_button = gr.Button("✨ Generate", variant="primary", size="lg")
379
+
380
  with gr.Column(scale=2):
381
  with gr.Row():
382
+ control_output = gr.Image(
383
+ label="🔍 Control Image",
384
  type="pil",
385
+ height=380
386
  )
387
  output_image = gr.Image(
388
+ label="✨ Generated Result",
389
  type="pil",
390
+ height=380
391
  )
392
 
393
  with gr.Accordion("⚙️ Advanced Settings", open=True):
 
400
  value=42
401
  )
402
  randomize_seed = gr.Checkbox(
403
+ label="🔀 Random",
404
  value=True
405
  )
406
 
 
410
  minimum=1.0,
411
  maximum=20.0,
412
  step=0.5,
413
+ value=7.5
 
414
  )
 
415
  adapter_strength = gr.Slider(
416
  label="🎨 Adapter Strength",
417
  minimum=0.0,
418
  maximum=1.0,
419
  step=0.05,
420
+ value=0.8
 
421
  )
422
 
423
  steps = gr.Slider(
 
429
  )
430
 
431
  run_button.click(
432
+ fn=generate_image,
433
  inputs=[
434
+ input_image, prompt, model_version, adapter_type,
435
+ seed, randomize_seed, guidance_scale, adapter_strength, steps
436
  ],
437
+ outputs=[output_image, control_output, seed]
438
  )
439
+
440
  gr.Markdown("""
441
  ---
442
+ ### 📚 Complete T2I-Adapter Guide
443
+
444
+ #### 🎨 **Adapter Types Explained:**
445
+
446
+ **🖼️ Canny** - แยก edge/โครงร่างจากภาพ
447
+ - เหมาะสำหรับ: รักษาโครงสร้างแต่เปลี่ยนสไตล์
448
+ - ใช้เมื่อ: ต้องการควบคุมรูปร่างของวัตถุ
449
+
450
+ **✏️ Sketch** - ใช้ภาพร่าง/scribble
451
+ - เหมาะสำหรับ: วาดร่างแล้วให้ AI เติมรายละเอียด
452
+ - ใช้เมื่อ: มีภาพร่างคร่าวๆ
453
+
454
+ **📏 Lineart** - ใช้เส้นสายศิลปะ
455
+ - เหมาะสำหรับ: manga, comic, line art
456
+ - ใช้เมื่อ: ต้องการเส้นสายที่สะอาด
457
+
458
+ **🏔️ Depth** - ใช้ depth map (ความลึก)
459
+ - เหมาะสำหรับ: ควบคุม perspective และ depth
460
+ - ใช้เมื่อ: ต้องการรักษาความลึกของภาพ
461
+ - **Midas** vs **Zoe**: Zoe แม่นยำกว่า
462
+
463
+ **🚶 OpenPose** - ใช้ท่าทางมนุษย์
464
+ - เหมาะสำหรับ: เปลี่ยนคนในภาพแต่รักษาท่าทาง
465
+ - ใช้เมื่อ: มีภาพคน ต้องการเปลี่ยนรูปลักษณ์
466
+
467
+ **🎨 Color** - ใช้ color palette
468
+ - เหมาะสำหรับ: ควบคุมสีในแต่ละพื้นที่
469
+ - ใช้เมื่อ: ต้องการกำหนดสีเฉพาะจุด
470
+
471
+ **🖌️ Seg** - ใช้ segmentation map
472
+ - เหมาะสำหรับ: แบ่งพื้นที่ของวัตถุ
473
+ - ใช้เมื่อ: ต้องการควบคุมแต่ละ object
474
+
475
+ **🎭 Style** - Style transfer
476
+ - เหมาะสำหรับ: เปลี่ยนสไตล์ภาพ
477
+ - ใช้เมื่อ: ต้องการเลียนแบบสไตล์
478
+
479
+ #### 💡 **Use Cases:**
480
+
481
+ **ลงสีภาพขาวดำ:**
482
+ - Adapter: **Canny** หรือ **Sketch**
483
+ - Strength: 0.75-0.85
484
+ - Prompt: "colorized photograph with realistic colors"
485
+
486
+ **เปลี่ยนท่าคน:**
487
+ - Adapter: **OpenPose**
488
+ - Strength: 0.8-0.9
489
+ - Prompt: อธิบายคนที่ต้องการ
490
+
491
+ **manga ➜ realistic:**
492
+ - Adapter: **Lineart**
493
+ - Strength: 0.7-0.8
494
+ - Prompt: "realistic photograph"
495
+
496
+ **เปลี่ยนสไตล์:**
497
+ - Adapter: **Canny** (รักษารูปร่าง)
498
+ - Strength: 0.6-0.75
499
+ - Prompt: "anime style" / "oil painting"
500
+
501
+ #### ⚙️ **Settings Guide:**
502
+
503
+ **Guidance Scale:**
504
+ - 5-7: Creative, loose
505
+ - 7-10: Balanced
506
+ - 10-15: Strict prompt following
507
+
508
+ **Adapter Strength:**
509
+ - 0.5-0.6: Very loose control
510
+ - 0.7-0.8: Balanced ✅
511
+ - 0.85-1.0: Strict structure preservation
512
+
513
+ **Model Choice:**
514
+ - **SD 1.5**: 20-40s, 512px, 4-6GB VRAM
515
+ - **SDXL**: 60-120s, 768-1024px, 8-12GB VRAM
516
+
517
+ #### 📦 **Installation:**
 
 
 
 
 
 
 
 
 
 
 
518
  ```bash
519
+ pip install diffusers transformers accelerate
520
  pip install controlnet-aux
521
  pip install opencv-python
522
+ pip install torch gradio
 
523
  ```
524
 
525
+ #### 🔬 **Technical Details:**
526
+
527
+ **T2I-Adapter vs ControlNet:**
528
+ - T2I-Adapter: ⚡ Lighter (~77M params)
529
+ - ControlNet: 💪 More control (~600M params)
530
+ - T2I-Adapter: Better for style changes
531
+ - ControlNet: Better for precise control
532
+
533
+ **Multi-Adapter Support:**
534
+ - รวม adapter หลายตัวได้ (เช่น Canny + Depth)
535
+ - ใช้ `MultiAdapter` class
536
+
537
  ---
538
 
539
  <div style="text-align:center; color:#666; padding:20px;">
540
+ <strong>🌟 T2I-Adapter Complete Suite</strong><br>
541
+ 13 Adapters: 7 for SD1.5 + 6 for SDXL<br>
542
+ <em>TencentARC × Peking University VILLA</em>
543
  </div>
544
  """)
545