Daniel251's picture
Update app.py
dc57a0b verified
Raw
History Blame Contribute Delete
19 kB
"""
🎨 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()