InstantTexture / app.py
CoelinhaJob's picture
Update app.py
c70ef7a verified
import os
import sys
import logging
# -----------------------------------------------------------------------------
# SEÇÃO 1: COMPATIBILIDADE DE SISTEMA (Polyfill para Python 3.13+)
# -----------------------------------------------------------------------------
# O Python 3.13 removeu o módulo 'audioop'. O Gradio e Pydub dependem dele.
# Este bloco tenta importar o substituto 'audioop-lts' e injetá-lo como 'audioop'.
try:
import audioop
except ImportError:
try:
import audioop_lts as audioop
sys.modules["audioop"] = audioop
print("Módulo 'audioop-lts' carregado com sucesso para compatibilidade Python 3.13.")
except ImportError:
print("AVISO CRÍTICO: 'audioop' não encontrado. Recursos de áudio falharão em Python 3.13+.")
# -----------------------------------------------------------------------------
# SEÇÃO 2: IMPORTAÇÕES E CONFIGURAÇÃO DE AMBIENTE
# -----------------------------------------------------------------------------
import gradio as gr
import spaces # Biblioteca essencial para gerenciamento de GPU em HF Spaces
import torch
from diffusers import StableDiffusionControlNetPipeline, ControlNetModel, UniPCMultistepScheduler
from PIL import Image
# Configuração de Logs para diagnóstico facilitado
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
logger.info(f"Python Version: {sys.version}")
logger.info(f"Gradio Version: {gr.__version__}")
logger.info(f"Pydantic Version: {sys.modules.get('pydantic', 'Not Loaded')}")
logger.info(f"CUDA Available: {torch.cuda.is_available()}")
# -----------------------------------------------------------------------------
# SEÇÃO 3: CARREGAMENTO DO MODELO (Cache Global)
# -----------------------------------------------------------------------------
# Carregar modelos fora da função de inferência para evitar recarregamento a cada requisição.
# Utiliza variáveis de ambiente ou IDs padrão.
MODEL_ID = "runwayml/stable-diffusion-v1-5"
CONTROLNET_ID = "lllyasviel/sd-controlnet-canny"
try:
if torch.cuda.is_available():
logger.info("Inicializando modelos na GPU...")
controlnet = ControlNetModel.from_pretrained(CONTROLNET_ID, torch_dtype=torch.float16)
pipe = StableDiffusionControlNetPipeline.from_pretrained(
MODEL_ID, controlnet=controlnet, torch_dtype=torch.float16
)
pipe.scheduler = UniPCMultistepScheduler.from_config(pipe.scheduler.config)
pipe.to("cuda")
pipe.enable_model_cpu_offload() # Otimização para Spaces com VRAM limitada
else:
logger.warning("GPU não detectada. O modelo rodará em CPU (extremamente lento).")
controlnet = ControlNetModel.from_pretrained(CONTROLNET_ID)
pipe = StableDiffusionControlNetPipeline.from_pretrained(MODEL_ID, controlnet=controlnet)
except Exception as e:
logger.error(f"Erro fatal ao carregar modelos: {e}")
# Não interrompemos o script aqui para permitir que a UI carregue e mostre o erro, se necessário
# -----------------------------------------------------------------------------
# SEÇÃO 4: LÓGICA DE INFERÊNCIA COM DECORADOR ZERO-GPU
# -----------------------------------------------------------------------------
@spaces.GPU(duration=120) # Aloca a GPU para esta função por até 120 segundos
def process_image(input_image, prompt, negative_prompt, num_steps, guidance_scale):
"""
Função principal de geração. O decorador @spaces.GPU gerencia a fila e a alocação
de hardware automaticamente nos Hugging Face Spaces.
"""
if input_image is None:
raise gr.Error("Por favor, forneça uma imagem de entrada.")
if not torch.cuda.is_available():
raise gr.Error("GPU não disponível neste ambiente. Impossível gerar imagem.")
try:
# Pre-processamento Canny (Exemplo para ControlNet Canny)
import cv2
import numpy as np
image = np.array(input_image)
low_threshold = 100
high_threshold = 200
image = cv2.Canny(image, low_threshold, high_threshold)
image = image[:, :, None]
image = np.concatenate([image, image, image], axis=2)
canny_image = Image.fromarray(image)
# Inferência
output = pipe(
prompt,
image=canny_image,
negative_prompt=negative_prompt,
num_inference_steps=int(num_steps),
guidance_scale=guidance_scale
).images
return output
except Exception as e:
logger.error(f"Erro durante a inferência: {e}")
raise gr.Error(f"Falha na geração: {str(e)}")
# -----------------------------------------------------------------------------
# SEÇÃO 5: CONSTRUÇÃO DA INTERFACE (Gradio Blocks)
# -----------------------------------------------------------------------------
with gr.Blocks(title="ControlNet Stable Diffusion", theme=gr.themes.Base()) as demo:
gr.Markdown("# 🎨 Stable Diffusion com ControlNet (Canny)")
gr.Markdown("Gere variações artísticas baseadas na estrutura de uma imagem de entrada.")
with gr.Row():
with gr.Column():
input_img = gr.Image(label="Imagem de Referência", type="pil", sources=["upload", "webcam"])
prompt_txt = gr.Textbox(label="Prompt Positivo", value="cyberpunk city, neon lights, highly detailed", lines=2)
neg_prompt_txt = gr.Textbox(label="Prompt Negativo", value="low quality, blurred, ugly, disfigured", lines=2)
with gr.Accordion("Configurações Avançadas", open=False):
steps_slider = gr.Slider(label="Passos de Inferência", minimum=10, maximum=50, value=25, step=1)
cfg_slider = gr.Slider(label="Guidance Scale", minimum=1.0, maximum=20.0, value=7.5, step=0.5)
generate_btn = gr.Button("Gerar Imagem", variant="primary", size="lg")
with gr.Column():
output_img = gr.Image(label="Resultado Gerado", interactive=False)
# Conexão de Eventos
generate_btn.click(
fn=process_image,
inputs=[input_img, prompt_txt, neg_prompt_txt, steps_slider, cfg_slider],
outputs=output_img
)
# -----------------------------------------------------------------------------
# SEÇÃO 6: LANÇAMENTO DO SERVIDOR
# -----------------------------------------------------------------------------
if __name__ == "__main__":
# share=False é CRUCIAL. share=True tenta criar túneis SSH que falham em Spaces e
# geram o erro "ValueError: When localhost is not accessible".
demo.launch(
server_name="0.0.0.0",
server_port=7860,
share=False,
allowed_paths=["."]
)