Spaces:
Running
on
Zero
Running
on
Zero
| import random | |
| from typing import List, Tuple | |
| import gradio as gr | |
| import numpy as np | |
| import spaces | |
| import torch | |
| from diffusers import FluxFillPipeline | |
| from loras import LoRA, loras | |
| MAX_SEED = np.iinfo(np.int32).max | |
| pipe = FluxFillPipeline.from_pretrained( | |
| "black-forest-labs/FLUX.1-Fill-dev", | |
| torch_dtype=torch.bfloat16 | |
| ) | |
| flux_keywords_available = [ | |
| "IMG_1025.HEIC", | |
| "Selfie", | |
| ] | |
| def activate_loras(pipe: FluxFillPipeline, loras_with_weights: list[tuple[LoRA, float]]): | |
| adapter_names = [] | |
| adapter_weights = [] | |
| for lora, weight in loras_with_weights: | |
| print(f"Loading LoRA: {lora.name} with weight {weight}") | |
| pipe.load_lora_weights(lora.id, weight=weight, adapter_name=lora.name) | |
| adapter_names.append(lora.name) | |
| adapter_weights.append(weight) | |
| print(f"Activating adapters: {adapter_names} with weights {adapter_weights}") | |
| pipe.set_adapters(adapter_names, adapter_weights=adapter_weights) | |
| return pipe | |
| def get_loras() -> list[dict]: | |
| return loras | |
| def deactivate_loras(pipe): | |
| print("Unloading all LoRAs...") | |
| pipe.unload_lora_weights() | |
| return pipe | |
| def calculate_optimal_dimensions(image): | |
| original_width, original_height = image.size | |
| MIN_ASPECT_RATIO = 9 / 16 | |
| MAX_ASPECT_RATIO = 16 / 9 | |
| FIXED_DIMENSION = 1024 | |
| original_aspect_ratio = original_width / original_height | |
| if original_aspect_ratio > 1: | |
| width = FIXED_DIMENSION | |
| height = round(FIXED_DIMENSION / original_aspect_ratio) | |
| else: | |
| height = FIXED_DIMENSION | |
| width = round(FIXED_DIMENSION * original_aspect_ratio) | |
| width = (width // 8) * 8 | |
| height = (height // 8) * 8 | |
| calculated_aspect_ratio = width / height | |
| if calculated_aspect_ratio > MAX_ASPECT_RATIO: | |
| width = int((height * MAX_ASPECT_RATIO // 8) * 8) | |
| elif calculated_aspect_ratio < MIN_ASPECT_RATIO: | |
| height = int((width / MIN_ASPECT_RATIO // 8) * 8) | |
| width = max(width, 576) if width == FIXED_DIMENSION else width | |
| height = max(height, 576) if height == FIXED_DIMENSION else height | |
| return width, height | |
| def inpaint( | |
| image, | |
| mask, | |
| prompt: str = "", | |
| seed: int = 0, | |
| num_inference_steps: int = 28, | |
| guidance_scale: int = 50, | |
| strength: float = 1.0 | |
| ): | |
| image = image.convert("RGB") | |
| mask = mask.convert("L") | |
| width, height = calculate_optimal_dimensions(image) | |
| pipe.to("cuda") | |
| result = pipe( | |
| image=image, | |
| mask_image=mask, | |
| prompt=prompt, | |
| width=width, | |
| height=height, | |
| num_inference_steps=num_inference_steps, | |
| guidance_scale=guidance_scale, | |
| strength=strength, | |
| generator=torch.Generator().manual_seed(seed) | |
| ).images[0] | |
| return result.convert("RGBA"), prompt, seed | |
| def inpaint_api( | |
| image, | |
| mask, | |
| prompt: str, | |
| seed: int, | |
| num_inference_steps: int, | |
| guidance_scale: int, | |
| strength: float, | |
| flux_keywords: List[str] = None, | |
| loras_selected: List[Tuple[str, float]] = None | |
| ): | |
| flux_keywords = flux_keywords or [] | |
| loras_selected = loras_selected or [] | |
| # Convertir nombres a objetos LoRA | |
| selected_loras_with_weights = [] | |
| for name, weight_value in loras_selected: | |
| try: | |
| # Convierte explícitamente el peso (que viene como string) a float | |
| weight = float(weight_value) | |
| except (ValueError, TypeError): | |
| # Ignora si el valor no es un número válido (ej: None o string vacío) | |
| print(f"Valor de peso inválido '{weight_value}' para LoRA '{name}', omitiendo.") | |
| continue # Pasa al siguiente LoRA | |
| lora_obj = next((l for l in loras if l.display_name == name), None) | |
| # Ahora la comparación 'weight != 0.0' es segura (float con float) | |
| if lora_obj and weight != 0.0: | |
| selected_loras_with_weights.append((lora_obj, weight)) | |
| deactivate_loras(pipe) | |
| if selected_loras_with_weights: | |
| activate_loras(pipe, selected_loras_with_weights) | |
| # Construir prompt final | |
| final_prompt = "" | |
| if flux_keywords: | |
| final_prompt += ", ".join(flux_keywords) + ", " | |
| for lora, _ in selected_loras_with_weights: | |
| if lora.keyword: | |
| if isinstance(lora.keyword, str): | |
| final_prompt += lora.keyword + ", " | |
| else: | |
| final_prompt += ", ".join(lora.keyword) + ", " | |
| if final_prompt: | |
| final_prompt += "\n\n" | |
| final_prompt += prompt | |
| if not isinstance(seed, int) or seed < 0: | |
| seed = random.randint(0, MAX_SEED) | |
| return inpaint( | |
| image=image, | |
| mask=mask, | |
| prompt=final_prompt, | |
| seed=seed, | |
| num_inference_steps=num_inference_steps, | |
| guidance_scale=guidance_scale, | |
| strength=strength | |
| ) | |
| # ======================== | |
| # UI DIRECTA A inpaint_api | |
| # ======================== | |
| with gr.Blocks(title="Flux.1 Fill dev Inpainting with LoRAs", theme=gr.themes.Soft()) as demo: | |
| gr.api(get_loras, api_name="get_loras") | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| prompt_input = gr.Text( | |
| label="Prompt", | |
| lines=4, | |
| value="a 25 years old woman" | |
| ) | |
| seed_slider = gr.Slider( | |
| label="Seed", | |
| minimum=-1, | |
| maximum=MAX_SEED, | |
| step=1, | |
| value=-1, | |
| info="(-1 = Random)", | |
| interactive=True | |
| ) | |
| num_inference_steps_input = gr.Number( | |
| label="Inference steps", | |
| value=40, interactive=True | |
| ) | |
| guidance_scale_input = gr.Number( | |
| label="Guidance scale", | |
| value=28, | |
| interactive=True | |
| ) | |
| strength_input = gr.Number( | |
| label="Strength", | |
| value=1.0, | |
| interactive=True, | |
| maximum=1.0 | |
| ) | |
| gr.Markdown("### Flux Keywords") | |
| flux_keywords_input = gr.CheckboxGroup( | |
| choices=flux_keywords_available, | |
| label="Flux Keywords" | |
| ) | |
| if loras: | |
| gr.Markdown("### Available LoRAs") | |
| lora_names = [l.display_name for l in loras] | |
| loras_selected_input = gr.Dataframe( | |
| type="array", | |
| headers=["LoRA", "Weight"], | |
| value=[[name, 0.0] for name in lora_names], | |
| datatype=["str", "number"], # Primera columna string, segunda número | |
| interactive=[False, True], # Solo la segunda columna editable | |
| static_columns=[0], | |
| label="LoRA selection (Weight 0 = disable)" | |
| ) | |
| with gr.Column(scale=3): | |
| image_input = gr.Image( | |
| label="Image", | |
| type="pil" | |
| ) | |
| mask_input = gr.Image( | |
| label="Mask", | |
| type="pil" | |
| ) | |
| run_btn = gr.Button( | |
| "Run", | |
| variant="primary" | |
| ) | |
| with gr.Column(scale=3): | |
| result_image = gr.Image( | |
| label="Result" | |
| ) | |
| used_prompt_box = gr.Text( | |
| label="Used prompt", | |
| lines=4 | |
| ) | |
| used_seed_box = gr.Number( | |
| label="Used seed" | |
| ) | |
| run_btn.click( | |
| fn=inpaint_api, | |
| inputs=[ | |
| image_input, | |
| mask_input, | |
| prompt_input, | |
| seed_slider, | |
| num_inference_steps_input, | |
| guidance_scale_input, | |
| strength_input, | |
| flux_keywords_input, | |
| loras_selected_input | |
| ], | |
| outputs=[ | |
| result_image, | |
| used_prompt_box, | |
| used_seed_box | |
| ], | |
| api_name="inpaint" | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch(share=False, show_error=True) | |