Spaces:
Runtime error
Runtime error
# install | |
import gradio as gr | |
import os | |
import subprocess | |
if os.getenv('SYSTEM') == 'spaces': | |
# subprocess.run('pip install pyembree'.split()) | |
try: | |
import pytorch3d | |
except ImportError: | |
subprocess.run( | |
'pip install --no-index --no-cache-dir pytorch3d -f https://dl.fbaipublicfiles.com/pytorch3d/packaging/wheels/py38_cu116_pyt1130/download.html' | |
.split() | |
) | |
subprocess.run("python setup.py build_ext --inplace".split(), cwd="./lib/common/libmesh/") | |
subprocess.run("python setup.py build_ext --inplace".split(), cwd="./lib/common/libvoxelize/") | |
from apps.infer import generate_model, generate_video | |
# running | |
title = ''' | |
# Unconstrained & Detailed Clothed Human Digitization (ECON + ControlNet) | |
### ECON: Explicit Clothed humans Optimized via Normal integration (CVPR 2023, Highlight) | |
''' | |
bottom = ''' | |
#### Citation | |
``` | |
@inproceedings{xiu2023econ, | |
title = {{ECON: Explicit Clothed humans Optimized via Normal integration}}, | |
author = {Xiu, Yuliang and Yang, Jinlong and Cao, Xu and Tzionas, Dimitrios and Black, Michael J.}, | |
booktitle = {Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR)}, | |
month = {June}, | |
year = {2023}, | |
} | |
``` | |
<details> | |
<summary>More</summary> | |
#### Acknowledgments: | |
- [controlnet-openpose](https://huggingface.co/spaces/diffusers/controlnet-openpose) | |
- [TEXTure](https://huggingface.co/spaces/TEXTurePaper/TEXTure) | |
#### Image Credits | |
* [Pinterest](https://www.pinterest.com/search/pins/?q=parkour&rs=sitelinks_searchbox) | |
#### Related works | |
* [ICON @ MPI-IS](https://icon.is.tue.mpg.de/) | |
* [MonoPort @ USC](https://xiuyuliang.cn/monoport) | |
* [Phorhum @ Google](https://phorhum.github.io/) | |
* [PIFuHD @ Meta](https://shunsukesaito.github.io/PIFuHD/) | |
* [PaMIR @ Tsinghua](http://www.liuyebin.com/pamir/pamir.html) | |
</details> | |
<center> | |
<a href="https://huggingface.co/spaces/Yuliang/ECON?duplicate=true"><img src="https://huggingface.co/datasets/huggingface/badges/raw/main/duplicate-this-space-lg-dark.svg"/></a> | |
<h2> Generate pose & prompt-guided images / Upload photos / Use examples → Submit Image (~3min) → Generate Video (~3min) </h2> | |
<h2><span style="color:red">ECON is only suitable for "humanoid images" and will not work well on cartoons with non-human shapes.</span></h2> | |
</center> | |
''' | |
description = ''' | |
<table> | |
<th width="20%"> | |
<ul> | |
<li><strong>Homepage</strong> <a href="https://econ.is.tue.mpg.de/">econ.is.tue.mpg.de</a></li> | |
<li><strong>Code</strong> <a href="https://github.com/YuliangXiu/ECON">YuliangXiu/ECON</a></li> | |
<li><strong>Paper</strong> <a href="https://arxiv.org/abs/2212.07422">arXiv</a>, <a href="https://readpaper.com/paper/4736821012688027649">ReadPaper</a></li> | |
<li><strong>Chatroom</strong> <a href="https://discord.gg/Vqa7KBGRyk">Discord</a></li> | |
</ul> | |
<br> | |
<ul> | |
<li><strong>Colab Notebook</strong> <a href='https://colab.research.google.com/drive/1YRgwoRCZIrSB2e7auEWFyG10Xzjbrbno?usp=sharing'><img style="display: inline-block;" src='https://colab.research.google.com/assets/colab-badge.svg' alt='Google Colab'></a></li> | |
<li><strong>Blender Plugin</strong> <a href='https://carlosedubarreto.gumroad.com/l/CEB_ECON'><img style="display: inline-block;" src='https://img.shields.io/badge/Blender-F6DDCC.svg?logo=Blender' alt='Blender'></a></li> | |
<li><strong>Docker Image</strong> <a href='https://github.com/YuliangXiu/ECON/blob/master/docs/installation-docker.md'><img style="display: inline-block;" src='https://img.shields.io/badge/Docker-9cf.svg?logo=Docker' alt='Docker'></a></li> | |
<li><strong>Windows Setup</strong> <a href="https://github.com/YuliangXiu/ECON/blob/master/docs/installation-windows.md"><img style="display: inline-block;" src='https://img.shields.io/badge/Windows-00a2ed.svg?logo=Windows' akt='Windows'></a></li> | |
</ul> | |
<br> | |
<a href="https://twitter.com/yuliangxiu"><img alt="Twitter Follow" src="https://img.shields.io/twitter/follow/yuliangxiu?style=social"></a><br> | |
<iframe src="https://ghbtns.com/github-btn.html?user=yuliangxiu&repo=ECON&type=star&count=true&v=2&size=small" frameborder="0" scrolling="0" width="100" height="20"></iframe> | |
</th> | |
<th width="40%"> | |
<iframe width="560" height="315" src="https://www.youtube.com/embed/5PEd_p90kS0" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> | |
</th> | |
<th width="40%"> | |
<iframe width="560" height="315" src="https://www.youtube.com/embed/sbWZbTf6ZYk" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> | |
</th> | |
</table> | |
''' | |
from controlnet_aux import OpenposeDetector | |
from diffusers import StableDiffusionControlNetPipeline, ControlNetModel | |
from diffusers import UniPCMultistepScheduler | |
import gradio as gr | |
import torch | |
import base64 | |
from io import BytesIO | |
from PIL import Image | |
# live conditioning | |
canvas_html = "<pose-canvas id='canvas-root' style='display:flex;max-width: 500px;margin: 0 auto;'></pose-canvas>" | |
load_js = """ | |
async () => { | |
const url = "https://huggingface.co/datasets/radames/gradio-components/raw/main/pose-gradio.js" | |
fetch(url) | |
.then(res => res.text()) | |
.then(text => { | |
const script = document.createElement('script'); | |
script.type = "module" | |
script.src = URL.createObjectURL(new Blob([text], { type: 'application/javascript' })); | |
document.head.appendChild(script); | |
}); | |
} | |
""" | |
get_js_image = """ | |
async (image_in_img, prompt, image_file_live_opt, live_conditioning) => { | |
const canvasEl = document.getElementById("canvas-root"); | |
const data = canvasEl? canvasEl._data : null; | |
return [image_in_img, prompt, image_file_live_opt, data] | |
} | |
""" | |
# Constants | |
cached = False | |
# Models | |
pose_model = OpenposeDetector.from_pretrained("lllyasviel/ControlNet") | |
controlnet = ControlNetModel.from_pretrained( | |
"lllyasviel/sd-controlnet-openpose", torch_dtype=torch.float16 | |
) | |
pipe = StableDiffusionControlNetPipeline.from_pretrained( | |
"runwayml/stable-diffusion-v1-5", | |
controlnet=controlnet, | |
safety_checker=None, | |
torch_dtype=torch.float16 | |
) | |
pipe.scheduler = UniPCMultistepScheduler.from_config(pipe.scheduler.config) | |
# This command loads the individual model components on GPU on-demand. So, we don't | |
# need to explicitly call pipe.to("cuda"). | |
pipe.enable_model_cpu_offload() | |
# xformers | |
pipe.enable_xformers_memory_efficient_attention() | |
# Generator seed, | |
generator = torch.manual_seed(0) | |
hint_prompts = ''' | |
<strong>Hints</strong>: <br> | |
best quality, extremely detailed, solid color background, | |
super detail, high detail, edge lighting, soft focus, | |
light and dark contrast, 8k, edge lighting, 3d, c4d, | |
blender, oc renderer, ultra high definition, 3d rendering | |
''' | |
def get_pose(image): | |
return pose_model(image) | |
import sys | |
def read_logs(): | |
sys.stdout.flush() | |
with open("output.log", "r") as f: | |
return f.read() | |
def generate_images(image, prompt, image_file_live_opt='file', live_conditioning=None): | |
if image is None and 'image' not in live_conditioning: | |
raise gr.Error("Please provide an image") | |
try: | |
if image_file_live_opt == 'file': | |
pose = get_pose(image) | |
elif image_file_live_opt == 'webcam': | |
base64_img = live_conditioning['image'] | |
image_data = base64.b64decode(base64_img.split(',')[1]) | |
pose = Image.open(BytesIO(image_data)).convert('RGB').resize((512, 512)) | |
output = pipe( | |
prompt, | |
pose, | |
generator=generator, | |
num_images_per_prompt=3, | |
num_inference_steps=50, | |
) | |
all_outputs = [] | |
all_outputs.append(pose) | |
for image in output.images: | |
all_outputs.append(image) | |
return all_outputs, all_outputs | |
except Exception as e: | |
raise gr.Error(str(e)) | |
def toggle(choice): | |
if choice == "file": | |
return gr.update(visible=True, value=None), gr.update(visible=False, value=None) | |
elif choice == "webcam": | |
return gr.update(visible=False, value=None), gr.update(visible=True, value=canvas_html) | |
examples_pose = 'examples/pose' | |
examples_cloth = 'examples/cloth' | |
def show_video(): | |
return gr.update(visible=True), gr.update(visible=True) | |
with gr.Blocks() as demo: | |
gr.Markdown(title) | |
gr.HTML(description) | |
gr.Markdown(bottom) | |
out_lst = [] | |
with gr.Row(): | |
with gr.Column(): | |
with gr.Row(): | |
live_conditioning = gr.JSON(value={}, visible=False) | |
with gr.Column(): | |
image_file_live_opt = gr.Radio(["file", "webcam"], | |
value="file", | |
label="How would you like to upload your image?") | |
with gr.Row(): | |
image_in_img = gr.Image( | |
visible=True, type="pil", label="Image for Pose" | |
) | |
canvas = gr.HTML(None, elem_id="canvas_html", visible=False) | |
image_file_live_opt.change( | |
fn=toggle, | |
inputs=[image_file_live_opt], | |
outputs=[image_in_img, canvas], | |
queue=False | |
) | |
prompt = gr.Textbox( | |
label="Enter your prompt to synthesise the image", | |
max_lines=10, | |
placeholder="best quality, extremely detailed", | |
) | |
gr.Markdown(hint_prompts) | |
with gr.Column(): | |
gallery = gr.Gallery(label="Generated Images", columns=[2],rows=[2]) | |
gallery_cache = gr.State() | |
gr.Markdown( | |
''' | |
<center> | |
<strong>Click the target generated image for Reconstruction.</strong> <br> | |
↓ | |
</center> | |
''' | |
) | |
inp = gr.Image(type="filepath", label="Input Image for Reconstruction") | |
fitting_step = gr.Slider( | |
10, | |
100, | |
step=10, | |
label='Fitting steps (Slower yet Better-aligned SMPL-X)', | |
value=50 | |
) | |
with gr.Row(): | |
btn_sample = gr.Button("Generate Image") | |
btn_submit = gr.Button("Submit Image (~3min)") | |
btn_sample.click( | |
fn=generate_images, | |
inputs=[image_in_img, prompt, image_file_live_opt, live_conditioning], | |
outputs=[gallery, gallery_cache], | |
js=get_js_image | |
) | |
def get_select_index(cache, evt: gr.SelectData): | |
return cache[evt.index] | |
gallery.select( | |
fn=get_select_index, | |
inputs=[gallery_cache], | |
outputs=[inp], | |
) | |
with gr.Row(): | |
gr.Examples( | |
examples=examples_pose, | |
inputs=[inp], | |
cache_examples=cached, | |
fn=generate_model, | |
outputs=out_lst, | |
label="Hard Pose Examples" | |
) | |
gr.Examples( | |
examples=examples_cloth, | |
inputs=[inp], | |
cache_examples=cached, | |
fn=generate_model, | |
outputs=out_lst, | |
label="Loose Cloth Examples" | |
) | |
with gr.Column(): | |
overlap_inp = gr.Image(type="filepath", label="Image Normal Overlap") | |
out_final = gr.Model3D( | |
clear_color=[0.0, 0.0, 0.0, 0.0], label="Clothed human", elem_id="avatar" | |
) | |
out_smpl = gr.Model3D( | |
clear_color=[0.0, 0.0, 0.0, 0.0], label="SMPL-X body (via PIXIE)", elem_id="avatar" | |
) | |
vis_tensor_path = gr.State() | |
# logs = gr.Textbox(max_lines=10, label="Logs") | |
btn_video = gr.Button("Generate Video (~3min)", visible=False) | |
out_vid = gr.Video(label="Shared on Twitter with #ECON", visible=False) | |
out_lst = [out_smpl, out_final, overlap_inp, vis_tensor_path] | |
btn_video.click( | |
fn=generate_video, | |
inputs=[vis_tensor_path], | |
outputs=[out_vid], | |
) | |
btn_submit.click(fn=generate_model, inputs=[inp, fitting_step], outputs=out_lst) | |
btn_submit.click(fn=show_video, outputs=[btn_video, out_vid]) | |
# demo.load(read_logs, None, logs, every=1, queue=True, scroll_to_output=True) | |
demo.load(None, None, None, js=load_js) | |
if __name__ == "__main__": | |
demo.queue() | |
demo.launch(max_threads=4) | |
# demo.launch(max_threads=2, debug=True, server_port=8888, server_name="0.0.0.0") |