Daniel251 commited on
Commit
2fc2c27
·
verified ·
1 Parent(s): aced007

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +78 -311
app.py CHANGED
@@ -1,30 +1,18 @@
1
  """
2
  🎨 Image Colorizer
3
- Versão 100% funcional para Hugging Face Spaces
4
  """
5
 
6
  import gradio as gr
7
- from PIL import Image, ImageOps, ImageEnhance, ImageFilter
8
  import numpy as np
9
  import tempfile
10
  import time
11
  import os
12
- import sys
13
- from pathlib import Path
14
 
15
- print("=" * 60)
16
- print("🚀 INICIANDO APP: IMAGE COLORIZER")
17
- print(f"Python version: {sys.version}")
18
- print(f"Pillow version: {Image.__version__}")
19
- print("=" * 60)
20
 
21
- # ========== CONFIGURAÇÃO ==========
22
-
23
- MAX_IMAGE_SIZE = 1024 # Para processamento rápido
24
-
25
- print(f"📏 Tamanho máximo: {MAX_IMAGE_SIZE}px")
26
-
27
- # ========== FUNÇÕES AUXILIARES ==========
28
 
29
  def log_message(message):
30
  """Log para debug"""
@@ -37,37 +25,25 @@ def validate_image(image):
37
  if image is None:
38
  return None, "❌ Nenhuma imagem fornecida"
39
 
40
- log_message(f"Validando imagem - Tipo: {type(image)}")
41
-
42
- # Converter para PIL Image
43
- if isinstance(image, dict): # Gradio dict
44
- if 'image' in image:
45
- img_array = image['image']
46
- if isinstance(img_array, np.ndarray):
47
- img = Image.fromarray(img_array.astype('uint8'))
48
- else:
49
- return None, "❌ Formato de array inválido"
50
- else:
51
- return None, "❌ Dicionário sem chave 'image'"
52
- elif isinstance(image, np.ndarray):
53
  img = Image.fromarray(image.astype('uint8'))
54
  else:
55
- img = image # Já é PIL Image
56
 
57
  # Verificar tamanho
58
  if max(img.size) > 4000:
59
- return None, "❌ Imagem muito grande (>4000px)."
60
 
61
  if min(img.size) < 32:
62
- return None, "❌ Imagem muito pequena (<32px)."
63
 
64
  log_message(f"✅ Imagem válida: {img.size}px, {img.mode}")
65
  return img, "ok"
66
 
67
  except Exception as e:
68
- error_msg = f"❌ Erro na validação: {str(e)}"
69
- log_message(error_msg)
70
- return None, error_msg
71
 
72
  def resize_image(image, max_size):
73
  """Redimensiona mantendo aspect ratio"""
@@ -78,157 +54,86 @@ def resize_image(image, max_size):
78
  new_width = int(image.width * ratio)
79
  new_height = int(image.height * ratio)
80
 
81
- log_message(f"Redimensionando: {image.size} -> ({new_width}, {new_height})")
82
-
83
  return image.resize((new_width, new_height), Image.Resampling.LANCZOS)
84
 
85
  def apply_colorization(image, style="realistic", intensity=0.8):
86
  """Aplica colorização à imagem"""
87
  try:
88
  log_message(f"Aplicando colorização - Estilo: {style}, Intensidade: {intensity}")
89
- start_time = time.time()
90
 
91
- # Converter para RGB se necessário
92
  if image.mode != 'RGB':
93
  rgb_img = image.convert('RGB')
94
  else:
95
  rgb_img = image.copy()
96
 
97
- # Converter para escala de cinza para análise
98
  gray_img = rgb_img.convert('L')
99
 
100
- # Aplicar diferentes efeitos baseados no estilo
101
  if style == "realistic":
102
- # Tons realistas - adicionar calor às cores
103
  result = rgb_img.copy()
104
-
105
- # Aumentar saturação moderadamente
106
  enhancer = ImageEnhance.Color(result)
107
  result = enhancer.enhance(1.0 + (intensity * 0.5))
108
 
109
- # Ajustar temperatura (mais quente)
110
  r, g, b = result.split()
111
  r = r.point(lambda x: min(255, int(x * (1.0 + intensity * 0.1))))
112
  b = b.point(lambda x: max(0, int(x * (1.0 - intensity * 0.05))))
113
  result = Image.merge('RGB', (r, g, b))
114
 
115
  elif style == "vibrant":
116
- # Cores vivas e saturadas
117
  result = rgb_img.copy()
118
-
119
- # Aumentar saturação significativamente
120
  enhancer = ImageEnhance.Color(result)
121
  result = enhancer.enhance(1.0 + (intensity * 1.0))
122
-
123
- # Aumentar contraste
124
  enhancer = ImageEnhance.Contrast(result)
125
  result = enhancer.enhance(1.0 + (intensity * 0.3))
126
-
127
- # Aumentar nitidez
128
  result = result.filter(ImageFilter.UnsharpMask(radius=1, percent=50, threshold=0))
129
 
130
  elif style == "vintage":
131
- # Tons sépia/vintage
132
  result = rgb_img.copy()
133
-
134
- # Aplicar efeito sépia
135
  r, g, b = result.split()
136
  r = r.point(lambda x: min(255, int(x * 1.1)))
137
  g = g.point(lambda x: int(x * 0.9))
138
  b = b.point(lambda x: int(x * 0.8))
139
  result = Image.merge('RGB', (r, g, b))
140
-
141
- # Reduzir saturação
142
  enhancer = ImageEnhance.Color(result)
143
  result = enhancer.enhance(0.7 + (intensity * 0.3))
144
 
145
- # Adicionar vinheta leve
146
- width, height = result.size
147
- vignette = Image.new('L', (width, height), color=255)
148
- draw = ImageDraw.Draw(vignette)
149
- for i in range(0, width // 2, 10):
150
- alpha = int(255 * (1 - (i / (width / 2)) * 0.3))
151
- draw.ellipse([i, i, width - i, height - i], outline=alpha)
152
- result.putalpha(vignette)
153
- result = result.convert('RGB')
154
-
155
  elif style == "cinematic":
156
- # Tons frios e dramáticos
157
  result = rgb_img.copy()
158
-
159
- # Ajustar cores para tons frios
160
  r, g, b = result.split()
161
  r = r.point(lambda x: int(x * 0.9))
162
  g = g.point(lambda x: int(x * 1.0))
163
  b = b.point(lambda x: min(255, int(x * 1.1)))
164
  result = Image.merge('RGB', (r, g, b))
165
-
166
- # Aumentar contraste
167
  enhancer = ImageEnhance.Contrast(result)
168
  result = enhancer.enhance(1.0 + (intensity * 0.4))
169
 
170
- else: # balanced (padrão)
171
  result = rgb_img.copy()
172
-
173
- # Ajuste balanceado
174
  enhancer = ImageEnhance.Color(result)
175
  result = enhancer.enhance(1.0 + (intensity * 0.6))
176
-
177
  enhancer = ImageEnhance.Contrast(result)
178
  result = enhancer.enhance(1.0 + (intensity * 0.2))
179
 
180
- # Se a imagem original era escala de cinza, misturar com o resultado colorizado
181
  if image.mode in ['L', 'LA', 'P']:
182
- # Criar máscara baseada na luminância original
183
  gray_array = np.array(gray_img).astype(np.float32) / 255.0
184
-
185
- # Converter resultados para arrays
186
  original_array = np.array(rgb_img).astype(np.float32)
187
  colorized_array = np.array(result).astype(np.float32)
188
 
189
- # Misturar baseado na intensidade
190
  mixed = (1 - intensity) * original_array + intensity * colorized_array
191
 
192
- # Ajustar baseado na luminância (áreas escuras menos coloridas)
193
  for i in range(3):
194
  mixed[:,:,i] = mixed[:,:,i] * (0.7 + 0.3 * gray_array)
195
 
196
  result = Image.fromarray(mixed.astype(np.uint8))
197
 
198
- process_time = time.time() - start_time
199
- log_message(f"✅ Colorização concluída em {process_time:.1f}s")
200
-
201
  return result
202
 
203
  except Exception as e:
204
  log_message(f"❌ Erro na colorização: {str(e)}")
205
- return image # Retorna original em caso de erro
206
-
207
- def save_image(image, prefix="colorized"):
208
- """Salva imagem para download"""
209
- try:
210
- if image is None:
211
- return None
212
-
213
- timestamp = time.strftime("%Y%m%d_%H%M%S")
214
-
215
- temp_file = tempfile.NamedTemporaryFile(
216
- delete=False,
217
- suffix=".png",
218
- prefix=f"{prefix}_"
219
- )
220
-
221
- # Salvar com alta qualidade
222
- image.save(temp_file.name, "PNG", optimize=True)
223
-
224
- file_size = os.path.getsize(temp_file.name) // 1024
225
- log_message(f"Imagem salva: {temp_file.name} ({file_size}KB)")
226
-
227
- return temp_file.name
228
-
229
- except Exception as e:
230
- log_message(f"❌ Erro ao salvar: {str(e)}")
231
- return None
232
 
233
  def create_comparison(original, colorized):
234
  """Cria comparação lado a lado"""
@@ -236,7 +141,6 @@ def create_comparison(original, colorized):
236
  if original is None or colorized is None:
237
  return None
238
 
239
- # Redimensionar para mesma altura
240
  target_height = 400
241
  target_width_orig = int(original.width * (target_height / original.height))
242
  target_width_color = int(colorized.width * (target_height / colorized.height))
@@ -244,21 +148,15 @@ def create_comparison(original, colorized):
244
  original_resized = original.resize((target_width_orig, target_height), Image.Resampling.LANCZOS)
245
  colorized_resized = colorized.resize((target_width_color, target_height), Image.Resampling.LANCZOS)
246
 
247
- # Criar imagem combinada
248
  total_width = original_resized.width + colorized_resized.width + 20
249
  total_height = target_height + 60
250
 
251
  comparison = Image.new('RGB', (total_width, total_height), color=(240, 240, 240))
252
-
253
- from PIL import ImageDraw
254
-
255
  draw = ImageDraw.Draw(comparison)
256
 
257
- # Títulos simples
258
  draw.text((10, 10), "ORIGINAL", fill=(100, 100, 100))
259
  draw.text((original_resized.width + 30, 10), "COLORIZED", fill=(0, 150, 0))
260
 
261
- # Colar imagens
262
  comparison.paste(original_resized, (0, 40))
263
  comparison.paste(colorized_resized, (original_resized.width + 20, 40))
264
 
@@ -269,149 +167,76 @@ def create_comparison(original, colorized):
269
  log_message(f"❌ Erro na comparação: {str(e)}")
270
  return None
271
 
272
- # Importar ImageDraw aqui para evitar erro
273
- from PIL import ImageDraw
274
-
275
- # ========== INTERFACE GRADIO ==========
276
-
277
- print("🎨 Criando interface Image Colorizer...")
 
 
 
 
 
 
 
 
 
278
 
279
- # Criar interface SEM css no construtor
280
- with gr.Blocks(title="🎨 Image Colorizer") as demo:
281
-
282
- # Aplicar CSS depois
283
- demo.css = """
284
- .header {
285
- text-align: center;
286
- padding: 20px;
287
- background: linear-gradient(135deg, #ff7e5f 0%, #feb47b 100%);
288
- border-radius: 10px;
289
- color: white;
290
- margin-bottom: 20px;
291
- }
292
- .card {
293
- padding: 15px;
294
- background: white;
295
- border-radius: 8px;
296
- border: 1px solid #e0e0e0;
297
- margin-bottom: 10px;
298
- }
299
- .btn-primary {
300
- background: linear-gradient(135deg, #ff7e5f 0%, #feb47b 100%);
301
- color: white;
302
- border: none;
303
- padding: 12px 24px;
304
- border-radius: 8px;
305
- font-weight: bold;
306
- margin: 10px 0;
307
- }
308
- .btn-primary:hover {
309
- transform: translateY(-2px);
310
- box-shadow: 0 4px 12px rgba(255, 126, 95, 0.3);
311
- }
312
- .style-btn {
313
- padding: 10px 15px;
314
- margin: 5px;
315
- border-radius: 6px;
316
- border: 1px solid #ddd;
317
- background: white;
318
- }
319
- .style-btn:hover {
320
- border-color: #ff7e5f;
321
- background: #fff5f2;
322
- }
323
- """
324
 
325
- # Cabeçalho
326
  gr.HTML("""
327
- <div class="header">
328
  <h1 style="margin: 0;">🎨 Image Colorizer</h1>
329
- <p style="margin: 5px 0 0 0;">App</p>
330
- <p style="margin: 5px 0 0 0; font-size: 0.9em;">Colorize fotos preto e branco automaticamente</p>
331
  </div>
332
  """)
333
 
334
  with gr.Row():
335
- # Coluna esquerda
336
  with gr.Column(scale=1):
337
- with gr.Column(elem_classes="card"):
338
- gr.Markdown("### 📤 Upload da Foto")
339
- image_input = gr.Image(
340
- type="pil",
341
- label="Clique ou arraste uma imagem",
342
- height=150
343
- )
344
 
345
- with gr.Column(elem_classes="card"):
346
- gr.Markdown("### ⚙️ Configurações")
347
-
348
- gr.Markdown("**Estilo de colorização:**")
349
- with gr.Row():
350
- style_realistic = gr.Button("Realista", elem_classes="style-btn")
351
- style_vibrant = gr.Button("Vibrante", elem_classes="style-btn")
352
- style_vintage = gr.Button("Vintage", elem_classes="style-btn")
353
-
354
- style_selected = gr.Textbox(value="realistic", visible=False)
355
-
356
- gr.Markdown("**Intensidade das cores:**")
357
- intensity_slider = gr.Slider(
358
- minimum=0.1,
359
- maximum=1.0,
360
- value=0.7,
361
- step=0.1,
362
- label="",
363
- info="0.1 = Suave | 1.0 = Intenso"
364
- )
365
 
366
- colorize_btn = gr.Button(
367
- "🎨 Colorizar Imagem",
368
- elem_classes="btn-primary",
369
- size="lg"
370
  )
 
 
371
 
372
- # Coluna direita
373
  with gr.Column(scale=1):
374
- with gr.Column(elem_classes="card"):
375
- gr.Markdown("### 📊 Resultado")
376
- status_output = gr.Markdown(
377
- value="**Status:** Aguardando imagem...",
378
- elem_classes="card"
379
- )
380
 
381
  with gr.Tabs():
382
  with gr.TabItem("🔄 Comparação"):
383
- comparison_output = gr.Image(
384
- type="pil",
385
- label="Antes e Depois",
386
- height=300
387
- )
388
 
389
  with gr.TabItem("📷 Original"):
390
- original_output = gr.Image(
391
- type="pil",
392
- label="Original",
393
- height=300
394
- )
395
 
396
  with gr.TabItem("🌈 Colorizada"):
397
- colorized_output = gr.Image(
398
- type="pil",
399
- label="Colorizada",
400
- height=300
401
- )
402
 
403
- with gr.Column(elem_classes="card"):
404
- gr.Markdown("### 💾 Download")
405
- with gr.Row():
406
- download_colorized = gr.Button("📥 Baixar Colorizada")
407
- download_comparison = gr.Button("📊 Baixar Comparação")
408
-
409
- download_file = gr.File(
410
- label="Arquivo para download",
411
- interactive=False
412
- )
413
 
414
- # Informações
415
  with gr.Accordion("📖 Como usar", open=False):
416
  gr.Markdown("""
417
  ## Instruções:
@@ -419,77 +244,46 @@ with gr.Blocks(title="🎨 Image Colorizer") as demo:
419
  2. **Escolha** o estilo de colorização
420
  3. **Ajuste** a intensidade das cores
421
  4. **Clique** em "Colorizar Imagem"
422
- 5. **Compare** os resultados
423
- 6. **Baixe** a imagem colorizada
424
 
425
- ## Dicas:
426
  - **Realista**: Tons naturais para retratos
427
  - **Vibrante**: Cores vivas para paisagens
428
  - **Vintage**: Estilo antigo e nostálgico
429
- - Use intensidade 0.7-0.8 para resultados naturais
430
  """)
431
 
432
- # Rodapé
433
  gr.HTML("""
434
  <div style="text-align: center; margin-top: 20px; padding: 15px; background: #f8f9fa; border-radius: 8px;">
435
- <p style="margin: 0; font-weight: bold; color: #333;">
436
- 🎨 Image Colorizer - App 5 do Photoshop AI Ecosystem
437
- </p>
438
- <p style="margin: 5px 0 0 0; color: #666; font-size: 0.9em;">
439
- ✨ Cada app tem uma função específica • Este app apenas coloriza imagens
440
- </p>
441
  </div>
442
  """)
443
 
444
- # ========== EVENT HANDLERS ==========
445
-
446
- # Botões de estilo
447
- def set_style_realistic():
448
- return "realistic"
449
-
450
- def set_style_vibrant():
451
- return "vibrant"
452
-
453
- def set_style_vintage():
454
- return "vintage"
455
-
456
- style_realistic.click(fn=set_style_realistic, outputs=[style_selected])
457
- style_vibrant.click(fn=set_style_vibrant, outputs=[style_selected])
458
- style_vintage.click(fn=set_style_vintage, outputs=[style_selected])
459
 
460
- # Processar imagem
461
  def process_colorization(image, style, intensity):
462
- log_message(f"Processando - Estilo: {style}, Intensidade: {intensity}")
463
-
464
  if image is None:
465
  return None, None, None, "❌ Por favor, carregue uma imagem primeiro"
466
 
467
- # Validar imagem
468
  valid_img, msg = validate_image(image)
469
  if valid_img is None:
470
  return None, None, None, f"❌ {msg}"
471
 
472
- # Redimensionar se necessário
473
  if max(valid_img.size) > MAX_IMAGE_SIZE:
474
  valid_img = resize_image(valid_img, MAX_IMAGE_SIZE)
475
 
476
- # Aplicar colorização
477
  colorized = apply_colorization(valid_img, style, intensity)
478
-
479
- # Criar comparação
480
  comparison = create_comparison(valid_img, colorized)
481
 
482
- # Mensagem de status
483
  status = f"""
484
  ✅ **Colorização aplicada com sucesso!**
485
 
486
  **Detalhes:**
487
  • Estilo: {style.title()}
488
  • Intensidade: {intensity*100:.0f}%
489
- • Tamanho original: {valid_img.size[0]}×{valid_img.size[1]}px
490
- • Tamanho colorizado: {colorized.size[0]}×{colorized.size[1]}px
491
-
492
- **Próximo passo:** Veja os resultados nas abas acima ou faça download.
493
  """
494
 
495
  return valid_img, colorized, comparison, status
@@ -500,20 +294,9 @@ with gr.Blocks(title="🎨 Image Colorizer") as demo:
500
  outputs=[original_output, colorized_output, comparison_output, status_output]
501
  )
502
 
503
- # Downloads
504
- download_colorized.click(
505
- fn=save_image,
506
- inputs=[colorized_output],
507
- outputs=[download_file]
508
- )
509
-
510
- download_comparison.click(
511
- fn=save_image,
512
- inputs=[comparison_output],
513
- outputs=[download_file]
514
- )
515
 
516
- # Limpar ao carregar nova imagem
517
  def clear_on_upload(image):
518
  if image is None:
519
  return None, None, None, "**Status:** Aguardando imagem..."
@@ -522,19 +305,7 @@ with gr.Blocks(title="🎨 Image Colorizer") as demo:
522
  if valid_img is None:
523
  return None, None, None, f"❌ {msg}"
524
 
525
- status = f"""
526
- ✅ **Imagem carregada com sucesso!**
527
-
528
- **Detalhes:**
529
- • Tamanho: {valid_img.size[0]}×{valid_img.size[1]}px
530
- • Formato: {valid_img.mode}
531
-
532
- **Próximos passos:**
533
- 1. Escolha um estilo de colorização
534
- 2. Ajuste a intensidade das cores
535
- 3. Clique em "Colorizar Imagem"
536
- """
537
-
538
  return None, None, None, status
539
 
540
  image_input.change(
@@ -543,10 +314,6 @@ with gr.Blocks(title="🎨 Image Colorizer") as demo:
543
  outputs=[original_output, colorized_output, comparison_output, status_output]
544
  )
545
 
546
- print("=" * 60)
547
- print("✅ APP 5: IMAGE COLORIZER PRONTO!")
548
- print("=" * 60)
549
- print("📦 Versão simplificada e funcional")
550
- print("=" * 60)
551
-
552
- # O Hugging Face Spaces cuida do demo.launch()
 
1
  """
2
  🎨 Image Colorizer
3
+ Versão otimizada para Hugging Face Spaces
4
  """
5
 
6
  import gradio as gr
7
+ from PIL import Image, ImageEnhance, ImageFilter, ImageDraw
8
  import numpy as np
9
  import tempfile
10
  import time
11
  import os
 
 
12
 
13
+ print("🚀 Iniciando Image Colorizer...")
 
 
 
 
14
 
15
+ MAX_IMAGE_SIZE = 1024
 
 
 
 
 
 
16
 
17
  def log_message(message):
18
  """Log para debug"""
 
25
  if image is None:
26
  return None, "❌ Nenhuma imagem fornecida"
27
 
28
+ # Converter para PIL Image se necessário
29
+ if isinstance(image, np.ndarray):
 
 
 
 
 
 
 
 
 
 
 
30
  img = Image.fromarray(image.astype('uint8'))
31
  else:
32
+ img = image
33
 
34
  # Verificar tamanho
35
  if max(img.size) > 4000:
36
+ return None, "❌ Imagem muito grande (>4000px)"
37
 
38
  if min(img.size) < 32:
39
+ return None, "❌ Imagem muito pequena (<32px)"
40
 
41
  log_message(f"✅ Imagem válida: {img.size}px, {img.mode}")
42
  return img, "ok"
43
 
44
  except Exception as e:
45
+ log_message(f"❌ Erro na validação: {str(e)}")
46
+ return None, str(e)
 
47
 
48
  def resize_image(image, max_size):
49
  """Redimensiona mantendo aspect ratio"""
 
54
  new_width = int(image.width * ratio)
55
  new_height = int(image.height * ratio)
56
 
 
 
57
  return image.resize((new_width, new_height), Image.Resampling.LANCZOS)
58
 
59
  def apply_colorization(image, style="realistic", intensity=0.8):
60
  """Aplica colorização à imagem"""
61
  try:
62
  log_message(f"Aplicando colorização - Estilo: {style}, Intensidade: {intensity}")
 
63
 
64
+ # Converter para RGB
65
  if image.mode != 'RGB':
66
  rgb_img = image.convert('RGB')
67
  else:
68
  rgb_img = image.copy()
69
 
 
70
  gray_img = rgb_img.convert('L')
71
 
72
+ # Aplicar efeitos baseados no estilo
73
  if style == "realistic":
 
74
  result = rgb_img.copy()
 
 
75
  enhancer = ImageEnhance.Color(result)
76
  result = enhancer.enhance(1.0 + (intensity * 0.5))
77
 
 
78
  r, g, b = result.split()
79
  r = r.point(lambda x: min(255, int(x * (1.0 + intensity * 0.1))))
80
  b = b.point(lambda x: max(0, int(x * (1.0 - intensity * 0.05))))
81
  result = Image.merge('RGB', (r, g, b))
82
 
83
  elif style == "vibrant":
 
84
  result = rgb_img.copy()
 
 
85
  enhancer = ImageEnhance.Color(result)
86
  result = enhancer.enhance(1.0 + (intensity * 1.0))
 
 
87
  enhancer = ImageEnhance.Contrast(result)
88
  result = enhancer.enhance(1.0 + (intensity * 0.3))
 
 
89
  result = result.filter(ImageFilter.UnsharpMask(radius=1, percent=50, threshold=0))
90
 
91
  elif style == "vintage":
 
92
  result = rgb_img.copy()
 
 
93
  r, g, b = result.split()
94
  r = r.point(lambda x: min(255, int(x * 1.1)))
95
  g = g.point(lambda x: int(x * 0.9))
96
  b = b.point(lambda x: int(x * 0.8))
97
  result = Image.merge('RGB', (r, g, b))
 
 
98
  enhancer = ImageEnhance.Color(result)
99
  result = enhancer.enhance(0.7 + (intensity * 0.3))
100
 
 
 
 
 
 
 
 
 
 
 
101
  elif style == "cinematic":
 
102
  result = rgb_img.copy()
 
 
103
  r, g, b = result.split()
104
  r = r.point(lambda x: int(x * 0.9))
105
  g = g.point(lambda x: int(x * 1.0))
106
  b = b.point(lambda x: min(255, int(x * 1.1)))
107
  result = Image.merge('RGB', (r, g, b))
 
 
108
  enhancer = ImageEnhance.Contrast(result)
109
  result = enhancer.enhance(1.0 + (intensity * 0.4))
110
 
111
+ else: # balanced
112
  result = rgb_img.copy()
 
 
113
  enhancer = ImageEnhance.Color(result)
114
  result = enhancer.enhance(1.0 + (intensity * 0.6))
 
115
  enhancer = ImageEnhance.Contrast(result)
116
  result = enhancer.enhance(1.0 + (intensity * 0.2))
117
 
118
+ # Misturar se imagem era grayscale
119
  if image.mode in ['L', 'LA', 'P']:
 
120
  gray_array = np.array(gray_img).astype(np.float32) / 255.0
 
 
121
  original_array = np.array(rgb_img).astype(np.float32)
122
  colorized_array = np.array(result).astype(np.float32)
123
 
 
124
  mixed = (1 - intensity) * original_array + intensity * colorized_array
125
 
 
126
  for i in range(3):
127
  mixed[:,:,i] = mixed[:,:,i] * (0.7 + 0.3 * gray_array)
128
 
129
  result = Image.fromarray(mixed.astype(np.uint8))
130
 
131
+ log_message("✅ Colorização concluída")
 
 
132
  return result
133
 
134
  except Exception as e:
135
  log_message(f"❌ Erro na colorização: {str(e)}")
136
+ return image
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
 
138
  def create_comparison(original, colorized):
139
  """Cria comparação lado a lado"""
 
141
  if original is None or colorized is None:
142
  return None
143
 
 
144
  target_height = 400
145
  target_width_orig = int(original.width * (target_height / original.height))
146
  target_width_color = int(colorized.width * (target_height / colorized.height))
 
148
  original_resized = original.resize((target_width_orig, target_height), Image.Resampling.LANCZOS)
149
  colorized_resized = colorized.resize((target_width_color, target_height), Image.Resampling.LANCZOS)
150
 
 
151
  total_width = original_resized.width + colorized_resized.width + 20
152
  total_height = target_height + 60
153
 
154
  comparison = Image.new('RGB', (total_width, total_height), color=(240, 240, 240))
 
 
 
155
  draw = ImageDraw.Draw(comparison)
156
 
 
157
  draw.text((10, 10), "ORIGINAL", fill=(100, 100, 100))
158
  draw.text((original_resized.width + 30, 10), "COLORIZED", fill=(0, 150, 0))
159
 
 
160
  comparison.paste(original_resized, (0, 40))
161
  comparison.paste(colorized_resized, (original_resized.width + 20, 40))
162
 
 
167
  log_message(f"❌ Erro na comparação: {str(e)}")
168
  return None
169
 
170
+ def save_image(image, prefix="colorized"):
171
+ """Salva imagem para download"""
172
+ try:
173
+ if image is None:
174
+ return None
175
+
176
+ temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".png", prefix=f"{prefix}_")
177
+ image.save(temp_file.name, "PNG", optimize=True)
178
+
179
+ log_message(f"Imagem salva: {temp_file.name}")
180
+ return temp_file.name
181
+
182
+ except Exception as e:
183
+ log_message(f"❌ Erro ao salvar: {str(e)}")
184
+ return None
185
 
186
+ # Interface Gradio
187
+ with gr.Blocks(title="🎨 Image Colorizer", theme=gr.themes.Soft()) as demo:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
 
 
189
  gr.HTML("""
190
+ <div style="text-align: center; padding: 20px; background: linear-gradient(135deg, #ff7e5f 0%, #feb47b 100%); border-radius: 10px; color: white; margin-bottom: 20px;">
191
  <h1 style="margin: 0;">🎨 Image Colorizer</h1>
192
+ <p style="margin: 5px 0 0 0;">Colorize fotos preto e branco automaticamente</p>
 
193
  </div>
194
  """)
195
 
196
  with gr.Row():
 
197
  with gr.Column(scale=1):
198
+ gr.Markdown("### 📤 Upload da Foto")
199
+ image_input = gr.Image(type="pil", label="Clique ou arraste uma imagem")
 
 
 
 
 
200
 
201
+ gr.Markdown("### ⚙️ Configurações")
202
+ gr.Markdown("**Estilo de colorização:**")
203
+
204
+ with gr.Row():
205
+ style_realistic = gr.Button("Realista", size="sm")
206
+ style_vibrant = gr.Button("Vibrante", size="sm")
207
+ style_vintage = gr.Button("Vintage", size="sm")
208
+
209
+ style_selected = gr.Textbox(value="realistic", visible=False)
 
 
 
 
 
 
 
 
 
 
 
210
 
211
+ gr.Markdown("**Intensidade das cores:**")
212
+ intensity_slider = gr.Slider(
213
+ minimum=0.1, maximum=1.0, value=0.7, step=0.1,
214
+ label="", info="0.1 = Suave | 1.0 = Intenso"
215
  )
216
+
217
+ colorize_btn = gr.Button("🎨 Colorizar Imagem", variant="primary", size="lg")
218
 
 
219
  with gr.Column(scale=1):
220
+ gr.Markdown("### 📊 Resultado")
221
+ status_output = gr.Markdown("**Status:** Aguardando imagem...")
 
 
 
 
222
 
223
  with gr.Tabs():
224
  with gr.TabItem("🔄 Comparação"):
225
+ comparison_output = gr.Image(type="pil", label="Antes e Depois")
 
 
 
 
226
 
227
  with gr.TabItem("📷 Original"):
228
+ original_output = gr.Image(type="pil", label="Original")
 
 
 
 
229
 
230
  with gr.TabItem("🌈 Colorizada"):
231
+ colorized_output = gr.Image(type="pil", label="Colorizada")
 
 
 
 
232
 
233
+ gr.Markdown("### 💾 Download")
234
+ with gr.Row():
235
+ download_colorized = gr.Button("📥 Baixar Colorizada")
236
+ download_comparison = gr.Button("📊 Baixar Comparação")
237
+
238
+ download_file = gr.File(label="Arquivo para download", interactive=False)
 
 
 
 
239
 
 
240
  with gr.Accordion("📖 Como usar", open=False):
241
  gr.Markdown("""
242
  ## Instruções:
 
244
  2. **Escolha** o estilo de colorização
245
  3. **Ajuste** a intensidade das cores
246
  4. **Clique** em "Colorizar Imagem"
247
+ 5. **Compare** os resultados e baixe
 
248
 
249
+ ## Estilos:
250
  - **Realista**: Tons naturais para retratos
251
  - **Vibrante**: Cores vivas para paisagens
252
  - **Vintage**: Estilo antigo e nostálgico
 
253
  """)
254
 
 
255
  gr.HTML("""
256
  <div style="text-align: center; margin-top: 20px; padding: 15px; background: #f8f9fa; border-radius: 8px;">
257
+ <p style="margin: 0; color: #666;">🎨 Image Colorizer - Photoshop AI Ecosystem</p>
 
 
 
 
 
258
  </div>
259
  """)
260
 
261
+ # Event handlers
262
+ style_realistic.click(fn=lambda: "realistic", outputs=[style_selected])
263
+ style_vibrant.click(fn=lambda: "vibrant", outputs=[style_selected])
264
+ style_vintage.click(fn=lambda: "vintage", outputs=[style_selected])
 
 
 
 
 
 
 
 
 
 
 
265
 
 
266
  def process_colorization(image, style, intensity):
 
 
267
  if image is None:
268
  return None, None, None, "❌ Por favor, carregue uma imagem primeiro"
269
 
 
270
  valid_img, msg = validate_image(image)
271
  if valid_img is None:
272
  return None, None, None, f"❌ {msg}"
273
 
 
274
  if max(valid_img.size) > MAX_IMAGE_SIZE:
275
  valid_img = resize_image(valid_img, MAX_IMAGE_SIZE)
276
 
 
277
  colorized = apply_colorization(valid_img, style, intensity)
 
 
278
  comparison = create_comparison(valid_img, colorized)
279
 
 
280
  status = f"""
281
  ✅ **Colorização aplicada com sucesso!**
282
 
283
  **Detalhes:**
284
  • Estilo: {style.title()}
285
  • Intensidade: {intensity*100:.0f}%
286
+ • Tamanho: {valid_img.size[0]}×{valid_img.size[1]}px
 
 
 
287
  """
288
 
289
  return valid_img, colorized, comparison, status
 
294
  outputs=[original_output, colorized_output, comparison_output, status_output]
295
  )
296
 
297
+ download_colorized.click(fn=save_image, inputs=[colorized_output], outputs=[download_file])
298
+ download_comparison.click(fn=save_image, inputs=[comparison_output], outputs=[download_file])
 
 
 
 
 
 
 
 
 
 
299
 
 
300
  def clear_on_upload(image):
301
  if image is None:
302
  return None, None, None, "**Status:** Aguardando imagem..."
 
305
  if valid_img is None:
306
  return None, None, None, f"❌ {msg}"
307
 
308
+ status = f"✅ **Imagem carregada!** ({valid_img.size[0]}×{valid_img.size[1]}px)\n\nEscolha um estilo e clique em Colorizar."
 
 
 
 
 
 
 
 
 
 
 
 
309
  return None, None, None, status
310
 
311
  image_input.change(
 
314
  outputs=[original_output, colorized_output, comparison_output, status_output]
315
  )
316
 
317
+ if __name__ == "__main__":
318
+ log_message("✅ Iniciando aplicação...")
319
+ demo.launch()