Spaces:
Running
Running
| """ | |
| models.py | |
| 4 background-removal models: | |
| fast -> U2-Net (rembg, MIT) | |
| quality -> BiRefNet-lite (transformers, MIT) | |
| hair -> BEN2 (ben2, MIT) -- best for hair/portraits/fur | |
| best -> BRIA RMBG-2.0 (transformers, non-commercial) | |
| """ | |
| import io | |
| import logging | |
| import warnings | |
| import numpy as np | |
| from PIL import Image | |
| logger = logging.getLogger("hd_remover.models") | |
| _cache: dict = {} | |
| # ββ U2-Net (fast) βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def load_u2net(): | |
| if "u2net" in _cache: | |
| return _cache["u2net"] | |
| try: | |
| from rembg import new_session | |
| _cache["u2net"] = new_session("u2net") | |
| logger.info("U2-Net ready") | |
| return _cache["u2net"] | |
| except Exception as e: | |
| logger.error(f"U2-Net load failed: {e}") | |
| return None | |
| def infer_fast(img: Image.Image) -> Image.Image: | |
| from rembg import remove | |
| if not load_u2net(): | |
| raise RuntimeError("U2-Net not available") | |
| buf = io.BytesIO() | |
| img.save(buf, "PNG") | |
| buf.seek(0) | |
| return Image.open(io.BytesIO(remove(buf.read(), session=_cache["u2net"]))).convert("RGBA") | |
| # ββ BiRefNet-lite (quality) ββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def load_birefnet(): | |
| if "birefnet" in _cache: | |
| return _cache["birefnet"] | |
| try: | |
| from transformers import AutoModelForImageSegmentation | |
| from torchvision import transforms | |
| m = AutoModelForImageSegmentation.from_pretrained( | |
| "ZhengPeng7/BiRefNet_lite", trust_remote_code=True) | |
| m = m.float() | |
| m.eval() | |
| _cache["birefnet"] = m | |
| _cache["birefnet_tf"] = transforms.Compose([ | |
| transforms.Resize((1024, 1024)), | |
| transforms.ToTensor(), | |
| transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]), | |
| ]) | |
| logger.info("BiRefNet-lite ready") | |
| return m | |
| except Exception as e: | |
| logger.error(f"BiRefNet-lite load failed: {e}") | |
| return None | |
| def infer_quality(img: Image.Image) -> Image.Image: | |
| if not load_birefnet(): | |
| raise RuntimeError("BiRefNet-lite not available") | |
| return _seg("birefnet", img) | |
| # ββ BEN2 (hair) β MIT license, best for portraits/hair/fur βββββββββββββββββββ | |
| def load_ben2(): | |
| if "ben2" in _cache: | |
| return _cache["ben2"] | |
| try: | |
| from ben2 import BEN_Base | |
| with warnings.catch_warnings(): | |
| warnings.filterwarnings("ignore", message=".*cuda.*CUDA is not available.*") | |
| m = BEN_Base.from_pretrained("PramaLLC/BEN2") | |
| m.eval() | |
| _cache["ben2"] = m | |
| logger.info("BEN2 ready") | |
| return m | |
| except Exception as e: | |
| logger.error(f"BEN2 load failed: {e}") | |
| return None | |
| def infer_hair(img: Image.Image) -> Image.Image: | |
| """BEN2 β MIT license. Best for hair, portraits, fur edges.""" | |
| if not load_ben2(): | |
| raise RuntimeError("BEN2 not available") | |
| import torch | |
| model = _cache["ben2"] | |
| model.to(torch.device("cpu")) | |
| foreground = model.inference(img.convert("RGB"), refine_foreground=True) | |
| return foreground.convert("RGBA") if foreground.mode != "RGBA" else foreground | |
| # ββ BRIA RMBG-2.0 (best) β NON-COMMERCIAL ββββββββββββββββββββββββββββββββββββ | |
| def load_rmbg(hf_token=None): | |
| if "rmbg" in _cache: | |
| return _cache["rmbg"] | |
| try: | |
| from transformers import AutoModelForImageSegmentation | |
| from torchvision import transforms | |
| m = AutoModelForImageSegmentation.from_pretrained( | |
| "briaai/RMBG-2.0", trust_remote_code=True, token=hf_token or None) | |
| m = m.float() | |
| m.eval() | |
| _cache["rmbg"] = m | |
| _cache["rmbg_tf"] = transforms.Compose([ | |
| transforms.Resize((1024, 1024)), | |
| transforms.ToTensor(), | |
| transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]), | |
| ]) | |
| logger.info("RMBG-2.0 ready") | |
| return m | |
| except Exception as e: | |
| logger.error(f"RMBG-2.0 load failed: {e}") | |
| return None | |
| def infer_best(img: Image.Image, hf_token=None) -> Image.Image: | |
| if not load_rmbg(hf_token): | |
| raise RuntimeError("BRIA RMBG-2.0 not available β check HF_TOKEN") | |
| return _seg("rmbg", img) | |
| # ββ Shared segmentation helper (BiRefNet + RMBG) βββββββββββββββββββββββββββββ | |
| def _seg(key: str, img: Image.Image) -> Image.Image: | |
| import torch | |
| m = _cache[key] | |
| tf = _cache[key + "_tf"] | |
| orig = img.size | |
| inp = tf(img.convert("RGB")).unsqueeze(0).float() | |
| with torch.no_grad(): | |
| out = m(inp) | |
| pred = (out[-1] if isinstance(out, (list, tuple)) else out).sigmoid() | |
| mask = Image.fromarray( | |
| (pred[0].squeeze().cpu().numpy() * 255).astype(np.uint8) | |
| ).resize(orig, Image.LANCZOS) | |
| r = img.convert("RGBA") | |
| r.putalpha(mask) | |
| return r | |
| # ββ Model registry ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| MODEL_INFO = { | |
| "fast": {"name": "U2-Net", "desc": "Fastest (~1-5s) β general use", "license": "MIT"}, | |
| "quality": {"name": "BiRefNet-lite", "desc": "High quality (~5-8s CPU) β products/objects", "license": "MIT"}, | |
| "hair": {"name": "BEN2", "desc": "Best for hair & portraits (~10-15s CPU)", "license": "MIT"}, | |
| "best": {"name": "BRIA RMBG-2.0", "desc": "Best quality (~20s CPU) β non-commercial", "license": "Non-commercial"}, | |
| } | |
| def preload_default(): | |
| """Called at startup β loads U2-Net blocking, rest in background thread.""" | |
| logger.info("Preloading U2-Net (startup)...") | |
| load_u2net() | |
| def preload_all_bg(): | |
| """Call in a background thread after startup.""" | |
| logger.info("Background preload: BiRefNet-lite...") | |
| load_birefnet() | |
| logger.info("Background preload: BEN2...") | |
| load_ben2() | |
| logger.info("Background preload: RMBG-2.0...") | |
| load_rmbg() | |
| logger.info("All 4 models ready") | |
| def models_ready() -> list: | |
| """Return list of currently loaded model keys.""" | |
| return [k for k in _cache if not k.endswith("_tf")] |