import spaces import gradio as gr import torch import math from PIL import Image from diffusers import QwenImageEditPlusPipeline, FlowMatchEulerDiscreteScheduler # Load pipeline with optimized scheduler at startup scheduler_config = { "base_image_seq_len": 256, "base_shift": math.log(3), "invert_sigmas": False, "max_image_seq_len": 8192, "max_shift": math.log(3), "num_train_timesteps": 1000, "shift": 1.0, "shift_terminal": None, "stochastic_sampling": False, "time_shift_type": "exponential", "use_beta_sigmas": False, "use_dynamic_shifting": True, "use_exponential_sigmas": False, "use_karras_sigmas": False, } scheduler = FlowMatchEulerDiscreteScheduler.from_config(scheduler_config) pipeline = QwenImageEditPlusPipeline.from_pretrained( "Qwen/Qwen-Image-Edit-2509", scheduler=scheduler, torch_dtype=torch.bfloat16 ) pipeline.to('cuda') pipeline.set_progress_bar_config(disable=None) # Load LoRA for faster inference pipeline.load_lora_weights( "lightx2v/Qwen-Image-Lightning", weight_name="Qwen-Image-Lightning-8steps-V2.0-bf16.safetensors" ) pipeline.fuse_lora() @spaces.GPU(duration=60) def edit_images(image1, image2, prompt, seed, true_cfg_scale, negative_prompt, num_steps, guidance_scale): if image1 is None or image2 is None: gr.Warning("Please upload both images") return None # Convert to PIL if needed if not isinstance(image1, Image.Image): image1 = Image.fromarray(image1) if not isinstance(image2, Image.Image): image2 = Image.fromarray(image2) inputs = { "image": [image1, image2], "prompt": prompt, "generator": torch.manual_seed(seed), "true_cfg_scale": true_cfg_scale, "negative_prompt": negative_prompt, "num_inference_steps": num_steps, "guidance_scale": guidance_scale, "num_images_per_prompt": 1, } with torch.inference_mode(): output = pipeline(**inputs) return output.images[0] # Example prompts and images example_prompts = [ "The magician bear is on the left, the alchemist bear is on the right, facing each other in the central park square.", "Two characters standing side by side in a beautiful garden with flowers blooming", "The hero on the left and the villain on the right, facing off in an epic battle scene", "Two friends sitting together on a park bench, enjoying the sunset", ] # Example image paths example_images = [ ["bear1.jpg", "bear2.jpg", "The magician bear is on the left, the alchemist bear is on the right, facing each other in the central park square."], ] with gr.Blocks(css="footer {visibility: hidden}") as demo: gr.Markdown( """ # Qwen Image Edit Plus (Optimized) Upload two images and describe how you want them combined or edited together. [Built with anycoder](https://huggingface.co/spaces/akhaliq/anycoder) """ ) with gr.Row(): with gr.Column(): image1_input = gr.Image( label="First Image", type="pil", height=300 ) image2_input = gr.Image( label="Second Image", type="pil", height=300 ) with gr.Column(): output_image = gr.Image( label="Edited Result", type="pil", height=620 ) with gr.Group(): prompt_input = gr.Textbox( label="Prompt", placeholder="Describe how you want the images combined or edited...", value=example_prompts[0], lines=3 ) gr.Examples( examples=example_images, inputs=[image1_input, image2_input, prompt_input], label="Example Images and Prompts" ) gr.Examples( examples=[[p] for p in example_prompts], inputs=[prompt_input], label="Example Prompts Only" ) with gr.Accordion("Advanced Settings", open=False): with gr.Row(): seed_input = gr.Number( label="Seed", value=0, precision=0 ) num_steps = gr.Slider( label="Number of Inference Steps", minimum=8, maximum=30, value=8, step=1 ) with gr.Row(): true_cfg_scale = gr.Slider( label="True CFG Scale", minimum=1.0, maximum=10.0, value=1.0, step=0.5 ) guidance_scale = gr.Slider( label="Guidance Scale", minimum=1.0, maximum=5.0, value=1.0, step=0.1 ) negative_prompt = gr.Textbox( label="Negative Prompt", value=" ", placeholder="What to avoid in the generation..." ) generate_btn = gr.Button("Generate Edited Image", variant="primary", size="lg") generate_btn.click( fn=edit_images, inputs=[ image1_input, image2_input, prompt_input, seed_input, true_cfg_scale, negative_prompt, num_steps, guidance_scale ], outputs=output_image, show_progress="full" ) demo.launch()