akhaliq HF Staff commited on
Commit
1ab6ece
Β·
verified Β·
1 Parent(s): ba80cdf

Upload folder using huggingface_hub

Browse files
Files changed (2) hide show
  1. app.py +358 -0
  2. requirements.txt +16 -0
app.py ADDED
@@ -0,0 +1,358 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+
4
+ from huggingface_hub import hf_hub_download
5
+ import spaces
6
+ from facenet_pytorch import MTCNN
7
+ from torchvision import transforms
8
+ import torch
9
+ import PIL
10
+ from PIL import Image
11
+ import gradio as gr
12
+
13
+ # Download models
14
+ modelarcanev4 = hf_hub_download(repo_id="akhaliq/ArcaneGANv0.4", filename="ArcaneGANv0.4.jit")
15
+ modelarcanev3 = hf_hub_download(repo_id="akhaliq/ArcaneGANv0.3", filename="ArcaneGANv0.3.jit")
16
+ modelarcanev2 = hf_hub_download(repo_id="akhaliq/ArcaneGANv0.2", filename="ArcaneGANv0.2.jit")
17
+
18
+ mtcnn = MTCNN(image_size=256, margin=80)
19
+
20
+ # Face detection
21
+ def detect(img):
22
+ batch_boxes, batch_probs, batch_points = mtcnn.detect(img, landmarks=True)
23
+ if not mtcnn.keep_all:
24
+ batch_boxes, batch_probs, batch_points = mtcnn.select_boxes(
25
+ batch_boxes, batch_probs, batch_points, img, method=mtcnn.selection_method
26
+ )
27
+ return batch_boxes, batch_points
28
+
29
+ def makeEven(_x):
30
+ return _x if (_x % 2 == 0) else _x+1
31
+
32
+ def scale(boxes, _img, max_res=1_500_000, target_face=256, fixed_ratio=0, max_upscale=2, VERBOSE=False):
33
+ x, y = _img.size
34
+ ratio = 2
35
+
36
+ if (boxes is not None):
37
+ if len(boxes)>0:
38
+ ratio = target_face/max(boxes[0][2:]-boxes[0][:2])
39
+ ratio = min(ratio, max_upscale)
40
+
41
+ if fixed_ratio>0:
42
+ ratio = fixed_ratio
43
+
44
+ x*=ratio
45
+ y*=ratio
46
+
47
+ res = x*y
48
+ if res > max_res:
49
+ ratio = pow(res/max_res,1/2)
50
+ x=int(x/ratio)
51
+ y=int(y/ratio)
52
+
53
+ x = makeEven(int(x))
54
+ y = makeEven(int(y))
55
+ size = (x, y)
56
+ return _img.resize(size)
57
+
58
+ def scale_by_face_size(_img, max_res=1_500_000, target_face=256, fix_ratio=0, max_upscale=2, VERBOSE=False):
59
+ boxes = None
60
+ boxes, _ = detect(_img)
61
+ img_resized = scale(boxes, _img, max_res, target_face, fix_ratio, max_upscale, VERBOSE)
62
+ return img_resized
63
+
64
+ # Image processing setup
65
+ size = 256
66
+ means = [0.485, 0.456, 0.406]
67
+ stds = [0.229, 0.224, 0.225]
68
+
69
+ img_transforms = transforms.Compose([
70
+ transforms.ToTensor(),
71
+ transforms.Normalize(means,stds)
72
+ ])
73
+
74
+ # Load models globally on CPU
75
+ modelv4_cpu = torch.jit.load(modelarcanev4, map_location='cpu').eval()
76
+ modelv3_cpu = torch.jit.load(modelarcanev3, map_location='cpu').eval()
77
+ modelv2_cpu = torch.jit.load(modelarcanev2, map_location='cpu').eval()
78
+
79
+ @spaces.GPU
80
+ def proc_pil_img(input_image, model_path):
81
+ """GPU-accelerated image processing"""
82
+ # Load model fresh on GPU to avoid device mismatch
83
+ model = torch.jit.load(model_path, map_location='cuda').eval()
84
+
85
+ # Create tensors on GPU
86
+ t_stds = torch.tensor(stds).cuda().view(3, 1, 1)
87
+ t_means = torch.tensor(means).cuda().view(3, 1, 1)
88
+
89
+ # Transform and move to GPU
90
+ transformed_image = img_transforms(input_image).unsqueeze(0).cuda()
91
+
92
+ with torch.no_grad():
93
+ result_image = model(transformed_image)[0]
94
+ output_image = result_image.mul(t_stds).add(t_means).mul(255.).clamp(0, 255).permute(1, 2, 0)
95
+ output_image = output_image.cpu().numpy().astype('uint8')
96
+ output_image = PIL.Image.fromarray(output_image)
97
+
98
+ # Clean up
99
+ del model
100
+ torch.cuda.empty_cache()
101
+
102
+ return output_image
103
+
104
+ @spaces.GPU
105
+ def process(im, version):
106
+ """Main processing function with GPU acceleration"""
107
+ if im is None:
108
+ raise gr.Error("Please upload an image first!")
109
+
110
+ try:
111
+ # Ensure image is PIL Image
112
+ if not isinstance(im, Image.Image):
113
+ im = Image.fromarray(im)
114
+
115
+ # Convert to RGB if needed
116
+ if im.mode != 'RGB':
117
+ im = im.convert('RGB')
118
+
119
+ # Scale image (CPU operation)
120
+ im = scale_by_face_size(im, target_face=256, max_res=1_500_000, max_upscale=1)
121
+
122
+ # Select model based on version
123
+ if version == 'v0.4 (Recommended)':
124
+ res = proc_pil_img(im, modelarcanev4)
125
+ elif version == 'v0.3':
126
+ res = proc_pil_img(im, modelarcanev3)
127
+ else:
128
+ res = proc_pil_img(im, modelarcanev2)
129
+
130
+ return res
131
+
132
+ except Exception as e:
133
+ raise gr.Error(f"Error processing image: {str(e)}")
134
+
135
+ # Custom theme
136
+ custom_theme = gr.themes.Soft(
137
+ primary_hue="blue",
138
+ secondary_hue="indigo",
139
+ neutral_hue="slate",
140
+ font=gr.themes.GoogleFont("Inter"),
141
+ text_size="lg",
142
+ spacing_size="md",
143
+ radius_size="lg"
144
+ ).set(
145
+ button_primary_background_fill="*primary_600",
146
+ button_primary_background_fill_hover="*primary_700",
147
+ block_title_text_weight="600",
148
+ block_border_width="2px",
149
+ block_shadow="*shadow_drop_lg",
150
+ )
151
+
152
+ # Custom CSS for mobile-friendly design
153
+ custom_css = """
154
+ .gradio-container {
155
+ max-width: 1200px !important;
156
+ margin: auto !important;
157
+ }
158
+
159
+ #header {
160
+ text-align: center;
161
+ margin-bottom: 2rem;
162
+ }
163
+
164
+ #header h1 {
165
+ font-size: 2.5rem;
166
+ font-weight: 700;
167
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
168
+ -webkit-background-clip: text;
169
+ -webkit-text-fill-color: transparent;
170
+ margin-bottom: 0.5rem;
171
+ }
172
+
173
+ #description {
174
+ font-size: 1.1rem;
175
+ color: #64748b;
176
+ margin-bottom: 1rem;
177
+ }
178
+
179
+ .input-column, .output-column {
180
+ border-radius: 16px;
181
+ padding: 1.5rem;
182
+ background: linear-gradient(135deg, rgba(102, 126, 234, 0.05) 0%, rgba(118, 75, 162, 0.05) 100%);
183
+ }
184
+
185
+ @media (max-width: 768px) {
186
+ #header h1 {
187
+ font-size: 2rem;
188
+ }
189
+
190
+ #description {
191
+ font-size: 1rem;
192
+ }
193
+
194
+ .input-column, .output-column {
195
+ padding: 1rem;
196
+ }
197
+ }
198
+
199
+ #footer {
200
+ text-align: center;
201
+ margin-top: 2rem;
202
+ padding: 1.5rem;
203
+ border-top: 2px solid #e2e8f0;
204
+ }
205
+
206
+ #footer a {
207
+ color: #667eea;
208
+ text-decoration: none;
209
+ font-weight: 600;
210
+ }
211
+
212
+ #footer a:hover {
213
+ color: #764ba2;
214
+ text-decoration: underline;
215
+ }
216
+
217
+ .example-container {
218
+ margin-top: 1rem;
219
+ }
220
+
221
+ .gpu-badge {
222
+ display: inline-block;
223
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
224
+ color: white;
225
+ padding: 0.5rem 1rem;
226
+ border-radius: 20px;
227
+ font-weight: 600;
228
+ margin-top: 0.5rem;
229
+ }
230
+
231
+ #anycoder-link {
232
+ text-align: center;
233
+ margin-top: 0.5rem;
234
+ }
235
+
236
+ #anycoder-link a {
237
+ color: #667eea;
238
+ text-decoration: none;
239
+ font-weight: 600;
240
+ font-size: 0.9rem;
241
+ }
242
+
243
+ #anycoder-link a:hover {
244
+ color: #764ba2;
245
+ text-decoration: underline;
246
+ }
247
+ """
248
+
249
+ # Build the interface
250
+ with gr.Blocks() as demo:
251
+
252
+ # Header
253
+ with gr.Column(elem_id="header"):
254
+ gr.Markdown(
255
+ """
256
+ # 🎨 ArcaneGAN
257
+ ### Transform Your Photos into Arcane-Style Art
258
+ Upload a portrait and watch it transform into the stunning visual style of Netflix's Arcane series.
259
+
260
+ <span class="gpu-badge">⚑ Powered by Zero-GPU</span>
261
+ """
262
+ )
263
+ gr.Markdown(
264
+ "[Built with anycoder](https://huggingface.co/spaces/akhaliq/anycoder)",
265
+ elem_id="anycoder-link"
266
+ )
267
+
268
+ # Main content
269
+ with gr.Row(equal_height=True):
270
+ # Input column
271
+ with gr.Column(scale=1, elem_classes="input-column"):
272
+ gr.Markdown("### πŸ“€ Upload Your Photo")
273
+ input_image = gr.Image(
274
+ type="pil",
275
+ label="Input Image",
276
+ sources=["upload", "webcam", "clipboard"],
277
+ height=400
278
+ )
279
+
280
+ version_selector = gr.Radio(
281
+ choices=['v0.4 (Recommended)', 'v0.3', 'v0.2'],
282
+ value='v0.4 (Recommended)',
283
+ label="Model Version",
284
+ info="v0.4 offers the best quality"
285
+ )
286
+
287
+ transform_btn = gr.Button(
288
+ "✨ Transform to Arcane Style",
289
+ variant="primary",
290
+ size="lg"
291
+ )
292
+
293
+ # Output column
294
+ with gr.Column(scale=1, elem_classes="output-column"):
295
+ gr.Markdown("### 🎭 Arcane-Style Result")
296
+ output_image = gr.Image(
297
+ type="pil",
298
+ label="Transformed Image",
299
+ height=400,
300
+ buttons=["download", "share"]
301
+ )
302
+
303
+ # Tips section
304
+ with gr.Row():
305
+ gr.Markdown(
306
+ """
307
+ ### πŸ’‘ Tips for Best Results
308
+ - Use clear, well-lit portrait photos
309
+ - Face should be clearly visible and not too small
310
+ - Works best with frontal or slightly angled faces
311
+ - Try different model versions for varied artistic styles
312
+ """
313
+ )
314
+
315
+ # Footer
316
+ with gr.Column(elem_id="footer"):
317
+ gr.Markdown(
318
+ """
319
+ ---
320
+ **ArcaneGAN** by [Alexander S](https://twitter.com/devdef) |
321
+ [GitHub Repository](https://github.com/Sxela/ArcaneGAN) |
322
+ [Original Space](https://huggingface.co/spaces/akhaliq/ArcaneGAN)
323
+
324
+ **⚑ Zero-GPU Optimization**: This Space uses Hugging Face's Zero-GPU infrastructure for efficient GPU allocation.
325
+
326
+ **Model Versions:**
327
+ - **v0.4**: Latest and recommended - best quality and style accuracy
328
+ - **v0.3**: Alternative style interpretation
329
+ - **v0.2**: Original version with unique characteristics
330
+
331
+ <div style='margin-top: 1rem;'>
332
+ <img src='https://visitor-badge.glitch.me/badge?page_id=akhaliq_arcanegan' alt='visitor badge'>
333
+ </div>
334
+ """
335
+ )
336
+
337
+ # Event handlers
338
+ transform_btn.click(
339
+ fn=process,
340
+ inputs=[input_image, version_selector],
341
+ outputs=output_image,
342
+ api_visibility="public"
343
+ )
344
+
345
+ input_image.upload(
346
+ fn=process,
347
+ inputs=[input_image, version_selector],
348
+ outputs=output_image
349
+ )
350
+
351
+ # Launch with Gradio 6 syntax
352
+ demo.launch(
353
+ theme=custom_theme,
354
+ css=custom_css,
355
+ footer_links=[
356
+ {"label": "Built with anycoder", "url": "https://huggingface.co/spaces/akhaliq/anycoder"}
357
+ ]
358
+ )
requirements.txt ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ torch
2
+ torchvision
3
+ torchaudio
4
+ facenet-pytorch
5
+ Pillow
6
+ huggingface-hub
7
+ gradio>=6.0
8
+ spaces
9
+ numpy
10
+ requests
11
+ tqdm
12
+ filelock
13
+ pandas
14
+ accelerate
15
+ transformers
16
+ sentencepiece