Spaces:
Running
on
Zero
Running
on
Zero
| import os | |
| import math | |
| import random | |
| import logging | |
| import requests | |
| import numpy as np | |
| import torch | |
| import spaces | |
| from fastapi import FastAPI, HTTPException | |
| from diffusers import FlowMatchEulerDiscreteScheduler | |
| from qwenimage.pipeline_qwenimage_edit_plus import QwenImageEditPlusPipeline | |
| from qwenimage.transformer_qwenimage import QwenImageTransformer2DModel | |
| from qwenimage.qwen_fa3_processor import QwenDoubleStreamAttnProcessorFA3 | |
| from optimization import optimize_pipeline_ | |
| from PIL import Image | |
| import gradio as gr | |
| logging.basicConfig( | |
| level=logging.INFO, | |
| filename="qwen_image_editor.log", | |
| filemode="a", | |
| format="%(asctime)s - %(levelname)s - %(message)s", | |
| ) | |
| logger = logging.getLogger(__name__) | |
| def translate_albanian_to_english(text: str, language: str = "en"): | |
| if not text.strip(): | |
| raise gr.Error("Please enter a description.") | |
| for attempt in range(2): | |
| try: | |
| response = requests.post( | |
| "https://hal1993-mdftranslation1234567890abcdef1234567890-fc073a6.hf.space/v1/translate", | |
| json={"from_language": "sq", "to_language": "en", "input_text": text}, | |
| headers={"accept": "application/json", "Content-Type": "application/json"}, | |
| timeout=5, | |
| ) | |
| response.raise_for_status() | |
| translated = response.json().get("translate", "") | |
| logger.info(f"Translation response: {translated}") | |
| return translated | |
| except Exception as e: | |
| logger.error(f"Translation error (attempt {attempt + 1}): {e}") | |
| if attempt == 1: | |
| raise gr.Error("Translation failed. Please try again.") | |
| raise gr.Error("Translation failed. Please try again.") | |
| dtype = torch.bfloat16 | |
| device = "cuda" if torch.cuda.is_available() else "cpu" | |
| scheduler_cfg = { | |
| "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_cfg) | |
| pipe = QwenImageEditPlusPipeline.from_pretrained( | |
| "Qwen/Qwen-Image-Edit-2509", | |
| scheduler=scheduler, | |
| torch_dtype=dtype, | |
| ).to(device) | |
| pipe.load_lora_weights( | |
| "lightx2v/Qwen-Image-Lightning", | |
| weight_name="Qwen-Image-Lightning-4steps-V2.0.safetensors", | |
| ) | |
| pipe.fuse_lora() | |
| pipe.transformer.__class__ = QwenImageTransformer2DModel | |
| pipe.transformer.set_attn_processor(QwenDoubleStreamAttnProcessorFA3()) | |
| optimize_pipeline_( | |
| pipe, | |
| image=[Image.new("RGB", (1024, 1024)), Image.new("RGB", (1024, 1024))], | |
| prompt="prompt", | |
| ) | |
| MAX_SEED = np.iinfo(np.int32).max | |
| QUALITY_PROMPT = ", high quality, detailed, vibrant, professional lighting" | |
| def infer(image, prompt): | |
| negative_prompt = "" | |
| seed = random.randint(0, MAX_SEED) | |
| generator = torch.Generator(device=device).manual_seed(seed) | |
| prompt_en = translate_albanian_to_english(prompt.strip(), language="en") + QUALITY_PROMPT | |
| pil_img = None | |
| if image is not None: | |
| if isinstance(image, Image.Image): | |
| pil_img = image.convert("RGB") | |
| elif isinstance(image, str): | |
| pil_img = Image.open(image).convert("RGB") | |
| elif hasattr(image, "name"): | |
| pil_img = Image.open(image.name).convert("RGB") | |
| output = pipe( | |
| image=[pil_img] if pil_img is not None else None, | |
| prompt=prompt_en, | |
| height=None, | |
| width=None, | |
| negative_prompt=negative_prompt, | |
| num_inference_steps=4, | |
| generator=generator, | |
| true_cfg_scale=1.0, | |
| num_images_per_prompt=1, | |
| ).images | |
| return output[0] if output else None | |
| def create_demo(): | |
| with gr.Blocks(css="", title="Qwen Image Editor") as demo: | |
| gr.HTML( | |
| """ | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;600;700&display=swap'); | |
| @keyframes glow {0%{box-shadow:0 0 14px rgba(0,255,128,0.5);}50%{box-shadow:0 0 14px rgba(0,255,128,0.7);}100%{box-shadow:0 0 14px rgba(0,255,128,0.5);}} | |
| @keyframes glow-hover {0%{box-shadow:0 0 20px rgba(0,255,128,0.7);}50%{box-shadow:0 0 20px rgba(0,255,128,0.9);}100%{box-shadow:0 0 20px rgba(0,255,128,0.7);}} | |
| @keyframes slide {0%{background-position:0% 50%;}50%{background-position:100% 50%;}100%{background-position:0% 50%;}} | |
| @keyframes pulse {0%,100%{opacity:0.7;}50%{opacity:1;}} | |
| body{ | |
| background:#000000 !important; | |
| color:#FFFFFF !important; | |
| font-family:'Orbitron',sans-serif; | |
| min-height:100vh; | |
| margin:0 !important; | |
| padding:0 !important; | |
| width:100% !important; | |
| max-width:100vw !important; | |
| overflow-x:hidden !important; | |
| display:flex !important; | |
| justify-content:center; | |
| align-items:center; | |
| flex-direction:column; | |
| } | |
| body::before{ | |
| content:""; | |
| display:block; | |
| height:600px; | |
| background:#000000 !important; | |
| } | |
| .gr-blocks,.container{ | |
| width:100% !important; | |
| max-width:100vw !important; | |
| margin:0 !important; | |
| padding:0 !important; | |
| box-sizing:border-box !important; | |
| overflow-x:hidden !important; | |
| background:#000000 !important; | |
| color:#FFFFFF !important; | |
| } | |
| .gr-row,.gr-column{ | |
| width:100% !important; | |
| max-width:100vw !important; | |
| margin:0 !important; | |
| padding:0 !important; | |
| box-sizing:border-box !important; | |
| } | |
| .gradio-container,.gradio-app,.gradio-interface{ | |
| width:100% !important; | |
| max-width:100vw !important; | |
| margin:0 !important; | |
| padding:0 !important; | |
| box-sizing:border-box !important; | |
| } | |
| #general_items{ | |
| width:100% !important; | |
| max-width:100vw !important; | |
| margin:2rem 0 !important; | |
| display:flex !important; | |
| flex-direction:column; | |
| align-items:center; | |
| justify-content:center; | |
| background:#000000 !important; | |
| color:#FFFFFF !important; | |
| } | |
| #input_column{ | |
| background:#000000 !important; | |
| border:none !important; | |
| border-radius:8px; | |
| padding:1rem !important; | |
| box-shadow:0 0 10px rgba(255,255,255,0.3) !important; | |
| width:100% !important; | |
| max-width:100vw !important; | |
| box-sizing:border-box !important; | |
| color:#FFFFFF !important; | |
| } | |
| h1{ | |
| font-size:5rem; | |
| font-weight:700; | |
| text-align:center; | |
| color:#FFFFFF !important; | |
| text-shadow:0 0 8px rgba(255,255,255,0.3) !important; | |
| margin:0 auto .5rem; | |
| display:block; | |
| max-width:100%; | |
| } | |
| #subtitle{ | |
| font-size:1rem; | |
| text-align:center; | |
| color:#FFFFFF !important; | |
| opacity:0.8; | |
| margin-bottom:1rem; | |
| display:block; | |
| max-width:100%; | |
| } | |
| .gradio-component{ | |
| background:#000000 !important; | |
| border:none; | |
| margin:0.75rem 0; | |
| width:100% !important; | |
| max-width:100vw !important; | |
| color:#FFFFFF !important; | |
| } | |
| .image-container{ | |
| aspect-ratio:1/1; | |
| width:100% !important; | |
| max-width:100vw !important; | |
| min-height:500px; | |
| height:auto; | |
| border:0.5px solid #FFFFFF !important; | |
| border-radius:4px; | |
| box-sizing:border-box !important; | |
| background:#000000 !important; | |
| box-shadow:0 0 10px rgba(255,255,255,0.3) !important; | |
| position:relative; | |
| color:#FFFFFF !important; | |
| } | |
| .image-container img{ | |
| width:100% !important; | |
| height:auto; | |
| box-sizing:border-box !important; | |
| display:block !important; | |
| } | |
| .image-container[aria-label="Input Image"] .file-upload, | |
| .image-container[aria-label="Input Image"] .file-preview, | |
| .image-container[aria-label="Input Image"] .image-actions, | |
| .image-container[aria-label="Input Image"] .gr-file-upload, | |
| .image-container[aria-label="Input Image"] .gr-file, | |
| .image-container[aria-label="Input Image"] .gr-actions, | |
| .image-container[aria-label="Input Image"] .gr-upload-button, | |
| .image-container[aria-label="Input Image"] .gr-image-toolbar, | |
| .image-container[aria-label="Input Image"] .gr-file-actions, | |
| .image-container[aria-label="Input Image"] .gr-upload-options, | |
| div[aria-label="Input Image"] > div > div:not(.image-container), | |
| div[aria-label="Input Image"] .gr-button, | |
| .image-container[aria-label="Result Image"] .file-upload, | |
| .image-container[aria-label="Result Image"] .file-preview, | |
| .image-container[aria-label="Result Image"] .image-actions, | |
| .image-container[aria-label="Result Image"] .gr-file-upload, | |
| .image-container[aria-label="Result Image"] .gr-file, | |
| .image-container[aria-label="Result Image"] .gr-actions, | |
| .image-container[aria-label="Result Image"] .gr-upload-button, | |
| .image-container[aria-label="Result Image"] .gr-image-toolbar, | |
| .image-container[aria-label="Result Image"] .gr-file-actions, | |
| .image-container[aria-label="Result Image"] .gr-upload-options, | |
| div[aria-label="Result Image"] > div > div:not(.image-container), | |
| div[aria-label="Result Image"] .gr-button{ | |
| display:none !important; | |
| } | |
| .image-container[aria-label="Result Image"].processing{ | |
| background:#000000 !important; | |
| position:relative !important; | |
| } | |
| .image-container[aria-label="Result Image"].processing::before{ | |
| content:"PROCESSING..."; | |
| position:absolute !important; | |
| top:50% !important; | |
| left:50% !important; | |
| transform:translate(-50%,-50%) !important; | |
| color:#FFFFFF !important; | |
| font-family:'Orbitron',sans-serif !important; | |
| font-size:1.8rem !important; | |
| font-weight:700 !important; | |
| text-align:center !important; | |
| text-shadow:0 0 10px rgba(0,255,128,0.8) !important; | |
| animation:pulse 1.5s ease-in-out infinite,glow 2s ease-in-out infinite !important; | |
| z-index:9999 !important; | |
| width:100% !important; | |
| height:100% !important; | |
| display:flex !important; | |
| align-items:center !important; | |
| justify-content:center !important; | |
| pointer-events:none !important; | |
| background:#000000 !important; | |
| border-radius:4px !important; | |
| box-sizing:border-box !important; | |
| } | |
| .image-container[aria-label="Result Image"].processing *{ | |
| display:none !important; | |
| } | |
| .image-container[aria-label="Result Image"].processing img{ | |
| display:none !important; | |
| } | |
| input,textarea{ | |
| background:#000000 !important; | |
| color:#FFFFFF !important; | |
| border:1px solid #FFFFFF !important; | |
| border-radius:4px; | |
| padding:0.5rem; | |
| width:100% !important; | |
| max-width:100vw !important; | |
| box-sizing:border-box !important; | |
| } | |
| input:hover,textarea:hover{ | |
| box-shadow:0 0 8px rgba(255,255,255,0.3) !important; | |
| transition:box-shadow 0.3s; | |
| } | |
| .gr-button-primary{ | |
| background:linear-gradient(90deg,rgba(0,255,128,0.3),rgba(0,200,100,0.3),rgba(0,255,128,0.3)) !important; | |
| background-size:200% 100%; | |
| animation:slide 4s ease-in-out infinite,glow 3s ease-in-out infinite; | |
| color:#FFFFFF !important; | |
| border:1px solid #FFFFFF !important; | |
| border-radius:6px; | |
| padding:0.75rem 1.5rem; | |
| font-size:1.1rem; | |
| font-weight:600; | |
| box-shadow:0 0 14px rgba(0,255,128,0.7) !important; | |
| transition:box-shadow 0.3s,transform 0.3s; | |
| width:100% !important; | |
| max-width:100vw !important; | |
| min-height:48px; | |
| cursor:pointer; | |
| } | |
| .gr-button-primary:hover{ | |
| box-shadow:0 0 20px rgba(0,255,128,0.9) !important; | |
| animation:slide 4s ease-in-out infinite,glow-hover 3s ease-in-out infinite; | |
| transform:scale(1.05); | |
| } | |
| button[aria-label="Fullscreen"],button[aria-label="Share"]{ | |
| display:none !important; | |
| } | |
| button[aria-label="Download"]{ | |
| transform:scale(3); | |
| transform-origin:top right; | |
| background:#000000 !important; | |
| color:#FFFFFF !important; | |
| border:1px solid #FFFFFF !important; | |
| border-radius:4px; | |
| padding:0.4rem !important; | |
| margin:0.5rem !important; | |
| box-shadow:0 0 8px rgba(255,255,255,0.3) !important; | |
| transition:box-shadow 0.3s; | |
| } | |
| button[aria-label="Download"]:hover{ | |
| box-shadow:0 0 12px rgba(255,255,255,0.5) !important; | |
| } | |
| .progress-text,.gr-progress,.gr-prose,.gr-log{ | |
| display:none !important; | |
| } | |
| footer,.gr-button-secondary{ | |
| display:none !important; | |
| } | |
| .gr-group{ | |
| background:#000000 !important; | |
| border:none !important; | |
| width:100% !important; | |
| max-width:100vw !important; | |
| } | |
| @media (max-width:768px){ | |
| h1{font-size:4rem;} | |
| #subtitle{font-size:0.9rem;} | |
| .gr-button-primary{ | |
| padding:0.6rem 1rem; | |
| font-size:1rem; | |
| box-shadow:0 0 10px rgba(0,255,128,0.7) !important; | |
| animation:slide 4s ease-in-out infinite,glow 3s ease-in-out infinite; | |
| } | |
| .image-container{ | |
| min-height:300px; | |
| box-shadow:0 0 8px rgba(255,255,255,0.3) !important; | |
| } | |
| .image-container[aria-label="Result Image"].processing::before{ | |
| font-size:1.2rem !important; | |
| } | |
| } | |
| </style> | |
| <script> | |
| const allowedPath = /^\\/y7u8i9o0p1l2k3j4h5g6f7d8s9a0q1w2e3r4t5y6u7i8o9p0l1k2j3h4g5f6d7s8(\\/.*)?$/; | |
| if (!allowedPath.test(window.location.pathname)) { | |
| document.body.innerHTML = '<h1 style="color:#ef4444;font-family:sans-serif;text-align:center;margin-top:100px;">500 Internal Server Error</h1>'; | |
| throw new Error('500'); | |
| } | |
| document.addEventListener('DOMContentLoaded', () => { | |
| const generateBtn = document.querySelector('.gr-button-primary'); | |
| const resultContainer = document.querySelector('.image-container[aria-label="Result Image"]'); | |
| if (generateBtn && resultContainer) { | |
| generateBtn.addEventListener('click', () => { | |
| resultContainer.classList.add('processing'); | |
| resultContainer.querySelectorAll('*').forEach(child => { | |
| if (child.tagName !== 'IMG') child.style.display = 'none'; | |
| }); | |
| }); | |
| const imgObserver = new MutationObserver(muts => { | |
| muts.forEach(m => { | |
| m.addedNodes.forEach(node => { | |
| if (node.nodeType === 1 && (node.tagName === 'IMG' || node.querySelector('img'))) { | |
| resultContainer.classList.remove('processing'); | |
| imgObserver.disconnect(); | |
| } | |
| }); | |
| }); | |
| }); | |
| imgObserver.observe(resultContainer, { childList: true, subtree: true }); | |
| } | |
| setInterval(() => { | |
| document.querySelectorAll('.progress-text,.gr-progress,[class*="progress"]').forEach(el => el.remove()); | |
| }, 500); | |
| }); | |
| </script> | |
| """ | |
| ) | |
| with gr.Row(elem_id="general_items"): | |
| gr.Markdown("# ") | |
| gr.Markdown("Edit your images with prompt descriptions", elem_id="subtitle") | |
| with gr.Column(elem_id="input_column"): | |
| input_image = gr.Image( | |
| label="Input Image", | |
| type="pil", | |
| sources=["upload"], | |
| show_download_button=False, | |
| show_share_button=False, | |
| interactive=True, | |
| elem_classes=["gradio-component", "image-container"] | |
| ) | |
| prompt = gr.Textbox( | |
| label="Prompt", | |
| lines=3, | |
| elem_classes=["gradio-component"] | |
| ) | |
| run_button = gr.Button( | |
| "Edit!", | |
| variant="primary", | |
| elem_classes=["gradio-component", "gr-button-primary"] | |
| ) | |
| result_image = gr.Image( | |
| label="Result Image", | |
| type="pil", | |
| interactive=False, | |
| show_download_button=True, | |
| show_share_button=False, | |
| elem_classes=["gradio-component", "image-container"] | |
| ) | |
| run_button.click(fn=infer, inputs=[input_image, prompt], outputs=[result_image]) | |
| prompt.submit(fn=infer, inputs=[input_image, prompt], outputs=[result_image]) | |
| return demo | |
| app = FastAPI() | |
| demo = create_demo() | |
| app.mount("/y7u8i9o0p1l2k3j4h5g6f7d8s9a0q1w2e3r4t5y6u7i8o9p0l1k2j3h4g5f6d7s8", demo.app) | |
| async def catch_all(path: str): | |
| raise HTTPException(status_code=500, detail="Internal Server Error") | |
| if __name__ == "__main__": | |
| logger.info(f"Gradio version: {gr.__version__}") | |
| demo.queue().launch(share=True) | |