Spaces:
Sleeping
Sleeping
| """ | |
| 🎨 Super Resolution | |
| Aumenta resolução e qualidade de imagens usando IA | |
| """ | |
| import gradio as gr | |
| from PIL import Image, ImageDraw, ImageFont, ImageEnhance | |
| import numpy as np | |
| import tempfile | |
| import traceback | |
| import sys | |
| import os | |
| import time | |
| from pathlib import Path | |
| print("=" * 60) | |
| print("🚀 INICIANDO APP 4: SUPER RESOLUTION") | |
| print(f"Python version: {sys.version}") | |
| print(f"Gradio version: {gr.__version__}") | |
| print("=" * 60) | |
| # ========== CONFIGURAÇÃO ========== | |
| # Detectar ambiente e instalar dependências necessárias | |
| try: | |
| import torch | |
| print(f"✅ PyTorch {torch.__version__} disponível") | |
| print(f"✅ CUDA available: {torch.cuda.is_available()}") | |
| TORCH_AVAILABLE = True | |
| DEVICE = "cuda" if torch.cuda.is_available() else "cpu" | |
| except ImportError: | |
| print("⚠️ PyTorch não disponível - usando fallback PIL") | |
| TORCH_AVAILABLE = False | |
| DEVICE = "cpu" | |
| DEFAULT_SCALE_FACTOR = 2 | |
| MAX_INPUT_SIZE = 1024 # Tamanho máximo para entrada | |
| print(f"⚡ Device: {DEVICE}") | |
| print(f"📏 Tamanho máximo de entrada: {MAX_INPUT_SIZE}px") | |
| # Cache para o modelo | |
| _model_cache = None | |
| def load_model(): | |
| """Carrega o modelo de super resolução com fallbacks""" | |
| global _model_cache | |
| if _model_cache is not None: | |
| return _model_cache | |
| try: | |
| print("🔄 Tentando carregar modelo Stable Diffusion Upscaler...") | |
| if not TORCH_AVAILABLE: | |
| raise ImportError("PyTorch não disponível") | |
| from diffusers import StableDiffusionUpscalePipeline | |
| # Configurar dtype baseado no device | |
| torch_dtype = torch.float16 if DEVICE == "cuda" else torch.float32 | |
| model = StableDiffusionUpscalePipeline.from_pretrained( | |
| "stabilityai/stable-diffusion-x4-upscaler", | |
| torch_dtype=torch_dtype, | |
| safety_checker=None, | |
| requires_safety_checker=False | |
| ) | |
| # Otimizações | |
| if DEVICE == "cuda": | |
| model.enable_attention_slicing() | |
| model.enable_model_cpu_offload() | |
| else: | |
| model = model.to("cpu") | |
| try: | |
| model.enable_xformers_memory_efficient_attention() | |
| print("✅ xFormers habilitado") | |
| except: | |
| print("⚠️ xFormers não disponível") | |
| _model_cache = model | |
| print(f"✅ Modelo Stable Diffusion carregado em {DEVICE}") | |
| return model | |
| except Exception as e: | |
| print(f"⚠️ Erro ao carregar Stable Diffusion: {str(e)}") | |
| print("🔄 Usando fallback com upscaling clássico PIL...") | |
| # Fallback: usar PIL puro | |
| _model_cache = "PIL_FALLBACK" | |
| return _model_cache | |
| # ========== FUNÇÕES AUXILIARES ========== | |
| def log_step(step, message): | |
| """Log para debug""" | |
| timestamp = time.strftime("%H:%M:%S") | |
| print(f"[{timestamp}] 🔹 {step}: {message}") | |
| def validate_image(image): | |
| """Valida e prepara imagem para processamento""" | |
| try: | |
| if image is None: | |
| return None, "❌ Nenhuma imagem fornecida" | |
| # Converter tipos diferentes | |
| if isinstance(image, dict): | |
| if 'image' in image: | |
| img = Image.fromarray(image['image'].astype('uint8')) | |
| else: | |
| return None, "❌ Formato de imagem inválido" | |
| elif isinstance(image, np.ndarray): | |
| img = Image.fromarray(image.astype('uint8')) | |
| elif isinstance(image, str): | |
| img = Image.open(image) | |
| else: | |
| img = image | |
| # Validar formato | |
| if img.mode not in ['RGB', 'RGBA', 'L']: | |
| img = img.convert('RGB') | |
| elif img.mode == 'RGBA': | |
| # Converter RGBA para RGB | |
| background = Image.new('RGB', img.size, (255, 255, 255)) | |
| background.paste(img, mask=img.split()[3] if len(img.split()) == 4 else None) | |
| img = background | |
| elif img.mode == 'L': | |
| img = img.convert('RGB') | |
| log_step("VALIDATE", f"Imagem: {img.size}px, {img.mode}") | |
| # Verificar tamanho mínimo | |
| if min(img.size) < 16: | |
| return None, "❌ Imagem muito pequena (mínimo 16px)" | |
| return img, "✅ Imagem validada" | |
| except Exception as e: | |
| error_msg = f"❌ Erro na validação: {str(e)}" | |
| log_step("VALIDATE_ERROR", error_msg) | |
| return None, error_msg | |
| def prepare_image_for_model(image, max_size=MAX_INPUT_SIZE): | |
| """Prepara imagem para o modelo""" | |
| try: | |
| # Se imagem for muito grande, redimensionar mantendo aspect ratio | |
| if max(image.size) > max_size: | |
| log_step("RESIZE", f"Redimensionando de {image.size} para máximo {max_size}px") | |
| ratio = max_size / max(image.size) | |
| new_width = int(image.width * ratio) | |
| new_height = int(image.height * ratio) | |
| image = image.resize((new_width, new_height), Image.Resampling.LANCZOS) | |
| # Garantir RGB | |
| if image.mode != 'RGB': | |
| image = image.convert('RGB') | |
| return image | |
| except Exception as e: | |
| log_step("PREPARE_ERROR", str(e)) | |
| return image | |
| def upscale_pil_fallback(image, scale_factor): | |
| """Fallback usando PIL puro com técnicas avançadas""" | |
| try: | |
| log_step("PIL_FALLBACK", f"Usando upscaling PIL {scale_factor}x") | |
| original_size = image.size | |
| target_size = (int(original_size[0] * scale_factor), int(original_size[1] * scale_factor)) | |
| # Usar Lanczos para melhor qualidade | |
| upscaled = image.resize(target_size, Image.Resampling.LANCZOS) | |
| # Aplicar sharpening | |
| enhancer = ImageEnhance.Sharpness(upscaled) | |
| upscaled = enhancer.enhance(1.5) | |
| # Aplicar contrast enhancement | |
| enhancer = ImageEnhance.Contrast(upscaled) | |
| upscaled = enhancer.enhance(1.1) | |
| log_step("PIL_FALLBACK", "Upscaling PIL completo") | |
| return upscaled | |
| except Exception as e: | |
| log_step("PIL_FALLBACK_ERROR", str(e)) | |
| raise | |
| # ========== FUNÇÃO PRINCIPAL ========== | |
| def upscale_image(image, scale_factor, prompt="", strength=0.75, progress=gr.Progress()): | |
| """Aumenta resolução da imagem""" | |
| log_step("START", f"Iniciando upscale {scale_factor}x") | |
| try: | |
| # Validar imagem | |
| progress(0.1, desc="Validando imagem...") | |
| img, validation_msg = validate_image(image) | |
| if img is None: | |
| return None, None, validation_msg | |
| original_size = img.size | |
| log_step("ORIGINAL", f"Tamanho original: {original_size}") | |
| # Preparar imagem | |
| progress(0.2, desc="Preparando imagem...") | |
| img = prepare_image_for_model(img) | |
| prepared_size = img.size | |
| log_step("PREPARED", f"Tamanho preparado: {prepared_size}") | |
| # Carregar modelo | |
| progress(0.3, desc="Carregando modelo...") | |
| model = load_model() | |
| start_time = time.time() | |
| # Processar baseado no modelo disponível | |
| if model == "PIL_FALLBACK": | |
| progress(0.5, desc=f"Aplicando upscale PIL {scale_factor}x...") | |
| result = upscale_pil_fallback(img, scale_factor) | |
| model_name = "PIL Lanczos + Enhancement" | |
| else: | |
| # Usar Stable Diffusion | |
| progress(0.5, desc=f"Aplicando IA upscale {scale_factor}x...") | |
| if not prompt: | |
| prompt = "high quality, detailed, sharp, clear" | |
| log_step("PROCESS", f"Prompt: {prompt[:50]}...") | |
| # Processar com diffusers | |
| img_np = np.array(img) | |
| result = model( | |
| prompt=prompt, | |
| image=img_np, | |
| num_inference_steps=20, | |
| guidance_scale=7.5, | |
| noise_level=20 if scale_factor == 4 else 10 | |
| ).images[0] | |
| # Garantir scale correto | |
| expected_size = (int(prepared_size[0] * scale_factor), int(prepared_size[1] * scale_factor)) | |
| if result.size != expected_size: | |
| result = result.resize(expected_size, Image.Resampling.LANCZOS) | |
| model_name = "Stable Diffusion x4 Upscaler" | |
| process_time = time.time() - start_time | |
| log_step("TIME", f"Processamento: {process_time:.1f}s") | |
| final_size = result.size | |
| log_step("FINAL", f"Tamanho final: {final_size}") | |
| # Estatísticas | |
| size_increase = (final_size[0] * final_size[1]) / (original_size[0] * original_size[1]) | |
| megapixels_in = (original_size[0] * original_size[1]) / 1_000_000 | |
| megapixels_out = (final_size[0] * final_size[1]) / 1_000_000 | |
| status_msg = f""" | |
| ✅ Super Resolução aplicada com sucesso! | |
| 📊 Estatísticas: | |
| • Tamanho original: {original_size[0]} × {original_size[1]}px ({megapixels_in:.1f} MP) | |
| • Tamanho final: {final_size[0]} × {final_size[1]}px ({megapixels_out:.1f} MP) | |
| • Fator de aumento: {scale_factor}x | |
| • Aumento de área: {size_increase:.1f}x | |
| • Tempo de processamento: {process_time:.1f}s | |
| • Modelo: {model_name} | |
| 💡 Dica: Compare as abas para ver a diferença! | |
| """ | |
| progress(1.0, desc="Completo!") | |
| return img, result, status_msg | |
| except Exception as e: | |
| error_msg = f"❌ Erro no processamento: {str(e)}" | |
| log_step("ERROR", error_msg) | |
| print(traceback.format_exc()) | |
| return None, None, error_msg | |
| def compare_images(original, upscaled, scale_factor): | |
| """Cria comparação lado a lado""" | |
| try: | |
| if original is None or upscaled is None: | |
| return None | |
| # Redimensionar original para comparação justa | |
| comparison_height = min(800, upscaled.height) | |
| comparison_width = int(original.width * (comparison_height / original.height)) | |
| original_resized = original.resize((comparison_width, comparison_height), Image.Resampling.LANCZOS) | |
| # Redimensionar upscaled proporcionalmente | |
| upscaled_width = int(upscaled.width * (comparison_height / upscaled.height)) | |
| upscaled_resized = upscaled.resize((upscaled_width, comparison_height), Image.Resampling.LANCZOS) | |
| # Criar canvas | |
| total_width = original_resized.width + upscaled_resized.width + 30 | |
| canvas_height = comparison_height + 50 | |
| comparison = Image.new('RGB', (total_width, canvas_height), color=(245, 245, 245)) | |
| draw = ImageDraw.Draw(comparison) | |
| # Usar fonte padrão | |
| try: | |
| font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 16) | |
| except: | |
| font = ImageFont.load_default() | |
| # Labels | |
| draw.text((10, 10), "ORIGINAL", fill=(80, 80, 80), font=font) | |
| draw.text((original_resized.width + 40, 10), f"UPSCALED {scale_factor}X", fill=(0, 120, 0), font=font) | |
| # Linha divisória | |
| line_x = original_resized.width + 15 | |
| draw.line([(line_x, 0), (line_x, canvas_height)], fill=(200, 200, 200), width=3) | |
| # Colar imagens | |
| comparison.paste(original_resized, (0, 40)) | |
| comparison.paste(upscaled_resized, (original_resized.width + 30, 40)) | |
| return comparison | |
| except Exception as e: | |
| log_step("COMPARE_ERROR", str(e)) | |
| return None | |
| def save_image(image): | |
| """Salva imagem para download""" | |
| try: | |
| if image is None: | |
| return None | |
| timestamp = time.strftime("%Y%m%d_%H%M%S") | |
| temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".png", prefix=f"upscaled_{timestamp}_") | |
| image.save(temp_file.name, "PNG", optimize=True) | |
| log_step("SAVE", f"Imagem salva") | |
| return temp_file.name | |
| except Exception as e: | |
| log_step("SAVE_ERROR", str(e)) | |
| return None | |
| # ========== INTERFACE GRADIO ========== | |
| print("🎨 Criando interface...") | |
| with gr.Blocks(title="🎨 Super Resolution", theme=gr.themes.Soft()) as demo: | |
| gr.HTML(""" | |
| <div style="text-align: center; padding: 25px; background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%); | |
| border-radius: 12px; color: white; margin-bottom: 25px; box-shadow: 0 6px 20px rgba(0,0,0,0.15);"> | |
| <h1 style="margin: 0; font-size: 2.6em;">🔍 Super Resolution</h1> | |
| <p style="margin: 10px 0 0 0; font-size: 1.3em;">App 4 do Photoshop AI Ecosystem</p> | |
| <p style="margin: 8px 0 0 0; font-size: 1.1em;">Aumente a resolução e qualidade de suas imagens com IA</p> | |
| </div> | |
| """) | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| gr.Markdown("### 📤 Upload da Imagem") | |
| image_input = gr.Image( | |
| type="pil", | |
| label="Arraste ou clique para selecionar", | |
| sources=["upload", "clipboard"], | |
| height=250 | |
| ) | |
| gr.Markdown("### ⚙️ Configurações") | |
| with gr.Row(): | |
| scale_2x = gr.Button("2×", variant="secondary", scale=1) | |
| scale_3x = gr.Button("3×", variant="secondary", scale=1) | |
| scale_4x = gr.Button("4×", variant="primary", scale=1) | |
| scale_factor = gr.Number(value=4, visible=False) | |
| prompt_input = gr.Textbox( | |
| label="Prompt (opcional)", | |
| value="high quality, detailed, sharp, clear", | |
| placeholder="Descreva o que deseja realçar" | |
| ) | |
| strength_slider = gr.Slider( | |
| minimum=0.1, | |
| maximum=1.0, | |
| value=0.75, | |
| step=0.05, | |
| label="Força do upscale", | |
| info="Controla intensidade da transformação" | |
| ) | |
| process_btn = gr.Button( | |
| "🚀 Aplicar Super Resolution", | |
| variant="primary", | |
| size="lg" | |
| ) | |
| with gr.Column(scale=1): | |
| gr.Markdown("### 📊 Resultados") | |
| status_output = gr.Markdown( | |
| value="### 📝 Status\nAguardando imagem..." | |
| ) | |
| with gr.Tabs(): | |
| with gr.TabItem("🔄 Comparação"): | |
| comparison_output = gr.Image( | |
| type="pil", | |
| label="Comparação lado a lado", | |
| height=350 | |
| ) | |
| with gr.TabItem("🔍 Original"): | |
| original_output = gr.Image( | |
| type="pil", | |
| label="Imagem original", | |
| height=350 | |
| ) | |
| with gr.TabItem("✨ Upscaled"): | |
| upscaled_output = gr.Image( | |
| type="pil", | |
| label="Imagem melhorada", | |
| height=350 | |
| ) | |
| with gr.Row(): | |
| download_upscaled = gr.Button("📥 Baixar Upscaled", variant="secondary") | |
| download_comparison = gr.Button("📊 Baixar Comparação", variant="secondary") | |
| download_file = gr.File(label="Arquivo para download", interactive=False) | |
| with gr.Accordion("📖 Guia de Uso", open=False): | |
| gr.Markdown(""" | |
| ## 🚀 Como usar: | |
| 1. **Carregue uma imagem** que deseja melhorar | |
| 2. **Escolha o fator** (2×, 3× ou 4×) | |
| 3. **Ajuste opcionalmente** prompt e força | |
| 4. **Clique em "Aplicar"** | |
| 5. **Compare** nas abas | |
| 6. **Baixe** o resultado | |
| ## 💡 Dicas: | |
| - Use imagens entre 256×256 e 1024×1024 pixels | |
| - Imagens menores se beneficiam mais do upscale | |
| - O fator 4× é ideal para imagens pequenas | |
| - Primeira execução é mais lenta (carrega modelo) | |
| ## ⚠️ Informações: | |
| - Tempo: 30-90 segundos dependendo do hardware | |
| - Formatos: JPG, PNG, WEBP | |
| - Processamento: 100% local/privado | |
| """) | |
| gr.HTML(f""" | |
| <div style="text-align: center; margin-top: 20px; padding: 15px; | |
| background: #f5f7fa; border-radius: 8px;"> | |
| <p style="margin: 0; font-weight: bold;"> | |
| 🔍 Super Resolution - Photoshop AI App 4 | |
| </p> | |
| <p style="margin: 5px 0 0 0; color: #666; font-size: 0.9em;"> | |
| Gradio {gr.__version__} • {'PyTorch ' + torch.__version__ if TORCH_AVAILABLE else 'PIL Fallback'} • {DEVICE.upper()} | |
| </p> | |
| </div> | |
| """) | |
| # Event handlers | |
| scale_2x.click(fn=lambda: 2, outputs=[scale_factor]) | |
| scale_3x.click(fn=lambda: 3, outputs=[scale_factor]) | |
| scale_4x.click(fn=lambda: 4, outputs=[scale_factor]) | |
| def process_wrapper(image, scale, prompt, strength): | |
| if image is None: | |
| return None, None, None, "❌ Carregue uma imagem primeiro" | |
| original, upscaled, status = upscale_image(image, scale, prompt, strength) | |
| if upscaled is None: | |
| return None, None, None, status | |
| comparison = compare_images(original, upscaled, scale) | |
| return original, upscaled, comparison, status | |
| process_btn.click( | |
| fn=process_wrapper, | |
| inputs=[image_input, scale_factor, prompt_input, strength_slider], | |
| outputs=[original_output, upscaled_output, comparison_output, status_output] | |
| ) | |
| download_upscaled.click( | |
| fn=save_image, | |
| inputs=[upscaled_output], | |
| outputs=[download_file] | |
| ) | |
| download_comparison.click( | |
| fn=save_image, | |
| inputs=[comparison_output], | |
| outputs=[download_file] | |
| ) | |
| def on_image_change(image): | |
| if image is None: | |
| return None, None, None, "### 📝 Status\nAguardando imagem..." | |
| img, msg = validate_image(image) | |
| if img is None: | |
| return None, None, None, msg | |
| w, h = img.size | |
| mp = (w * h) / 1_000_000 | |
| return None, None, None, f""" | |
| ### 📝 Status | |
| ✅ Imagem carregada: {w}×{h}px ({mp:.1f} MP) | |
| Escolha o fator de aumento e clique em "Aplicar"! | |
| """ | |
| image_input.change( | |
| fn=on_image_change, | |
| inputs=[image_input], | |
| outputs=[original_output, upscaled_output, comparison_output, status_output] | |
| ) | |
| print("=" * 60) | |
| print("✅ APP 4: SUPER RESOLUTION PRONTO!") | |
| print("=" * 60) | |
| if __name__ == "__main__": | |
| demo.launch() |