test-all-models / src /birefnet.py
vincentraj's picture
Inital Commit
a42ddbb
Raw History Blame Contribute Delete
4.53 kB
import torch
from transformers import AutoModelForImageSegmentation
from torchvision import transforms
from PIL import Image
import numpy as np
# Device setup
_device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# Global models
_birefnet = None
_birefnet_lite = None
# Preprocessing transform
_transform = transforms.Compose(
[
transforms.Resize((768, 768)),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
]
)
def load_model(fast_mode=False):
global _birefnet, _birefnet_lite
if fast_mode:
if _birefnet_lite is None:
_birefnet_lite = AutoModelForImageSegmentation.from_pretrained(
"ZhengPeng7/BiRefNet_lite", trust_remote_code=True
).to(_device)
return _birefnet_lite
else:
if _birefnet is None:
_birefnet = AutoModelForImageSegmentation.from_pretrained(
"ZhengPeng7/BiRefNet", trust_remote_code=True
).to(_device)
return _birefnet
def unload_model():
global _birefnet, _birefnet_lite
_birefnet = None
_birefnet_lite = None
if torch.cuda.is_available():
torch.cuda.empty_cache()
def _apply_mask(image: Image.Image, mask_tensor, bg, bg_type: str):
size = image.size
mask = transforms.ToPILImage()(mask_tensor.squeeze()).resize(size)
if bg_type == "Color" and isinstance(bg, str) and bg.startswith("#"):
color_rgb = tuple(int(bg[i : i + 2], 16) for i in (1, 3, 5))
bg_image = Image.new("RGBA", size, color_rgb + (255,))
elif bg_type in ["Image", "Video"]:
bg_image = bg.convert("RGBA").resize(size)
else:
raise ValueError("Invalid background type or format")
return Image.composite(image.convert("RGBA"), bg_image, mask)
def _get_transform(image_size: tuple[int, int] | None = None):
if image_size:
resize = transforms.Resize(image_size)
else:
resize = transforms.Resize((768, 768)) # default fallback
return transforms.Compose(
[
resize,
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
]
)
def predict(
input: Image.Image | list[Image.Image],
bg: str | Image.Image,
bg_type: str = "Color",
fast_mode: bool = True,
bg_frames: list[np.ndarray] | None = None,
image_size_mode: str = "default", # "default", "origin", or "custom"
custom_size: tuple[int, int] | None = None,
) -> Image.Image | list[Image.Image]:
model = _birefnet_lite if fast_mode else _birefnet
if model is None:
raise RuntimeError("Model not loaded. Please call `load_model()` first.")
def get_transform_for_img(img: Image.Image):
if image_size_mode == "origin":
return _get_transform(img.size)
elif image_size_mode == "custom":
if not custom_size:
raise ValueError("`custom_size` must be provided for 'custom' mode.")
return _get_transform(custom_size)
else:
return _get_transform() # default to (768, 768)
if isinstance(input, Image.Image):
_transform = get_transform_for_img(input)
input_tensor = _transform(input).unsqueeze(0).to(_device)
with torch.no_grad():
pred = model(input_tensor)[-1].sigmoid().cpu()
bg_image = (
Image.fromarray(bg_frames[0]) if bg_type == "Video" and bg_frames else bg
)
return _apply_mask(input, pred[0], bg_image, bg_type)
elif isinstance(input, list):
results = []
for idx, frame in enumerate(input):
_transform = get_transform_for_img(frame)
input_tensor = _transform(frame).unsqueeze(0).to(_device)
with torch.no_grad():
pred = model(input_tensor)[-1].sigmoid().cpu()
if bg_type == "Video" and bg_frames:
bg_image = Image.fromarray(bg_frames[idx % len(bg_frames)])
else:
bg_image = bg
results.append(_apply_mask(frame, pred[0], bg_image, bg_type))
return results
else:
raise TypeError("`input` must be a PIL.Image or a list of PIL.Image.")
# # Default behavior (768x768)
# predict(img, "#ffffff", "Color")
# # Keep original image size
# predict(img, "#ffffff", "Color", image_size_mode="origin")
# # Resize to 1024x1024
# predict(img, "#ffffff", "Color", image_size_mode="custom", custom_size=(1024, 1024))