import os import gc import spaces import gradio as gr import numpy as np import torch import random from PIL import Image from diffusers import FlowMatchEulerDiscreteScheduler from qwenimage.pipeline_qwenimage_edit_plus import QwenImageEditPlusPipeline from qwenimage.transformer_qwenimage import QwenImageTransformer2DModel dtype = torch.bfloat16 device = torch.device("cuda" if torch.cuda.is_available() else "cpu") print("torch.__version__ =", torch.__version__) print("Using device:", device) MAX_SEED = np.iinfo(np.int32).max pipe = QwenImageEditPlusPipeline.from_pretrained( "Qwen/Qwen-Image-Edit-2511", transformer=QwenImageTransformer2DModel.from_pretrained( "prithivMLmods/Qwen-Image-Edit-Rapid-AIO-V19", torch_dtype=dtype, device_map='cuda' ), torch_dtype=dtype ).to(device) ADAPTER_SPECS = { "Multiple-Angles": {"repo": "dx8152/Qwen-Edit-2509-Multiple-angles", "weights": "镜头转换.safetensors", "adapter_name": "multiple-angles"}, "Photo-to-Anime": {"repo": "autoweeb/Qwen-Image-Edit-2509-Photo-to-Anime", "weights": "Qwen-Image-Edit-2509-Photo-to-Anime_000001000.safetensors", "adapter_name": "photo-to-anime"}, "Anime-V2": {"repo": "prithivMLmods/Qwen-Image-Edit-2511-Anime", "weights": "Qwen-Image-Edit-2511-Anime-2000.safetensors", "adapter_name": "anime-v2"}, "Light-Migration": {"repo": "dx8152/Qwen-Edit-2509-Light-Migration", "weights": "参考色调.safetensors", "adapter_name": "light-migration"}, "Upscaler": {"repo": "starsfriday/Qwen-Image-Edit-2511-Upscale2K", "weights": "qwen_image_edit_2511_upscale.safetensors", "adapter_name": "upscale-2k"}, "Style-Transfer": {"repo": "zooeyy/Style-Transfer", "weights": "Style Transfer-Alpha-V0.1.safetensors", "adapter_name": "style-transfer"}, "Manga-Tone": {"repo": "nappa114514/Qwen-Image-Edit-2509-Manga-Tone", "weights": "tone001.safetensors", "adapter_name": "manga-tone"}, "Anything2Real": {"repo": "lrzjason/Anything2Real_2601", "weights": "anything2real_2601.safetensors", "adapter_name": "anything2real"}, "Fal-Multiple-Angles": {"repo": "fal/Qwen-Image-Edit-2511-Multiple-Angles-LoRA", "weights": "qwen-image-edit-2511-multiple-angles-lora.safetensors", "adapter_name": "fal-multiple-angles"}, "Polaroid-Photo": {"repo": "prithivMLmods/Qwen-Image-Edit-2511-Polaroid-Photo", "weights": "Qwen-Image-Edit-2511-Polaroid-Photo.safetensors", "adapter_name": "polaroid-photo"}, "Unblur-Anything": {"repo": "prithivMLmods/Qwen-Image-Edit-2511-Unblur-Upscale", "weights": "Qwen-Image-Edit-Unblur-Upscale_15.safetensors", "adapter_name": "unblur-anything"}, "Midnight-Noir-Eyes-Spotlight": {"repo": "prithivMLmods/Qwen-Image-Edit-2511-Midnight-Noir-Eyes-Spotlight", "weights": "Qwen-Image-Edit-2511-Midnight-Noir-Eyes-Spotlight.safetensors", "adapter_name": "midnight-noir-eyes-spotlight"}, "Hyper-Realistic-Portrait": {"repo": "prithivMLmods/Qwen-Image-Edit-2511-Hyper-Realistic-Portrait", "weights": "HRP_20.safetensors", "adapter_name": "hyper-realistic-portrait"}, "Ultra-Realistic-Portrait": {"repo": "prithivMLmods/Qwen-Image-Edit-2511-Ultra-Realistic-Portrait", "weights": "URP_20.safetensors", "adapter_name": "ultra-realistic-portrait"}, "Pixar-Inspired-3D": {"repo": "prithivMLmods/Qwen-Image-Edit-2511-Pixar-Inspired-3D", "weights": "PI3_20.safetensors", "adapter_name": "pi3"}, "Noir-Comic-Book": {"repo": "prithivMLmods/Qwen-Image-Edit-2511-Noir-Comic-Book-Panel", "weights": "Noir-Comic-Book-Panel_20.safetensors", "adapter_name": "ncb"}, "Any-light": {"repo": "lilylilith/QIE-2511-MP-AnyLight", "weights": "QIE-2511-AnyLight_.safetensors", "adapter_name": "any-light"}, "Studio-DeLight": {"repo": "prithivMLmods/QIE-2511-Studio-DeLight", "weights": "QIE-2511-Studio-DeLight-5000.safetensors", "adapter_name": "studio-delight"}, "Cinematic-FlatLog": {"repo": "prithivMLmods/QIE-2511-Cinematic-FlatLog-Control", "weights": "QIE-2511-Cinematic-FlatLog-Control-3200.safetensors", "adapter_name": "flat-log"}, } LOADED_ADAPTERS = set() def update_dimensions_on_upload(image): if image is None: return 1024, 1024 original_width, original_height = image.size if original_width > original_height: new_width = 1024 aspect_ratio = original_height / original_width new_height = int(new_width * aspect_ratio) else: new_height = 1024 aspect_ratio = original_width / original_height new_width = int(new_height * aspect_ratio) new_width = (new_width // 8) * 8 new_height = (new_height // 8) * 8 return new_width, new_height @spaces.GPU def infer(images, prompt, lora_adapter, seed, randomize_seed, guidance_scale, steps, progress=gr.Progress(track_tqdm=True)): gc.collect() torch.cuda.empty_cache() if not images: raise gr.Error("Please upload at least one image to edit.") pil_images = [] if images is not None: for item in images: try: if isinstance(item, tuple) or isinstance(item, list): path_or_img = item[0] else: path_or_img = item if isinstance(path_or_img, str): pil_images.append(Image.open(path_or_img).convert("RGB")) elif isinstance(path_or_img, Image.Image): pil_images.append(path_or_img.convert("RGB")) else: pil_images.append(Image.open(path_or_img.name).convert("RGB")) except Exception as e: print(f"Skipping invalid image item: {e}") continue if not pil_images: raise gr.Error("Could not process uploaded images.") spec = ADAPTER_SPECS.get(lora_adapter) if not spec: raise gr.Error(f"Configuration not found for: {lora_adapter}") adapter_name = spec["adapter_name"] if adapter_name not in LOADED_ADAPTERS: print(f"--- Downloading and Loading Adapter: {lora_adapter} ---") pipe.load_lora_weights(spec["repo"], weight_name=spec["weights"], adapter_name=adapter_name) LOADED_ADAPTERS.add(adapter_name) else: print(f"--- Adapter {lora_adapter} is already loaded. ---") pipe.set_adapters([adapter_name], adapter_weights=[1.0]) if randomize_seed: seed = random.randint(0, MAX_SEED) generator = torch.Generator(device=device).manual_seed(seed) negative_prompt = "worst quality, low quality, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, jpeg artifacts, signature, watermark, username, blurry" width, height = update_dimensions_on_upload(pil_images[0]) result_image = pipe( image=pil_images, prompt=prompt, negative_prompt=negative_prompt, height=height, width=width, num_inference_steps=steps, generator=generator, true_cfg_scale=guidance_scale, ).images[0] gc.collect() torch.cuda.empty_cache() return result_image, seed css = """ #col-container {margin: 0 auto; max-width: 1000px;} """ with gr.Blocks(css=css) as demo: gr.Markdown("# **Qwen-Image-Edit-2511-LoRAs-Fast**") with gr.Row(): with gr.Column(): images = gr.Gallery(label="Upload Images", type="filepath", columns=2, rows=1, height=300, allow_preview=True) prompt = gr.Text(label="Edit Prompt", show_label=True, placeholder="e.g., transform into anime..") run_button = gr.Button("Edit Image", variant="primary") with gr.Column(): output_image = gr.Image(label="Output Image", interactive=False, format="png", height=365) lora_adapter = gr.Dropdown(label="Choose Editing Style", choices=list(ADAPTER_SPECS.keys()), value="Photo-to-Anime") with gr.Accordion("Advanced Settings", open=False): seed = gr.Slider(label="Seed", minimum=0, maximum=MAX_SEED, step=1, value=0) randomize_seed = gr.Checkbox(label="Randomize Seed", value=True) guidance_scale = gr.Slider(label="Guidance Scale", minimum=1.0, maximum=10.0, step=0.1, value=1.0) steps = gr.Slider(label="Inference Steps", minimum=1, maximum=50, step=1, value=4) run_button.click(fn=infer, inputs=[images, prompt, lora_adapter, seed, randomize_seed, guidance_scale, steps], outputs=[output_image, seed]) if __name__ == "__main__": demo.queue(max_size=30).launch(css=css, show_error=True)