sdsjcos's picture
Upload folder using huggingface_hub
cadc145 verified
Raw
History Blame Contribute Delete
6.63 kB
"""Zero123++ v1.2 Gradio demo: single image -> 6 consistent multi-views."""
import spaces # MUST come before torch / diffusers
import torch
import gradio as gr
from PIL import Image
from diffusers import DiffusionPipeline, EulerAncestralDiscreteScheduler
MODEL_ID = "sudo-ai/zero123plus-v1.2"
CUSTOM_PIPELINE = "sudo-ai/zero123plus-pipeline"
VIEW_LABELS = [
"Azimuth 30° / Elev 20°",
"Azimuth 90° / Elev -10°",
"Azimuth 150° / Elev 20°",
"Azimuth 210° / Elev -10°",
"Azimuth 270° / Elev 20°",
"Azimuth 330° / Elev -10°",
]
pipe = DiffusionPipeline.from_pretrained(
MODEL_ID,
custom_pipeline=CUSTOM_PIPELINE,
torch_dtype=torch.float16,
)
pipe.scheduler = EulerAncestralDiscreteScheduler.from_config(
pipe.scheduler.config,
timestep_spacing="trailing",
)
pipe.to("cuda")
def expand2square(pil_img: Image.Image, background_color: tuple[int, int, int, int]) -> Image.Image:
"""Pad a non-square image to a square canvas."""
width, height = pil_img.size
if width == height:
return pil_img
if width > height:
result = Image.new(pil_img.mode, (width, width), background_color)
result.paste(pil_img, (0, (width - height) // 2))
return result
result = Image.new(pil_img.mode, (height, height), background_color)
result.paste(pil_img, ((height - width) // 2, 0))
return result
def prepare_input(image: Image.Image, remove_bg: bool) -> Image.Image:
"""Square-pad and optionally rembg the input image."""
if image is None:
raise gr.Error("Please upload an input image.")
img = image.convert("RGBA")
if max(img.size) > 1280:
scale = 1280 / max(img.size)
img = img.resize(
(round(img.width * scale), round(img.height * scale)),
Image.Resampling.LANCZOS,
)
if remove_bg:
from rembg import remove
img = remove(img)
img = expand2square(img, (127, 127, 127, 0))
# Composite onto gray background expected by Zero123++ VAE
rgb = Image.new("RGB", img.size, (127, 127, 127))
rgb.paste(img, mask=img.split()[-1] if img.mode == "RGBA" else None)
return rgb
def split_views(sheet: Image.Image) -> list[Image.Image]:
"""Split the 2x3 Zero123++ sheet into six view images."""
side = sheet.width // 2
views: list[Image.Image] = []
for y in range(0, sheet.height, side):
for x in range(0, sheet.width, side):
views.append(sheet.crop((x, y, x + side, y + side)))
return views[:6]
def maybe_remove_bg_views(views: list[Image.Image], enabled: bool) -> list[Image.Image]:
"""Optionally remove gray background from each generated view."""
if not enabled:
return views
from rembg import remove
cleaned: list[Image.Image] = []
for view in views:
out = remove(view.convert("RGBA"))
cleaned.append(out)
return cleaned
@spaces.GPU(duration=120)
def generate_multiview(
image: Image.Image,
remove_input_bg: bool,
remove_output_bg: bool,
guidance_scale: float,
num_inference_steps: int,
seed: int,
) -> tuple[Image.Image, Image.Image, Image.Image, Image.Image, Image.Image, Image.Image, Image.Image]:
"""Generate six consistent novel views from a single input image with Zero123++ v1.2."""
cond = prepare_input(image, remove_input_bg)
generator = torch.Generator(device=pipe.device).manual_seed(int(seed))
sheet = pipe(
cond,
num_inference_steps=int(num_inference_steps),
guidance_scale=float(guidance_scale),
generator=generator,
).images[0]
views = maybe_remove_bg_views(split_views(sheet), remove_output_bg)
while len(views) < 6:
views.append(Image.new("RGB", (320, 320), (127, 127, 127)))
return (sheet, views[0], views[1], views[2], views[3], views[4], views[5])
TITLE = "Zero123++ v1.2 Multi-View Demo"
DESCRIPTION = """
Upload one image → get **6 consistent multi-views** via
[`sudo-ai/zero123plus-v1.2`](https://huggingface.co/sudo-ai/zero123plus-v1.2).
Fixed cameras (relative azimuth / absolute elevation):
`30°/20°, 90°/-10°, 150°/20°, 210°/-10°, 270°/20°, 330°/-10°` · FOV `30°`.
**License note:** model weights are **CC-BY-NC 4.0** (non-commercial). Code is Apache-2.0.
"""
with gr.Blocks(title=TITLE, theme=gr.themes.Soft(primary_hue="blue")) as demo:
gr.Markdown(f"# {TITLE}")
gr.Markdown(DESCRIPTION)
with gr.Row():
with gr.Column(scale=1):
input_image = gr.Image(
type="pil",
image_mode="RGBA",
label="Input image",
height=360,
)
remove_input_bg = gr.Checkbox(value=True, label="Remove input background (rembg)")
remove_output_bg = gr.Checkbox(value=False, label="Remove output backgrounds (rembg)")
guidance_scale = gr.Slider(1.0, 10.0, value=4.0, step=0.5, label="CFG scale")
num_inference_steps = gr.Slider(
15,
100,
value=36,
step=1,
label="Inference steps",
info="~28 for general objects; 75+ for faces / fine detail",
)
seed = gr.Number(value=42, precision=0, label="Seed")
run_btn = gr.Button("Generate 6 views", variant="primary")
gr.Examples(
examples=[
["https://d.skis.ltd/nrp/sample-data/lysol.png"],
],
inputs=[input_image],
label="Examples",
cache_examples=False,
)
with gr.Column(scale=1):
sheet_out = gr.Image(type="pil", label="Full 2×3 sheet", height=360)
with gr.Row():
v0 = gr.Image(type="pil", label=VIEW_LABELS[0], height=200)
v1 = gr.Image(type="pil", label=VIEW_LABELS[1], height=200)
v2 = gr.Image(type="pil", label=VIEW_LABELS[2], height=200)
with gr.Row():
v3 = gr.Image(type="pil", label=VIEW_LABELS[3], height=200)
v4 = gr.Image(type="pil", label=VIEW_LABELS[4], height=200)
v5 = gr.Image(type="pil", label=VIEW_LABELS[5], height=200)
run_btn.click(
fn=generate_multiview,
inputs=[
input_image,
remove_input_bg,
remove_output_bg,
guidance_scale,
num_inference_steps,
seed,
],
outputs=[sheet_out, v0, v1, v2, v3, v4, v5],
)
if __name__ == "__main__":
demo.queue(max_size=8).launch(mcp_server=True)