timefractal commited on
Commit
757340c
·
verified ·
1 Parent(s): 5be6c81

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +152 -0
app.py ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import spaces
2
+ import gradio as gr
3
+ import torch
4
+ from transformers import CLIPModel, CLIPProcessor
5
+ from PIL import Image
6
+
7
+ # ─── Model Loading (module-level for ZeroGPU) ───────────────────────────
8
+ MODEL_ID = "kwanY/styleid"
9
+ device = "cuda" if torch.cuda.is_available() else "cpu"
10
+ model = CLIPModel.from_pretrained(MODEL_ID).to(device)
11
+ processor = CLIPProcessor.from_pretrained(MODEL_ID)
12
+ model.eval()
13
+
14
+ # ─── Inference ───────────────────────────────────────────────────────────
15
+
16
+ @spaces.GPU(duration=30)
17
+ def compare_faces(img1: Image.Image, img2: Image.Image):
18
+ """Compare face identity between two images using StyleID."""
19
+ if img1 is None or img2 is None:
20
+ return "⚠️ Please upload both images.", ""
21
+
22
+ def get_embedding(img):
23
+ inputs = processor(images=img.convert("RGB"), return_tensors="pt").to(device)
24
+ with torch.no_grad():
25
+ emb = model.get_image_features(**inputs)
26
+ emb = emb / emb.norm(dim=-1, keepdim=True)
27
+ return emb
28
+
29
+ e1 = get_embedding(img1)
30
+ e2 = get_embedding(img2)
31
+ score = (e1 * e2).sum(dim=-1).item()
32
+
33
+ # Interpretation based on paper thresholds
34
+ if score >= 0.55:
35
+ verdict = "✅ Confidently Same Person"
36
+ color = "#22c55e"
37
+ elif score >= 0.45:
38
+ verdict = "✅ Very Likely Same Person"
39
+ color = "#4ade80"
40
+ elif score >= 0.35:
41
+ verdict = "🟡 Probably Same Person"
42
+ color = "#facc15"
43
+ elif score >= 0.25:
44
+ verdict = "🟠 Uncertain — Borderline"
45
+ color = "#fb923c"
46
+ else:
47
+ verdict = "❌ Likely Different People"
48
+ color = "#ef4444"
49
+
50
+ # Create a visual gauge
51
+ score_pct = max(0, min(100, int(score * 100)))
52
+ gauge_html = f"""
53
+ <div style="text-align:center; padding: 20px;">
54
+ <div style="font-size: 48px; font-weight: bold; color: {color};">{score:.3f}</div>
55
+ <div style="font-size: 20px; margin-top: 8px; color: {color}; font-weight: 600;">{verdict}</div>
56
+ <div style="margin-top: 16px; background: #1f2937; border-radius: 12px; height: 24px; overflow: hidden; position: relative;">
57
+ <div style="background: linear-gradient(90deg, #ef4444, #fb923c, #facc15, #4ade80, #22c55e); height: 100%; width: {score_pct}%; border-radius: 12px; transition: width 0.5s;"></div>
58
+ </div>
59
+ <div style="display: flex; justify-content: space-between; font-size: 11px; color: #9ca3af; margin-top: 4px;">
60
+ <span>Different</span>
61
+ <span>Uncertain</span>
62
+ <span>Same</span>
63
+ </div>
64
+ </div>
65
+ """
66
+
67
+ details = f"""### How to Read the Score
68
+ | Range | Meaning |
69
+ |-------|---------|
70
+ | **> 0.55** | ✅ Confidently same person |
71
+ | **0.45 – 0.55** | ✅ Very likely same person |
72
+ | **0.35 – 0.45** | 🟡 Probably same person |
73
+ | **0.25 – 0.35** | 🟠 Uncertain / borderline |
74
+ | **< 0.25** | ❌ Likely different people |
75
+
76
+ **Your Score: {score:.4f}**
77
+
78
+ > **Note:** StyleID is optimized for comparing a real photo against its stylized version (anime, cartoon, painting, sketch, etc.). Works best with a single prominent face per image.
79
+ """
80
+
81
+ return gauge_html, details
82
+
83
+ # ─── UI ──────────────────────────────────────────────────────────────────
84
+
85
+ DESCRIPTION = """
86
+ # 🎭 StyleID — Does AI Preserve Your Face?
87
+
88
+ Upload a **real photo** and a **stylized portrait** (anime, cartoon, painting, sketch, etc.)
89
+ → StyleID checks if the identity is preserved across art styles.
90
+
91
+ **Powered by [StyleID](https://huggingface.co/kwanY/styleid)** — a perception-aware face identity model trained to match human judgments across stylizations.
92
+
93
+ ### 🔬 How it works
94
+ StyleID uses a fine-tuned CLIP ViT-L/14 encoder calibrated against human psychometric experiments.
95
+ Unlike standard face recognition (ArcFace, AdaFace), StyleID is specifically designed for **cross-style** identity matching —
96
+ real photo ↔ anime, cartoon, painting, caricature, 3D render, etc.
97
+
98
+ ### ⚡ Use Cases
99
+ - Check if your AI-stylized portrait still looks like you
100
+ - Evaluate face-swap / face-transfer quality
101
+ - Compare identity preservation across different AI art tools
102
+ - Research: benchmark stylization models for ID preservation
103
+ """
104
+
105
+ TIPS = """
106
+ ### 💡 Tips for Best Results
107
+ 1. **One face per image** — StyleID doesn't do face detection
108
+ 2. **Face should be prominent** — at least 25% of the image
109
+ 3. **Frontal or 3/4 view works best** — extreme profiles may reduce scores
110
+ 4. **Works across styles** — anime, cartoon, oil painting, watercolor, pencil sketch, 3D render, caricature
111
+ 5. **NOT a security tool** — for research/evaluation only
112
+ """
113
+
114
+ with gr.Blocks(
115
+ title="StyleID — Face Identity Across Art Styles",
116
+ theme=gr.themes.Soft(
117
+ primary_hue="violet",
118
+ secondary_hue="blue",
119
+ ),
120
+ css="footer { display: none !important; }"
121
+ ) as demo:
122
+ gr.Markdown(DESCRIPTION)
123
+
124
+ with gr.Row():
125
+ with gr.Column():
126
+ img1 = gr.Image(type="pil", label="📸 Original Photo (Real Face)", height=400)
127
+ with gr.Column():
128
+ img2 = gr.Image(type="pil", label="🎨 Stylized Portrait (Anime, Cartoon, etc.)", height=400)
129
+
130
+ btn = gr.Button("🔍 Compare Identity", variant="primary", size="lg")
131
+
132
+ with gr.Row():
133
+ with gr.Column():
134
+ gauge = gr.HTML(label="Identity Score")
135
+ with gr.Column():
136
+ details = gr.Markdown(label="Details")
137
+
138
+ btn.click(
139
+ fn=compare_faces,
140
+ inputs=[img1, img2],
141
+ outputs=[gauge, details],
142
+ )
143
+
144
+ with gr.Accordion("💡 Tips & Info", open=False):
145
+ gr.Markdown(TIPS)
146
+
147
+ gr.Markdown("""
148
+ ---
149
+ **Model:** [kwanY/styleid](https://huggingface.co/kwanY/styleid) | **Paper:** [StyleID (arXiv 2604.21689)](https://arxiv.org/abs/2604.21689) | **License:** Research use only — not for biometric authentication.
150
+ """)
151
+
152
+ demo.launch()