| import cv2 |
| import gradio as gr |
| import numpy as np |
|
|
| from functools import lru_cache |
| from rembg import new_session, remove |
|
|
|
|
| GOOD_MODELS = [ |
| "u2net_human_seg", |
| "silueta", |
| "u2netp", |
| "isnet-general-use", |
| "bria-rmbg", |
| "birefnet-general-lite", |
| "birefnet-portrait", |
| ] |
|
|
|
|
| MODEL_NOTES = { |
| "u2net_human_seg": "Fast human-specific baseline. Good for full body/person masks.", |
| "silueta": "Small and fast. Lower quality, useful for latency testing.", |
| "u2netp": "Very light U2Net variant. Fastest baseline, lower quality.", |
| "isnet-general-use": "General-purpose background removal fallback.", |
| "bria-rmbg": "High-quality general background remover. Check license for your use.", |
| "birefnet-general-lite": "Lighter BiRefNet. Better quality than tiny models, slower than U2Net variants.", |
| "birefnet-portrait": "Best BiRefNet choice for portraits / people, usually slower.", |
| } |
|
|
|
|
| @lru_cache(maxsize=3) |
| def get_session(model_name: str): |
| return new_session(model_name) |
|
|
|
|
| def ensure_uint8_rgb(image: np.ndarray) -> np.ndarray: |
| if image is None: |
| raise gr.Error("Please upload an image.") |
|
|
| image = np.asarray(image) |
|
|
| if image.ndim == 2: |
| image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB) |
|
|
| if image.shape[-1] == 4: |
| image = image[:, :, :3] |
|
|
| if image.dtype != np.uint8: |
| image = np.clip(image, 0, 255).astype(np.uint8) |
|
|
| return image |
|
|
|
|
| def normalize_mask(mask, width: int, height: int) -> np.ndarray: |
| mask = np.asarray(mask) |
|
|
| if mask.ndim == 3: |
| mask = mask[:, :, 0] |
|
|
| if mask.shape[:2] != (height, width): |
| mask = cv2.resize( |
| mask, |
| (width, height), |
| interpolation=cv2.INTER_LANCZOS4, |
| ) |
|
|
| if mask.dtype != np.uint8: |
| mask = np.clip(mask, 0, 255).astype(np.uint8) |
|
|
| return mask |
|
|
|
|
| def composite_on_white( |
| image_rgb: np.ndarray, |
| mask_u8: np.ndarray, |
| edge_blur: float, |
| ) -> np.ndarray: |
| alpha = mask_u8.astype(np.float32) / 255.0 |
|
|
| if edge_blur > 0: |
| alpha = cv2.GaussianBlur(alpha, (0, 0), sigmaX=edge_blur) |
| alpha = np.clip(alpha, 0.0, 1.0) |
|
|
| alpha = alpha[:, :, None] |
|
|
| white = np.full_like(image_rgb, 255) |
|
|
| result = ( |
| image_rgb.astype(np.float32) * alpha |
| + white.astype(np.float32) * (1.0 - alpha) |
| ) |
|
|
| return np.clip(result, 0, 255).astype(np.uint8) |
|
|
|
|
| def inference( |
| image, |
| model, |
| post_process_mask, |
| alpha_matting, |
| edge_blur, |
| ): |
| image_rgb = ensure_uint8_rgb(image) |
| height, width = image_rgb.shape[:2] |
|
|
| session = get_session(model) |
|
|
| mask = remove( |
| image_rgb, |
| session=session, |
| only_mask=True, |
| post_process_mask=post_process_mask, |
| alpha_matting=alpha_matting, |
| ) |
|
|
| mask_u8 = normalize_mask(mask, width=width, height=height) |
|
|
| white_result = composite_on_white( |
| image_rgb=image_rgb, |
| mask_u8=mask_u8, |
| edge_blur=edge_blur, |
| ) |
|
|
| return white_result, mask_u8, MODEL_NOTES.get(model, "") |
|
|
|
|
| def compare_all( |
| image, |
| post_process_mask, |
| alpha_matting, |
| edge_blur, |
| ): |
| image_rgb = ensure_uint8_rgb(image) |
|
|
| outputs = [] |
|
|
| for model in GOOD_MODELS: |
| result, _, _ = inference( |
| image=image_rgb, |
| model=model, |
| post_process_mask=post_process_mask, |
| alpha_matting=alpha_matting, |
| edge_blur=edge_blur, |
| ) |
|
|
| outputs.append((result, model)) |
|
|
| return outputs |
|
|
|
|
| with gr.Blocks() as app: |
| gr.Markdown("# Person Background Removal Benchmark") |
| gr.Markdown( |
| """ |
| Remove background with [Rembg](https://github.com/danielgatis/rembg) models. |
| """ |
| ) |
|
|
| with gr.Row(): |
| input_image = gr.Image(type="numpy", label="Input Image") |
|
|
| with gr.Column(): |
| output_image = gr.Image(type="numpy", label="White Background Result") |
| output_mask = gr.Image(type="numpy", label="Mask") |
| model_note = gr.Textbox(label="Model note", interactive=False) |
|
|
| with gr.Row(): |
| model_selector = gr.Dropdown( |
| GOOD_MODELS, |
| value="u2net_human_seg", |
| label="Model", |
| ) |
|
|
| edge_blur = gr.Slider( |
| minimum=0.0, |
| maximum=3.0, |
| value=0.0, |
| step=0.25, |
| label="Edge blur", |
| ) |
|
|
| with gr.Row(): |
| post_process_mask = gr.Checkbox( |
| value=False, |
| label="Post-process mask", |
| ) |
|
|
| alpha_matting = gr.Checkbox( |
| value=False, |
| label="Alpha matting", |
| ) |
|
|
| with gr.Row(): |
| process_button = gr.Button("Process selected model", variant="primary") |
| compare_button = gr.Button("Compare all models") |
|
|
| gallery = gr.Gallery( |
| label="Compare all models", |
| columns=3, |
| height="auto", |
| object_fit="contain", |
| ) |
|
|
| process_button.click( |
| inference, |
| inputs=[ |
| input_image, |
| model_selector, |
| post_process_mask, |
| alpha_matting, |
| edge_blur, |
| ], |
| outputs=[ |
| output_image, |
| output_mask, |
| model_note, |
| ], |
| ) |
|
|
| compare_button.click( |
| compare_all, |
| inputs=[ |
| input_image, |
| post_process_mask, |
| alpha_matting, |
| edge_blur, |
| ], |
| outputs=gallery, |
| ) |
|
|
|
|
| app.launch() |