ARPAN2026 commited on
Commit
0027825
·
verified ·
1 Parent(s): 27d1bc5

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +277 -0
app.py ADDED
@@ -0,0 +1,277 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import urllib.request
3
+ import numpy as np
4
+ import cv2
5
+ import torch
6
+ import torch.nn as nn
7
+ import torch.nn.functional as F
8
+ import torchvision.transforms as transforms
9
+ import timm
10
+ import gradio as gr
11
+ from PIL import Image
12
+
13
+ # =====================
14
+ # CONFIG
15
+ # =====================
16
+ MODEL_URL = "https://huggingface.co/ARPAN2026/dfake-hcnext/resolve/main/best_model_New.pth"
17
+ MODEL_PATH = "best_model_New.pth"
18
+ IMG_SIZE = 224
19
+ DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
20
+
21
+ # =====================
22
+ # MODEL DEFINITION
23
+ # =====================
24
+ class DeepfakeModel(nn.Module):
25
+ def __init__(self):
26
+ super().__init__()
27
+ self.backbone = timm.create_model("convnext_base", pretrained=False, num_classes=0)
28
+ dim = self.backbone.num_features
29
+ self.classifier = nn.Sequential(
30
+ nn.LayerNorm(dim),
31
+ nn.Linear(dim, 256),
32
+ nn.GELU(),
33
+ nn.Dropout(0.4),
34
+ nn.Linear(256, 2),
35
+ )
36
+
37
+ def forward(self, x):
38
+ f = self.backbone.forward_features(x)
39
+ if len(f.shape) == 4:
40
+ f = f.flatten(2).mean(-1)
41
+ return self.classifier(f)
42
+
43
+
44
+ # =====================
45
+ # DOWNLOAD + LOAD
46
+ # =====================
47
+ def download_model():
48
+ if not os.path.exists(MODEL_PATH):
49
+ print("Downloading model weights…")
50
+ urllib.request.urlretrieve(MODEL_URL, MODEL_PATH)
51
+ print("Download complete.")
52
+
53
+ download_model()
54
+
55
+ model = DeepfakeModel().to(DEVICE)
56
+ model.load_state_dict(torch.load(MODEL_PATH, map_location=DEVICE))
57
+ model.eval()
58
+ print("Model loaded successfully.")
59
+
60
+ # =====================
61
+ # TRANSFORM
62
+ # =====================
63
+ transform = transforms.Compose([
64
+ transforms.Resize((IMG_SIZE, IMG_SIZE)),
65
+ transforms.ToTensor(),
66
+ transforms.Normalize([0.5] * 3, [0.5] * 3),
67
+ ])
68
+
69
+ # =====================
70
+ # INFERENCE
71
+ # =====================
72
+ def predict(image: Image.Image):
73
+ if image is None:
74
+ return {"Error": 1.0}, "⚠️ Please upload an image."
75
+
76
+ img_tensor = transform(image.convert("RGB")).unsqueeze(0).to(DEVICE)
77
+
78
+ with torch.no_grad():
79
+ logits = model(img_tensor)
80
+ probs = torch.softmax(logits, dim=1).cpu().numpy()[0]
81
+
82
+ real_prob = float(probs[0])
83
+ fake_prob = float(probs[1])
84
+ confidence = max(real_prob, fake_prob) * 100
85
+
86
+ if fake_prob > real_prob:
87
+ verdict = "🔴 DEEPFAKE DETECTED"
88
+ verdict_md = f"## {verdict}\n**Confidence:** {confidence:.1f}%"
89
+ else:
90
+ verdict = "🟢 LIKELY REAL"
91
+ verdict_md = f"## {verdict}\n**Confidence:** {confidence:.1f}%"
92
+
93
+ label_dict = {
94
+ "Real": round(real_prob, 4),
95
+ "Fake": round(fake_prob, 4),
96
+ }
97
+
98
+ return label_dict, verdict_md
99
+
100
+
101
+ # =====================
102
+ # CUSTOM CSS (dark forensic theme)
103
+ # =====================
104
+ CSS = """
105
+ @import url('https://fonts.googleapis.com/css2?family=Share+Tech+Mono&family=Syne:wght@400;700;800&display=swap');
106
+
107
+ :root {
108
+ --bg: #0a0c10;
109
+ --surface: #111318;
110
+ --border: #1e2330;
111
+ --accent: #00e5ff;
112
+ --danger: #ff3b5c;
113
+ --safe: #00e676;
114
+ --text: #d0d8f0;
115
+ --muted: #5a6480;
116
+ --radius: 8px;
117
+ }
118
+
119
+ body, .gradio-container {
120
+ background: var(--bg) !important;
121
+ font-family: 'Syne', sans-serif !important;
122
+ color: var(--text) !important;
123
+ }
124
+
125
+ /* ---- header ---- */
126
+ .gr-header {
127
+ background: transparent !important;
128
+ }
129
+
130
+ h1.title-heading {
131
+ font-family: 'Syne', sans-serif;
132
+ font-weight: 800;
133
+ font-size: 2.4rem;
134
+ letter-spacing: -0.02em;
135
+ background: linear-gradient(90deg, var(--accent), #7b61ff);
136
+ -webkit-background-clip: text;
137
+ -webkit-text-fill-color: transparent;
138
+ margin: 0;
139
+ }
140
+
141
+ p.subtitle {
142
+ color: var(--muted);
143
+ font-family: 'Share Tech Mono', monospace;
144
+ font-size: 0.85rem;
145
+ margin-top: 4px;
146
+ letter-spacing: 0.08em;
147
+ }
148
+
149
+ /* ---- panels ---- */
150
+ .gr-box, .gr-panel, .gr-form {
151
+ background: var(--surface) !important;
152
+ border: 1px solid var(--border) !important;
153
+ border-radius: var(--radius) !important;
154
+ }
155
+
156
+ /* ---- upload zone ---- */
157
+ .gr-image, .svelte-1n8nu59 {
158
+ border: 2px dashed var(--border) !important;
159
+ border-radius: var(--radius) !important;
160
+ background: #0d0f14 !important;
161
+ }
162
+
163
+ /* ---- buttons ---- */
164
+ button.primary {
165
+ background: var(--accent) !important;
166
+ color: #000 !important;
167
+ font-family: 'Syne', sans-serif !important;
168
+ font-weight: 700 !important;
169
+ border: none !important;
170
+ border-radius: var(--radius) !important;
171
+ letter-spacing: 0.05em;
172
+ }
173
+
174
+ button.secondary {
175
+ background: transparent !important;
176
+ border: 1px solid var(--border) !important;
177
+ color: var(--muted) !important;
178
+ font-family: 'Syne', sans-serif !important;
179
+ border-radius: var(--radius) !important;
180
+ }
181
+
182
+ /* ---- labels / markdown output ---- */
183
+ .gr-markdown h2 {
184
+ font-family: 'Syne', sans-serif;
185
+ font-size: 1.4rem;
186
+ font-weight: 700;
187
+ margin: 0 0 4px;
188
+ }
189
+
190
+ /* ---- confidence bars ---- */
191
+ .gr-label .wrap {
192
+ background: var(--surface) !important;
193
+ border: 1px solid var(--border) !important;
194
+ border-radius: var(--radius) !important;
195
+ }
196
+
197
+ .gr-label .label-wrap span {
198
+ font-family: 'Share Tech Mono', monospace !important;
199
+ color: var(--text) !important;
200
+ }
201
+
202
+ /* confidence fill colors */
203
+ .gr-label .bar {
204
+ background: linear-gradient(90deg, var(--accent), #7b61ff) !important;
205
+ }
206
+
207
+ /* ---- footer ---- */
208
+ footer { display: none !important; }
209
+ """
210
+
211
+ # =====================
212
+ # GRADIO UI
213
+ # =====================
214
+ with gr.Blocks(css=CSS, title="DeepFake Detector") as demo:
215
+
216
+ gr.HTML("""
217
+ <div style="text-align:center; padding: 32px 0 16px;">
218
+ <h1 class='title-heading'>DEEPFAKE DETECTOR</h1>
219
+ <p class='subtitle'>ConvNeXt-Base · Trained on RVF Faces · Hackathon Edition</p>
220
+ </div>
221
+ """)
222
+
223
+ with gr.Row():
224
+ with gr.Column(scale=1):
225
+ image_input = gr.Image(
226
+ type="pil",
227
+ label="Upload Face Image",
228
+ height=320,
229
+ )
230
+ with gr.Row():
231
+ submit_btn = gr.Button("🔍 Analyze", variant="primary")
232
+ clear_btn = gr.ClearButton([image_input], value="✕ Clear")
233
+
234
+ gr.HTML("""
235
+ <div style="margin-top:12px; padding:12px 16px;
236
+ background:#0d0f14; border:1px solid #1e2330;
237
+ border-radius:8px; font-family:'Share Tech Mono',monospace;
238
+ font-size:0.78rem; color:#5a6480; line-height:1.7;">
239
+ <b style="color:#00e5ff;">MODEL</b> ConvNeXt-Base + custom head<br>
240
+ <b style="color:#00e5ff;">TRAINED</b> Real vs Fake Faces (80/20 split)<br>
241
+ <b style="color:#00e5ff;">INPUT</b> 224 × 224 · RGB · normalized<br>
242
+ <b style="color:#00e5ff;">CLASSES</b> Real · Fake
243
+ </div>
244
+ """)
245
+
246
+ with gr.Column(scale=1):
247
+ verdict_output = gr.Markdown(
248
+ value="*Upload an image and click **Analyze** to begin.*",
249
+ label="Verdict",
250
+ )
251
+ label_output = gr.Label(
252
+ num_top_classes=2,
253
+ label="Class Probabilities",
254
+ )
255
+
256
+ # example images (optional — works if you add them to the Space repo)
257
+ gr.Examples(
258
+ examples=[], # add paths like [["examples/real1.jpg"], ["examples/fake1.jpg"]]
259
+ inputs=image_input,
260
+ label="Example Images",
261
+ )
262
+
263
+ submit_btn.click(
264
+ fn=predict,
265
+ inputs=image_input,
266
+ outputs=[label_output, verdict_output],
267
+ )
268
+
269
+ gr.HTML("""
270
+ <div style="text-align:center; padding:24px 0 8px;
271
+ font-family:'Share Tech Mono',monospace;
272
+ font-size:0.75rem; color:#2a3050;">
273
+ Built with ❤ · Gradio · HuggingFace Spaces · PyTorch
274
+ </div>
275
+ """)
276
+
277
+ demo.launch()