idol / background.py
OzzyGT's picture
OzzyGT HF Staff
app
4df4c39
Raw History Blame Contribute Delete
1.84 kB
"""BEN2 background removal for the source image."""
import os
from pathlib import Path
import numpy as np
import torch
from huggingface_hub import snapshot_download
from PIL import Image
from safetensors.torch import load_file
REVISION = "e48a20765fb421d19dcdb0bf3cc61e802ca5ec8f"
MEAN = (0.485, 0.456, 0.406)
STD = (0.229, 0.224, 0.225)
def load_background():
from ben2.model import BEN_Base
source = os.getenv("IDOL_BACKGROUND_MODEL", "PramaLLC/BEN2")
path = Path(source)
if not path.is_dir():
path = Path(
snapshot_download(
source,
revision=os.getenv("IDOL_BACKGROUND_REVISION", REVISION),
allow_patterns=["model.safetensors"],
)
)
model = BEN_Base()
model.load_state_dict(load_file(str(path / "model.safetensors")), strict=True)
return model.eval()
def remove_background(model, image):
"""Return the image as RGBA with BEN2's alpha matte, at its original size."""
rgb = image.convert("RGB")
pixels = np.asarray(rgb.resize((1024, 1024), Image.Resampling.LANCZOS), dtype=np.float32) / 255
# Normalize and post-process in float16 like upstream's torchvision pipeline; this matches its matte exactly.
pixels = torch.from_numpy(pixels).permute(2, 0, 1).to("cuda", torch.float16)
mean = torch.tensor(MEAN, dtype=torch.float16, device="cuda").view(3, 1, 1)
std = torch.tensor(STD, dtype=torch.float16, device="cuda").view(3, 1, 1)
alpha = model(((pixels - mean) / std)[None])
alpha = torch.nn.functional.interpolate(alpha, size=(rgb.height, rgb.width), mode="bilinear")[0, 0]
alpha = (alpha - alpha.min()) / (alpha.max() - alpha.min())
matte = Image.fromarray((alpha * 255).cpu().numpy().astype(np.uint8))
result = rgb.copy()
result.putalpha(matte)
return result