import spaces import gradio as gr import torch from PIL import Image from torchvision import transforms from transformers import AutoModelForImageSegmentation torch.set_float32_matmul_precision("high") print("Loading BiRefNet...") birefnet = AutoModelForImageSegmentation.from_pretrained( "ZhengPeng7/BiRefNet", trust_remote_code=True ) birefnet.to("cuda") birefnet.eval() birefnet.half() print("Model ready.") IMAGE_SIZE = (1024, 1024) transform_image = transforms.Compose([ transforms.Resize(IMAGE_SIZE), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]), ]) @spaces.GPU(duration=30) def cutout_model(image: Image.Image): """Removes the background from the model photo, returns RGBA cutout.""" original = image.convert("RGB") input_tensor = transform_image(original).unsqueeze(0).to("cuda").half() with torch.no_grad(): preds = birefnet(input_tensor)[-1].sigmoid().cpu() mask = preds[0].squeeze() mask_pil = transforms.ToPILImage()(mask).resize(original.size) cutout = original.copy() cutout.putalpha(mask_pil) return cutout def compose_thumbnail(thumbnail, model_photo, scale, x_pos, y_pos): if thumbnail is None: raise gr.Error("Please upload a thumbnail/background image.") if model_photo is None: raise gr.Error("Please upload a model photo.") try: thumbnail = thumbnail.convert("RGBA") cutout = cutout_model(model_photo) # Scale the cutout relative to the thumbnail's height thumb_w, thumb_h = thumbnail.size target_h = int(thumb_h * (scale / 100.0)) aspect = cutout.width / cutout.height target_w = int(target_h * aspect) cutout_resized = cutout.resize((target_w, target_h), Image.LANCZOS) # Position: x_pos/y_pos are percentages of thumbnail width/height, # representing where the CENTER of the cutout should land. center_x = int(thumb_w * (x_pos / 100.0)) center_y = int(thumb_h * (y_pos / 100.0)) paste_x = center_x - target_w // 2 paste_y = center_y - target_h // 2 result = thumbnail.copy() result.paste(cutout_resized, (paste_x, paste_y), cutout_resized) return result.convert("RGB") except Exception as e: import traceback traceback.print_exc() raise gr.Error(f"Compositing failed: {e}") css = """ #header { text-align: center; padding: 24px 0 8px; } #header h1 { font-size: 32px; font-weight: 700; background: linear-gradient(135deg, #6366f1, #06b6d4); -webkit-background-clip: text; -webkit-text-fill-color: transparent; margin-bottom: 4px; } #header p { color: #888; font-size: 14px; } #run-btn { background: linear-gradient(135deg, #6366f1, #06b6d4) !important; color: white !important; font-weight: 600 !important; border: none !important; } """ with gr.Blocks(title="Peace Network Thumbnail Composer", css=css) as demo: gr.HTML( """
Thumbnail background + model photo daaliye — model automatically fit ho jayega.