Spaces:
Running
Running
| import os | |
| os.environ["TRANSFORMERS_NO_TF"] = "1" # force PyTorch-only pipelines | |
| import numpy as np | |
| from PIL import Image, ImageFilter | |
| import gradio as gr | |
| import torch | |
| from transformers import pipeline | |
| # ---- Config ---- | |
| DEVICE = 0 if torch.cuda.is_available() else -1 | |
| SEG_MODEL = "nvidia/segformer-b0-finetuned-ade-512-512" | |
| DEPTH_MODEL = "Intel/dpt-hybrid-midas" | |
| SIZE = (512, 512) | |
| # ---- Pipelines (loaded once) ---- | |
| seg_pipe = pipeline("image-segmentation", model=SEG_MODEL, device=DEVICE, framework="pt") | |
| depth_pipe = pipeline("depth-estimation", model=DEPTH_MODEL, device=DEVICE, framework="pt") | |
| # ---- Helpers ---- | |
| def resize_center_crop(img: Image.Image, size=SIZE) -> Image.Image: | |
| img = img.convert("RGB") | |
| w, h = img.size | |
| tw, th = size | |
| s = max(tw / w, th / h) | |
| nw, nh = int(round(w * s)), int(round(h * s)) | |
| img = img.resize((nw, nh), Image.BICUBIC) | |
| left, top = (nw - tw) // 2, (nh - th) // 2 | |
| return img.crop((left, top, left + tw, top + th)) | |
| def person_mask(img_512: Image.Image) -> Image.Image: | |
| results = seg_pipe(img_512) | |
| person = next((r for r in results if r.get("label", "").lower() == "person"), None) | |
| if person is None: | |
| person = next((r for r in results if "person" in r.get("label", "").lower()), None) | |
| if person is None: | |
| return Image.new("L", img_512.size, 0) # no person detected | |
| m = person["mask"].convert("L") | |
| m = (np.array(m) > 127).astype(np.uint8) * 255 | |
| return Image.fromarray(m, mode="L") | |
| def gaussian_bg_blur(img_512: Image.Image, sigma: int = 15) -> Image.Image: | |
| m = person_mask(img_512) | |
| blurred = img_512.filter(ImageFilter.GaussianBlur(radius=int(sigma))) | |
| return Image.composite(img_512, blurred, m) # white=person -> keep sharp | |
| def depth_lens_blur(img_512: Image.Image, max_radius: int = 15, keep_subject: bool = True) -> Image.Image: | |
| out = depth_pipe(img_512) | |
| d = out["depth"].resize(SIZE, Image.BICUBIC) | |
| dnp = np.array(d).astype(np.float32) | |
| d01 = (dnp - dnp.min()) / (dnp.max() - dnp.min() + 1e-8) # 0..1 | |
| far = 1.0 - d01 # larger=farther -> more blur | |
| if keep_subject: | |
| m = person_mask(img_512) | |
| m01 = (np.array(m) > 127).astype(np.float32) | |
| far = far * (1.0 - 0.85 * m01) # suppress blur on detected subject | |
| max_radius = int(max(0, min(30, max_radius))) | |
| idx = np.clip(np.rint(far * max_radius).astype(np.int32), 0, max_radius) | |
| stack = [img_512 if r == 0 else img_512.filter(ImageFilter.GaussianBlur(radius=r)) | |
| for r in range(max_radius + 1)] | |
| stack_np = np.stack([np.array(im) for im in stack], axis=0) # [R+1,H,W,3] | |
| H, W = idx.shape | |
| h = np.arange(H)[:, None]; w = np.arange(W)[None, :] | |
| out_np = stack_np[idx, h, w] | |
| return Image.fromarray(out_np.astype(np.uint8)) | |
| def run(image, effect, sigma, max_radius, keep_subject): | |
| if image is None: | |
| return None, None | |
| img_512 = resize_center_crop(image, SIZE) | |
| if effect == "Gaussian Background Blur (subject sharp)": | |
| out = gaussian_bg_blur(img_512, sigma=int(sigma)) | |
| else: | |
| out = depth_lens_blur(img_512, max_radius=int(max_radius), keep_subject=bool(keep_subject)) | |
| return img_512, out | |
| # ---- UI ---- | |
| with gr.Blocks(title="Gaussian & Lens Blur Lab") as demo: | |
| gr.Markdown("# Gaussian & Lens Blur Lab\nUpload an image and compare effects.") | |
| with gr.Row(): | |
| with gr.Column(): | |
| in_img = gr.Image(type="pil", label="Upload image") | |
| effect = gr.Radio( | |
| ["Gaussian Background Blur (subject sharp)", "Depth-based Lens Blur"], | |
| value="Gaussian Background Blur (subject sharp)", label="Effect" | |
| ) | |
| sigma = gr.Slider(1, 40, value=15, step=1, label="Gaussian sigma") | |
| max_r = gr.Slider(4, 30, value=15, step=1, label="Max blur radius (lens blur)") | |
| keep = gr.Checkbox(True, label="Keep detected subject sharper (lens blur)") | |
| btn = gr.Button("Run") | |
| with gr.Column(): | |
| out_a = gr.Image(label="Preprocessed 512×512") | |
| out_b = gr.Image(label="Result") | |
| btn.click(run, inputs=[in_img, effect, sigma, max_r, keep], outputs=[out_a, out_b]) | |
| if __name__ == "__main__": | |
| demo.launch() | |