Spaces:
Running
on
Zero
Running
on
Zero
update the code of gradio
Browse files- gradio_app.py +78 -38
gradio_app.py
CHANGED
|
@@ -4,18 +4,16 @@ import os
|
|
| 4 |
import json
|
| 5 |
import torch
|
| 6 |
import sys
|
| 7 |
-
import
|
| 8 |
-
import importlib
|
| 9 |
import numpy as np
|
| 10 |
-
from omegaconf import OmegaConf
|
| 11 |
from huggingface_hub import hf_hub_download
|
| 12 |
-
from diffusers import DiffusionPipeline
|
| 13 |
|
| 14 |
import PIL
|
| 15 |
from PIL import Image
|
| 16 |
from collections import OrderedDict
|
| 17 |
import trimesh
|
| 18 |
import rembg
|
|
|
|
| 19 |
import gradio as gr
|
| 20 |
from typing import Any
|
| 21 |
|
|
@@ -50,7 +48,7 @@ _CITE_ = r"""
|
|
| 50 |
If you find our work useful for your research or applications, please cite using this bibtex:
|
| 51 |
```bibtex
|
| 52 |
@article{li2024craftsman,
|
| 53 |
-
author = {Weiyu Li and Jiarui Liu and Rui Chen and Yixun Liang and Xuelin Chen and Ping Tan and Xiaoxiao Long},
|
| 54 |
title = {CraftsMan: High-fidelity Mesh Generation with 3D Native Generation and Interactive Geometry Refiner},
|
| 55 |
journal = {arXiv preprint arXiv:2405.14979},
|
| 56 |
year = {2024},
|
|
@@ -67,7 +65,6 @@ If you have any questions, feel free to open a discussion or contact us at <b>we
|
|
| 67 |
model = None
|
| 68 |
cached_dir = None
|
| 69 |
|
| 70 |
-
|
| 71 |
def check_input_image(input_image):
|
| 72 |
if input_image is None:
|
| 73 |
raise gr.Error("No image uploaded!")
|
|
@@ -87,7 +84,7 @@ class RMBG(object):
|
|
| 87 |
if image.mode == "RGBA" and image.getextrema()[3][0] < 255:
|
| 88 |
# explain why current do not rm bg
|
| 89 |
print("alhpa channl not enpty, skip remove background, using alpha channel as mask")
|
| 90 |
-
background = Image.new("RGBA", image.size, background_color)
|
| 91 |
image = Image.alpha_composite(background, image)
|
| 92 |
do_remove = False
|
| 93 |
do_remove = do_remove or force
|
|
@@ -97,11 +94,11 @@ class RMBG(object):
|
|
| 97 |
# calculate the min bbox of the image
|
| 98 |
alpha = image.split()[-1]
|
| 99 |
image = image.crop(alpha.getbbox())
|
| 100 |
-
|
| 101 |
return image
|
| 102 |
return _rembg_remove(input_image, None, force_remove=True)
|
| 103 |
|
| 104 |
-
def run(self, rm_type,
|
|
|
|
| 105 |
if "Original" in background_choice:
|
| 106 |
return image
|
| 107 |
else:
|
|
@@ -120,7 +117,7 @@ class RMBG(object):
|
|
| 120 |
# Resize the image while maintaining the aspect ratio
|
| 121 |
resized_image = image.resize(new_size)
|
| 122 |
# Create a new image with the original size and white background
|
| 123 |
-
padded_image = PIL.Image.new("RGBA", image.size, (
|
| 124 |
paste_position = ((image.width - resized_image.width) // 2, (image.height - resized_image.height) // 2)
|
| 125 |
padded_image.paste(resized_image, paste_position)
|
| 126 |
|
|
@@ -129,24 +126,29 @@ class RMBG(object):
|
|
| 129 |
if width == height:
|
| 130 |
return padded_image
|
| 131 |
new_size = (max(width, height), max(width, height))
|
| 132 |
-
image = PIL.Image.new("RGBA", new_size, (
|
| 133 |
paste_position = ((new_size[0] - width) // 2, (new_size[1] - height) // 2)
|
| 134 |
image.paste(padded_image, paste_position)
|
| 135 |
-
|
|
|
|
|
|
|
| 136 |
|
| 137 |
-
@spaces.GPU
|
| 138 |
def image2mesh(image: Any,
|
| 139 |
more: bool = False,
|
| 140 |
scheluder_name: str ="DDIMScheduler",
|
| 141 |
guidance_scale: int = 7.5,
|
| 142 |
-
steps: int =
|
| 143 |
seed: int = 4,
|
| 144 |
target_face_count: int = 2000,
|
| 145 |
octree_depth: int = 7):
|
| 146 |
|
|
|
|
|
|
|
|
|
|
| 147 |
sample_inputs = {
|
| 148 |
"image": [
|
| 149 |
-
|
| 150 |
]
|
| 151 |
}
|
| 152 |
|
|
@@ -169,7 +171,7 @@ def image2mesh(image: Any,
|
|
| 169 |
assert len(mesh_outputs) == 1, "Only support single mesh output for gradio demo"
|
| 170 |
mesh = trimesh.Trimesh(mesh_outputs[0][0], mesh_outputs[0][1])
|
| 171 |
# filepath = f"{cached_dir}/{time.time()}.obj"
|
| 172 |
-
filepath = tempfile.NamedTemporaryFile(suffix=f".
|
| 173 |
mesh.export(filepath, include_normals=True)
|
| 174 |
|
| 175 |
if 'Remesh' in more:
|
|
@@ -181,6 +183,30 @@ def image2mesh(image: Any,
|
|
| 181 |
|
| 182 |
return filepath
|
| 183 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 184 |
if __name__=="__main__":
|
| 185 |
parser = argparse.ArgumentParser()
|
| 186 |
parser.add_argument("--model_path", type=str, default="", help="Path to the object file",)
|
|
@@ -238,39 +264,23 @@ if __name__=="__main__":
|
|
| 238 |
label="Image Input",
|
| 239 |
image_mode="RGBA",
|
| 240 |
sources="upload",
|
| 241 |
-
type="
|
| 242 |
)
|
| 243 |
run_btn = gr.Button('Generate', variant='primary', interactive=True)
|
| 244 |
|
| 245 |
with gr.Row():
|
| 246 |
gr.Markdown('''Try a different <b>seed and MV Model</b> for better results. Good Luck :)''')
|
| 247 |
with gr.Row():
|
| 248 |
-
seed = gr.Number(
|
| 249 |
more = gr.CheckboxGroup(["Remesh"], label="More", show_label=False)
|
| 250 |
target_face_count = gr.Number(2000, label='Target Face Count', show_label=True)
|
| 251 |
|
| 252 |
-
with gr.Row():
|
| 253 |
-
gr.Examples(
|
| 254 |
-
examples=[os.path.join("./examples", i) for i in os.listdir("./examples")],
|
| 255 |
-
inputs=[image_input],
|
| 256 |
-
examples_per_page=8
|
| 257 |
-
)
|
| 258 |
|
| 259 |
-
with gr.Column(scale=4):
|
| 260 |
-
with gr.Row():
|
| 261 |
-
output_model_obj = gr.Model3D(
|
| 262 |
-
label="Output Model (OBJ Format)",
|
| 263 |
-
camera_position=(90.0, 90.0, 3.5),
|
| 264 |
-
interactive=False,
|
| 265 |
-
)
|
| 266 |
-
with gr.Row():
|
| 267 |
-
gr.Markdown('''*please note that the model is fliped due to the gradio viewer, please download the obj file and you will get the correct orientation.''')
|
| 268 |
-
|
| 269 |
with gr.Accordion('Advanced options', open=False):
|
| 270 |
with gr.Row():
|
| 271 |
background_choice = gr.Dropdown(label="Backgroud Choice", value="Auto Remove Background",choices=list(background_choice.keys()))
|
| 272 |
rmbg_type = gr.Dropdown(label="Backgroud Remove Type", value="rembg",choices=['sam', "rembg"])
|
| 273 |
-
foreground_ratio = gr.Slider(label="Foreground Ratio", value=
|
| 274 |
|
| 275 |
with gr.Row():
|
| 276 |
guidance_scale = gr.Number(label="3D Guidance Scale", value=7.5, minimum=3.0, maximum=10.0)
|
|
@@ -279,10 +289,34 @@ if __name__=="__main__":
|
|
| 279 |
with gr.Row():
|
| 280 |
scheduler = gr.Dropdown(label="scheluder", value="DDIMScheduler",choices=list(scheluder_dict.keys()))
|
| 281 |
octree_depth = gr.Slider(label="Octree Depth", value=7, minimum=4, maximum=8, step=1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 282 |
|
| 283 |
gr.Markdown(_CITE_)
|
| 284 |
|
| 285 |
-
outputs = [output_model_obj]
|
|
|
|
| 286 |
rmbg = RMBG()
|
| 287 |
|
| 288 |
# model = load_model(ckpt_path, config_path, device)
|
|
@@ -304,7 +338,13 @@ if __name__=="__main__":
|
|
| 304 |
).success(
|
| 305 |
fn=image2mesh,
|
| 306 |
inputs=[image_input, more, scheduler, guidance_scale, steps, seed, target_face_count, octree_depth],
|
| 307 |
-
outputs=
|
| 308 |
-
api_name="generate_img2obj"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 309 |
|
| 310 |
demo.queue().launch(share=True, allowed_paths=[args.cached_dir])
|
|
|
|
| 4 |
import json
|
| 5 |
import torch
|
| 6 |
import sys
|
| 7 |
+
import base64
|
|
|
|
| 8 |
import numpy as np
|
|
|
|
| 9 |
from huggingface_hub import hf_hub_download
|
|
|
|
| 10 |
|
| 11 |
import PIL
|
| 12 |
from PIL import Image
|
| 13 |
from collections import OrderedDict
|
| 14 |
import trimesh
|
| 15 |
import rembg
|
| 16 |
+
import requests
|
| 17 |
import gradio as gr
|
| 18 |
from typing import Any
|
| 19 |
|
|
|
|
| 48 |
If you find our work useful for your research or applications, please cite using this bibtex:
|
| 49 |
```bibtex
|
| 50 |
@article{li2024craftsman,
|
| 51 |
+
author = {Weiyu Li and Jiarui Liu and Hongyu Yan and Rui Chen and Yixun Liang and Xuelin Chen and Ping Tan and Xiaoxiao Long},
|
| 52 |
title = {CraftsMan: High-fidelity Mesh Generation with 3D Native Generation and Interactive Geometry Refiner},
|
| 53 |
journal = {arXiv preprint arXiv:2405.14979},
|
| 54 |
year = {2024},
|
|
|
|
| 65 |
model = None
|
| 66 |
cached_dir = None
|
| 67 |
|
|
|
|
| 68 |
def check_input_image(input_image):
|
| 69 |
if input_image is None:
|
| 70 |
raise gr.Error("No image uploaded!")
|
|
|
|
| 84 |
if image.mode == "RGBA" and image.getextrema()[3][0] < 255:
|
| 85 |
# explain why current do not rm bg
|
| 86 |
print("alhpa channl not enpty, skip remove background, using alpha channel as mask")
|
| 87 |
+
background = Image.new("RGBA", image.size, (*background_color, 0))
|
| 88 |
image = Image.alpha_composite(background, image)
|
| 89 |
do_remove = False
|
| 90 |
do_remove = do_remove or force
|
|
|
|
| 94 |
# calculate the min bbox of the image
|
| 95 |
alpha = image.split()[-1]
|
| 96 |
image = image.crop(alpha.getbbox())
|
|
|
|
| 97 |
return image
|
| 98 |
return _rembg_remove(input_image, None, force_remove=True)
|
| 99 |
|
| 100 |
+
def run(self, rm_type, image_file, foreground_ratio, background_choice, background_color=(255, 255, 255)):
|
| 101 |
+
image = Image.open(image_file)
|
| 102 |
if "Original" in background_choice:
|
| 103 |
return image
|
| 104 |
else:
|
|
|
|
| 117 |
# Resize the image while maintaining the aspect ratio
|
| 118 |
resized_image = image.resize(new_size)
|
| 119 |
# Create a new image with the original size and white background
|
| 120 |
+
padded_image = PIL.Image.new("RGBA", image.size, (*background_color, 0))
|
| 121 |
paste_position = ((image.width - resized_image.width) // 2, (image.height - resized_image.height) // 2)
|
| 122 |
padded_image.paste(resized_image, paste_position)
|
| 123 |
|
|
|
|
| 126 |
if width == height:
|
| 127 |
return padded_image
|
| 128 |
new_size = (max(width, height), max(width, height))
|
| 129 |
+
image = PIL.Image.new("RGBA", new_size, (*background_color, 1))
|
| 130 |
paste_position = ((new_size[0] - width) // 2, (new_size[1] - height) // 2)
|
| 131 |
image.paste(padded_image, paste_position)
|
| 132 |
+
filepath = tempfile.NamedTemporaryFile(suffix=f".png", delete=False).name
|
| 133 |
+
image.save(filepath)
|
| 134 |
+
return filepath
|
| 135 |
|
| 136 |
+
# @spaces.GPU
|
| 137 |
def image2mesh(image: Any,
|
| 138 |
more: bool = False,
|
| 139 |
scheluder_name: str ="DDIMScheduler",
|
| 140 |
guidance_scale: int = 7.5,
|
| 141 |
+
steps: int = 50,
|
| 142 |
seed: int = 4,
|
| 143 |
target_face_count: int = 2000,
|
| 144 |
octree_depth: int = 7):
|
| 145 |
|
| 146 |
+
# global rmbg
|
| 147 |
+
# processed_image = rmbg.run(rm_type, image, foreground_ratio, background_choice)
|
| 148 |
+
processed_image = Image.open(image)
|
| 149 |
sample_inputs = {
|
| 150 |
"image": [
|
| 151 |
+
processed_image
|
| 152 |
]
|
| 153 |
}
|
| 154 |
|
|
|
|
| 171 |
assert len(mesh_outputs) == 1, "Only support single mesh output for gradio demo"
|
| 172 |
mesh = trimesh.Trimesh(mesh_outputs[0][0], mesh_outputs[0][1])
|
| 173 |
# filepath = f"{cached_dir}/{time.time()}.obj"
|
| 174 |
+
filepath = tempfile.NamedTemporaryFile(suffix=f".glb", delete=False).name
|
| 175 |
mesh.export(filepath, include_normals=True)
|
| 176 |
|
| 177 |
if 'Remesh' in more:
|
|
|
|
| 183 |
|
| 184 |
return filepath
|
| 185 |
|
| 186 |
+
def mesh2texture(mesh_file, image_file):
|
| 187 |
+
headers = {'Content-Type': 'application/json'}
|
| 188 |
+
server_url = "114.249.238.184:34119"
|
| 189 |
+
with open(image_file, 'rb') as f:
|
| 190 |
+
image_bytes = f.read()
|
| 191 |
+
with open(mesh_file, 'rb') as f:
|
| 192 |
+
mesh_bytes = f.read()
|
| 193 |
+
request = {
|
| 194 |
+
'png_base64_image': base64.b64encode(image_bytes).decode('utf-8'),
|
| 195 |
+
'glb_base64_mesh': base64.b64encode(mesh_bytes).decode('utf-8'),
|
| 196 |
+
}
|
| 197 |
+
response = requests.post(
|
| 198 |
+
url=f"http://{server_url}/generate_texture",
|
| 199 |
+
headers=headers,
|
| 200 |
+
data=json.dumps(request),
|
| 201 |
+
).json()
|
| 202 |
+
mesh_bytes = base64.b64decode(response['glb_base64_mesh'])
|
| 203 |
+
filepath = tempfile.NamedTemporaryFile(suffix=f".glb", delete=False).name
|
| 204 |
+
with open(filepath, 'wb') as f:
|
| 205 |
+
f.write(mesh_bytes)
|
| 206 |
+
|
| 207 |
+
return filepath
|
| 208 |
+
|
| 209 |
+
|
| 210 |
if __name__=="__main__":
|
| 211 |
parser = argparse.ArgumentParser()
|
| 212 |
parser.add_argument("--model_path", type=str, default="", help="Path to the object file",)
|
|
|
|
| 264 |
label="Image Input",
|
| 265 |
image_mode="RGBA",
|
| 266 |
sources="upload",
|
| 267 |
+
type="filepath",
|
| 268 |
)
|
| 269 |
run_btn = gr.Button('Generate', variant='primary', interactive=True)
|
| 270 |
|
| 271 |
with gr.Row():
|
| 272 |
gr.Markdown('''Try a different <b>seed and MV Model</b> for better results. Good Luck :)''')
|
| 273 |
with gr.Row():
|
| 274 |
+
seed = gr.Number(42, label='Seed', show_label=True)
|
| 275 |
more = gr.CheckboxGroup(["Remesh"], label="More", show_label=False)
|
| 276 |
target_face_count = gr.Number(2000, label='Target Face Count', show_label=True)
|
| 277 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 278 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 279 |
with gr.Accordion('Advanced options', open=False):
|
| 280 |
with gr.Row():
|
| 281 |
background_choice = gr.Dropdown(label="Backgroud Choice", value="Auto Remove Background",choices=list(background_choice.keys()))
|
| 282 |
rmbg_type = gr.Dropdown(label="Backgroud Remove Type", value="rembg",choices=['sam', "rembg"])
|
| 283 |
+
foreground_ratio = gr.Slider(label="Foreground Ratio", value=0.95, minimum=0.5, maximum=1.0, step=0.01)
|
| 284 |
|
| 285 |
with gr.Row():
|
| 286 |
guidance_scale = gr.Number(label="3D Guidance Scale", value=7.5, minimum=3.0, maximum=10.0)
|
|
|
|
| 289 |
with gr.Row():
|
| 290 |
scheduler = gr.Dropdown(label="scheluder", value="DDIMScheduler",choices=list(scheluder_dict.keys()))
|
| 291 |
octree_depth = gr.Slider(label="Octree Depth", value=7, minimum=4, maximum=8, step=1)
|
| 292 |
+
|
| 293 |
+
with gr.Row():
|
| 294 |
+
gr.Examples(
|
| 295 |
+
examples=[os.path.join("./assets/examples", i) for i in os.listdir("./assets/examples")],
|
| 296 |
+
inputs=[image_input],
|
| 297 |
+
examples_per_page=8
|
| 298 |
+
)
|
| 299 |
+
|
| 300 |
+
with gr.Column(scale=4):
|
| 301 |
+
with gr.Row():
|
| 302 |
+
output_model_obj = gr.Model3D(
|
| 303 |
+
label="Output Model (GLB Format)",
|
| 304 |
+
camera_position=(90.0, 90.0, 3.5),
|
| 305 |
+
interactive=False,
|
| 306 |
+
)
|
| 307 |
+
with gr.Row():
|
| 308 |
+
output_model_tex = gr.Model3D(
|
| 309 |
+
label="Output Textured Model (GLB Format)",
|
| 310 |
+
camera_position=(90.0, 90.0, 3.5),
|
| 311 |
+
interactive=False,
|
| 312 |
+
)
|
| 313 |
+
# with gr.Row():
|
| 314 |
+
# gr.Markdown('''*please note that the model is fliped due to the gradio viewer, please download the obj file and you will get the correct orientation.''')
|
| 315 |
|
| 316 |
gr.Markdown(_CITE_)
|
| 317 |
|
| 318 |
+
# outputs = [output_model_obj]
|
| 319 |
+
# outputs_tex = [output_model_tex]
|
| 320 |
rmbg = RMBG()
|
| 321 |
|
| 322 |
# model = load_model(ckpt_path, config_path, device)
|
|
|
|
| 338 |
).success(
|
| 339 |
fn=image2mesh,
|
| 340 |
inputs=[image_input, more, scheduler, guidance_scale, steps, seed, target_face_count, octree_depth],
|
| 341 |
+
outputs=[output_model_obj],
|
| 342 |
+
api_name="generate_img2obj"
|
| 343 |
+
).success(
|
| 344 |
+
fn=mesh2texture,
|
| 345 |
+
inputs=[output_model_obj, image_input],
|
| 346 |
+
outputs=[output_model_tex],
|
| 347 |
+
api_name="generate_obj2tex"
|
| 348 |
+
)
|
| 349 |
|
| 350 |
demo.queue().launch(share=True, allowed_paths=[args.cached_dir])
|