asis-ai's picture
Update app.py
1168554 verified
Raw
History Blame Contribute Delete
4.65 kB
# app.py
import gradio as gr
import numpy as np
import torch
from PIL import Image, ImageFilter
from torchvision import transforms
from transformers import AutoModelForImageSegmentation
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
MODEL_ID = "ZhengPeng7/BiRefNet"
model = AutoModelForImageSegmentation.from_pretrained(
MODEL_ID,
trust_remote_code=True
).to(DEVICE).float()
model.eval()
transform = transforms.Compose([
transforms.Resize((1024, 1024)),
transforms.ToTensor(),
transforms.Normalize(
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]
),
])
def predict_subject_mask(image: Image.Image) -> Image.Image:
"""Returns white subject / black background mask."""
image_rgb = image.convert("RGB")
original_size = image_rgb.size
x = transform(image_rgb).unsqueeze(0).to(DEVICE).float()
with torch.no_grad():
output = model(x)
# BiRefNet often returns: (scaled_preds, class_preds)
if isinstance(output, (list, tuple)):
if isinstance(output[0], (list, tuple)):
pred = output[0][-1]
else:
pred = output[0]
elif hasattr(output, "logits"):
pred = output.logits
else:
pred = output
pred = pred.sigmoid().cpu()[0].squeeze()
mask = transforms.ToPILImage()(pred)
mask = mask.resize(original_size, Image.BILINEAR)
return mask
def lock_background(
reference,
target,
threshold=128,
feather=8,
protect_both_frames=True,
):
ref = Image.fromarray(reference).convert("RGB")
tgt = Image.fromarray(target).convert("RGB")
if ref.size != tgt.size:
tgt = tgt.resize(ref.size, Image.BICUBIC)
ref_np = np.array(ref).astype(np.float32)
tgt_np = np.array(tgt).astype(np.float32)
ref_mask = predict_subject_mask(ref)
tgt_mask = predict_subject_mask(tgt)
ref_m = np.array(ref_mask)
tgt_m = np.array(tgt_mask)
# foreground = white, background = black
ref_bg = ref_m < threshold
tgt_bg = tgt_m < threshold
if protect_both_frames:
shared_bg = ref_bg & tgt_bg
else:
shared_bg = tgt_bg
alpha = Image.fromarray((shared_bg * 255).astype(np.uint8))
alpha = alpha.filter(ImageFilter.GaussianBlur(radius=feather))
alpha_np = np.array(alpha).astype(np.float32) / 255.0
alpha_np = alpha_np[..., None]
# copy canonical background from reference into target
fixed_np = tgt_np * (1 - alpha_np) + ref_np * alpha_np
fixed_np = np.clip(fixed_np, 0, 255).astype(np.uint8)
# diagnostic difference before/after
before_diff = np.abs(ref_np - tgt_np).mean(axis=2)
after_diff = np.abs(ref_np - fixed_np.astype(np.float32)).mean(axis=2)
heatmap = np.clip((before_diff - after_diff) * 4, 0, 255).astype(np.uint8)
heatmap_rgb = np.stack([heatmap, np.zeros_like(heatmap), 255 - heatmap], axis=2)
shared_bg_vis = np.stack([
shared_bg.astype(np.uint8) * 255,
shared_bg.astype(np.uint8) * 255,
shared_bg.astype(np.uint8) * 255,
], axis=2)
before_score = float(before_diff[shared_bg].mean()) if shared_bg.any() else 0
after_score = float(after_diff[shared_bg].mean()) if shared_bg.any() else 0
report = f"""
### Background Lock Report
- Shared background pixels: **{shared_bg.mean() * 100:.2f}%**
- Mean background difference before: **{before_score:.2f}**
- Mean background difference after: **{after_score:.2f}**
- Improvement: **{before_score - after_score:.2f}**
"""
return fixed_np, shared_bg_vis, heatmap_rgb, report
demo = gr.Interface(
fn=lock_background,
inputs=[
gr.Image(label="Reference frame", type="numpy"),
gr.Image(label="End frame", type="numpy"),
gr.Slider(0, 255, value=128, step=1, label="Foreground protection threshold"),
gr.Slider(0, 40, value=0, step=1, label="Feather edge"),
gr.Checkbox(value=True, label="Protect foreground in both frames"),
],
outputs=[
gr.Image(label="Fixed end frame", format="png"),
gr.Image(label="Shared background mask", format="png"),
gr.Image(label="Improvement heatmap", format="png"),
gr.Markdown(label="Report"),
],
examples=[
["Frame1.jpg", "Frame2.jpg", 32, 0, True],
["Frame2.jpg", "Frame1.jpg", 32, 0, True],
],
title="End Frame Background Lock",
description=(
"Locks the background of an end frame to a reference frame for aligned, "
"locked-off AI-generated shots while preserving the foreground subject."
),
)
if __name__ == "__main__":
demo.launch()