Peace-Network commited on
Commit
ecb3e52
·
verified ·
1 Parent(s): 8a330ff

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +141 -0
app.py ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import spaces
2
+ import gradio as gr
3
+ import torch
4
+ from PIL import Image
5
+ from torchvision import transforms
6
+ from transformers import AutoModelForImageSegmentation
7
+
8
+ torch.set_float32_matmul_precision("high")
9
+
10
+ print("Loading BiRefNet...")
11
+ birefnet = AutoModelForImageSegmentation.from_pretrained(
12
+ "ZhengPeng7/BiRefNet", trust_remote_code=True
13
+ )
14
+ birefnet.to("cuda")
15
+ birefnet.eval()
16
+ birefnet.half()
17
+ print("Model ready.")
18
+
19
+ IMAGE_SIZE = (1024, 1024)
20
+
21
+ transform_image = transforms.Compose([
22
+ transforms.Resize(IMAGE_SIZE),
23
+ transforms.ToTensor(),
24
+ transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
25
+ ])
26
+
27
+
28
+ @spaces.GPU(duration=30)
29
+ def cutout_model(image: Image.Image):
30
+ """Removes the background from the model photo, returns RGBA cutout."""
31
+ original = image.convert("RGB")
32
+ input_tensor = transform_image(original).unsqueeze(0).to("cuda").half()
33
+
34
+ with torch.no_grad():
35
+ preds = birefnet(input_tensor)[-1].sigmoid().cpu()
36
+
37
+ mask = preds[0].squeeze()
38
+ mask_pil = transforms.ToPILImage()(mask).resize(original.size)
39
+
40
+ cutout = original.copy()
41
+ cutout.putalpha(mask_pil)
42
+ return cutout
43
+
44
+
45
+ def compose_thumbnail(thumbnail, model_photo, scale, x_pos, y_pos):
46
+ if thumbnail is None:
47
+ raise gr.Error("Please upload a thumbnail/background image.")
48
+ if model_photo is None:
49
+ raise gr.Error("Please upload a model photo.")
50
+
51
+ try:
52
+ thumbnail = thumbnail.convert("RGBA")
53
+ cutout = cutout_model(model_photo)
54
+
55
+ # Scale the cutout relative to the thumbnail's height
56
+ thumb_w, thumb_h = thumbnail.size
57
+ target_h = int(thumb_h * (scale / 100.0))
58
+ aspect = cutout.width / cutout.height
59
+ target_w = int(target_h * aspect)
60
+ cutout_resized = cutout.resize((target_w, target_h), Image.LANCZOS)
61
+
62
+ # Position: x_pos/y_pos are percentages of thumbnail width/height,
63
+ # representing where the CENTER of the cutout should land.
64
+ center_x = int(thumb_w * (x_pos / 100.0))
65
+ center_y = int(thumb_h * (y_pos / 100.0))
66
+ paste_x = center_x - target_w // 2
67
+ paste_y = center_y - target_h // 2
68
+
69
+ result = thumbnail.copy()
70
+ result.paste(cutout_resized, (paste_x, paste_y), cutout_resized)
71
+ return result.convert("RGB")
72
+
73
+ except Exception as e:
74
+ import traceback
75
+ traceback.print_exc()
76
+ raise gr.Error(f"Compositing failed: {e}")
77
+
78
+
79
+ css = """
80
+ #header {
81
+ text-align: center;
82
+ padding: 24px 0 8px;
83
+ }
84
+ #header h1 {
85
+ font-size: 32px;
86
+ font-weight: 700;
87
+ background: linear-gradient(135deg, #6366f1, #06b6d4);
88
+ -webkit-background-clip: text;
89
+ -webkit-text-fill-color: transparent;
90
+ margin-bottom: 4px;
91
+ }
92
+ #header p {
93
+ color: #888;
94
+ font-size: 14px;
95
+ }
96
+ #run-btn {
97
+ background: linear-gradient(135deg, #6366f1, #06b6d4) !important;
98
+ color: white !important;
99
+ font-weight: 600 !important;
100
+ border: none !important;
101
+ }
102
+ """
103
+
104
+ with gr.Blocks(title="Peace Network Thumbnail Composer", css=css) as demo:
105
+ gr.HTML(
106
+ """
107
+ <div id="header">
108
+ <h1>🎬 Peace Network Thumbnail Composer</h1>
109
+ <p>Thumbnail background + model photo daaliye — model automatically fit ho jayega.</p>
110
+ </div>
111
+ """
112
+ )
113
+
114
+ with gr.Row():
115
+ thumb_input = gr.Image(type="pil", label="1. Thumbnail / background")
116
+ model_input = gr.Image(type="pil", label="2. Model photo")
117
+
118
+ with gr.Row():
119
+ scale_slider = gr.Slider(
120
+ minimum=10, maximum=100, value=60, step=1,
121
+ label="Model size (% of thumbnail height)"
122
+ )
123
+ x_slider = gr.Slider(
124
+ minimum=0, maximum=100, value=50, step=1,
125
+ label="Horizontal position (%)"
126
+ )
127
+ y_slider = gr.Slider(
128
+ minimum=0, maximum=100, value=70, step=1,
129
+ label="Vertical position (%)"
130
+ )
131
+
132
+ btn = gr.Button("✨ Compose Thumbnail", variant="primary", elem_id="run-btn")
133
+ output = gr.Image(type="pil", label="Result")
134
+
135
+ btn.click(
136
+ compose_thumbnail,
137
+ inputs=[thumb_input, model_input, scale_slider, x_slider, y_slider],
138
+ outputs=output,
139
+ )
140
+
141
+ demo.queue().launch()