Spaces:
Running
on
Zero
Running
on
Zero
Commit
·
a176955
0
Parent(s):
Initial commit: EditP23 project with LFS tracking for binary files
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .gitattributes +48 -0
- .gitignore +30 -0
- .gitmodules +3 -0
- README.md +231 -0
- app.py +899 -0
- assets/stormtrooper.glb +3 -0
- examples/batman_backpack/edited.png +3 -0
- examples/batman_backpack/src.png +3 -0
- examples/batman_backpack/src_mv.png +3 -0
- examples/batman_jetpack/edited.png +3 -0
- examples/batman_jetpack/src.png +3 -0
- examples/batman_jetpack/src_mv.png +3 -0
- examples/bike_harley/edited.png +3 -0
- examples/bike_harley/src.png +3 -0
- examples/bike_harley/src_mv.png +3 -0
- examples/bike_modern/edited.png +3 -0
- examples/bike_modern/src.png +3 -0
- examples/bike_modern/src_mv.png +3 -0
- examples/bike_sport/edited.png +3 -0
- examples/bike_sport/src.png +3 -0
- examples/bike_sport/src_mv.png +3 -0
- examples/bike_vintage/edited.png +3 -0
- examples/bike_vintage/src.png +3 -0
- examples/bike_vintage/src_mv.png +3 -0
- examples/bmw_speedy/edited.png +3 -0
- examples/bmw_speedy/src.png +3 -0
- examples/bmw_speedy/src_mv.png +3 -0
- examples/cabin_alpine/edited.png +3 -0
- examples/cabin_alpine/src.png +3 -0
- examples/cabin_alpine/src_mv.png +3 -0
- examples/cabin_gothic/edited.png +3 -0
- examples/cabin_gothic/src.png +3 -0
- examples/cabin_gothic/src_mv.png +3 -0
- examples/cabin_haunted/edited.png +3 -0
- examples/cabin_haunted/src.png +3 -0
- examples/cabin_haunted/src_mv.png +3 -0
- examples/cake_oreo/edited.png +3 -0
- examples/cake_oreo/src.png +3 -0
- examples/cake_oreo/src_mv.png +3 -0
- examples/car_cartoon/edited.png +3 -0
- examples/car_cartoon/src.png +3 -0
- examples/car_cartoon/src_mv.png +3 -0
- examples/car_engine/edited.png +3 -0
- examples/car_engine/src.png +3 -0
- examples/car_engine/src_mv.png +3 -0
- examples/car_steampunk/edited.png +3 -0
- examples/car_steampunk/src.png +3 -0
- examples/car_steampunk/src_mv.png +3 -0
- examples/deer_pixar/edited.png +3 -0
- examples/deer_pixar/src.png +3 -0
.gitattributes
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
*.png filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
*.jpg filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
*.jpeg filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
*.glb filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
*.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
*.fbx filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
*.stl filter=lfs diff=lfs merge=lfs -text
|
| 43 |
+
*.ply filter=lfs diff=lfs merge=lfs -text
|
| 44 |
+
*.gif filter=lfs diff=lfs merge=lfs -text
|
| 45 |
+
*.bmp filter=lfs diff=lfs merge=lfs -text
|
| 46 |
+
*.tiff filter=lfs diff=lfs merge=lfs -text
|
| 47 |
+
*.gltf filter=lfs diff=lfs merge=lfs -text
|
| 48 |
+
*.obj filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Byte-compiled / cache files
|
| 2 |
+
__pycache__/
|
| 3 |
+
*.py[cod]
|
| 4 |
+
|
| 5 |
+
# Gradio-specific temporary files
|
| 6 |
+
.gradio_cache/
|
| 7 |
+
.gradio/
|
| 8 |
+
|
| 9 |
+
# Temporal
|
| 10 |
+
tmp/
|
| 11 |
+
# SLURM logs or files
|
| 12 |
+
slurm/
|
| 13 |
+
*.slurm
|
| 14 |
+
|
| 15 |
+
# VSCode settings
|
| 16 |
+
.vscode/
|
| 17 |
+
|
| 18 |
+
# Environment files
|
| 19 |
+
.env
|
| 20 |
+
*.env
|
| 21 |
+
|
| 22 |
+
# Jupyter Notebook checkpoints
|
| 23 |
+
.ipynb_checkpoints/
|
| 24 |
+
|
| 25 |
+
# Mac and OS metadata
|
| 26 |
+
.DS_Store
|
| 27 |
+
|
| 28 |
+
# Python virtual environments
|
| 29 |
+
venv/
|
| 30 |
+
.env/
|
.gitmodules
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[submodule "external/instant-mesh"]
|
| 2 |
+
path = external/instant-mesh
|
| 3 |
+
url = https://github.com/TencentARC/InstantMesh.git
|
README.md
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: EditP23
|
| 3 |
+
emoji: 🎨
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: purple
|
| 6 |
+
sdk: gradio
|
| 7 |
+
sdk_version: 5.38.2
|
| 8 |
+
app_file: app.py
|
| 9 |
+
pinned: false
|
| 10 |
+
---
|
| 11 |
+
|
| 12 |
+
# EditP23: 3D Editing via Propagation of Image Prompts to Multi-View
|
| 13 |
+
|
| 14 |
+
[](https://editp23.github.io/)
|
| 15 |
+
[](https://arxiv.org/abs/2506.20652)
|
| 16 |
+
|
| 17 |
+
This repository contains the official implementation for **EditP23**, a method for fast, mask-free 3D editing that propagates 2D image edits to multi-view representations in a 3D-consistent manner.
|
| 18 |
+
The edit is guided by an image pair, allowing users to leverage any preferred 2D editing tool, from manual painting to generative pipelines.
|
| 19 |
+
|
| 20 |
+
### Installation
|
| 21 |
+
<details>
|
| 22 |
+
<summary>Click to expand installation instructions</summary>
|
| 23 |
+
|
| 24 |
+
This project was tested on a Linux system with Python 3.11 and CUDA 12.6.
|
| 25 |
+
|
| 26 |
+
**1. Clone the Repository**
|
| 27 |
+
```bash
|
| 28 |
+
git clone --recurse-submodules https://github.com/editp23/EditP23.git
|
| 29 |
+
cd EditP23
|
| 30 |
+
```
|
| 31 |
+
|
| 32 |
+
**2. Install Dependencies**
|
| 33 |
+
```bash
|
| 34 |
+
conda create -n editp23 python=3.11 -y
|
| 35 |
+
conda activate editp23
|
| 36 |
+
pip install torch torchvision --index-url https://download.pytorch.org/whl/cu126 # Ensure compatibility with your CUDA version. (tested with torch 2.6, cuda 12.6)
|
| 37 |
+
pip install diffusers==0.30.1 transformers accelerate pillow huggingface_hub numpy tqdm
|
| 38 |
+
```
|
| 39 |
+
|
| 40 |
+
</details>
|
| 41 |
+
|
| 42 |
+
### Quick Start
|
| 43 |
+
|
| 44 |
+
**1. Prepare Your Experiment Directory**
|
| 45 |
+
|
| 46 |
+
Create a directory for your experiment. Inside this directory, you must place three specific PNG files:
|
| 47 |
+
|
| 48 |
+
* `src.png`: The original, unedited view of your object.
|
| 49 |
+
* `edited.png`: The same view after you have applied your desired 2D edit.
|
| 50 |
+
* `src_mv.png`: The multi-view grid of the original object, which will be edited.
|
| 51 |
+
|
| 52 |
+
Your directory structure should look like this:
|
| 53 |
+
```text
|
| 54 |
+
examples/
|
| 55 |
+
└── robot_sunglasses/
|
| 56 |
+
├── src.png
|
| 57 |
+
├── edited.png
|
| 58 |
+
└── src_mv.png
|
| 59 |
+
```
|
| 60 |
+
|
| 61 |
+
**2. Run the Editing Script**
|
| 62 |
+
|
| 63 |
+
Execute the `main.py` script, pointing it to your experiment directory. You can adjust the guidance parameters based on the complexity of your edit.
|
| 64 |
+
|
| 65 |
+
#### Execution Examples
|
| 66 |
+
|
| 67 |
+
* **Mild Edit (Appearance Change):**
|
| 68 |
+
```bash
|
| 69 |
+
python src/main.py --exp_dir examples/robot_sunglasses --tar_guidance_scale 5.0 --n_max 31
|
| 70 |
+
```
|
| 71 |
+
* **Hard Edit (Large Geometry Change):**
|
| 72 |
+
```bash
|
| 73 |
+
python src/main.py --exp_dir examples/deer_wings --tar_guidance_scale 21.0 --n_max 39
|
| 74 |
+
```
|
| 75 |
+
|
| 76 |
+
The output will be saved in the `output/` subdirectory within your experiment folder.
|
| 77 |
+
|
| 78 |
+
### Command-Line Arguments
|
| 79 |
+
|
| 80 |
+
* `--exp_dir`: (Required) Path to the experiment directory.
|
| 81 |
+
* `--T_steps`: Total number of denoising steps. Default: `50`.
|
| 82 |
+
* `--n_max`: The number of denoising steps to apply edit-aware guidance. Higher values can help with more complex edits. Default: `31`. This value shouldn't exceed `T_steps`.
|
| 83 |
+
* `--src_guidance_scale`: CFG scale for the source condition. Can typically remain constant. Default: `3.5`.
|
| 84 |
+
* `--tar_guidance_scale`: CFG scale for the target (edited) condition. Higher values apply the edit more strongly. Default: `5.0`.
|
| 85 |
+
* `--seed`: Random seed for reproducibility. Default: `18`.
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
# Results in Multi-View
|
| 89 |
+
|
| 90 |
+
### Deer - Pixar style & Wings
|
| 91 |
+
|
| 92 |
+
| | Cond. View | View 1 | View 2 | View 3 |
|
| 93 |
+
| :--- |:-----------------------------------------------------------------:|:----------------------------------------------------:|:----------------------------------------------------:|:----------------------------------------------------:|
|
| 94 |
+
| **Original** |  |  |  |  |
|
| 95 |
+
| **Pixar style** |  |  |  |  |
|
| 96 |
+
| **Wings** |  |  |  |  |
|
| 97 |
+
|
| 98 |
+
<br>
|
| 99 |
+
|
| 100 |
+
### Person - Old & Zombie
|
| 101 |
+
|
| 102 |
+
| | Cond. View | View 1 | View 2 | View 3 |
|
| 103 |
+
|:-------------|:-----------------------------------------------------------------:|:----------------------------------------------------:|:----------------------------------------------------:|:----------------------------------------------------:|
|
| 104 |
+
| **Original** |  |  |  |  |
|
| 105 |
+
| **Old** |  |  |  |  |
|
| 106 |
+
| **Zombie** |  |  |  |  |
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
# Project Structure
|
| 110 |
+
The repository is organized as follows:
|
| 111 |
+
```text
|
| 112 |
+
EditP23/
|
| 113 |
+
├── examples/ # Example assets for quick testing
|
| 114 |
+
│ ├── deer_wings/
|
| 115 |
+
│ │ ├── src.png
|
| 116 |
+
│ │ ├── edited.png
|
| 117 |
+
│ │ └── src_mv.png
|
| 118 |
+
│ └── robot_sunglasses/
|
| 119 |
+
│ └── ...
|
| 120 |
+
├── assets/ # Raw asset files
|
| 121 |
+
│ └── stormtrooper.glb
|
| 122 |
+
├── scripts/ # Helper scripts for data preparation
|
| 123 |
+
│ ├── render_mesh.py
|
| 124 |
+
│ └── img2mv.py
|
| 125 |
+
├── src/ # Main source code
|
| 126 |
+
│ ├── init.py
|
| 127 |
+
│ ├── edit_mv.py
|
| 128 |
+
│ ├── main.py
|
| 129 |
+
│ ├── pipeline.py
|
| 130 |
+
│ └── utils.py
|
| 131 |
+
├── .gitignore
|
| 132 |
+
└── README.md
|
| 133 |
+
```
|
| 134 |
+
|
| 135 |
+
# Utilities
|
| 136 |
+
|
| 137 |
+
## Setup
|
| 138 |
+
|
| 139 |
+
This guide shows how to prepare inputs for **EditP23** and run an edit.
|
| 140 |
+
|
| 141 |
+
These helper scripts create the three PNG files every experiment needs:
|
| 142 |
+
|
| 143 |
+
| File | Purpose |
|
| 144 |
+
|---------------|-----------------------------------------------------------------|
|
| 145 |
+
| `src.png` | Original single view (the one you will edit). |
|
| 146 |
+
| `edited.png` | Your 2D edit of `src.png`. |
|
| 147 |
+
| `src_mv.png` | 6-view grid of the original object. |
|
| 148 |
+
|
| 149 |
+
### 1. Generate `src.png` and `src_mv.png`
|
| 150 |
+
**EditP23** needs a **source view** (`src.png`) and a **multi-view grid** (`src_mv.png`).
|
| 151 |
+
The grid contains six extra views at fixed azimuth/elevation pairs:
|
| 152 |
+
Angles (azimuth, elevation): `(30°, 20°) (90°, -10°) (150°, 20°) (210°, -10°) (270°, 20°) (330°, -10°)` and for the prompt view `(0°, 20°)`.
|
| 153 |
+
We provide two methods to generate these inputs. Both methods produce views on a clean, white background.
|
| 154 |
+
Both methods below produce the multi-view grid and the source view from the relevant angles on a white background.
|
| 155 |
+
|
| 156 |
+
#### Method A: From a Single Image
|
| 157 |
+
|
| 158 |
+
You can generate the multi-view grid from a single image of an object using our `img2mv.py` script. This script leverages the Zero123++ pipeline with a checkpoint from InstantMesh, which is fine-tuned to produce white backgrounds.
|
| 159 |
+
|
| 160 |
+
```bash
|
| 161 |
+
# This script takes a single input image and generates the corresponding multi-view grid.
|
| 162 |
+
python scripts/img2mv.py \
|
| 163 |
+
--input_image "examples/robot_sunglasses/src.png" \
|
| 164 |
+
--output_dir "examples/robot_sunglasses/"
|
| 165 |
+
```
|
| 166 |
+
**Note:** In this case, `src.png` serves as the source view for EditP23.
|
| 167 |
+
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
#### Method B: From a 3D Mesh
|
| 171 |
+
If you have a 3D model, you can use our Blender script to render both the source view and the multi-view grid.
|
| 172 |
+
**Prerequisite:** This script requires Blender (`pip install bpy`).
|
| 173 |
+
|
| 174 |
+
```bash
|
| 175 |
+
# This script renders a source view and a multi-view grid from a 3D mesh.
|
| 176 |
+
python scripts/render_mesh.py \
|
| 177 |
+
--mesh_path "assets/stormtrooper.glb" \
|
| 178 |
+
--output_dir "examples/stormtrooper/"
|
| 179 |
+
```
|
| 180 |
+
|
| 181 |
+
### 2. Generating `edited.png`
|
| 182 |
+
Once you have your **source view**, you can use any 2D image editor to make your desired changes. We use this user-provided edit to guide the 3D modification.
|
| 183 |
+
For quick edits, you can use readily available online tools, such as the following HuggingFace Spaces:
|
| 184 |
+
- [FlowEdit](https://huggingface.co/spaces/fallenshock/FlowEdit): Excellent for global, structural edits.
|
| 185 |
+
- [Flux-Inpainting](https://huggingface.co/spaces/black-forest-labs/FLUX.1-Fill-dev): Great for local modifications and inpainting.
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
## Reconstruction
|
| 189 |
+
After generating an edited multi-view image (`edited_mv.png`) with our main script, you can reconstruct it into a 3D model. We provide a helper script that uses the [InstantMesh](https://github.com/TencentARC/InstantMesh) framework to produce a textured `.obj` file and a turntable video.
|
| 190 |
+
|
| 191 |
+
|
| 192 |
+
### Additional Dependencies
|
| 193 |
+
First, you'll need to install several libraries required for the reconstruction process.
|
| 194 |
+
|
| 195 |
+
<details>
|
| 196 |
+
<summary>Click to expand installation instructions</summary>
|
| 197 |
+
|
| 198 |
+
```bash
|
| 199 |
+
# Install general dependencies
|
| 200 |
+
pip install opencv-python einops xatlas imageio[ffmpeg]
|
| 201 |
+
|
| 202 |
+
# Install NVIDIA's nvdiffrast library
|
| 203 |
+
pip install git+https://github.com/NVlabs/nvdiffrast/
|
| 204 |
+
|
| 205 |
+
# For video export, ensure ffmpeg is installed
|
| 206 |
+
# On conda, you can run:
|
| 207 |
+
conda install ffmpeg
|
| 208 |
+
```
|
| 209 |
+
</details>
|
| 210 |
+
|
| 211 |
+
### Running the Reconstruction
|
| 212 |
+
The reconstruction script takes the multi-view PNG as input and generates the 3D assets. The necessary model config file (instant-mesh-large.yaml) is included in the configs/ directory of the InstanMesh repository.
|
| 213 |
+
#### Example Command
|
| 214 |
+
````bash
|
| 215 |
+
python scripts/recon.py \
|
| 216 |
+
external/instant-mesh/configs/instant-mesh-large.yaml \
|
| 217 |
+
--input_file "examples/robot_sunglasses/output/edited_mv.png" \
|
| 218 |
+
--output_dir "examples/robot_sunglasses/output/recon/"
|
| 219 |
+
````
|
| 220 |
+
|
| 221 |
+
### Command-Line Arguments
|
| 222 |
+
Here are the arguments for the recon.py script:
|
| 223 |
+
|
| 224 |
+
| Argument | Description | Default |
|
| 225 |
+
| :------------ | :----------------------------------------------------------------- | :----------- |
|
| 226 |
+
| `config` | **(Required)** Path to the InstantMesh model config file. | |
|
| 227 |
+
| `--input_file`| **(Required)** Path to the multi-view PNG file you want to reconstruct. | |
|
| 228 |
+
| `--output_dir`| Directory where the output `.obj` and `.mp4` files will be saved. | `"outputs/"` |
|
| 229 |
+
| `--scale` | Scale of the input cameras. | `1.0` |
|
| 230 |
+
| `--distance` | Camera distance for rendering the output video. | `4.5` |
|
| 231 |
+
| `--no_video` | A flag to disable saving the `.mp4` video. | `False` |
|
app.py
ADDED
|
@@ -0,0 +1,899 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import gradio as gr
|
| 3 |
+
import numpy as np
|
| 4 |
+
from PIL import Image
|
| 5 |
+
import tempfile
|
| 6 |
+
import sys
|
| 7 |
+
import subprocess
|
| 8 |
+
from pathlib import Path
|
| 9 |
+
from typing import Optional
|
| 10 |
+
import re
|
| 11 |
+
import spaces
|
| 12 |
+
# NOTE: This script assumes it is located in the project root directory.
|
| 13 |
+
PYTHON_EXECUTABLE = sys.executable # Or specify a path like "/path/to/your/python"
|
| 14 |
+
# Construct paths relative to this app's location in the root.
|
| 15 |
+
APP_DIR = os.path.dirname(os.path.abspath(__file__))
|
| 16 |
+
MAIN_SCRIPT_PATH = os.path.join(APP_DIR, "src", "main.py")
|
| 17 |
+
EXAMPLES_PATH = os.path.join(APP_DIR, "examples")
|
| 18 |
+
|
| 19 |
+
# --- Gradio Interface ---
|
| 20 |
+
|
| 21 |
+
@spaces.GPU
|
| 22 |
+
def run_main_script(
|
| 23 |
+
src_cond_image_np: np.ndarray,
|
| 24 |
+
tgt_cond_image_np: np.ndarray,
|
| 25 |
+
original_mv_image_np: np.ndarray,
|
| 26 |
+
t_steps: int,
|
| 27 |
+
n_max: int,
|
| 28 |
+
src_gs: float,
|
| 29 |
+
tar_gs: float,
|
| 30 |
+
seed: int,
|
| 31 |
+
progress=gr.Progress(),
|
| 32 |
+
):
|
| 33 |
+
"""
|
| 34 |
+
Wrapper function for Gradio to prepare files and run the main.py script.
|
| 35 |
+
"""
|
| 36 |
+
# Ensure consistent types for filename generation and command-line args.
|
| 37 |
+
tar_gs = float(tar_gs)
|
| 38 |
+
n_max = int(n_max)
|
| 39 |
+
t_steps = int(t_steps)
|
| 40 |
+
src_gs = float(src_gs)
|
| 41 |
+
seed = int(seed)
|
| 42 |
+
|
| 43 |
+
if (
|
| 44 |
+
src_cond_image_np is None
|
| 45 |
+
or tgt_cond_image_np is None
|
| 46 |
+
or original_mv_image_np is None
|
| 47 |
+
):
|
| 48 |
+
raise gr.Error("Please provide all three input images.")
|
| 49 |
+
|
| 50 |
+
if not os.path.exists(MAIN_SCRIPT_PATH):
|
| 51 |
+
raise gr.Error(
|
| 52 |
+
f"Main script not found at '{MAIN_SCRIPT_PATH}'. Please update the MAIN_SCRIPT_PATH variable in the app."
|
| 53 |
+
)
|
| 54 |
+
|
| 55 |
+
progress(0, desc="Preparing experiment...")
|
| 56 |
+
with tempfile.TemporaryDirectory() as exp_dir:
|
| 57 |
+
# Save uploaded images to the required filenames
|
| 58 |
+
src_cond_img = Image.fromarray(src_cond_image_np).convert("RGB")
|
| 59 |
+
tgt_cond_img = Image.fromarray(tgt_cond_image_np).convert("RGB")
|
| 60 |
+
original_mv_img = Image.fromarray(original_mv_image_np).convert("RGB")
|
| 61 |
+
|
| 62 |
+
src_cond_img.save(os.path.join(exp_dir, "src.png"))
|
| 63 |
+
tgt_cond_img.save(os.path.join(exp_dir, "edited.png"))
|
| 64 |
+
original_mv_img.save(os.path.join(exp_dir, "src_mv.png"))
|
| 65 |
+
|
| 66 |
+
# Construct the command to run main.py
|
| 67 |
+
cmd = [
|
| 68 |
+
PYTHON_EXECUTABLE,
|
| 69 |
+
"-u", # Force unbuffered stdout
|
| 70 |
+
MAIN_SCRIPT_PATH,
|
| 71 |
+
"--exp_dir",
|
| 72 |
+
exp_dir,
|
| 73 |
+
"--T_steps",
|
| 74 |
+
str(t_steps),
|
| 75 |
+
"--n_max",
|
| 76 |
+
str(n_max),
|
| 77 |
+
"--src_guidance_scale",
|
| 78 |
+
str(src_gs),
|
| 79 |
+
"--tar_guidance_scale",
|
| 80 |
+
str(tar_gs),
|
| 81 |
+
"--seed",
|
| 82 |
+
str(seed),
|
| 83 |
+
]
|
| 84 |
+
|
| 85 |
+
print(f"🚀 Running command: {' '.join(cmd)}")
|
| 86 |
+
progress(0.05, desc="Executing main script...")
|
| 87 |
+
|
| 88 |
+
# Execute the script as a subprocess and stream the output.
|
| 89 |
+
process = subprocess.Popen(
|
| 90 |
+
cmd,
|
| 91 |
+
stdout=subprocess.PIPE,
|
| 92 |
+
stderr=subprocess.STDOUT, # Merge stderr with stdout
|
| 93 |
+
text=True,
|
| 94 |
+
bufsize=1,
|
| 95 |
+
universal_newlines=True,
|
| 96 |
+
)
|
| 97 |
+
|
| 98 |
+
# Regex to find the main tqdm progress in the format "current/total [time<remaining, speed]"
|
| 99 |
+
tqdm_regex = re.compile(r"(\d+)/(\d+)\s*\[.*<.*,.*s/it\]")
|
| 100 |
+
|
| 101 |
+
# Print stdout in real-time and update progress bar
|
| 102 |
+
if process.stdout:
|
| 103 |
+
for line in iter(process.stdout.readline, ""):
|
| 104 |
+
print(line, end="")
|
| 105 |
+
match = tqdm_regex.search(line)
|
| 106 |
+
if match:
|
| 107 |
+
current_step = int(match.group(1))
|
| 108 |
+
total_steps = int(match.group(2))
|
| 109 |
+
# This condition ensures we only track the main, multi-step progress bar
|
| 110 |
+
if total_steps > 1:
|
| 111 |
+
percent = current_step / total_steps
|
| 112 |
+
# Update progress bar with a clean description
|
| 113 |
+
progress(percent, desc=f"Denoising...")
|
| 114 |
+
|
| 115 |
+
# Wait for the process to finish and get the return code
|
| 116 |
+
process.wait()
|
| 117 |
+
|
| 118 |
+
# Check for errors after the process has completed
|
| 119 |
+
if process.returncode != 0:
|
| 120 |
+
print(f"❌ Script failed with exit code: {process.returncode}")
|
| 121 |
+
raise gr.Error(f"Script execution failed. Check console for details.")
|
| 122 |
+
|
| 123 |
+
print("\n✅ Script executed successfully.")
|
| 124 |
+
|
| 125 |
+
# Find the output file
|
| 126 |
+
output_dir = os.path.join(exp_dir, "output")
|
| 127 |
+
output_filename = f"result_tgs_{tar_gs}_nmax_{n_max}.png"
|
| 128 |
+
save_path = os.path.join(output_dir, output_filename)
|
| 129 |
+
|
| 130 |
+
progress(0.98, desc="Loading result...")
|
| 131 |
+
if not os.path.exists(save_path):
|
| 132 |
+
print(f"❌ Output file not found at expected path: {save_path}")
|
| 133 |
+
raise gr.Error(
|
| 134 |
+
"Output file was not created by the script. Check console logs for errors."
|
| 135 |
+
)
|
| 136 |
+
|
| 137 |
+
result_img = Image.open(save_path)
|
| 138 |
+
progress(1, desc="Done.")
|
| 139 |
+
return result_img
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
def clear_inputs():
|
| 143 |
+
"""Resets all input fields to their default state and makes them interactive."""
|
| 144 |
+
return {
|
| 145 |
+
original_mv_image: None,
|
| 146 |
+
src_cond_image: None,
|
| 147 |
+
tgt_cond_image: None,
|
| 148 |
+
t_steps: gr.Slider(value=50, interactive=True),
|
| 149 |
+
n_max: gr.Slider(value=31, interactive=True),
|
| 150 |
+
src_gs: gr.Slider(value=3.5, interactive=True),
|
| 151 |
+
tar_gs: gr.Slider(value=5.0, interactive=True),
|
| 152 |
+
seed: gr.Slider(value=18, interactive=True),
|
| 153 |
+
output_image: None,
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
# --- Markdown Content for UI ---
|
| 158 |
+
ABOUT_TEXT = """
|
| 159 |
+
<div style="text-align: center; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;">
|
| 160 |
+
<h1 style="font-weight: 800; font-size: 2.5em; margin-bottom: 0.2em;">EditP23: 3D Editing via Propagation of Image Prompts to Multi-View</h1>
|
| 161 |
+
<div style="margin-bottom: 1.5em; display: flex; justify-content: center; align-items: center; gap: 12px; flex-wrap: wrap;">
|
| 162 |
+
<a href="https://editp23.github.io/" target="_blank" class="link-button" style="background-color: #1d6aef;">▶️ Project Page</a>
|
| 163 |
+
<a href="https://arxiv.org/abs/2506.20652" target="_blank" class="link-button" style="background-color: #b31b1b;">📄 arXiv</a>
|
| 164 |
+
<a href="https://github.com/editp23/editp23" target="_blank" class="link-button" style="background-color: #24292e;">💻 GitHub</a>
|
| 165 |
+
</div>
|
| 166 |
+
<p style="font-size: 1.1em; max-width: 800px; margin: auto; line-height: 1.6;">
|
| 167 |
+
This is the official Gradio demo for <strong>EditP23</strong>, 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.
|
| 168 |
+
</p>
|
| 169 |
+
</div>
|
| 170 |
+
"""
|
| 171 |
+
|
| 172 |
+
HOW_TO_USE_TEXT = """
|
| 173 |
+
<div id="how-to-use-container">
|
| 174 |
+
<h2 id="understanding-inputs">Understanding the Inputs</h2>
|
| 175 |
+
<p><strong>EditP23</strong> requires three specific images to perform an edit. This demo automates the process, but understanding each component is key.</p>
|
| 176 |
+
<ol>
|
| 177 |
+
<li><strong>Original Multi-View Image (`src_mv.png`)</strong>: 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.</li>
|
| 178 |
+
<li><strong>Source Condition (`src.png`)</strong>: This is a single, frontal view of the original object. It acts as the "before" image for the edit.</li>
|
| 179 |
+
<li><strong>Target Condition (`edited.png`)</strong>: This is the "after" image. It's the same view as <code>src.png</code>, but with your desired 2D modification applied. The difference between this image and <code>src.png</code> is what guides the 3D edit.</li>
|
| 180 |
+
</ol>
|
| 181 |
+
<hr>
|
| 182 |
+
<h2 id="prepare-images">How to Prepare Your Own Images</h2>
|
| 183 |
+
<p>You can generate the required input images using the helper scripts provided in our <a href="https://github.com/editp23/editp23" target="_blank">GitHub repository</a>.</p>
|
| 184 |
+
<h4><strong>Step 1: Generate <code>src.png</code> and <code>src_mv.png</code></strong></h4>
|
| 185 |
+
<p>You have two options for creating the initial views of your object.</p>
|
| 186 |
+
<ul>
|
| 187 |
+
<li><strong>Method A: From a Single Image</strong><br>If you have a single image of an object, you can generate the multi-view grid using our <code>img2mv.py</code> script.
|
| 188 |
+
<pre><code>python scripts/img2mv.py --input_image "path/to/your_image.png" --output_dir "path/to/output/"</code></pre>
|
| 189 |
+
</li>
|
| 190 |
+
<li><strong>Method B: From a 3D Mesh (<code>.glb</code>, <code>.obj</code>)</strong><br>If you have a 3D model, you can render the required views using our Blender script.
|
| 191 |
+
<pre><code>python scripts/render_mesh.py --mesh_path "path/to/your_model.glb" --output_dir "path/to/output/"</code></pre>
|
| 192 |
+
</li>
|
| 193 |
+
</ul>
|
| 194 |
+
<h4><strong>Step 2: Create <code>edited.png</code></strong></h4>
|
| 195 |
+
<p>Use any 2D image editor to modify your <code>src.png</code>. This is where your creativity comes in! For quick edits, we recommend these online tools:</p>
|
| 196 |
+
<ul>
|
| 197 |
+
<li><a href="https://huggingface.co/spaces/fallenshock/FlowEdit" target="_blank">FlowEdit</a>: Excellent for global, structural edits.</li>
|
| 198 |
+
<li><a href="https://huggingface.co/spaces/black-forest-labs/FLUX.1-Fill-dev" target="_blank">Flux-Inpainting</a>: Great for local modifications and inpainting.</li>
|
| 199 |
+
</ul>
|
| 200 |
+
<hr>
|
| 201 |
+
<h2 id="understanding-params">Understanding the Parameters</h2>
|
| 202 |
+
<ul>
|
| 203 |
+
<li><strong><code>n_max</code></strong>: Controls how many denoising steps are influenced by your edit. Higher values are needed for more significant geometric changes.</li>
|
| 204 |
+
<li><strong><code>tar_guidance_scale</code></strong>: Determines the strength of your edit. Increase this for more dramatic changes, but be aware that very high values can sometimes introduce artifacts.</li>
|
| 205 |
+
<li><strong><code>src_guidance_scale</code></strong>: Controls how strongly the model adheres to the original object's identity. This can usually be left at its default value.</li>
|
| 206 |
+
</ul>
|
| 207 |
+
<hr>
|
| 208 |
+
<h2 id="reconstruct-model">Reconstructing a 3D Model</h2>
|
| 209 |
+
<p>After this demo generates an edited multi-view image, you can use the <code>scripts/recon.py</code> script from our repository to convert it back into a 3D model (<code>.obj</code> file).</p>
|
| 210 |
+
<pre><code>python scripts/recon.py path/to/instant-mesh-large.yaml --input_file "path/to/edited_mv.png" --output_dir "path/to/output/"</code></pre>
|
| 211 |
+
</div>
|
| 212 |
+
"""
|
| 213 |
+
|
| 214 |
+
# --- Gradio UI Layout ---
|
| 215 |
+
# Create a custom theme to match the website's color
|
| 216 |
+
theme = gr.themes.Base(
|
| 217 |
+
primary_hue=gr.themes.colors.blue,
|
| 218 |
+
secondary_hue=gr.themes.colors.blue,
|
| 219 |
+
font=[gr.themes.GoogleFont("Inter"), "ui-sans-serif", "system-ui", "sans-serif"],
|
| 220 |
+
).set(
|
| 221 |
+
button_primary_background_fill="*primary_500",
|
| 222 |
+
button_primary_background_fill_hover="*primary_600",
|
| 223 |
+
)
|
| 224 |
+
|
| 225 |
+
# Custom CSS for better layout and fixing UI quirks
|
| 226 |
+
CUSTOM_CSS = """
|
| 227 |
+
.gradio-container { max-width: 95% !important; }
|
| 228 |
+
.label-wrap { padding-top: 6px !import ant; } /* Fix label overlap */
|
| 229 |
+
.help-text { color: #9CA3AF; font-size: 0.9rem; margin-top: 4px; margin-bottom: 12px; }
|
| 230 |
+
.link-button { text-decoration: none; color: white; padding: 8px 16px; border-radius: 8px; font-weight: bold; transition: background-color 0.2s ease; }
|
| 231 |
+
.link-button:hover { background-color: #4a5568 !important; }
|
| 232 |
+
#action-buttons { margin-top: 1rem; }
|
| 233 |
+
|
| 234 |
+
/* --- CSS Rules for the Examples Table --- */
|
| 235 |
+
|
| 236 |
+
/* 1. CRITICAL FIX: Target the image's wrapper to prevent clipping. */
|
| 237 |
+
#example-table td > div {
|
| 238 |
+
overflow: visible !important; /* This is the key to stop cropping. */
|
| 239 |
+
display: flex;
|
| 240 |
+
justify-content: center;
|
| 241 |
+
align-items: center;
|
| 242 |
+
}
|
| 243 |
+
|
| 244 |
+
/* 2. General cell styling for alignment and spacing */
|
| 245 |
+
#example-table td {
|
| 246 |
+
vertical-align: middle !important;
|
| 247 |
+
padding: 8px !important;
|
| 248 |
+
}
|
| 249 |
+
|
| 250 |
+
/* 3. Force parameter columns (4-7) to have the same width */
|
| 251 |
+
#example-table th:nth-child(n+4):nth-child(-n+7),
|
| 252 |
+
#example-table td:nth-child(n+4):nth-child(-n+7) {
|
| 253 |
+
width: 85px !important;
|
| 254 |
+
max-width: 85px !important;
|
| 255 |
+
text-align: center;
|
| 256 |
+
word-break: break-word;
|
| 257 |
+
}
|
| 258 |
+
|
| 259 |
+
/* 4. Enlarge multi-view image (Col 1) with a 3:2 height:width ratio */
|
| 260 |
+
#example-table td:nth-child(1) img {
|
| 261 |
+
height: 180px !important;
|
| 262 |
+
width: 120px !important; /* 180px / 120px = 3:2 ratio */
|
| 263 |
+
object-fit: contain !important; /* Ensures the whole image is visible */
|
| 264 |
+
}
|
| 265 |
+
|
| 266 |
+
/* 5. Enlarge condition images (Col 2 & 3) */
|
| 267 |
+
#example-table td:nth-child(2) img,
|
| 268 |
+
#example-table td:nth-child(3) img {
|
| 269 |
+
height: 150px !important;
|
| 270 |
+
width: 150px !important;
|
| 271 |
+
object-fit: contain !important;
|
| 272 |
+
}
|
| 273 |
+
"""
|
| 274 |
+
|
| 275 |
+
|
| 276 |
+
with gr.Blocks(theme=theme, css=CUSTOM_CSS) as demo:
|
| 277 |
+
gr.Markdown(ABOUT_TEXT)
|
| 278 |
+
|
| 279 |
+
with gr.Tabs() as tabs:
|
| 280 |
+
with gr.TabItem("Interactive Demo", id=0):
|
| 281 |
+
with gr.Row(variant="panel", equal_height=False):
|
| 282 |
+
# Column 1: Inputs
|
| 283 |
+
with gr.Column(scale=1):
|
| 284 |
+
gr.Markdown("### 1. Input Images")
|
| 285 |
+
gr.Markdown(
|
| 286 |
+
'See the "How to Use" tab for details on generating the **Multi-View Image** and creating your own **Edited Condition**.',
|
| 287 |
+
elem_classes="help-text",
|
| 288 |
+
)
|
| 289 |
+
original_mv_image = gr.Image(
|
| 290 |
+
type="numpy",
|
| 291 |
+
label="Original Multi-View Image (src_mv.png)",
|
| 292 |
+
height=675,
|
| 293 |
+
width=450,
|
| 294 |
+
)
|
| 295 |
+
with gr.Row():
|
| 296 |
+
src_cond_image = gr.Image(
|
| 297 |
+
type="numpy",
|
| 298 |
+
label="Source Condition (src.png)",
|
| 299 |
+
height=350,
|
| 300 |
+
width=350,
|
| 301 |
+
)
|
| 302 |
+
tgt_cond_image = gr.Image(
|
| 303 |
+
type="numpy",
|
| 304 |
+
label="Target Condition (edited.png)",
|
| 305 |
+
height=350,
|
| 306 |
+
width=350,
|
| 307 |
+
)
|
| 308 |
+
|
| 309 |
+
# Column 2: Parameters & Action
|
| 310 |
+
with gr.Column(scale=1, min_width=300):
|
| 311 |
+
gr.Markdown("### 2. Parameters")
|
| 312 |
+
with gr.Accordion("Advanced Parameters", open=True):
|
| 313 |
+
t_steps = gr.Slider(
|
| 314 |
+
minimum=1,
|
| 315 |
+
maximum=100,
|
| 316 |
+
value=50,
|
| 317 |
+
step=1,
|
| 318 |
+
label="T_steps",
|
| 319 |
+
info="Total number of denoising steps.",
|
| 320 |
+
)
|
| 321 |
+
n_max = gr.Slider(
|
| 322 |
+
minimum=1,
|
| 323 |
+
maximum=50,
|
| 324 |
+
value=31,
|
| 325 |
+
step=1,
|
| 326 |
+
label="n_max",
|
| 327 |
+
info="Number of scheduler steps for edit-aware guidance. Increase for more significant edits.",
|
| 328 |
+
)
|
| 329 |
+
src_gs = gr.Slider(
|
| 330 |
+
minimum=1.0,
|
| 331 |
+
maximum=10.0,
|
| 332 |
+
value=3.5,
|
| 333 |
+
step=0.1,
|
| 334 |
+
label="Source CFG",
|
| 335 |
+
info="Guidance scale for the source condition. Can typically remain constant.",
|
| 336 |
+
)
|
| 337 |
+
tar_gs = gr.Slider(
|
| 338 |
+
minimum=1.0,
|
| 339 |
+
maximum=30.0,
|
| 340 |
+
value=5.0,
|
| 341 |
+
step=0.1,
|
| 342 |
+
label="Target CFG",
|
| 343 |
+
info="Guidance scale for the target condition. Increase for more significant edits.",
|
| 344 |
+
)
|
| 345 |
+
seed = gr.Slider(
|
| 346 |
+
minimum=0,
|
| 347 |
+
maximum=10000,
|
| 348 |
+
value=18,
|
| 349 |
+
step=1,
|
| 350 |
+
label="Seed",
|
| 351 |
+
info="Random seed for reproducibility.",
|
| 352 |
+
)
|
| 353 |
+
|
| 354 |
+
with gr.Row(elem_id="action-buttons"):
|
| 355 |
+
clear_button = gr.Button("Clear", variant="secondary", scale=1)
|
| 356 |
+
run_button = gr.Button("Generate", variant="primary", scale=2)
|
| 357 |
+
|
| 358 |
+
# Column 3: Output
|
| 359 |
+
with gr.Column(scale=2, min_width=350):
|
| 360 |
+
gr.Markdown("### 3. Output Image")
|
| 361 |
+
output_image = gr.Image(
|
| 362 |
+
type="pil",
|
| 363 |
+
label="Edited Result",
|
| 364 |
+
height=450,
|
| 365 |
+
width=450,
|
| 366 |
+
interactive=False,
|
| 367 |
+
)
|
| 368 |
+
gr.Markdown(
|
| 369 |
+
'After generating, you can use the `recon.py` script to create a 3D model. See the "How to Use" tab for the full command.',
|
| 370 |
+
elem_classes="help-text",
|
| 371 |
+
)
|
| 372 |
+
|
| 373 |
+
# --- Examples Section ---
|
| 374 |
+
if os.path.exists(EXAMPLES_PATH):
|
| 375 |
+
gr.Markdown("---")
|
| 376 |
+
gr.Markdown("### Click an Example to Load")
|
| 377 |
+
|
| 378 |
+
example_inputs = [
|
| 379 |
+
original_mv_image,
|
| 380 |
+
src_cond_image,
|
| 381 |
+
tgt_cond_image,
|
| 382 |
+
t_steps,
|
| 383 |
+
n_max,
|
| 384 |
+
src_gs,
|
| 385 |
+
tar_gs,
|
| 386 |
+
]
|
| 387 |
+
|
| 388 |
+
example_data = [
|
| 389 |
+
[
|
| 390 |
+
os.path.join(EXAMPLES_PATH, "bike_vintage", "src_mv.png"),
|
| 391 |
+
os.path.join(EXAMPLES_PATH, "bike_vintage", "src.png"),
|
| 392 |
+
os.path.join(EXAMPLES_PATH, "bike_vintage", "edited.png"),
|
| 393 |
+
50,
|
| 394 |
+
31,
|
| 395 |
+
3.5,
|
| 396 |
+
5.0,
|
| 397 |
+
18,
|
| 398 |
+
],
|
| 399 |
+
[
|
| 400 |
+
os.path.join(EXAMPLES_PATH, "robot_sunglasses", "src_mv.png"),
|
| 401 |
+
os.path.join(EXAMPLES_PATH, "robot_sunglasses", "src.png"),
|
| 402 |
+
os.path.join(EXAMPLES_PATH, "robot_sunglasses", "edited.png"),
|
| 403 |
+
50,
|
| 404 |
+
31,
|
| 405 |
+
3.5,
|
| 406 |
+
5.0,
|
| 407 |
+
18,
|
| 408 |
+
],
|
| 409 |
+
[
|
| 410 |
+
os.path.join(EXAMPLES_PATH, "stormtrooper_donut", "src_mv.png"),
|
| 411 |
+
os.path.join(EXAMPLES_PATH, "stormtrooper_donut", "src.png"),
|
| 412 |
+
os.path.join(EXAMPLES_PATH, "stormtrooper_donut", "edited.png"),
|
| 413 |
+
50,
|
| 414 |
+
42,
|
| 415 |
+
3.5,
|
| 416 |
+
12.0,
|
| 417 |
+
18,
|
| 418 |
+
],
|
| 419 |
+
[
|
| 420 |
+
os.path.join(EXAMPLES_PATH, "figure_zombie", "src_mv.png"),
|
| 421 |
+
os.path.join(EXAMPLES_PATH, "figure_zombie", "src.png"),
|
| 422 |
+
os.path.join(EXAMPLES_PATH, "figure_zombie", "edited.png"),
|
| 423 |
+
50,
|
| 424 |
+
31,
|
| 425 |
+
3.5,
|
| 426 |
+
5.0,
|
| 427 |
+
18,
|
| 428 |
+
],
|
| 429 |
+
[
|
| 430 |
+
os.path.join(EXAMPLES_PATH, "deer_pixar", "src_mv.png"),
|
| 431 |
+
os.path.join(EXAMPLES_PATH, "deer_pixar", "src.png"),
|
| 432 |
+
os.path.join(EXAMPLES_PATH, "deer_pixar", "edited.png"),
|
| 433 |
+
50,
|
| 434 |
+
31,
|
| 435 |
+
3.5,
|
| 436 |
+
5.0,
|
| 437 |
+
18,
|
| 438 |
+
],
|
| 439 |
+
[
|
| 440 |
+
os.path.join(EXAMPLES_PATH, "german-shep_plush", "src_mv.png"),
|
| 441 |
+
os.path.join(EXAMPLES_PATH, "german-shep_plush", "src.png"),
|
| 442 |
+
os.path.join(EXAMPLES_PATH, "german-shep_plush", "edited.png"),
|
| 443 |
+
50,
|
| 444 |
+
41,
|
| 445 |
+
3.5,
|
| 446 |
+
6.0,
|
| 447 |
+
18,
|
| 448 |
+
],
|
| 449 |
+
|
| 450 |
+
[
|
| 451 |
+
os.path.join(EXAMPLES_PATH, "deer_wings", "src_mv.png"),
|
| 452 |
+
os.path.join(EXAMPLES_PATH, "deer_wings", "src.png"),
|
| 453 |
+
os.path.join(EXAMPLES_PATH, "deer_wings", "edited.png"),
|
| 454 |
+
50,
|
| 455 |
+
39,
|
| 456 |
+
3.5,
|
| 457 |
+
21.0,
|
| 458 |
+
18,
|
| 459 |
+
],
|
| 460 |
+
[
|
| 461 |
+
os.path.join(EXAMPLES_PATH, "lego-car_spoiler", "src_mv.png"),
|
| 462 |
+
os.path.join(EXAMPLES_PATH, "lego-car_spoiler", "src.png"),
|
| 463 |
+
os.path.join(EXAMPLES_PATH, "lego-car_spoiler", "edited.png"),
|
| 464 |
+
50,
|
| 465 |
+
42,
|
| 466 |
+
3.5,
|
| 467 |
+
12.0,
|
| 468 |
+
18,
|
| 469 |
+
],
|
| 470 |
+
[
|
| 471 |
+
os.path.join(EXAMPLES_PATH, "batman_jetpack", "src_mv.png"),
|
| 472 |
+
os.path.join(EXAMPLES_PATH, "batman_jetpack", "src.png"),
|
| 473 |
+
os.path.join(EXAMPLES_PATH, "batman_jetpack", "edited.png"),
|
| 474 |
+
50,
|
| 475 |
+
31,
|
| 476 |
+
3.5,
|
| 477 |
+
5.0,
|
| 478 |
+
18,
|
| 479 |
+
],
|
| 480 |
+
[
|
| 481 |
+
os.path.join(EXAMPLES_PATH, "bike_sport", "src_mv.png"),
|
| 482 |
+
os.path.join(EXAMPLES_PATH, "bike_sport", "src.png"),
|
| 483 |
+
os.path.join(EXAMPLES_PATH, "bike_sport", "edited.png"),
|
| 484 |
+
50,
|
| 485 |
+
31,
|
| 486 |
+
3.5,
|
| 487 |
+
5.0,
|
| 488 |
+
18,
|
| 489 |
+
],
|
| 490 |
+
[
|
| 491 |
+
os.path.join(EXAMPLES_PATH, "red-dragon_tail", "src_mv.png"),
|
| 492 |
+
os.path.join(EXAMPLES_PATH, "red-dragon_tail", "src.png"),
|
| 493 |
+
os.path.join(EXAMPLES_PATH, "red-dragon_tail", "edited.png"),
|
| 494 |
+
50,
|
| 495 |
+
41,
|
| 496 |
+
3.5,
|
| 497 |
+
6.0,
|
| 498 |
+
18,
|
| 499 |
+
],
|
| 500 |
+
[
|
| 501 |
+
os.path.join(EXAMPLES_PATH, "cake_oreo", "src_mv.png"),
|
| 502 |
+
os.path.join(EXAMPLES_PATH, "cake_oreo", "src.png"),
|
| 503 |
+
os.path.join(EXAMPLES_PATH, "cake_oreo", "edited.png"),
|
| 504 |
+
50,
|
| 505 |
+
31,
|
| 506 |
+
3.5,
|
| 507 |
+
5.0,
|
| 508 |
+
18,
|
| 509 |
+
],
|
| 510 |
+
|
| 511 |
+
[
|
| 512 |
+
os.path.join(EXAMPLES_PATH, "bike_harley", "src_mv.png"),
|
| 513 |
+
os.path.join(EXAMPLES_PATH, "bike_harley", "src.png"),
|
| 514 |
+
os.path.join(EXAMPLES_PATH, "bike_harley", "edited.png"),
|
| 515 |
+
50,
|
| 516 |
+
31,
|
| 517 |
+
3.5,
|
| 518 |
+
5.0,
|
| 519 |
+
18,
|
| 520 |
+
],
|
| 521 |
+
[
|
| 522 |
+
os.path.join(EXAMPLES_PATH, "bike_modern", "src_mv.png"),
|
| 523 |
+
os.path.join(EXAMPLES_PATH, "bike_modern", "src.png"),
|
| 524 |
+
os.path.join(EXAMPLES_PATH, "bike_modern", "edited.png"),
|
| 525 |
+
50,
|
| 526 |
+
31,
|
| 527 |
+
3.5,
|
| 528 |
+
5.0,
|
| 529 |
+
18,
|
| 530 |
+
],
|
| 531 |
+
[
|
| 532 |
+
os.path.join(EXAMPLES_PATH, "bmw_speedy", "src_mv.png"),
|
| 533 |
+
os.path.join(EXAMPLES_PATH, "bmw_speedy", "src.png"),
|
| 534 |
+
os.path.join(EXAMPLES_PATH, "bmw_speedy", "edited.png"),
|
| 535 |
+
50,
|
| 536 |
+
31,
|
| 537 |
+
3.5,
|
| 538 |
+
5.0,
|
| 539 |
+
18,
|
| 540 |
+
],
|
| 541 |
+
[
|
| 542 |
+
os.path.join(EXAMPLES_PATH, "batman_backpack", "src_mv.png"),
|
| 543 |
+
os.path.join(EXAMPLES_PATH, "batman_backpack", "src.png"),
|
| 544 |
+
os.path.join(EXAMPLES_PATH, "batman_backpack", "edited.png"),
|
| 545 |
+
50,
|
| 546 |
+
31,
|
| 547 |
+
3.5,
|
| 548 |
+
5.0,
|
| 549 |
+
18,
|
| 550 |
+
],
|
| 551 |
+
|
| 552 |
+
[
|
| 553 |
+
os.path.join(EXAMPLES_PATH, "figure_backpack", "src_mv.png"),
|
| 554 |
+
os.path.join(EXAMPLES_PATH, "figure_backpack", "src.png"),
|
| 555 |
+
os.path.join(EXAMPLES_PATH, "figure_backpack", "edited.png"),
|
| 556 |
+
50,
|
| 557 |
+
31,
|
| 558 |
+
3.5,
|
| 559 |
+
5.0,
|
| 560 |
+
18,
|
| 561 |
+
],
|
| 562 |
+
[
|
| 563 |
+
os.path.join(EXAMPLES_PATH, "car_cartoon", "src_mv.png"),
|
| 564 |
+
os.path.join(EXAMPLES_PATH, "car_cartoon", "src.png"),
|
| 565 |
+
os.path.join(EXAMPLES_PATH, "car_cartoon", "edited.png"),
|
| 566 |
+
50,
|
| 567 |
+
31,
|
| 568 |
+
3.5,
|
| 569 |
+
5.0,
|
| 570 |
+
18,
|
| 571 |
+
],
|
| 572 |
+
[
|
| 573 |
+
os.path.join(EXAMPLES_PATH, "car_engine", "src_mv.png"),
|
| 574 |
+
os.path.join(EXAMPLES_PATH, "car_engine", "src.png"),
|
| 575 |
+
os.path.join(EXAMPLES_PATH, "car_engine", "edited.png"),
|
| 576 |
+
50,
|
| 577 |
+
31,
|
| 578 |
+
3.5,
|
| 579 |
+
5.0,
|
| 580 |
+
18,
|
| 581 |
+
],
|
| 582 |
+
[
|
| 583 |
+
os.path.join(EXAMPLES_PATH, "car_steampunk", "src_mv.png"),
|
| 584 |
+
os.path.join(EXAMPLES_PATH, "car_steampunk", "src.png"),
|
| 585 |
+
os.path.join(EXAMPLES_PATH, "car_steampunk", "edited.png"),
|
| 586 |
+
50,
|
| 587 |
+
41,
|
| 588 |
+
3.5,
|
| 589 |
+
6.0,
|
| 590 |
+
18,
|
| 591 |
+
],
|
| 592 |
+
[
|
| 593 |
+
os.path.join(EXAMPLES_PATH, "green-dragon_skirt", "src_mv.png"),
|
| 594 |
+
os.path.join(EXAMPLES_PATH, "green-dragon_skirt", "src.png"),
|
| 595 |
+
os.path.join(EXAMPLES_PATH, "green-dragon_skirt", "edited.png"),
|
| 596 |
+
50,
|
| 597 |
+
41,
|
| 598 |
+
3.5,
|
| 599 |
+
6.0,
|
| 600 |
+
18,
|
| 601 |
+
],
|
| 602 |
+
[
|
| 603 |
+
os.path.join(EXAMPLES_PATH, "gazebo_pagoda", "src_mv.png"),
|
| 604 |
+
os.path.join(EXAMPLES_PATH, "gazebo_pagoda", "src.png"),
|
| 605 |
+
os.path.join(EXAMPLES_PATH, "gazebo_pagoda", "edited.png"),
|
| 606 |
+
50,
|
| 607 |
+
41,
|
| 608 |
+
3.5,
|
| 609 |
+
6.0,
|
| 610 |
+
18,
|
| 611 |
+
],
|
| 612 |
+
[
|
| 613 |
+
os.path.join(EXAMPLES_PATH, "oasis_magical", "src_mv.png"),
|
| 614 |
+
os.path.join(EXAMPLES_PATH, "oasis_magical", "src.png"),
|
| 615 |
+
os.path.join(EXAMPLES_PATH, "oasis_magical", "edited.png"),
|
| 616 |
+
50,
|
| 617 |
+
39,
|
| 618 |
+
3.5,
|
| 619 |
+
5.0,
|
| 620 |
+
18,
|
| 621 |
+
],
|
| 622 |
+
[
|
| 623 |
+
os.path.join(EXAMPLES_PATH, "cabin_alpine", "src_mv.png"),
|
| 624 |
+
os.path.join(EXAMPLES_PATH, "cabin_alpine", "src.png"),
|
| 625 |
+
os.path.join(EXAMPLES_PATH, "cabin_alpine", "edited.png"),
|
| 626 |
+
50,
|
| 627 |
+
42,
|
| 628 |
+
3.5,
|
| 629 |
+
12.0,
|
| 630 |
+
18,
|
| 631 |
+
],
|
| 632 |
+
[
|
| 633 |
+
os.path.join(EXAMPLES_PATH, "cabin_gothic", "src_mv.png"),
|
| 634 |
+
os.path.join(EXAMPLES_PATH, "cabin_gothic", "src.png"),
|
| 635 |
+
os.path.join(EXAMPLES_PATH, "cabin_gothic", "edited.png"),
|
| 636 |
+
50,
|
| 637 |
+
42,
|
| 638 |
+
3.5,
|
| 639 |
+
12.0,
|
| 640 |
+
18,
|
| 641 |
+
],
|
| 642 |
+
[
|
| 643 |
+
os.path.join(EXAMPLES_PATH, "fox_tuxedo", "src_mv.png"),
|
| 644 |
+
os.path.join(EXAMPLES_PATH, "fox_tuxedo", "src.png"),
|
| 645 |
+
os.path.join(EXAMPLES_PATH, "fox_tuxedo", "edited.png"),
|
| 646 |
+
50,
|
| 647 |
+
31,
|
| 648 |
+
3.5,
|
| 649 |
+
5.0,
|
| 650 |
+
18,
|
| 651 |
+
],
|
| 652 |
+
|
| 653 |
+
[
|
| 654 |
+
os.path.join(EXAMPLES_PATH, "cabin_haunted", "src_mv.png"),
|
| 655 |
+
os.path.join(EXAMPLES_PATH, "cabin_haunted", "src.png"),
|
| 656 |
+
os.path.join(EXAMPLES_PATH, "cabin_haunted", "edited.png"),
|
| 657 |
+
50,
|
| 658 |
+
42,
|
| 659 |
+
3.5,
|
| 660 |
+
12.0,
|
| 661 |
+
18,
|
| 662 |
+
],
|
| 663 |
+
[
|
| 664 |
+
os.path.join(EXAMPLES_PATH, "fox_eyes", "src_mv.png"),
|
| 665 |
+
os.path.join(EXAMPLES_PATH, "fox_eyes", "src.png"),
|
| 666 |
+
os.path.join(EXAMPLES_PATH, "fox_eyes", "edited.png"),
|
| 667 |
+
50,
|
| 668 |
+
31,
|
| 669 |
+
3.5,
|
| 670 |
+
5.0,
|
| 671 |
+
18,
|
| 672 |
+
],
|
| 673 |
+
[
|
| 674 |
+
os.path.join(EXAMPLES_PATH, "gazebo_disney", "src_mv.png"),
|
| 675 |
+
os.path.join(EXAMPLES_PATH, "gazebo_disney", "src.png"),
|
| 676 |
+
os.path.join(EXAMPLES_PATH, "gazebo_disney", "edited.png"),
|
| 677 |
+
50,
|
| 678 |
+
31,
|
| 679 |
+
3.5,
|
| 680 |
+
5.0,
|
| 681 |
+
18,
|
| 682 |
+
],
|
| 683 |
+
[
|
| 684 |
+
os.path.join(EXAMPLES_PATH, "desk_wizard", "src_mv.png"),
|
| 685 |
+
os.path.join(EXAMPLES_PATH, "desk_wizard", "src.png"),
|
| 686 |
+
os.path.join(EXAMPLES_PATH, "desk_wizard", "edited.png"),
|
| 687 |
+
50,
|
| 688 |
+
42,
|
| 689 |
+
3.5,
|
| 690 |
+
12.0,
|
| 691 |
+
18,
|
| 692 |
+
],
|
| 693 |
+
|
| 694 |
+
[
|
| 695 |
+
os.path.join(EXAMPLES_PATH, "gazebo_light", "src_mv.png"),
|
| 696 |
+
os.path.join(EXAMPLES_PATH, "gazebo_light", "src.png"),
|
| 697 |
+
os.path.join(EXAMPLES_PATH, "gazebo_light", "edited.png"),
|
| 698 |
+
50,
|
| 699 |
+
31,
|
| 700 |
+
3.5,
|
| 701 |
+
5.0,
|
| 702 |
+
18,
|
| 703 |
+
],
|
| 704 |
+
[
|
| 705 |
+
os.path.join(EXAMPLES_PATH, "gazebo_roof", "src_mv.png"),
|
| 706 |
+
os.path.join(EXAMPLES_PATH, "gazebo_roof", "src.png"),
|
| 707 |
+
os.path.join(EXAMPLES_PATH, "gazebo_roof", "edited.png"),
|
| 708 |
+
50,
|
| 709 |
+
39,
|
| 710 |
+
3.5,
|
| 711 |
+
5.0,
|
| 712 |
+
18,
|
| 713 |
+
],
|
| 714 |
+
[
|
| 715 |
+
os.path.join(EXAMPLES_PATH, "grogu_earphones", "src_mv.png"),
|
| 716 |
+
os.path.join(EXAMPLES_PATH, "grogu_earphones", "src.png"),
|
| 717 |
+
os.path.join(EXAMPLES_PATH, "grogu_earphones", "edited.png"),
|
| 718 |
+
50,
|
| 719 |
+
41,
|
| 720 |
+
3.5,
|
| 721 |
+
6.0,
|
| 722 |
+
18,
|
| 723 |
+
],
|
| 724 |
+
|
| 725 |
+
[
|
| 726 |
+
os.path.join(EXAMPLES_PATH, "gazebo_rust", "src_mv.png"),
|
| 727 |
+
os.path.join(EXAMPLES_PATH, "gazebo_rust", "src.png"),
|
| 728 |
+
os.path.join(EXAMPLES_PATH, "gazebo_rust", "edited.png"),
|
| 729 |
+
50,
|
| 730 |
+
39,
|
| 731 |
+
3.5,
|
| 732 |
+
5.0,
|
| 733 |
+
18,
|
| 734 |
+
],
|
| 735 |
+
|
| 736 |
+
[
|
| 737 |
+
os.path.join(EXAMPLES_PATH, "german-shep_pixar", "src_mv.png"),
|
| 738 |
+
os.path.join(EXAMPLES_PATH, "german-shep_pixar", "src.png"),
|
| 739 |
+
os.path.join(EXAMPLES_PATH, "german-shep_pixar", "edited.png"),
|
| 740 |
+
50,
|
| 741 |
+
39,
|
| 742 |
+
3.5,
|
| 743 |
+
5.0,
|
| 744 |
+
18,
|
| 745 |
+
],
|
| 746 |
+
[
|
| 747 |
+
os.path.join(EXAMPLES_PATH, "grogu_kimono", "src_mv.png"),
|
| 748 |
+
os.path.join(EXAMPLES_PATH, "grogu_kimono", "src.png"),
|
| 749 |
+
os.path.join(EXAMPLES_PATH, "grogu_kimono", "edited.png"),
|
| 750 |
+
50,
|
| 751 |
+
39,
|
| 752 |
+
3.5,
|
| 753 |
+
21.0,
|
| 754 |
+
18,
|
| 755 |
+
],
|
| 756 |
+
[
|
| 757 |
+
os.path.join(EXAMPLES_PATH, "ship_fantasy", "src_mv.png"),
|
| 758 |
+
os.path.join(EXAMPLES_PATH, "ship_fantasy", "src.png"),
|
| 759 |
+
os.path.join(EXAMPLES_PATH, "ship_fantasy", "edited.png"),
|
| 760 |
+
50,
|
| 761 |
+
31,
|
| 762 |
+
3.5,
|
| 763 |
+
5.0,
|
| 764 |
+
18,
|
| 765 |
+
],
|
| 766 |
+
|
| 767 |
+
[
|
| 768 |
+
os.path.join(EXAMPLES_PATH, "grogu_lego-fig", "src_mv.png"),
|
| 769 |
+
os.path.join(EXAMPLES_PATH, "grogu_lego-fig", "src.png"),
|
| 770 |
+
os.path.join(EXAMPLES_PATH, "grogu_lego-fig", "edited.png"),
|
| 771 |
+
50,
|
| 772 |
+
39,
|
| 773 |
+
3.5,
|
| 774 |
+
21.0,
|
| 775 |
+
18,
|
| 776 |
+
],
|
| 777 |
+
[
|
| 778 |
+
os.path.join(EXAMPLES_PATH, "lego-car_spoiler", "src_mv.png"),
|
| 779 |
+
os.path.join(EXAMPLES_PATH, "lego-car_spoiler", "src.png"),
|
| 780 |
+
os.path.join(EXAMPLES_PATH, "lego-car_spoiler", "edited.png"),
|
| 781 |
+
50,
|
| 782 |
+
39,
|
| 783 |
+
3.5,
|
| 784 |
+
5.0,
|
| 785 |
+
18,
|
| 786 |
+
],
|
| 787 |
+
[
|
| 788 |
+
os.path.join(EXAMPLES_PATH, "nurse_sporty", "src_mv.png"),
|
| 789 |
+
os.path.join(EXAMPLES_PATH, "nurse_sporty", "src.png"),
|
| 790 |
+
os.path.join(EXAMPLES_PATH, "nurse_sporty", "edited.png"),
|
| 791 |
+
50,
|
| 792 |
+
31,
|
| 793 |
+
3.5,
|
| 794 |
+
5.0,
|
| 795 |
+
18,
|
| 796 |
+
],
|
| 797 |
+
[
|
| 798 |
+
os.path.join(EXAMPLES_PATH, "r2d2_golden", "src_mv.png"),
|
| 799 |
+
os.path.join(EXAMPLES_PATH, "r2d2_golden", "src.png"),
|
| 800 |
+
os.path.join(EXAMPLES_PATH, "r2d2_golden", "edited.png"),
|
| 801 |
+
50,
|
| 802 |
+
31,
|
| 803 |
+
3.5,
|
| 804 |
+
5.0,
|
| 805 |
+
18,
|
| 806 |
+
],
|
| 807 |
+
[
|
| 808 |
+
os.path.join(EXAMPLES_PATH, "grogu_the-force", "src_mv.png"),
|
| 809 |
+
os.path.join(EXAMPLES_PATH, "grogu_the-force", "src.png"),
|
| 810 |
+
os.path.join(EXAMPLES_PATH, "grogu_the-force", "edited.png"),
|
| 811 |
+
50,
|
| 812 |
+
39,
|
| 813 |
+
3.5,
|
| 814 |
+
21.0,
|
| 815 |
+
18,
|
| 816 |
+
],
|
| 817 |
+
|
| 818 |
+
[
|
| 819 |
+
os.path.join(EXAMPLES_PATH, "spiderbot_chrome", "src_mv.png"),
|
| 820 |
+
os.path.join(EXAMPLES_PATH, "spiderbot_chrome", "src.png"),
|
| 821 |
+
os.path.join(EXAMPLES_PATH, "spiderbot_chrome", "edited.png"),
|
| 822 |
+
50,
|
| 823 |
+
31,
|
| 824 |
+
3.5,
|
| 825 |
+
5.0,
|
| 826 |
+
18,
|
| 827 |
+
],
|
| 828 |
+
[
|
| 829 |
+
os.path.join(
|
| 830 |
+
EXAMPLES_PATH, "spiderbot_steampunk", "src_mv.png"
|
| 831 |
+
),
|
| 832 |
+
os.path.join(EXAMPLES_PATH, "spiderbot_steampunk", "src.png"),
|
| 833 |
+
os.path.join(
|
| 834 |
+
EXAMPLES_PATH, "spiderbot_steampunk", "edited.png"
|
| 835 |
+
),
|
| 836 |
+
50,
|
| 837 |
+
31,
|
| 838 |
+
3.5,
|
| 839 |
+
5.0,
|
| 840 |
+
18,
|
| 841 |
+
],
|
| 842 |
+
[
|
| 843 |
+
os.path.join(EXAMPLES_PATH, "superman_crossed", "src_mv.png"),
|
| 844 |
+
os.path.join(EXAMPLES_PATH, "superman_crossed", "src.png"),
|
| 845 |
+
os.path.join(EXAMPLES_PATH, "superman_crossed", "edited.png"),
|
| 846 |
+
50,
|
| 847 |
+
39,
|
| 848 |
+
3.5,
|
| 849 |
+
5.0,
|
| 850 |
+
18,
|
| 851 |
+
],
|
| 852 |
+
]
|
| 853 |
+
|
| 854 |
+
gr.Examples(
|
| 855 |
+
examples=example_data,
|
| 856 |
+
inputs=example_inputs,
|
| 857 |
+
label="Example Edits",
|
| 858 |
+
examples_per_page=10,
|
| 859 |
+
elem_id="example-table"
|
| 860 |
+
|
| 861 |
+
)
|
| 862 |
+
|
| 863 |
+
with gr.TabItem("How to Use", id=1):
|
| 864 |
+
gr.Markdown(HOW_TO_USE_TEXT)
|
| 865 |
+
|
| 866 |
+
# Define button actions
|
| 867 |
+
run_button.click(
|
| 868 |
+
fn=run_main_script,
|
| 869 |
+
inputs=[
|
| 870 |
+
src_cond_image,
|
| 871 |
+
tgt_cond_image,
|
| 872 |
+
original_mv_image,
|
| 873 |
+
t_steps,
|
| 874 |
+
n_max,
|
| 875 |
+
src_gs,
|
| 876 |
+
tar_gs,
|
| 877 |
+
seed,
|
| 878 |
+
],
|
| 879 |
+
outputs=output_image,
|
| 880 |
+
)
|
| 881 |
+
|
| 882 |
+
clear_button.click(
|
| 883 |
+
fn=clear_inputs,
|
| 884 |
+
inputs=[],
|
| 885 |
+
outputs=[
|
| 886 |
+
original_mv_image,
|
| 887 |
+
src_cond_image,
|
| 888 |
+
tgt_cond_image,
|
| 889 |
+
t_steps,
|
| 890 |
+
n_max,
|
| 891 |
+
src_gs,
|
| 892 |
+
tar_gs,
|
| 893 |
+
seed,
|
| 894 |
+
output_image,
|
| 895 |
+
],
|
| 896 |
+
)
|
| 897 |
+
|
| 898 |
+
if __name__ == "__main__":
|
| 899 |
+
demo.launch(share=True)
|
assets/stormtrooper.glb
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:921f8656fac3332d100969f40724455b7d34565625cf2a29d9e36f6c81b1d1c9
|
| 3 |
+
size 1659928
|
examples/batman_backpack/edited.png
ADDED
|
Git LFS Details
|
examples/batman_backpack/src.png
ADDED
|
Git LFS Details
|
examples/batman_backpack/src_mv.png
ADDED
|
Git LFS Details
|
examples/batman_jetpack/edited.png
ADDED
|
Git LFS Details
|
examples/batman_jetpack/src.png
ADDED
|
Git LFS Details
|
examples/batman_jetpack/src_mv.png
ADDED
|
Git LFS Details
|
examples/bike_harley/edited.png
ADDED
|
Git LFS Details
|
examples/bike_harley/src.png
ADDED
|
Git LFS Details
|
examples/bike_harley/src_mv.png
ADDED
|
Git LFS Details
|
examples/bike_modern/edited.png
ADDED
|
Git LFS Details
|
examples/bike_modern/src.png
ADDED
|
Git LFS Details
|
examples/bike_modern/src_mv.png
ADDED
|
Git LFS Details
|
examples/bike_sport/edited.png
ADDED
|
Git LFS Details
|
examples/bike_sport/src.png
ADDED
|
Git LFS Details
|
examples/bike_sport/src_mv.png
ADDED
|
Git LFS Details
|
examples/bike_vintage/edited.png
ADDED
|
Git LFS Details
|
examples/bike_vintage/src.png
ADDED
|
Git LFS Details
|
examples/bike_vintage/src_mv.png
ADDED
|
Git LFS Details
|
examples/bmw_speedy/edited.png
ADDED
|
Git LFS Details
|
examples/bmw_speedy/src.png
ADDED
|
Git LFS Details
|
examples/bmw_speedy/src_mv.png
ADDED
|
Git LFS Details
|
examples/cabin_alpine/edited.png
ADDED
|
Git LFS Details
|
examples/cabin_alpine/src.png
ADDED
|
Git LFS Details
|
examples/cabin_alpine/src_mv.png
ADDED
|
Git LFS Details
|
examples/cabin_gothic/edited.png
ADDED
|
Git LFS Details
|
examples/cabin_gothic/src.png
ADDED
|
Git LFS Details
|
examples/cabin_gothic/src_mv.png
ADDED
|
Git LFS Details
|
examples/cabin_haunted/edited.png
ADDED
|
Git LFS Details
|
examples/cabin_haunted/src.png
ADDED
|
Git LFS Details
|
examples/cabin_haunted/src_mv.png
ADDED
|
Git LFS Details
|
examples/cake_oreo/edited.png
ADDED
|
Git LFS Details
|
examples/cake_oreo/src.png
ADDED
|
Git LFS Details
|
examples/cake_oreo/src_mv.png
ADDED
|
Git LFS Details
|
examples/car_cartoon/edited.png
ADDED
|
Git LFS Details
|
examples/car_cartoon/src.png
ADDED
|
Git LFS Details
|
examples/car_cartoon/src_mv.png
ADDED
|
Git LFS Details
|
examples/car_engine/edited.png
ADDED
|
Git LFS Details
|
examples/car_engine/src.png
ADDED
|
Git LFS Details
|
examples/car_engine/src_mv.png
ADDED
|
Git LFS Details
|
examples/car_steampunk/edited.png
ADDED
|
Git LFS Details
|
examples/car_steampunk/src.png
ADDED
|
Git LFS Details
|
examples/car_steampunk/src_mv.png
ADDED
|
Git LFS Details
|
examples/deer_pixar/edited.png
ADDED
|
Git LFS Details
|
examples/deer_pixar/src.png
ADDED
|
Git LFS Details
|