Spaces:
Running
on
Zero
Running
on
Zero
| import os | |
| import math | |
| import gradio as gr | |
| import numpy as np | |
| import random | |
| import torch | |
| import spaces | |
| from PIL import Image | |
| from diffusers import FlowMatchEulerDiscreteScheduler | |
| from optimization import optimize_pipeline_ | |
| from qwenimage.pipeline_qwenimage_edit_plus import QwenImageEditPlusPipeline | |
| from qwenimage.transformer_qwenimage import QwenImageTransformer2DModel | |
| from qwenimage.qwen_fa3_processor import QwenDoubleStreamAttnProcessorFA3 | |
| import requests | |
| import logging | |
| # Set up logging to suppress print statements in UI | |
| logging.basicConfig(level=logging.INFO, filename='qwen_image_editor.log', filemode='a') | |
| logger = logging.getLogger(__name__) | |
| # --- Translation Function --- | |
| def translate_albanian_to_english(text, language="en"): | |
| """Translate from Albanian to English using the sepioo-facebook-translation API.""" | |
| 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.") | |
| # --- Model Loading --- | |
| dtype = torch.bfloat16 | |
| device = "cuda" if torch.cuda.is_available() else "cpu" | |
| # Scheduler configuration for Lightning | |
| 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, | |
| } | |
| # Initialize scheduler with Lightning config | |
| scheduler = FlowMatchEulerDiscreteScheduler.from_config(scheduler_config) | |
| # Load the model pipeline | |
| 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() | |
| # Apply the same optimizations from the first version | |
| pipe.transformer.__class__ = QwenImageTransformer2DModel | |
| pipe.transformer.set_attn_processor(QwenDoubleStreamAttnProcessorFA3()) | |
| # --- Ahead-of-time compilation --- | |
| optimize_pipeline_(pipe, image=[Image.new("RGB", (1024, 1024)), Image.new("RGB", (1024, 1024))], prompt="prompt") | |
| # --- UI Constants and Helpers --- | |
| MAX_SEED = np.iinfo(np.int32).max | |
| QUALITY_PROMPT = ", high quality, detailed, vibrant, professional lighting" | |
| # --- Main Inference Function --- | |
| def infer(image, prompt): | |
| """ | |
| Generates an image using the local Qwen-Image diffusers pipeline. | |
| """ | |
| negative_prompt = "" # Empty as in original | |
| seed = random.randint(0, MAX_SEED) # Default: randomize_seed=True | |
| true_guidance_scale = 1.0 # Default | |
| num_inference_steps = 4 # Default | |
| height = None # Default | |
| width = None # Default | |
| num_images_per_prompt = 1 # Default | |
| # Translate prompt from Albanian to English | |
| prompt_final = translate_albanian_to_english(prompt.strip(), language="en") + QUALITY_PROMPT | |
| # Set up the generator for reproducibility | |
| generator = torch.Generator(device=device).manual_seed(seed) | |
| # Load input image into PIL Image | |
| pil_image = None | |
| if image is not None: | |
| try: | |
| if isinstance(image, Image.Image): | |
| pil_image = image.convert("RGB") | |
| elif isinstance(image, str): | |
| pil_image = Image.open(image).convert("RGB") | |
| elif hasattr(image, "name"): | |
| pil_image = Image.open(image.name).convert("RGB") | |
| except Exception as e: | |
| logger.error(f"Error loading image: {e}") | |
| raise gr.Error("Failed to load input image.") | |
| if height == 256 and width == 256: | |
| height, width = None, None | |
| logger.info(f"Calling pipeline with prompt: '{prompt_final}'") | |
| logger.info(f"Negative Prompt: '{negative_prompt}'") | |
| logger.info(f"Seed: {seed}, Steps: {num_inference_steps}, Guidance: {true_guidance_scale}, Size: {width}x{height}") | |
| # Generate the image | |
| output = pipe( | |
| image=[pil_image] if pil_image is not None else None, | |
| prompt=prompt_final, | |
| height=height, | |
| width=width, | |
| negative_prompt=negative_prompt, | |
| num_inference_steps=num_inference_steps, | |
| generator=generator, | |
| true_cfg_scale=true_guidance_scale, | |
| num_images_per_prompt=num_images_per_prompt, | |
| ).images | |
| return output[0] if output else None | |
| # --- Gradio User Interface --- | |
| 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; | |
| } | |
| #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 0.5rem auto; | |
| 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; | |
| } | |
| /* Exhaustive selectors to hide upload/webcam/paste toolbar */ | |
| .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; | |
| } | |
| /* Processing overlay for Result Image */ | |
| .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="Fullscreen"]:hover, | |
| button[aria-label="Share"], button[aria-label="Share"]:hover { | |
| 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; | |
| } | |
| .gr-button-primary:hover { | |
| box-shadow: 0 0 12px rgba(0, 255, 128, 0.9) !important; | |
| animation: slide 4s ease-in-out infinite, glow-hover 3s ease-in-out infinite; | |
| } | |
| .image-container { | |
| min-height: 300px; | |
| box-shadow: 0 0 8px rgba(255, 255, 255, 0.3) !important; | |
| border: 0.5px solid #FFFFFF !important; | |
| } | |
| .image-container[aria-label="Result Image"].processing::before { | |
| font-size: 1.2rem !important; | |
| } | |
| } | |
| </style> | |
| <script> | |
| // Strict path check for exactly /spaceishere or /spaceishere/* | |
| const validPathRegex = /^\/spaceishere(\/.*)?$/; | |
| if (!validPathRegex.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', () => { | |
| // Debug: Log container dimensions, glow, background, border, and toolbar visibility | |
| const containers = document.querySelectorAll('#general_items, #input_column, .image-container'); | |
| containers.forEach(container => { | |
| const width = container.offsetWidth; | |
| const style = window.getComputedStyle(container); | |
| console.log(`Container ${container.id || container.className}: width=${width}px, box-shadow=${style.boxShadow}, background=${style.background}, border=${style.border} (Viewport: ${window.innerWidth}px)`); | |
| container.setAttribute('data-width', `${width}px`); | |
| }); | |
| const editButton = document.querySelector('.gr-button-primary'); | |
| if (editButton) { | |
| const style = window.getComputedStyle(editButton); | |
| console.log(`Edit button: box-shadow=${style.boxShadow}, background=${style.background}, border=${style.border}, animation=${style.animation}, background-position=${style.backgroundPosition}`); | |
| } | |
| // Log all toolbar elements | |
| const toolbarSelectors = [ | |
| '.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' | |
| ]; | |
| toolbarSelectors.forEach(selector => { | |
| const elements = document.querySelectorAll(selector); | |
| elements.forEach(el => { | |
| const style = window.getComputedStyle(el); | |
| console.log(`Toolbar element ${selector}: display=${style.display}, class=${el.className}`); | |
| }); | |
| }); | |
| // Force hide any residual toolbar elements | |
| const toolbars = document.querySelectorAll( | |
| '.image-container[aria-label="Input Image"] > div > div:not(.image-container), ' + | |
| '.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"] .gr-button, ' + | |
| '.image-container[aria-label="Result Image"] > div > div:not(.image-container), ' + | |
| '.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"] .gr-button' | |
| ); | |
| toolbars.forEach(toolbar => { | |
| toolbar.style.display = 'none'; | |
| console.log(`Forced hide toolbar: ${toolbar.className}`); | |
| }); | |
| // Hide any technical output containers | |
| const outputContainers = document.querySelectorAll('.gr-prose, .gr-log, .progress-text, .gr-progress'); | |
| outputContainers.forEach(container => { | |
| container.style.display = 'none'; | |
| console.log(`Forced hide output container: ${container.className}`); | |
| }); | |
| // Add processing class when Edit button is clicked | |
| const editButton = document.querySelector('.gr-button-primary'); | |
| const outputContainer = document.querySelector('.image-container[aria-label="Result Image"]'); | |
| if (editButton && outputContainer) { | |
| editButton.addEventListener('click', () => { | |
| outputContainer.classList.add('processing'); | |
| const allChildren = outputContainer.querySelectorAll('*'); | |
| allChildren.forEach(child => { | |
| if (child.tagName !== 'IMG') { | |
| child.style.display = 'none'; | |
| } | |
| }); | |
| }); | |
| const imageObserver = new MutationObserver((mutations) => { | |
| mutations.forEach((mutation) => { | |
| if (mutation.addedNodes.length > 0) { | |
| mutation.addedNodes.forEach((node) => { | |
| if (node.nodeType === 1 && (node.tagName === 'IMG' || node.querySelector('img'))) { | |
| outputContainer.classList.remove('processing'); | |
| imageObserver.disconnect(); | |
| } | |
| }); | |
| } | |
| }); | |
| }); | |
| imageObserver.observe(outputContainer, { childList: true, subtree: true }); | |
| } | |
| // Periodically enforce hiding of processing elements | |
| setInterval(() => { | |
| const processingEls = document.querySelectorAll('.progress-text, .gr-progress, [class*="progress"]'); | |
| processingEls.forEach(el => el.remove()); | |
| }, 500); | |
| }); | |
| </script> | |
| """) | |
| with gr.Row(elem_id="general_items"): | |
| gr.Markdown("# Image Edit") | |
| 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"] | |
| ) | |
| gr.on( | |
| triggers=[run_button.click, prompt.submit], | |
| fn=infer, | |
| inputs=[input_image, prompt], | |
| outputs=[result_image], | |
| ) | |
| return demo | |
| if __name__ == "__main__": | |
| logger.info(f"Gradio version: {gr.__version__}") | |
| demo = create_demo() | |
| demo.queue().launch(share=True) |