Spaces:
Runtime error
Runtime error
| 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 | |
| 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() |