bonsaibinary1 / app.py
dramoneaon's picture
Create app.py
287a803 verified
Raw
History Blame Contribute Delete
10.6 kB
import os
import sys
# Activar la transferencia acelerada de Hugging Face antes de importar otros m贸dulos
os.environ["HF_HUB_ENABLE_HF_TRANSFER"] = "1"
import gradio as gr
import torch
from diffusers import DiffusionPipeline
# Intentar importar spaces para compatibilidad con Hugging Face Zero GPU
try:
import spaces
USING_SPACES = True
except ImportError:
USING_SPACES = False
# Inicializaci贸n del modelo
# Al usar hf_transfer activo, la descarga del modelo de ~4.4GB se optimiza al m谩ximo del ancho de banda disponible.
model_id = "prism-ml/bonsai-image-binary-4B-gemlite-1bit"
print("Cargando modelo... (Usando hf_transfer para m谩xima velocidad)")
pipe = DiffusionPipeline.from_pretrained(
model_id,
dtype=torch.bfloat16,
device_map="auto" if not USING_SPACES else None
)
if not USING_SPACES and torch.cuda.is_available():
pipe = pipe.to("cuda")
# CSS personalizado con est茅tica Brutalista Moderna
brutalist_css = """
body, .gradio-container {
background-color: #f3f3f3 !important;
font-family: 'Courier New', Courier, monospace !important;
}
.brutal-card {
border: 3px solid #000000 !important;
box-shadow: 6px 6px 0px #000000 !important;
background-color: #ffffff !important;
border-radius: 0px !important;
padding: 15px !important;
margin-bottom: 15px !important;
}
.brutal-button {
border: 3px solid #000000 !important;
box-shadow: 4px 4px 0px #000000 !important;
background-color: #ff5757 !important;
color: #ffffff !important;
font-weight: 900 !important;
border-radius: 0px !important;
text-transform: uppercase !important;
letter-spacing: 1px !important;
transition: all 0.1s ease !important;
}
.brutal-button:hover {
background-color: #e04343 !important;
}
.brutal-button:active {
box-shadow: 1px 1px 0px #000000 !important;
transform: translate(3px, 3px) !important;
}
input, select, textarea, .secondary {
border: 3px solid #000000 !important;
border-radius: 0px !important;
box-shadow: 3px 3px 0px #000000 !important;
background-color: #ffffff !important;
font-weight: bold !important;
}
.gr-form {
border: none !important;
}
"""
def dynamic_style_inputs(style):
"""
Controla la visibilidad de los paneles de opciones seg煤n el estilo seleccionado.
"""
return (
gr.update(visible=(style == "Realista")),
gr.update(visible=(style == "Acuarela")),
gr.update(visible=(style == "Pintura al 脫leo")),
gr.update(visible=(style == "Custom"))
)
def calculate_dimensions(quality, aspect_ratio):
"""
Calcula el ancho y alto bas谩ndose en la calidad y la relaci贸n de aspecto.
"""
# Escala de p铆xeles base orientativa para evitar excesos de memoria
base_pixels = 512 if quality == "0.5K" else (1024 if quality == "1K" else 1536) # 2K limitado a 1536 para estabilidad de VRAM
if aspect_ratio == "1:1 (Cuadrado)":
return base_pixels, base_pixels
elif aspect_ratio == "16:9 (Horizontal)":
return int(base_pixels * 1.25), int(base_pixels * 0.75)
elif aspect_ratio == "9:16 (Vertical)":
return int(base_pixels * 0.75), int(base_pixels * 1.25)
return 1024, 1024
# Funci贸n contenedora para Zero GPU si aplica
def generate_inference(prompt, steps, width, height):
if USING_SPACES:
# En Zero GPU de Hugging Face se requiere decorar localmente o llamar dentro de un entorno compatible
@spaces.GPU(duration=30)
def run_on_space():
pipe.to("cuda")
return pipe(prompt, num_inference_steps=steps, width=width, height=height).images[0]
return run_on_space()
else:
return pipe(prompt, num_inference_steps=steps, width=width, height=height).images[0]
def generate_image(
prompt_base, style,
realista_light, realista_color, realista_texture, realista_comp,
watercolor_paper, watercolor_wetness, watercolor_blend,
oil_tech, oil_brush, oil_text,
custom_text,
quality, aspect_ratio, steps
):
if not prompt_base.strip():
return None, "Por favor, introduce un prompt base."
# Construcci贸n estructurada del prompt final seg煤n el estilo elegido
full_prompt = prompt_base
if style == "Realista":
modifiers = f"{realista_light}, {realista_color}, {realista_texture} texture, {realista_comp} composition, highly realistic, photo"
full_prompt = f"{full_prompt}, {modifiers}"
elif style == "Acuarela":
modifiers = f"watercolor painting, {watercolor_paper} paper, {watercolor_wetness} style, {watercolor_blend} colors, artistic"
full_prompt = f"{full_prompt}, {modifiers}"
elif style == "Pintura al 脫leo":
modifiers = f"oil painting, {oil_tech} technique, {oil_brush} brushstrokes, {oil_text} texture"
full_prompt = f"{full_prompt}, {modifiers}"
elif style == "Custom" and custom_text.strip():
full_prompt = f"{full_prompt}, {custom_text}"
width, height = calculate_dimensions(quality, aspect_ratio)
# Asegurar dimensiones m煤ltiplos de 8 para evitar errores en el backend de diffusers
width = (width // 8) * 8
height = (height // 8) * 8
try:
image = generate_inference(full_prompt, int(steps), width, height)
return image, f"Prompt generado: {full_prompt}"
except Exception as e:
return None, f"Error durante la generaci贸n: {str(e)}"
# Construcci贸n de la interfaz
with gr.Blocks(css=brutalist_css, title="Bonsai Studio") as demo:
gr.HTML("<h1 style='text-align: center; font-weight: 900; margin-top: 20px; font-size: 2.5em;'>BONSAI STUDIO</h1>")
gr.HTML("<p style='text-align: center; margin-bottom: 30px; font-weight: bold;'>Generaci贸n de Im谩genes con Estilo Brutalista</p>")
with gr.Row():
with gr.Column(scale=1):
with gr.Group(elem_classes="brutal-card"):
prompt_input = gr.Textbox(
label="Prompt Base",
placeholder="Ejemplo: Un astronauta en un bosque templado...",
lines=3
)
style_select = gr.Dropdown(
choices=["Realista", "Acuarela", "Pintura al 脫leo", "Custom"],
value="Realista",
label="Estilo Art铆stico"
)
# Sub-opciones de estilo din谩micas
with gr.Group(elem_classes="brutal-card"):
# Opciones para Realista
with gr.Column(visible=True) as col_realista:
gr.Markdown("### Opciones de Realismo")
r_light = gr.Dropdown(choices=["Estudio", "Natural", "Cinematogr谩fica", "Luz de Ne贸n", "Contraluz"], value="Cinematogr谩fica", label="Iluminaci贸n")
r_color = gr.Dropdown(choices=["Vibrante", "C谩lido", "Fr铆o", "Desaturado", "Monocromo"], value="Vibrante", label="Paleta de Colores")
r_texture = gr.Dropdown(choices=["Porosa/Micro-detalles", "Piel Suave", "Imperfecto/Org谩nico", "Pulido/Liso"], value="Porosa/Micro-detalles", label="Texturas")
r_comp = gr.Dropdown(choices=["Plano Detalle", "Primer Plano", "Plano Medio", "Gran Plano General"], value="Plano Medio", label="Composici贸n")
# Opciones para Acuarela
with gr.Column(visible=False) as col_watercolor:
gr.Markdown("### Opciones de Acuarela")
w_paper = gr.Dropdown(choices=["Prensado en Fr铆o", "Texturizado Rugoso", "Papel Suave Japones"], value="Prensado en Fr铆o", label="Tipo de Papel")
w_wetness = gr.Dropdown(choices=["Mojado sobre Mojado (L铆quido)", "Pincel Seco (Detalle)", "Aguada Uniforme"], value="Mojado sobre Mojado (L铆quido)", label="Efecto de Humedad")
w_blend = gr.Dropdown(choices=["Sangrado Suave", "Bordes Definidos", "Gradiente de Color"], value="Sangrado Suave", label="Mezcla de Colores")
# Opciones para Pintura al 脫leo
with gr.Column(visible=False) as col_oil:
gr.Markdown("### Opciones de Pintura al 脫leo")
o_tech = gr.Dropdown(choices=["Impasto (Espeso)", "Glaseado (Capas finas)", "Alla Prima (Directo)"], value="Impasto (Espeso)", label="T茅cnica de Pintura")
o_brush = gr.Dropdown(choices=["Pinceladas Gruesas y Visibles", "Pinceladas Difuminadas", "Esp谩tula de Pintor"], value="Pinceladas Gruesas y Visibles", label="Patr贸n de Pinceladas")
o_text = gr.Dropdown(choices=["Muy Texturizado", "Relieve Medio", "Superficie Plana"], value="Muy Texturizado", label="Textura F铆sica")
# Opci贸n para Custom
with gr.Column(visible=False) as col_custom:
gr.Markdown("### Modificadores Manuales")
custom_text = gr.Textbox(placeholder="A帽ade modificadores personalizados separados por comas...", label="Modificadores de Prompt", lines=2)
with gr.Group(elem_classes="brutal-card"):
gr.Markdown("### Configuraci贸n T茅cnica")
quality_select = gr.Dropdown(choices=["0.5K", "1K", "2K"], value="1K", label="Calidad/Resoluci贸n")
aspect_ratio_select = gr.Dropdown(choices=["1:1 (Cuadrado)", "16:9 (Horizontal)", "9:16 (Vertical)"], value="1:1 (Cuadrado)", label="Relaci贸n de Aspecto")
steps_slider = gr.Slider(minimum=1, maximum=8, value=4, step=1, label="Pasos de Inferencia (Steps)")
generate_btn = gr.Button("GENERAR IMAGEN", elem_classes="brutal-button")
with gr.Column(scale=1):
with gr.Group(elem_classes="brutal-card"):
output_image = gr.Image(label="Imagen Generada", type="pil")
output_status = gr.Textbox(label="Informaci贸n del Prompt Aplicado", interactive=False)
# Conectar l贸gica de cambio de visibilidad de componentes
style_select.change(
fn=dynamic_style_inputs,
inputs=[style_select],
outputs=[col_realista, col_watercolor, col_oil, col_custom]
)
# Acci贸n del bot贸n generar
generate_btn.click(
fn=generate_image,
inputs=[
prompt_input, style_select,
r_light, r_color, r_texture, r_comp,
w_paper, w_wetness, w_blend,
o_tech, o_brush, o_text,
custom_text,
quality_select, aspect_ratio_select, steps_slider
],
outputs=[output_image, output_status]
)
if __name__ == "__main__":
demo.launch()