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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +298 -419
app.py CHANGED
@@ -37,198 +37,119 @@ class SteelBlueTheme(Soft):
37
  steel_blue_theme = SteelBlueTheme()
38
 
39
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
40
- dtype = torch.bfloat16 if torch.cuda.is_available() else torch.float32
41
 
42
  print("=" * 50)
43
- print("🎨 Anime/Manga Photo Restoration with ControlNet")
44
  print("=" * 50)
45
  print("Using device:", device)
 
46
  print("=" * 50)
47
 
48
- from diffusers import ControlNetModel, StableDiffusionXLControlNetPipeline, StableDiffusionXLPipeline, AutoencoderKL
 
 
 
 
 
 
49
  import gc
50
 
51
- # ControlNet Models - Using manga recolor
52
- CONTROLNET_MODELS = {
53
- "ไม่ใช้ (None)": None,
54
- "Manga Recolor (Recommended)": "SubMaroon/ControlNet-manga-recolor",
55
- }
56
-
57
  # Initialize models
58
- base_pipe = None
59
- controlnet_cache = {}
60
- current_controlnet = None
61
- current_pipe = None
 
62
 
63
- # Load base SDXL model
64
  try:
65
- print("🔄 กำลังโหลด Base Model (SDXL Anime)...")
66
-
67
- # Load VAE for better quality
68
- vae = AutoencoderKL.from_pretrained(
69
- "madebyollin/sdxl-vae-fp16-fix",
70
  torch_dtype=dtype
71
- )
72
 
73
- base_pipe = StableDiffusionXLPipeline.from_pretrained(
74
- "John6666/nsfw-anime-xl-v1-sdxl",
75
- vae=vae,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  torch_dtype=dtype,
77
- use_safetensors=True,
78
  variant="fp16"
79
- )
80
- base_pipe.to(device)
 
 
 
81
 
82
- # Optimizations
83
- base_pipe.enable_attention_slicing()
84
- if hasattr(base_pipe, 'enable_vae_slicing'):
85
- base_pipe.enable_vae_slicing()
86
- if hasattr(base_pipe, 'enable_model_cpu_offload') and device.type == "cuda":
87
- base_pipe.enable_model_cpu_offload()
88
 
89
- print("✅ โหลด Base Model สำเร็จ!")
90
  except Exception as e:
91
- print(f"❌ Base Model Error: {e}")
92
- print("💡 Trying fallback model...")
93
- try:
94
- base_pipe = StableDiffusionXLPipeline.from_pretrained(
95
- "stabilityai/stable-diffusion-xl-base-1.0",
96
- torch_dtype=dtype,
97
- use_safetensors=True,
98
- variant="fp16"
99
- )
100
- base_pipe.to(device)
101
- print("✅ Loaded fallback model")
102
- except Exception as e2:
103
- print(f"❌ Fallback also failed: {e2}")
104
 
105
  MAX_SEED = np.iinfo(np.int32).max
106
 
 
107
  RESTORATION_PRESETS = {
108
- "ลงสีอนิเมะ/มังงะ (Anime Colorize)": "beautiful colorful anime art, vibrant colors, high quality illustration, detailed shading",
109
- "เปลี่ยนเป็นอนิเมะสไตล์ (Anime Style)": "transform into beautiful anime art style, detailed anime illustration, vibrant colors",
110
- "มังงะคุณภาพสูง (High Quality Manga)": "high quality manga illustration, detailed lineart, professional manga style",
111
- "อนิเมะสไตล์สดใส (Vibrant Anime)": "vibrant colorful anime style, bright colors, detailed anime art, studio quality",
112
- "อนิเมะแฟนตาซี (Fantasy Anime)": "fantasy anime art style, magical atmosphere, detailed illustration, dramatic lighting",
113
- "อนิเมะโรแมนติก (Romantic Anime)": "romantic anime style, soft colors, beautiful illustration, gentle lighting",
114
- "อนิเมะแอคชั่น (Action Anime)": "dynamic action anime style, intense colors, dramatic composition, high energy",
115
  "กำหนดเอง (Custom)": ""
116
  }
117
 
118
- def process_controlnet_image(image, controlnet_type):
119
- """Process image for manga recolor ControlNet"""
120
- if controlnet_type == "ไม่ใช้ (None)" or controlnet_type is None:
121
- return None
122
-
123
- img_array = np.array(image)
124
-
125
- if controlnet_type == "Manga Recolor (Recommended)":
126
- # Convert to grayscale for manga-style lineart
127
- gray = cv2.cvtColor(img_array, cv2.COLOR_RGB2GRAY)
128
-
129
- # Apply adaptive thresholding for manga-like effect
130
- manga = cv2.adaptiveThreshold(
131
- gray, 255,
132
- cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
133
- cv2.THRESH_BINARY,
134
- 11, 2
135
- )
136
-
137
- # Invert to get black lines on white
138
- manga = cv2.bitwise_not(manga)
139
-
140
- # Convert back to RGB
141
- manga_rgb = cv2.cvtColor(manga, cv2.COLOR_GRAY2RGB)
142
-
143
- return Image.fromarray(manga_rgb)
144
-
145
- return image
146
-
147
- def load_controlnet_model(controlnet_type):
148
- """Load ControlNet model for SDXL"""
149
- global current_controlnet, current_pipe, controlnet_cache
150
-
151
- if controlnet_type == "ไม่ใช้ (None)":
152
- current_pipe = base_pipe
153
- current_controlnet = None
154
- return base_pipe
155
-
156
- # Check cache
157
- if controlnet_type in controlnet_cache:
158
- current_pipe = controlnet_cache[controlnet_type]
159
- current_controlnet = controlnet_type
160
- return current_pipe
161
-
162
- model_id = CONTROLNET_MODELS.get(controlnet_type)
163
- if model_id is None:
164
- return base_pipe
165
-
166
- try:
167
- print(f"🔄 Loading {controlnet_type}...")
168
-
169
- # Load ControlNet
170
- controlnet = ControlNetModel.from_pretrained(
171
- model_id,
172
- torch_dtype=dtype
173
- )
174
-
175
- # Load VAE
176
- vae = AutoencoderKL.from_pretrained(
177
- "madebyollin/sdxl-vae-fp16-fix",
178
- torch_dtype=dtype
179
- )
180
-
181
- # Create ControlNet Pipeline with SDXL
182
- pipe = StableDiffusionXLControlNetPipeline.from_pretrained(
183
- "John6666/nsfw-anime-xl-v1-sdxl",
184
- controlnet=controlnet,
185
- vae=vae,
186
- torch_dtype=dtype,
187
- use_safetensors=True,
188
- variant="fp16"
189
- )
190
- pipe.to(device)
191
-
192
- # Optimizations
193
- pipe.enable_attention_slicing()
194
- if hasattr(pipe, 'enable_vae_slicing'):
195
- pipe.enable_vae_slicing()
196
- if hasattr(pipe, 'enable_model_cpu_offload') and device.type == "cuda":
197
- pipe.enable_model_cpu_offload()
198
-
199
- # Cache it
200
- controlnet_cache[controlnet_type] = pipe
201
- current_pipe = pipe
202
- current_controlnet = controlnet_type
203
-
204
- print(f"✅ Loaded {controlnet_type}!")
205
- return pipe
206
-
207
- except Exception as e:
208
- print(f"❌ ControlNet Error: {e}")
209
- print("💡 Using base model instead")
210
- current_pipe = base_pipe
211
- current_controlnet = None
212
- return base_pipe
213
 
214
  def restore_photo(
215
  input_image,
216
- prompt,
217
- negative_prompt,
218
- controlnet_type,
219
- controlnet_strength,
220
  seed,
221
  randomize_seed,
222
  guidance_scale,
 
223
  steps,
 
 
224
  progress=gr.Progress(track_tqdm=True)
225
  ):
226
- """Restore/colorize anime/manga photo"""
227
  if input_image is None:
228
  raise gr.Error("กรุณาอัพโหลดภาพ")
229
 
230
- if base_pipe is None:
231
- raise gr.Error("โมเดลยังไม่พร้อมใช้งาน")
 
 
 
 
 
232
 
233
  if randomize_seed:
234
  seed = random.randint(0, MAX_SEED)
@@ -236,99 +157,49 @@ def restore_photo(
236
  generator = torch.Generator(device=device).manual_seed(seed)
237
  original_image = input_image.convert("RGB")
238
 
239
- # Resize for SDXL (1024x1024 optimal)
240
  width, height = original_image.size
241
- target_size = 1024
242
-
243
- # Maintain aspect ratio
244
- if width > height:
245
- new_width = target_size
246
- new_height = int(height * (target_size / width))
247
- else:
248
- new_height = target_size
249
- new_width = int(width * (target_size / height))
250
-
251
- # Ensure dimensions are divisible by 8
252
- new_width = (new_width // 8) * 8
253
- new_height = (new_height // 8) * 8
254
- original_image = original_image.resize((new_width, new_height), Image.LANCZOS)
255
 
256
- control_preview = None
 
257
 
258
  try:
259
- # Use ControlNet if selected
260
- if controlnet_type != "ไม่ใช้ (None)":
261
- progress(0.2, desc=f"Processing {controlnet_type}...")
262
-
263
- # Process control image
264
- control_image = process_controlnet_image(original_image, controlnet_type)
265
- control_preview = control_image
266
-
267
- if control_image is not None:
268
- # Load ControlNet model
269
- progress(0.4, desc="Loading model...")
270
- pipe = load_controlnet_model(controlnet_type)
271
-
272
- if pipe is not None and current_controlnet == controlnet_type:
273
- progress(0.6, desc="Generating with ControlNet...")
274
-
275
- result = pipe(
276
- prompt=prompt,
277
- negative_prompt=negative_prompt,
278
- image=control_image,
279
- num_inference_steps=int(steps),
280
- guidance_scale=float(guidance_scale),
281
- controlnet_conditioning_scale=float(controlnet_strength),
282
- generator=generator,
283
- ).images[0]
284
-
285
- if device.type == "cuda":
286
- torch.cuda.empty_cache()
287
- gc.collect()
288
-
289
- return result, control_preview, seed
290
 
291
- # Use base pipeline (img2img style)
292
- progress(0.5, desc="Generating with base model...")
 
 
293
 
294
- pipe = base_pipe
295
- if pipe is not None:
296
- # Use img2img with low denoising for colorization
297
- result = pipe(
298
- prompt=prompt,
299
- negative_prompt=negative_prompt,
300
- num_inference_steps=int(steps),
301
- guidance_scale=float(guidance_scale),
302
- generator=generator,
303
- ).images[0]
304
-
305
- # Blend with original for better preservation
306
- result = Image.blend(original_image, result, alpha=0.7)
307
-
308
- if device.type == "cuda":
309
- torch.cuda.empty_cache()
310
- gc.collect()
311
-
312
- return result, control_preview, seed
313
-
314
- raise gr.Error("ไม่มีโมเดลพร้อมใช้งาน")
315
 
316
  except Exception as e:
317
- print(f"Error details: {e}")
318
- import traceback
319
- traceback.print_exc()
320
  raise gr.Error(f"เกิดข้อผิดพลาด: {str(e)}")
321
 
322
  def load_preset(preset_name):
323
  """Load restoration preset"""
324
  return RESTORATION_PRESETS.get(preset_name, "")
325
 
326
- def update_controlnet_visibility(controlnet_type):
327
- """Update visibility of controlnet strength slider"""
328
- if controlnet_type == "ไม่ใช้ (None)":
329
- return gr.update(visible=False)
330
- return gr.update(visible=True)
331
-
332
  css="""
333
  #col-container {
334
  margin: 0 auto;
@@ -337,260 +208,268 @@ css="""
337
  #main-title h1 {
338
  font-size: 2.5em !important;
339
  text-align: center;
340
- background: linear-gradient(135deg, #FF6B9D 0%, #C06C84 50%, #6C5B7B 100%);
341
  -webkit-background-clip: text;
342
  -webkit-text-fill-color: transparent;
343
  background-clip: text;
344
  }
345
  .feature-box {
346
- background: linear-gradient(135deg, #FF6B9D 0%, #C06C84 50%, #6C5B7B 100%);
347
  color: white;
348
  border-radius: 12px;
349
  padding: 25px;
350
  margin: 20px 0;
351
  box-shadow: 0 4px 6px rgba(0,0,0,0.1);
352
  }
353
- .controlnet-info {
354
- background: #fff0f5;
355
- border-left: 4px solid #FF6B9D;
356
- padding: 15px;
357
- margin: 10px 0;
358
- border-radius: 8px;
359
- }
360
  """
361
 
362
  with gr.Blocks(css=css, theme=steel_blue_theme) as demo:
363
  with gr.Column(elem_id="col-container"):
364
- gr.Markdown("# 🎨 Anime/Manga Photo Restoration with ControlNet", elem_id="main-title")
365
- gr.Markdown("### ✨ AI-Powered Anime Colorization + Manga Recolor ControlNet")
366
 
367
  gr.HTML("""
368
  <div class="feature-box">
369
- <h3>🎮 Features</h3>
370
- <table style="width:100%; color:white;">
371
  <tr>
372
- <td>🎨 <strong>Anime SDXL Model</strong></td>
373
- <td>High-quality anime generation (John6666/nsfw-anime-xl-v1-sdxl)</td>
374
  </tr>
375
  <tr>
376
- <td>📚 <strong>Manga Recolor ControlNet</strong></td>
377
- <td>Perfect for colorizing manga/lineart (SubMaroon/ControlNet-manga-recolor)</td>
378
  </tr>
379
  <tr>
380
- <td>🖼️ <strong>SDXL Quality</strong></td>
381
- <td>1024x1024 resolution for stunning results</td>
382
  </tr>
383
  <tr>
384
- <td>⚡ <strong>Optimized</strong></td>
385
- <td>VAE fp16, attention slicing, model offloading</td>
386
  </tr>
387
  </table>
388
- <div style="margin-top:15px; padding:10px; background:rgba(255,255,255,0.1); border-radius:8px;">
389
- <strong>💡 Tip:</strong> Use "Manga Recolor" ControlNet for best colorization results on black & white manga/lineart!
390
- </div>
391
  </div>
392
  """)
393
 
394
- with gr.Row(equal_height=True):
395
  with gr.Column(scale=1):
396
  input_image = gr.Image(
397
- label="📤 อัพโหลดภาพอนิเมะ/มังงะ",
398
  type="pil",
399
  height=400
400
  )
401
 
402
- gr.Markdown("### 🎯 การตั้งค่าการซ่อมแซม")
 
 
 
 
 
403
 
404
  preset = gr.Dropdown(
405
  choices=list(RESTORATION_PRESETS.keys()),
406
- label="เลือกพรีเซ็ต",
407
- value="ลงสีอนิเมะ/มังงะ (Anime Colorize)",
408
- interactive=True
409
  )
410
 
411
- prompt = gr.Textbox(
412
- label="💬 Prompt (ภาษาอังกฤษ)",
413
- placeholder="Describe the desired anime style...",
414
  lines=3,
415
- value=RESTORATION_PRESETS["ลงสีอนิเมะ/มังงะ (Anime Colorize)"]
416
  )
417
 
418
- negative_prompt = gr.Textbox(
419
- label="🚫 Negative Prompt",
420
- placeholder="What to avoid...",
421
- lines=2,
422
- value="lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry"
423
- )
424
-
425
- gr.Markdown("### 🎮 ControlNet Settings")
426
-
427
- controlnet_type = gr.Dropdown(
428
- choices=list(CONTROLNET_MODELS.keys()),
429
- label="🎛️ เลือก ControlNet Type",
430
- value="Manga Recolor (Recommended)",
431
- interactive=True,
432
- info="แนะนำให้ใช้ Manga Recolor สำหรับลงสี"
433
- )
434
-
435
- controlnet_strength = gr.Slider(
436
- label="💪 ControlNet Strength",
437
- minimum=0.3,
438
- maximum=2.0,
439
- step=0.1,
440
- value=0.8,
441
- visible=True,
442
- info="ความแรงของ ControlNet (0.6-1.0 แนะนำ)"
443
- )
444
-
445
- gr.HTML("""
446
- <div class="controlnet-info">
447
- <strong>📝 แนะนำสำหรับ Anime/Manga:</strong><br><br>
448
- • <strong>Manga Recolor</strong> - ดีที่สุดสำหรับลงสีมังงะ ⭐<br>
449
- • ตั้ง Strength = <strong>0.6-1.0</strong> <br>
450
- • ใช้ prompt ที่บอกสีและบรรยากาศที่ต้องการ<br>
451
- • Negative prompt ช่วยป้องกันคุณภาพต่ำ
452
- </div>
453
- """)
454
-
455
  preset.change(
456
  fn=load_preset,
457
  inputs=[preset],
458
- outputs=[prompt]
459
  )
460
 
461
- controlnet_type.change(
462
- fn=update_controlnet_visibility,
463
- inputs=[controlnet_type],
464
- outputs=[controlnet_strength]
465
- )
 
 
 
 
 
 
 
 
 
 
466
 
467
- run_button = gr.Button("✨ เริ่มสร้าง/ลงสีภาพ", variant="primary", size="lg")
468
 
469
- with gr.Column(scale=1):
470
  with gr.Row():
471
- output_image = gr.Image(
472
- label=" ผลลัพธ์",
473
- interactive=False,
474
- height=400
475
  )
476
- control_image = gr.Image(
477
- label="🎮 ControlNet Preview (Lineart)",
478
- interactive=False,
479
- height=400,
480
- visible=True
481
  )
482
 
483
- with gr.Accordion("⚙️ การตั้งค่าขั้นสูง", open=True):
484
- seed = gr.Slider(
485
- label="🎲 Seed",
486
- minimum=0,
487
- maximum=MAX_SEED,
488
- step=1,
489
- value=42,
490
- info="เลข seed สำหรับผลลัพธ์ที่เหมือนเดิม"
491
- )
492
-
493
- randomize_seed = gr.Checkbox(
494
- label="🔀 สุ่ม Seed ทุกครั้ง",
495
- value=True
496
- )
497
 
498
- guidance_scale = gr.Slider(
499
- label="💬 Guidance Scale",
500
- minimum=3.0,
501
- maximum=15.0,
502
- step=0.5,
503
- value=7.5,
504
- info="SDXL แนะนำ 7-10"
505
- )
 
 
 
 
 
 
 
 
 
 
506
 
507
  steps = gr.Slider(
508
- label="🔢 Steps",
509
- minimum=20,
510
- maximum=50,
511
- step=5,
512
- value=30,
513
- info="SDXL แนะนำ 25-40 steps"
514
  )
515
-
516
- gr.Markdown("""
517
- **💡 การตั้งค่าแนะนำสำหรับ SDXL Anime:**
518
- - **Steps:** 25-35 (SDXL ต้องการมากกว่า SD1.5)
519
- - **Guidance:** 7-10 (SDXL ทำงานดีที่ 7.5)
520
- - **ControlNet Strength:** 0.6-1.0
521
- - **Resolution:** ระบบจะ resize อัตโนมัติเป็น 1024px
522
- """)
523
 
524
  run_button.click(
525
  fn=restore_photo,
526
- inputs=[input_image, prompt, negative_prompt, controlnet_type,
527
- controlnet_strength, seed, randomize_seed,
528
- guidance_scale, steps],
529
- outputs=[output_image, control_image, seed]
 
530
  )
531
 
532
  gr.Markdown("""
533
  ---
534
- ## 📚 คู่มือการใช้งาน
535
-
536
- ### 🎨 โมเดลที่ใช้:
537
-
538
- - **Base Model:** John6666/nsfw-anime-xl-v1-sdxl (SDXL anime model)
539
- - **ControlNet:** SubMaroon/ControlNet-manga-recolor (manga colorization)
540
- - **VAE:** madebyollin/sdxl-vae-fp16-fix (better quality)
541
-
542
- ### 🚀 วิธีใช้งาน:
543
-
544
- 1. **อัพโหลดภาพ** - รองรับมังงะ, ภาพขาวดำ, อนิเมะ
545
- 2. **เลือกพรีเซ็ต** หรือเขียน prompt เอง
546
- 3. **เลือก Manga Recolor** ControlNet (แนะนำ)
547
- 4. **ปรับ Strength** = 0.6-1.0
548
- 5. **กดปุ่ม "เริ่มสร้าง/ลงสีภาพ"**
549
- 6. **รอ 1-3 นาที** (CPU) หรือ 30-90 วินาที (GPU)
550
-
551
- ### 💡 เคล็ดลับ:
552
-
553
- **สำหรับลงสีมังงะ/ภาพขาวดำ:**
554
- - เลือก **Manga Recolor** ControlNet
555
- - Strength = **0.7-0.9**
556
- - Steps = **25-35**
557
- - Guidance = **7-9**
558
- - Prompt: "beautiful colorful anime art, vibrant colors"
559
-
560
- ✅ **สำหรับสร้างอนิเมะสไตล์:**
561
- - ไม่ต้องใช้ ControlNet
562
- - Steps = **30-40**
563
- - Guidance = **7.5-10**
564
- - ใช้ prompt ที่ละเอียด
565
-
566
- **Negative Prompt ที่ดี:**
567
- - รวมคีย์เวิร์ด: lowres, bad anatomy, worst quality
568
- - ช่วยป้องกันความผิดพลาด
569
- - เพิ่มคุณภาพโดยรวม
570
-
571
- ### ⚡ ประสิทธิภาพ SDXL:
572
-
573
- - **CPU:** 2-5 นาที/ภาพ (ไม่แนะนำ)
574
- - **CPU + ControlNet:** 4-8 นาที/ภาพ
575
- - **GPU (8GB+):** 30-90 วินาที/ภาพ
576
- - **GPU + ControlNet:** 60-120 วินาที/ภาพ
577
-
578
- ### 🎯 ใช้กับ:
579
-
580
- - ✅ Manga panels (black & white)
581
- - Lineart drawings
582
- - Sketch to anime
583
- - Old anime screenshots
584
- - Character designs
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
585
 
586
  ---
587
 
588
- <div style="text-align:center; padding:20px;">
589
- <strong>🌟 Anime/Manga Restoration with SDXL</strong><br>
590
- Professional Quality Colorization with Manga Recolor ControlNet<br>
591
- <em>Powered by SDXL + ControlNet</em>
592
  </div>
593
  """)
594
 
595
- if __name__ == "__main__":
596
- demo.launch(server_name="0.0.0.0", server_port=7860, share=False)
 
37
  steel_blue_theme = SteelBlueTheme()
38
 
39
  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)
 
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:
166
+ new_width = max_size
167
+ new_height = int(height * (max_size / width))
168
+ else:
169
+ new_height = max_size
170
+ new_width = int(width * (max_size / height))
171
+
172
+ new_width = (new_width // 8) * 8
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;
 
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%);
218
  color: white;
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):
320
+ with gr.Row():
321
+ seed = gr.Slider(
322
+ label="🎲 Seed",
323
+ minimum=0,
324
+ maximum=MAX_SEED,
325
+ step=1,
326
+ value=42
327
+ )
328
+ randomize_seed = gr.Checkbox(
329
+ label="🔀 Random Seed",
330
+ value=True
331
+ )
 
332
 
333
+ with gr.Row():
334
+ guidance_scale = gr.Slider(
335
+ label="💬 Guidance Scale",
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(
353
+ label="🔢 Steps",
354
+ minimum=10,
355
+ maximum=50,
356
+ step=5,
357
+ value=25
 
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
 
475
+ demo.launch(server_name="0.0.0.0", server_port=7860, share=False)