import os import gradio as gr import numpy as np from PIL import Image import tempfile import sys import subprocess from pathlib import Path from typing import Optional import re import spaces # NOTE: This script assumes it is located in the project root directory. PYTHON_EXECUTABLE = sys.executable # Or specify a path like "/path/to/your/python" # Construct paths relative to this app's location in the root. APP_DIR = os.path.dirname(os.path.abspath(__file__)) MAIN_SCRIPT_PATH = os.path.join(APP_DIR, "src", "main.py") EXAMPLES_PATH = os.path.join(APP_DIR, "examples") # --- Gradio Interface --- @spaces.GPU def run_main_script( src_cond_image_np: np.ndarray, tgt_cond_image_np: np.ndarray, original_mv_image_np: np.ndarray, t_steps: int, n_max: int, src_gs: float, tar_gs: float, seed: int, progress=gr.Progress(), ): """ Wrapper function for Gradio to prepare files and run the main.py script. """ # Ensure consistent types for filename generation and command-line args. tar_gs = float(tar_gs) n_max = int(n_max) t_steps = int(t_steps) src_gs = float(src_gs) seed = int(seed) if ( src_cond_image_np is None or tgt_cond_image_np is None or original_mv_image_np is None ): raise gr.Error("Please provide all three input images.") if not os.path.exists(MAIN_SCRIPT_PATH): raise gr.Error( f"Main script not found at '{MAIN_SCRIPT_PATH}'. Please update the MAIN_SCRIPT_PATH variable in the app." ) progress(0, desc="Preparing experiment...") with tempfile.TemporaryDirectory() as exp_dir: # Save uploaded images to the required filenames src_cond_img = Image.fromarray(src_cond_image_np).convert("RGB") tgt_cond_img = Image.fromarray(tgt_cond_image_np).convert("RGB") original_mv_img = Image.fromarray(original_mv_image_np).convert("RGB") src_cond_img.save(os.path.join(exp_dir, "src.png")) tgt_cond_img.save(os.path.join(exp_dir, "edited.png")) original_mv_img.save(os.path.join(exp_dir, "src_mv.png")) # Construct the command to run main.py cmd = [ PYTHON_EXECUTABLE, "-u", # Force unbuffered stdout MAIN_SCRIPT_PATH, "--exp_dir", exp_dir, "--T_steps", str(t_steps), "--n_max", str(n_max), "--src_guidance_scale", str(src_gs), "--tar_guidance_scale", str(tar_gs), "--seed", str(seed), ] print(f"šŸš€ Running command: {' '.join(cmd)}") progress(0.05, desc="Executing main script...") # Execute the script as a subprocess and stream the output. process = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, # Merge stderr with stdout text=True, bufsize=1, universal_newlines=True, ) # Regex to find the main tqdm progress in the format "current/total [time 1: percent = current_step / total_steps # Update progress bar with a clean description progress(percent, desc=f"Denoising...") # Wait for the process to finish and get the return code process.wait() # Check for errors after the process has completed if process.returncode != 0: print(f"āŒ Script failed with exit code: {process.returncode}") raise gr.Error(f"Script execution failed. Check console for details.") print("\nāœ… Script executed successfully.") # Find the output file output_dir = os.path.join(exp_dir, "output") output_filename = f"result_tgs_{tar_gs}_nmax_{n_max}.png" save_path = os.path.join(output_dir, output_filename) progress(0.98, desc="Loading result...") if not os.path.exists(save_path): print(f"āŒ Output file not found at expected path: {save_path}") raise gr.Error( "Output file was not created by the script. Check console logs for errors." ) result_img = Image.open(save_path) progress(1, desc="Done.") return result_img def clear_inputs(): """Resets all input fields to their default state and makes them interactive.""" return { original_mv_image: None, src_cond_image: None, tgt_cond_image: None, t_steps: gr.Slider(value=50, interactive=True), n_max: gr.Slider(value=31, interactive=True), src_gs: gr.Slider(value=3.5, interactive=True), tar_gs: gr.Slider(value=5.0, interactive=True), seed: gr.Slider(value=18, interactive=True), output_image: None, } # --- Markdown Content for UI --- ABOUT_TEXT = """

EditP23: 3D Editing via Propagation of Image Prompts to Multi-View

ā–¶ļø Project Page šŸ“„ arXiv šŸ’» GitHub

This is the official Gradio demo for EditP23, a method for fast, mask-free 3D editing that propagates 2D image edits to multi-view representations in a 3D-consistent manner. The edit is guided by an image pair, allowing users to leverage any preferred 2D editing tool, from manual painting to generative pipelines.

""" HOW_TO_USE_TEXT = """

Understanding the Inputs

EditP23 requires three specific images to perform an edit. This demo automates the process, but understanding each component is key.

  1. Original Multi-View Image (`src_mv.png`): This is a 2x3 grid of six different views of the original, unedited object. The model uses this as the base to apply the edit consistently across all angles.
  2. Source Condition (`src.png`): This is a single, frontal view of the original object. It acts as the "before" image for the edit.
  3. Target Condition (`edited.png`): This is the "after" image. It's the same view as src.png, but with your desired 2D modification applied. The difference between this image and src.png is what guides the 3D edit.

How to Prepare Your Own Images

You can generate the required input images using the helper scripts provided in our GitHub repository.

Step 1: Generate src.png and src_mv.png

You have two options for creating the initial views of your object.

Step 2: Create edited.png

Use any 2D image editor to modify your src.png. This is where your creativity comes in! For quick edits, we recommend these online tools:


Understanding the Parameters


Reconstructing a 3D Model

After this demo generates an edited multi-view image, you can use the scripts/recon.py script from our repository to convert it back into a 3D model (.obj file).

python scripts/recon.py path/to/instant-mesh-large.yaml --input_file "path/to/edited_mv.png" --output_dir "path/to/output/"
""" # --- Gradio UI Layout --- # Create a custom theme to match the website's color theme = gr.themes.Base( primary_hue=gr.themes.colors.blue, secondary_hue=gr.themes.colors.blue, font=[gr.themes.GoogleFont("Inter"), "ui-sans-serif", "system-ui", "sans-serif"], ).set( button_primary_background_fill="*primary_500", button_primary_background_fill_hover="*primary_600", ) # Custom CSS for better layout and fixing UI quirks CUSTOM_CSS = """ .gradio-container { max-width: 95% !important; } .label-wrap { padding-top: 6px !import ant; } /* Fix label overlap */ .help-text { color: #9CA3AF; font-size: 0.9rem; margin-top: 4px; margin-bottom: 12px; } .link-button { text-decoration: none; color: white; padding: 8px 16px; border-radius: 8px; font-weight: bold; transition: background-color 0.2s ease; } .link-button:hover { background-color: #4a5568 !important; } #action-buttons { margin-top: 1rem; } /* --- CSS Rules for the Examples Table --- */ /* 1. CRITICAL FIX: Target the image's wrapper to prevent clipping. */ #example-table td > div { overflow: visible !important; /* This is the key to stop cropping. */ display: flex; justify-content: center; align-items: center; } /* 2. General cell styling for alignment and spacing */ #example-table td { vertical-align: middle !important; padding: 8px !important; } /* 3. Force parameter columns (4-7) to have the same width */ #example-table th:nth-child(n+4):nth-child(-n+7), #example-table td:nth-child(n+4):nth-child(-n+7) { width: 85px !important; max-width: 85px !important; text-align: center; word-break: break-word; } /* 4. Enlarge multi-view image (Col 1) with a 3:2 height:width ratio */ #example-table td:nth-child(1) img { height: 180px !important; width: 120px !important; /* 180px / 120px = 3:2 ratio */ object-fit: contain !important; /* Ensures the whole image is visible */ } /* 5. Enlarge condition images (Col 2 & 3) */ #example-table td:nth-child(2) img, #example-table td:nth-child(3) img { height: 150px !important; width: 150px !important; object-fit: contain !important; } """ with gr.Blocks(theme=theme, css=CUSTOM_CSS) as demo: gr.Markdown(ABOUT_TEXT) with gr.Tabs() as tabs: with gr.TabItem("Interactive Demo", id=0): with gr.Row(variant="panel", equal_height=False): # Column 1: Inputs with gr.Column(scale=1): gr.Markdown("### 1. Input Images") gr.Markdown( 'See the "How to Use" tab for details on generating the **Multi-View Image** and creating your own **Edited Condition**.', elem_classes="help-text", ) original_mv_image = gr.Image( type="numpy", label="Original Multi-View Image (src_mv.png)", height=675, width=450, ) with gr.Row(): src_cond_image = gr.Image( type="numpy", label="Source Condition (src.png)", height=350, width=350, ) tgt_cond_image = gr.Image( type="numpy", label="Target Condition (edited.png)", height=350, width=350, ) # Column 2: Parameters & Action with gr.Column(scale=1, min_width=300): gr.Markdown("### 2. Parameters") with gr.Accordion("Advanced Parameters", open=True): t_steps = gr.Slider( minimum=1, maximum=100, value=50, step=1, label="T_steps", info="Total number of denoising steps.", ) n_max = gr.Slider( minimum=1, maximum=50, value=31, step=1, label="n_max", info="Number of scheduler steps for edit-aware guidance. Increase for more significant edits.", ) src_gs = gr.Slider( minimum=1.0, maximum=10.0, value=3.5, step=0.1, label="Source CFG", info="Guidance scale for the source condition. Can typically remain constant.", ) tar_gs = gr.Slider( minimum=1.0, maximum=30.0, value=5.0, step=0.1, label="Target CFG", info="Guidance scale for the target condition. Increase for more significant edits.", ) seed = gr.Slider( minimum=0, maximum=10000, value=18, step=1, label="Seed", info="Random seed for reproducibility.", ) with gr.Row(elem_id="action-buttons"): clear_button = gr.Button("Clear", variant="secondary", scale=1) run_button = gr.Button("Generate", variant="primary", scale=2) # Column 3: Output with gr.Column(scale=2, min_width=350): gr.Markdown("### 3. Output Image") output_image = gr.Image( type="pil", label="Edited Result", height=450, width=450, interactive=False, ) gr.Markdown( 'After generating, you can use the `recon.py` script to create a 3D model. See the "How to Use" tab for the full command.', elem_classes="help-text", ) # --- Examples Section --- if os.path.exists(EXAMPLES_PATH): gr.Markdown("---") gr.Markdown("### Click an Example to Load") example_inputs = [ original_mv_image, src_cond_image, tgt_cond_image, t_steps, n_max, src_gs, tar_gs, ] example_data = [ [ os.path.join(EXAMPLES_PATH, "bike_vintage", "src_mv.png"), os.path.join(EXAMPLES_PATH, "bike_vintage", "src.png"), os.path.join(EXAMPLES_PATH, "bike_vintage", "edited.png"), 50, 31, 3.5, 5.0, 18, ], [ os.path.join(EXAMPLES_PATH, "robot_sunglasses", "src_mv.png"), os.path.join(EXAMPLES_PATH, "robot_sunglasses", "src.png"), os.path.join(EXAMPLES_PATH, "robot_sunglasses", "edited.png"), 50, 31, 3.5, 5.0, 18, ], [ os.path.join(EXAMPLES_PATH, "stormtrooper_donut", "src_mv.png"), os.path.join(EXAMPLES_PATH, "stormtrooper_donut", "src.png"), os.path.join(EXAMPLES_PATH, "stormtrooper_donut", "edited.png"), 50, 42, 3.5, 12.0, 18, ], [ os.path.join(EXAMPLES_PATH, "figure_zombie", "src_mv.png"), os.path.join(EXAMPLES_PATH, "figure_zombie", "src.png"), os.path.join(EXAMPLES_PATH, "figure_zombie", "edited.png"), 50, 31, 3.5, 5.0, 18, ], [ os.path.join(EXAMPLES_PATH, "deer_pixar", "src_mv.png"), os.path.join(EXAMPLES_PATH, "deer_pixar", "src.png"), os.path.join(EXAMPLES_PATH, "deer_pixar", "edited.png"), 50, 31, 3.5, 5.0, 18, ], [ os.path.join(EXAMPLES_PATH, "german-shep_plush", "src_mv.png"), os.path.join(EXAMPLES_PATH, "german-shep_plush", "src.png"), os.path.join(EXAMPLES_PATH, "german-shep_plush", "edited.png"), 50, 41, 3.5, 6.0, 18, ], [ os.path.join(EXAMPLES_PATH, "deer_wings", "src_mv.png"), os.path.join(EXAMPLES_PATH, "deer_wings", "src.png"), os.path.join(EXAMPLES_PATH, "deer_wings", "edited.png"), 50, 39, 3.5, 21.0, 18, ], [ os.path.join(EXAMPLES_PATH, "lego-car_spoiler", "src_mv.png"), os.path.join(EXAMPLES_PATH, "lego-car_spoiler", "src.png"), os.path.join(EXAMPLES_PATH, "lego-car_spoiler", "edited.png"), 50, 42, 3.5, 12.0, 18, ], [ os.path.join(EXAMPLES_PATH, "batman_jetpack", "src_mv.png"), os.path.join(EXAMPLES_PATH, "batman_jetpack", "src.png"), os.path.join(EXAMPLES_PATH, "batman_jetpack", "edited.png"), 50, 31, 3.5, 5.0, 18, ], [ os.path.join(EXAMPLES_PATH, "bike_sport", "src_mv.png"), os.path.join(EXAMPLES_PATH, "bike_sport", "src.png"), os.path.join(EXAMPLES_PATH, "bike_sport", "edited.png"), 50, 31, 3.5, 5.0, 18, ], [ os.path.join(EXAMPLES_PATH, "red-dragon_tail", "src_mv.png"), os.path.join(EXAMPLES_PATH, "red-dragon_tail", "src.png"), os.path.join(EXAMPLES_PATH, "red-dragon_tail", "edited.png"), 50, 41, 3.5, 6.0, 18, ], [ os.path.join(EXAMPLES_PATH, "cake_oreo", "src_mv.png"), os.path.join(EXAMPLES_PATH, "cake_oreo", "src.png"), os.path.join(EXAMPLES_PATH, "cake_oreo", "edited.png"), 50, 31, 3.5, 5.0, 18, ], [ os.path.join(EXAMPLES_PATH, "bike_harley", "src_mv.png"), os.path.join(EXAMPLES_PATH, "bike_harley", "src.png"), os.path.join(EXAMPLES_PATH, "bike_harley", "edited.png"), 50, 31, 3.5, 5.0, 18, ], [ os.path.join(EXAMPLES_PATH, "bike_modern", "src_mv.png"), os.path.join(EXAMPLES_PATH, "bike_modern", "src.png"), os.path.join(EXAMPLES_PATH, "bike_modern", "edited.png"), 50, 31, 3.5, 5.0, 18, ], [ os.path.join(EXAMPLES_PATH, "bmw_speedy", "src_mv.png"), os.path.join(EXAMPLES_PATH, "bmw_speedy", "src.png"), os.path.join(EXAMPLES_PATH, "bmw_speedy", "edited.png"), 50, 31, 3.5, 5.0, 18, ], [ os.path.join(EXAMPLES_PATH, "batman_backpack", "src_mv.png"), os.path.join(EXAMPLES_PATH, "batman_backpack", "src.png"), os.path.join(EXAMPLES_PATH, "batman_backpack", "edited.png"), 50, 31, 3.5, 5.0, 18, ], [ os.path.join(EXAMPLES_PATH, "figure_backpack", "src_mv.png"), os.path.join(EXAMPLES_PATH, "figure_backpack", "src.png"), os.path.join(EXAMPLES_PATH, "figure_backpack", "edited.png"), 50, 31, 3.5, 5.0, 18, ], [ os.path.join(EXAMPLES_PATH, "car_cartoon", "src_mv.png"), os.path.join(EXAMPLES_PATH, "car_cartoon", "src.png"), os.path.join(EXAMPLES_PATH, "car_cartoon", "edited.png"), 50, 31, 3.5, 5.0, 18, ], [ os.path.join(EXAMPLES_PATH, "car_engine", "src_mv.png"), os.path.join(EXAMPLES_PATH, "car_engine", "src.png"), os.path.join(EXAMPLES_PATH, "car_engine", "edited.png"), 50, 31, 3.5, 5.0, 18, ], [ os.path.join(EXAMPLES_PATH, "car_steampunk", "src_mv.png"), os.path.join(EXAMPLES_PATH, "car_steampunk", "src.png"), os.path.join(EXAMPLES_PATH, "car_steampunk", "edited.png"), 50, 41, 3.5, 6.0, 18, ], [ os.path.join(EXAMPLES_PATH, "green-dragon_skirt", "src_mv.png"), os.path.join(EXAMPLES_PATH, "green-dragon_skirt", "src.png"), os.path.join(EXAMPLES_PATH, "green-dragon_skirt", "edited.png"), 50, 41, 3.5, 6.0, 18, ], [ os.path.join(EXAMPLES_PATH, "gazebo_pagoda", "src_mv.png"), os.path.join(EXAMPLES_PATH, "gazebo_pagoda", "src.png"), os.path.join(EXAMPLES_PATH, "gazebo_pagoda", "edited.png"), 50, 41, 3.5, 6.0, 18, ], [ os.path.join(EXAMPLES_PATH, "oasis_magical", "src_mv.png"), os.path.join(EXAMPLES_PATH, "oasis_magical", "src.png"), os.path.join(EXAMPLES_PATH, "oasis_magical", "edited.png"), 50, 39, 3.5, 5.0, 18, ], [ os.path.join(EXAMPLES_PATH, "cabin_alpine", "src_mv.png"), os.path.join(EXAMPLES_PATH, "cabin_alpine", "src.png"), os.path.join(EXAMPLES_PATH, "cabin_alpine", "edited.png"), 50, 42, 3.5, 12.0, 18, ], [ os.path.join(EXAMPLES_PATH, "cabin_gothic", "src_mv.png"), os.path.join(EXAMPLES_PATH, "cabin_gothic", "src.png"), os.path.join(EXAMPLES_PATH, "cabin_gothic", "edited.png"), 50, 42, 3.5, 12.0, 18, ], [ os.path.join(EXAMPLES_PATH, "fox_tuxedo", "src_mv.png"), os.path.join(EXAMPLES_PATH, "fox_tuxedo", "src.png"), os.path.join(EXAMPLES_PATH, "fox_tuxedo", "edited.png"), 50, 31, 3.5, 5.0, 18, ], [ os.path.join(EXAMPLES_PATH, "cabin_haunted", "src_mv.png"), os.path.join(EXAMPLES_PATH, "cabin_haunted", "src.png"), os.path.join(EXAMPLES_PATH, "cabin_haunted", "edited.png"), 50, 42, 3.5, 12.0, 18, ], [ os.path.join(EXAMPLES_PATH, "fox_eyes", "src_mv.png"), os.path.join(EXAMPLES_PATH, "fox_eyes", "src.png"), os.path.join(EXAMPLES_PATH, "fox_eyes", "edited.png"), 50, 31, 3.5, 5.0, 18, ], [ os.path.join(EXAMPLES_PATH, "gazebo_disney", "src_mv.png"), os.path.join(EXAMPLES_PATH, "gazebo_disney", "src.png"), os.path.join(EXAMPLES_PATH, "gazebo_disney", "edited.png"), 50, 31, 3.5, 5.0, 18, ], [ os.path.join(EXAMPLES_PATH, "desk_wizard", "src_mv.png"), os.path.join(EXAMPLES_PATH, "desk_wizard", "src.png"), os.path.join(EXAMPLES_PATH, "desk_wizard", "edited.png"), 50, 42, 3.5, 12.0, 18, ], [ os.path.join(EXAMPLES_PATH, "gazebo_light", "src_mv.png"), os.path.join(EXAMPLES_PATH, "gazebo_light", "src.png"), os.path.join(EXAMPLES_PATH, "gazebo_light", "edited.png"), 50, 31, 3.5, 5.0, 18, ], [ os.path.join(EXAMPLES_PATH, "gazebo_roof", "src_mv.png"), os.path.join(EXAMPLES_PATH, "gazebo_roof", "src.png"), os.path.join(EXAMPLES_PATH, "gazebo_roof", "edited.png"), 50, 39, 3.5, 5.0, 18, ], [ os.path.join(EXAMPLES_PATH, "grogu_earphones", "src_mv.png"), os.path.join(EXAMPLES_PATH, "grogu_earphones", "src.png"), os.path.join(EXAMPLES_PATH, "grogu_earphones", "edited.png"), 50, 41, 3.5, 6.0, 18, ], [ os.path.join(EXAMPLES_PATH, "gazebo_rust", "src_mv.png"), os.path.join(EXAMPLES_PATH, "gazebo_rust", "src.png"), os.path.join(EXAMPLES_PATH, "gazebo_rust", "edited.png"), 50, 39, 3.5, 5.0, 18, ], [ os.path.join(EXAMPLES_PATH, "german-shep_pixar", "src_mv.png"), os.path.join(EXAMPLES_PATH, "german-shep_pixar", "src.png"), os.path.join(EXAMPLES_PATH, "german-shep_pixar", "edited.png"), 50, 39, 3.5, 5.0, 18, ], [ os.path.join(EXAMPLES_PATH, "grogu_kimono", "src_mv.png"), os.path.join(EXAMPLES_PATH, "grogu_kimono", "src.png"), os.path.join(EXAMPLES_PATH, "grogu_kimono", "edited.png"), 50, 39, 3.5, 21.0, 18, ], [ os.path.join(EXAMPLES_PATH, "ship_fantasy", "src_mv.png"), os.path.join(EXAMPLES_PATH, "ship_fantasy", "src.png"), os.path.join(EXAMPLES_PATH, "ship_fantasy", "edited.png"), 50, 31, 3.5, 5.0, 18, ], [ os.path.join(EXAMPLES_PATH, "grogu_lego-fig", "src_mv.png"), os.path.join(EXAMPLES_PATH, "grogu_lego-fig", "src.png"), os.path.join(EXAMPLES_PATH, "grogu_lego-fig", "edited.png"), 50, 39, 3.5, 21.0, 18, ], [ os.path.join(EXAMPLES_PATH, "lego-car_spoiler", "src_mv.png"), os.path.join(EXAMPLES_PATH, "lego-car_spoiler", "src.png"), os.path.join(EXAMPLES_PATH, "lego-car_spoiler", "edited.png"), 50, 39, 3.5, 5.0, 18, ], [ os.path.join(EXAMPLES_PATH, "nurse_sporty", "src_mv.png"), os.path.join(EXAMPLES_PATH, "nurse_sporty", "src.png"), os.path.join(EXAMPLES_PATH, "nurse_sporty", "edited.png"), 50, 31, 3.5, 5.0, 18, ], [ os.path.join(EXAMPLES_PATH, "r2d2_golden", "src_mv.png"), os.path.join(EXAMPLES_PATH, "r2d2_golden", "src.png"), os.path.join(EXAMPLES_PATH, "r2d2_golden", "edited.png"), 50, 31, 3.5, 5.0, 18, ], [ os.path.join(EXAMPLES_PATH, "grogu_the-force", "src_mv.png"), os.path.join(EXAMPLES_PATH, "grogu_the-force", "src.png"), os.path.join(EXAMPLES_PATH, "grogu_the-force", "edited.png"), 50, 39, 3.5, 21.0, 18, ], [ os.path.join(EXAMPLES_PATH, "spiderbot_chrome", "src_mv.png"), os.path.join(EXAMPLES_PATH, "spiderbot_chrome", "src.png"), os.path.join(EXAMPLES_PATH, "spiderbot_chrome", "edited.png"), 50, 31, 3.5, 5.0, 18, ], [ os.path.join( EXAMPLES_PATH, "spiderbot_steampunk", "src_mv.png" ), os.path.join(EXAMPLES_PATH, "spiderbot_steampunk", "src.png"), os.path.join( EXAMPLES_PATH, "spiderbot_steampunk", "edited.png" ), 50, 31, 3.5, 5.0, 18, ], [ os.path.join(EXAMPLES_PATH, "superman_crossed", "src_mv.png"), os.path.join(EXAMPLES_PATH, "superman_crossed", "src.png"), os.path.join(EXAMPLES_PATH, "superman_crossed", "edited.png"), 50, 39, 3.5, 5.0, 18, ], ] gr.Examples( examples=example_data, inputs=example_inputs, label="Example Edits", examples_per_page=10, elem_id="example-table" ) with gr.TabItem("How to Use", id=1): gr.Markdown(HOW_TO_USE_TEXT) # Define button actions run_button.click( fn=run_main_script, inputs=[ src_cond_image, tgt_cond_image, original_mv_image, t_steps, n_max, src_gs, tar_gs, seed, ], outputs=output_image, ) clear_button.click( fn=clear_inputs, inputs=[], outputs=[ original_mv_image, src_cond_image, tgt_cond_image, t_steps, n_max, src_gs, tar_gs, seed, output_image, ], ) if __name__ == "__main__": demo.launch(share=True)