""" 🌤️ Sky Replacer Substitui o céu em imagens com opções profissionais Autor: Daniel251 Versão: 2.0.0 Data: 2026 """ import gradio as gr from PIL import Image, ImageDraw, ImageFilter, ImageEnhance import numpy as np import cv2 import tempfile import time import os import sys import math import random import colorsys from io import BytesIO import warnings warnings.filterwarnings('ignore') print("=" * 60) print("🚀 INICIANDO APP 10: SKY REPLACER") print(f"Python version: {sys.version}") print(f"Pillow version: {Image.__version__}") print("=" * 60) # ========== CONFIGURAÇÃO ========== MAX_IMAGE_SIZE = 1024 SKY_LIBRARY = { "sunny_day": "☀️ Dia Ensolarado", "blue_sky": "🔵 Céu Azul Limpo", "cloudy": "☁️ Céu Nublado", "stormy": "⛈️ Tempestade", "sunset": "🌅 Pôr do Sol", "sunrise": "🌇 Nascer do Sol", "golden_hour": "✨ Hora Dourada", "night_sky": "🌙 Noite Estrelada", "northern_lights": "🌈 Aurora Boreal", "space": "🚀 Espaço Sideral", "foggy": "🌫️ Nebuloso", "rainbow": "🌈 Arco-Íris", "dramatic": "🎭 Dramático", "pastel": "🎨 Pastel", "gradient": "🔄 Gradiente" } print(f"📏 Tamanho máximo: {MAX_IMAGE_SIZE}px") print(f"🌤️ Céus disponíveis: {len(SKY_LIBRARY)}") # ========== FUNÇÕES AUXILIARES ========== def log_message(message): """Log para debug""" timestamp = time.strftime("%H:%M:%S") print(f"[{timestamp}] {message}") def validate_image(image): """Valida e prepara imagem""" try: if image is None: return None, "❌ Nenhuma imagem fornecida" # Converter para PIL Image if isinstance(image, np.ndarray): img = Image.fromarray(image) elif isinstance(image, Image.Image): img = image else: return None, "❌ Tipo de imagem desconhecido" # Verificar tamanho if max(img.size) > 4000: img = resize_image(img, 4000) log_message(f"Imagem redimensionada para {img.size}") if min(img.size) < 50: return None, "❌ Imagem muito pequena (<50px)." log_message(f"✅ Imagem válida: {img.size}px, {img.mode}") return img, "ok" except Exception as e: error_msg = f"❌ Erro na validação: {str(e)}" log_message(error_msg) return None, error_msg def resize_image(image, max_size): """Redimensiona mantendo aspect ratio""" if max(image.size) <= max_size: return image ratio = max_size / max(image.size) new_width = int(image.width * ratio) new_height = int(image.height * ratio) log_message(f"Redimensionando: {image.size} -> ({new_width}, {new_height})") return image.resize((new_width, new_height), Image.Resampling.LANCZOS) def detect_sky_region(image): """Detecta automaticamente a região do céu na imagem""" try: # Converter para array numpy img_array = np.array(image.convert('RGB')) height, width = img_array.shape[:2] # Método 1: Baseado em cores (azul/branco) hsv = cv2.cvtColor(img_array, cv2.COLOR_RGB2HSV) # Definição de cores de céu lower_blue1 = np.array([90, 50, 50]) upper_blue1 = np.array([130, 255, 255]) lower_blue2 = np.array([100, 40, 40]) upper_blue2 = np.array([140, 255, 255]) # Céu nublado (branco/cinza) lower_white = np.array([0, 0, 180]) upper_white = np.array([180, 50, 255]) # Criar máscaras mask_blue1 = cv2.inRange(hsv, lower_blue1, upper_blue1) mask_blue2 = cv2.inRange(hsv, lower_blue2, upper_blue2) mask_white = cv2.inRange(hsv, lower_white, upper_white) # Combinar máscaras sky_mask = cv2.bitwise_or(mask_blue1, mask_blue2) sky_mask = cv2.bitwise_or(sky_mask, mask_white) # Método 2: Baseado em posição (céu geralmente está no topo) position_mask = np.zeros((height, width), dtype=np.uint8) for y in range(height): weight = max(0, 1.0 - (y / height) * 1.5) position_mask[y, :] = int(255 * weight) # Método 3: Detecção de bordas para encontrar horizonte gray = cv2.cvtColor(img_array, cv2.COLOR_RGB2GRAY) edges = cv2.Canny(gray, 50, 150) lines = cv2.HoughLinesP(edges, 1, np.pi/180, threshold=50, minLineLength=width//4, maxLineGap=10) horizon_y = height * 0.3 if lines is not None: horizontal_lines = [] for line in lines: x1, y1, x2, y2 = line[0] angle = abs(np.arctan2(y2-y1, x2-x1) * 180 / np.pi) if abs(angle) < 10 or abs(angle - 180) < 10: horizontal_lines.append(min(y1, y2)) if horizontal_lines: horizon_y = np.median(horizontal_lines) # Criar máscara final combined_mask = cv2.addWeighted(sky_mask, 0.6, position_mask, 0.4, 0) combined_mask[int(horizon_y):, :] = 0 # Operações morfológicas para limpar kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5)) combined_mask = cv2.morphologyEx(combined_mask, cv2.MORPH_CLOSE, kernel) combined_mask = cv2.morphologyEx(combined_mask, cv2.MORPH_OPEN, kernel) # Preencher buracos e suavizar combined_mask = cv2.morphologyEx(combined_mask, cv2.MORPH_CLOSE, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (15,15))) combined_mask = cv2.GaussianBlur(combined_mask, (11, 11), 0) # Se a máscara for muito pequena, usar padrão if np.sum(combined_mask > 127) < 100: log_message("⚠️ Céu não detectado automaticamente, usando método padrão") combined_mask = np.zeros((height, width), dtype=np.uint8) combined_mask[:int(height*0.4), :] = 255 sky_percentage = np.sum(combined_mask > 127) / (width * height) * 100 log_message(f"✅ Céu detectado: {sky_percentage:.1f}% da imagem") return combined_mask except Exception as e: log_message(f"❌ Erro na detecção de céu: {str(e)}") height, width = image.size[1], image.size[0] mask = np.zeros((height, width), dtype=np.uint8) mask[:int(height*0.4), :] = 255 return mask # ========== GERADORES DE CÉU ========== def create_sunny_sky(size): """Cria céu de dia ensolarado""" width, height = size img = Image.new('RGB', size, (135, 206, 235)) draw = ImageDraw.Draw(img) # Gradiente de azul for y in range(height): ratio = y / height r = int(135 * (1 - ratio) + 255 * ratio) g = int(206 * (1 - ratio) + 250 * ratio) b = int(235 * (1 - ratio) + 255 * ratio) draw.line([(0, y), (width, y)], fill=(r, g, b)) # Adicionar sol sun_size = min(width, height) // 10 sun_x = width * 0.8 sun_y = height * 0.2 # Brilho do sol for i in range(5, 0, -1): radius = sun_size * (1 + i * 0.3) temp_img = Image.new('RGBA', size, (0, 0, 0, 0)) temp_draw = ImageDraw.Draw(temp_img) alpha = int(50 / i) temp_draw.ellipse([sun_x-radius, sun_y-radius, sun_x+radius, sun_y+radius], fill=(255, 255, 200, alpha)) img = Image.alpha_composite(img.convert('RGBA'), temp_img).convert('RGB') # Sol principal sun_img = Image.new('RGBA', size, (0, 0, 0, 0)) sun_draw = ImageDraw.Draw(sun_img) sun_draw.ellipse([sun_x-sun_size, sun_y-sun_size, sun_x+sun_size, sun_y+sun_size], fill=(255, 255, 100), outline=(255, 200, 50), width=3) img = Image.alpha_composite(img.convert('RGBA'), sun_img).convert('RGB') # Adicionar nuvens simples img = add_simple_clouds(img, count=5) return img def create_blue_sky(size): """Cria céu azul limpo""" width, height = size img = Image.new('RGB', size, (0, 0, 0)) # Gradiente azul profundo for y in range(height): ratio = y / height r = int(30 * ratio) g = int(144 * (0.7 + 0.3 * ratio)) b = int(255 * (0.8 + 0.2 * ratio)) for x in range(width): variation = math.sin(x * 0.01) * 0.05 + math.cos(y * 0.005) * 0.03 vr = int(r * (1 + variation)) vg = int(g * (1 + variation)) vb = int(b * (1 + variation)) img.putpixel((x, y), (vr, vg, vb)) return img def create_cloudy_sky(size): """Cria céu nublado""" width, height = size img = Image.new('RGB', size, (200, 220, 240)) # Base de nuvens for y in range(height): ratio = y / height base_r = 200 + int(30 * ratio) base_g = 220 + int(20 * ratio) base_b = 240 + int(10 * ratio) for x in range(width): noise = (math.sin(x * 0.01) + math.sin(y * 0.007) + math.sin((x + y) * 0.005)) / 3 r = max(100, min(255, base_r + int(noise * 40))) g = max(120, min(255, base_g + int(noise * 30))) b = max(140, min(255, base_b + int(noise * 20))) img.putpixel((x, y), (r, g, b)) # Adicionar mais nuvens img = add_simple_clouds(img, count=8, cloud_type="cumulus") return img def create_stormy_sky(size): """Cria céu de tempestade""" width, height = size img = Image.new('RGB', size, (50, 50, 70)) draw = ImageDraw.Draw(img) # Gradiente dramático for y in range(height): ratio = y / height r = int(30 + 70 * ratio) g = int(30 + 60 * ratio) b = int(50 + 80 * ratio) draw.line([(0, y), (width, y)], fill=(r, g, b)) # Nuvens escuras img_array = np.array(img) for i in range(5): cloud_x = random.randint(0, width) cloud_y = random.randint(0, height // 2) cloud_size = random.randint(width // 4, width // 2) # Escurecer área da nuvem for dy in range(-cloud_size//2, cloud_size//2): for dx in range(-cloud_size//2, cloud_size//2): x = cloud_x + dx y = cloud_y + dy if 0 <= x < width and 0 <= y < height: dist = math.sqrt(dx*dx + dy*dy) if dist < cloud_size // 2: darkness = 1.0 - (dist / (cloud_size // 2)) * 0.7 img_array[y, x] = (img_array[y, x] * darkness).astype(np.uint8) img = Image.fromarray(img_array) return img def create_sunset_sky(size): """Cria céu de pôr do sol""" width, height = size img = Image.new('RGB', size, (0, 0, 0)) # Gradiente de pôr do sol for y in range(height): ratio = y / height if ratio < 0.3: r = 255 g = int(100 + 100 * (ratio / 0.3)) b = int(50 * (ratio / 0.3)) elif ratio < 0.6: sub_ratio = (ratio - 0.3) / 0.3 r = int(255 - 100 * sub_ratio) g = int(200 - 80 * sub_ratio) b = int(100 + 80 * sub_ratio) else: sub_ratio = (ratio - 0.6) / 0.4 r = int(155 - 100 * sub_ratio) g = int(120 - 70 * sub_ratio) b = int(180 + 75 * sub_ratio) for x in range(width): h_variation = math.sin(x * 0.005 + y * 0.002) * 0.1 vr = max(0, min(255, int(r * (1 + h_variation)))) vg = max(0, min(255, int(g * (1 + h_variation)))) vb = max(0, min(255, int(b * (1 + h_variation)))) img.putpixel((x, y), (vr, vg, vb)) return img def create_sunrise_sky(size): """Cria céu de nascer do sol""" width, height = size img = Image.new('RGB', size, (0, 0, 0)) draw = ImageDraw.Draw(img) for y in range(height): ratio = y / height if ratio < 0.4: r = 255 g = int(180 + 50 * (ratio / 0.4)) b = int(100 + 100 * (ratio / 0.4)) elif ratio < 0.7: sub_ratio = (ratio - 0.4) / 0.3 r = int(255 - 80 * sub_ratio) g = int(230 - 60 * sub_ratio) b = int(200 + 30 * sub_ratio) else: sub_ratio = (ratio - 0.7) / 0.3 r = int(175 - 100 * sub_ratio) g = int(170 - 70 * sub_ratio) b = int(230 + 25 * sub_ratio) draw.line([(0, y), (width, y)], fill=(r, g, b)) return img def create_golden_hour_sky(size): """Cria céu da hora dourada""" width, height = size img = Image.new('RGB', size, (0, 0, 0)) draw = ImageDraw.Draw(img) for y in range(height): ratio = y / height if ratio < 0.5: r = 255 g = int(200 + 55 * (ratio / 0.5)) b = int(100 + 100 * (ratio / 0.5)) else: sub_ratio = (ratio - 0.5) / 0.5 r = 255 g = int(255 - 100 * sub_ratio) b = int(200 - 150 * sub_ratio) brightness = 1.0 + 0.3 * math.sin(ratio * math.pi) r = min(255, int(r * brightness)) g = min(255, int(g * brightness)) b = min(255, int(b * brightness)) draw.line([(0, y), (width, y)], fill=(r, g, b)) return img def create_night_sky(size): """Cria céu noturno estrelado""" width, height = size img = Image.new('RGB', size, (10, 10, 30)) draw = ImageDraw.Draw(img) # Gradiente do horizonte for y in range(height): ratio = y / height r = int(10 + 20 * ratio) g = int(10 + 30 * ratio) b = int(30 + 50 * ratio) draw.line([(0, y), (width, y)], fill=(r, g, b)) # Estrelas for _ in range(200): x = random.randint(0, width) y = random.randint(0, height//1.5) brightness = random.choice([0.3, 0.5, 0.7, 0.9, 1.0]) size = 1 + int(brightness * 2) color = (int(255*brightness), int(255*brightness), int(255*brightness)) draw.ellipse([x-size, y-size, x+size, y+size], fill=color) return img def create_northern_lights_sky(size): """Cria céu com aurora boreal""" width, height = size img = Image.new('RGB', size, (10, 15, 40)) draw = ImageDraw.Draw(img) for y in range(height): ratio = y / height r = int(10 + 15 * ratio) g = int(15 + 20 * ratio) b = int(40 + 30 * ratio) draw.line([(0, y), (width, y)], fill=(r, g, b)) # Efeito de aurora simplificado aurora_img = Image.new('RGBA', size, (0, 0, 0, 0)) aurora_draw = ImageDraw.Draw(aurora_img) for y in range(height//4, height//2, 5): for x in range(0, width, 10): wave = int(20 * math.sin(x * 0.02 + y * 0.01)) color = random.choice([(0, 255, 150), (100, 0, 255), (0, 200, 255)]) alpha = random.randint(30, 80) aurora_draw.ellipse([x-15, y+wave-5, x+15, y+wave+5], fill=(*color, alpha)) img = Image.alpha_composite(img.convert('RGBA'), aurora_img).convert('RGB') return img def create_space_sky(size): """Cria céu espacial""" width, height = size img = Image.new('RGB', size, (0, 0, 0)) draw = ImageDraw.Draw(img) # Estrelas for _ in range(300): x = random.randint(0, width) y = random.randint(0, height) brightness = random.uniform(0.3, 1.0) size_star = 1 + int(brightness * 2) color = (int(255*brightness), int(255*brightness), int(255*brightness)) draw.ellipse([x-size_star, y-size_star, x+size_star, y+size_star], fill=color) return img def create_foggy_sky(size): """Cria céu nebuloso""" width, height = size img = Image.new('RGB', size, (200, 210, 220)) draw = ImageDraw.Draw(img) for y in range(height): ratio = y / height r = int(220 - 40 * ratio) g = int(225 - 30 * ratio) b = int(230 - 20 * ratio) draw.line([(0, y), (width, y)], fill=(r, g, b)) return img def create_rainbow_sky(size): """Cria céu com arco-íris""" width, height = size img = create_sunny_sky(size) draw = ImageDraw.Draw(img) # Arco-íris simplificado rainbow_colors = [(255,0,0), (255,127,0), (255,255,0), (0,255,0), (0,0,255), (75,0,130), (148,0,211)] center_x, center_y = width // 2, height * 1.5 for i, color in enumerate(rainbow_colors): radius = int(height * 0.8 - i * 10) for angle in np.linspace(-0.3, 0.3, 50): x = int(center_x + radius * math.sin(angle * math.pi)) y = int(center_y + radius * math.cos(angle * math.pi)) if 0 <= x < width and 0 <= y < height: draw.point((x, y), fill=color) return img def create_dramatic_sky(size): """Cria céu dramático""" width, height = size img = Image.new('RGB', size, (60, 80, 120)) draw = ImageDraw.Draw(img) for y in range(height): ratio = y / height r = int(80 - 40 * ratio) g = int(60 + 40 * ratio) b = int(120 + 60 * ratio) variation = math.sin(y * 0.01) * 0.1 r = int(r * (1 + variation)) g = int(g * (1 + variation)) b = int(b * (1 + variation)) draw.line([(0, y), (width, y)], fill=(r, g, b)) return img def create_pastel_sky(size): """Cria céu pastel suave""" width, height = size img = Image.new('RGB', size, (0, 0, 0)) draw = ImageDraw.Draw(img) pastel_colors = [(255, 200, 220), (220, 230, 255), (220, 255, 220), (255, 240, 200)] for y in range(height): ratio = y / height color_idx = ratio * (len(pastel_colors) - 1) idx1 = int(color_idx) idx2 = min(idx1 + 1, len(pastel_colors) - 1) blend = color_idx - idx1 r1, g1, b1 = pastel_colors[idx1] r2, g2, b2 = pastel_colors[idx2] r = int(r1 * (1 - blend) + r2 * blend) g = int(g1 * (1 - blend) + g2 * blend) b = int(b1 * (1 - blend) + b2 * blend) draw.line([(0, y), (width, y)], fill=(r, g, b)) return img def create_gradient_sky(size): """Cria céu com gradiente""" width, height = size img = Image.new('RGB', size, (0, 0, 0)) draw = ImageDraw.Draw(img) # Gradiente azul para roxo for y in range(height): ratio = y / height r = int(100 + 155 * ratio) g = int(100 + 100 * ratio) b = int(255 - 100 * ratio) draw.line([(0, y), (width, y)], fill=(r, g, b)) return img def add_simple_clouds(base_image, count=5, cloud_type="cumulus"): """Adiciona nuvens simples à imagem""" img = base_image.copy() width, height = img.size if img.mode != 'RGBA': img = img.convert('RGBA') cloud_img = Image.new('RGBA', (width, height), (0, 0, 0, 0)) cloud_draw = ImageDraw.Draw(cloud_img) for _ in range(count): cloud_x = random.randint(0, width) cloud_y = random.randint(0, height // 2) cloud_size = random.randint(width // 8, width // 4) if cloud_type == "cumulus": # Nuvens redondas cloud_color = (255, 255, 255, 180) cloud_draw.ellipse([cloud_x-cloud_size, cloud_y-cloud_size//3, cloud_x+cloud_size, cloud_y+cloud_size//3], fill=cloud_color) else: # Nuvens alongadas cloud_color = (240, 240, 250, 150) for i in range(3): offset_x = random.randint(-cloud_size//3, cloud_size//3) offset_y = random.randint(-cloud_size//6, cloud_size//6) ellipse_size = cloud_size * random.uniform(0.3, 0.6) cloud_draw.ellipse([cloud_x+offset_x-ellipse_size, cloud_y+offset_y-ellipse_size//2, cloud_x+offset_x+ellipse_size, cloud_y+offset_y+ellipse_size//2], fill=cloud_color) # Aplicar suavização cloud_img = cloud_img.filter(ImageFilter.GaussianBlur(radius=3)) img = Image.alpha_composite(img, cloud_img) return img.convert('RGB') # ========== FUNÇÃO PRINCIPAL ========== def replace_sky(image, sky_type, intensity=0.8, auto_detect=True): """Substitui o céu na imagem""" try: log_message(f"Substituindo céu - Tipo: {sky_type}, Intensidade: {intensity}") # Validar imagem img, msg = validate_image(image) if img is None: return None, f"❌ {msg}", None # Redimensionar se necessário img = resize_image(img, MAX_IMAGE_SIZE) width, height = img.size # Detectar céu if auto_detect: sky_mask = detect_sky_region(img) log_message("✅ Detecção automática concluída") else: sky_mask = np.zeros((height, width), dtype=np.uint8) sky_mask[:int(height*0.4), :] = 255 log_message("⚠️ Usando máscara padrão") # Criar novo céu sky_generators = { "sunny_day": create_sunny_sky, "blue_sky": create_blue_sky, "cloudy": create_cloudy_sky, "stormy": create_stormy_sky, "sunset": create_sunset_sky, "sunrise": create_sunrise_sky, "golden_hour": create_golden_hour_sky, "night_sky": create_night_sky, "northern_lights": create_northern_lights_sky, "space": create_space_sky, "foggy": create_foggy_sky, "rainbow": create_rainbow_sky, "dramatic": create_dramatic_sky, "pastel": create_pastel_sky, "gradient": create_gradient_sky } generator = sky_generators.get(sky_type, create_sunny_sky) new_sky = generator((width, height)) # Converter máscara mask_pil = Image.fromarray(sky_mask).convert('L') mask_pil = mask_pil.filter(ImageFilter.GaussianBlur(radius=5)) # Ajustar intensidade if intensity != 1.0: mask_array = np.array(mask_pil).astype(float) mask_array = mask_array * intensity mask_pil = Image.fromarray(np.clip(mask_array, 0, 255).astype(np.uint8)) # Aplicar substituição result = img.copy() result.paste(new_sky, (0, 0), mask_pil) # Ajustar cores para integração if intensity > 0.5: # Suavizar transição edges = mask_pil.filter(ImageFilter.FIND_EDGES()) edges = edges.point(lambda x: 255 if x > 30 else 0) if np.any(np.array(edges) > 0): blurred_result = result.filter(ImageFilter.GaussianBlur(radius=2)) transition_mask = edges.filter(ImageFilter.GaussianBlur(radius=3)) transition_mask = transition_mask.point(lambda x: x // 2) result = Image.composite(blurred_result, result, transition_mask) log_message("✅ Céu substituído com sucesso") # Máscara para visualização mask_display = Image.fromarray(sky_mask).convert('RGB') mask_display = mask_display.resize((width//2, height//2)) status_msg = f"✅ Céu '{SKY_LIBRARY.get(sky_type, sky_type)}' aplicado!" return result, status_msg, mask_display except Exception as e: error_msg = f"❌ Erro ao substituir céu: {str(e)}" log_message(error_msg) import traceback traceback.print_exc() return None, error_msg, None def save_image(image, filename="sky_replaced.png"): """Salva imagem em arquivo temporário""" try: if image is None: return None temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".png") image.save(temp_file.name, "PNG", optimize=True) file_size = os.path.getsize(temp_file.name) // 1024 log_message(f"Imagem salva: {temp_file.name} ({file_size}KB)") return temp_file.name except Exception as e: log_message(f"❌ Erro ao salvar: {str(e)}") return None # ========== INTERFACE GRADIO ========== print("🌤️ Criando interface Sky Replacer...") custom_css = """ .gradio-container { max-width: 1400px !important; margin: 0 auto !important; } .header { text-align: center; margin-bottom: 30px; padding: 25px; background: linear-gradient(135deg, #36D1DC 0%, #5B86E5 100%); border-radius: 20px; color: white; } .header h1 { margin: 0; font-size: 2.8em; font-weight: 700; } .header p { margin: 10px 0 0 0; font-size: 1.2em; opacity: 0.9; } .sky-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 15px; margin: 20px 0; } .sky-card { background: white; border-radius: 15px; padding: 20px; text-align: center; cursor: pointer; transition: all 0.3s ease; border: 2px solid transparent; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); } .sky-card:hover { transform: translateY(-5px); box-shadow: 0 8px 15px rgba(54, 209, 220, 0.3); border-color: #36D1DC; } .sky-card.active { border-color: #5B86E5; background: #f0f9ff; } .sky-icon { font-size: 2.5em; margin-bottom: 10px; } .sky-name { font-weight: 600; color: #333; } .image-container { border-radius: 15px; overflow: hidden; border: 2px solid #e0e0e0; } .status-box { padding: 15px; border-radius: 10px; margin: 15px 0; font-weight: 500; } .status-success { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; } .status-error { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; } .download-btn { background: linear-gradient(135deg, #36D1DC 0%, #5B86E5 100%); color: white; border: none; padding: 12px 30px; border-radius: 25px; font-weight: 600; cursor: pointer; transition: all 0.3s ease; width: 100%; } .download-btn:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(54, 209, 220, 0.4); } .footer { text-align: center; margin-top: 30px; padding: 20px; color: #666; font-size: 0.9em; } """ # Interface principal def create_interface(): with gr.Blocks(title="🌤️ Sky Replacer - Photoshop AI App 10", theme=gr.themes.Soft(), css=custom_css) as demo: # Header gr.HTML(f"""
App 10 do Photoshop AI Ecosystem - Substituição profissional de céus
💡 Dica: Use intensidade mais baixa (0.3-0.5) para transições mais sutis