Jdp-08 commited on
Commit
175b0d0
·
verified ·
1 Parent(s): fb3d72d

Upload 4 files

Browse files
Files changed (4) hide show
  1. Dockerfile +11 -0
  2. index.html +327 -0
  3. requirements.txt +6 -0
  4. server.py +61 -0
Dockerfile ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9
2
+
3
+ WORKDIR /app
4
+
5
+ COPY . .
6
+
7
+ RUN pip install --no-cache-dir -r requirements.txt
8
+
9
+ EXPOSE 7860
10
+
11
+ CMD ["uvicorn", "backend.server:app", "--host", "0.0.0.0", "--port", "7860"]
index.html ADDED
@@ -0,0 +1,327 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>DeepTrust - Deepfake Detection</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@latest/dist/tf.min.js"></script>
9
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
10
+ <style>
11
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
12
+ body {
13
+ font-family: 'Inter', sans-serif;
14
+ overflow-x: hidden;
15
+ }
16
+ .bg-dark {
17
+ background: linear-gradient(135deg, #0a0a0f 0%, #111827 100%);
18
+ }
19
+ .upload-area {
20
+ transition: border-color 0.2s ease;
21
+ border: 2px dashed #4b5563;
22
+ }
23
+ .upload-area:focus-within,
24
+ .upload-area.dragover {
25
+ border-color: #3b82f6;
26
+ background: rgba(59, 130, 246, 0.05);
27
+ }
28
+ .result-card {
29
+ background: rgba(17, 24, 39, 0.95);
30
+ backdrop-filter: blur(10px);
31
+ }
32
+ .progress-bar {
33
+ transition: width 0.3s ease;
34
+ }
35
+ .confidence-fill {
36
+ height: 6px;
37
+ background: linear-gradient(to right, #dc2626 0%, #f59e0b 50%, #16a34a 100%);
38
+ border-radius: 3px;
39
+ }
40
+ main { min-height: calc(100vh - 200px); }
41
+ </style>
42
+ </head>
43
+ <body class="bg-dark text-white min-h-screen">
44
+ <!-- Header -->
45
+ <header class="p-6 border-b border-gray-800">
46
+ <nav class="max-w-6xl mx-auto flex justify-between items-center">
47
+ <div class="flex items-center space-x-3">
48
+ <i class="fas fa-shield-halved text-xl text-blue-500"></i>
49
+ <h1 class="text-xl font-semibold">DeepTrust</h1>
50
+ <span class="text-xs bg-blue-900/50 px-2 py-0.5 rounded text-blue-200 font-medium">Forensics AI</span>
51
+ </div>
52
+ <div class="hidden md:flex space-x-6 text-sm text-gray-400">
53
+ <a href="#" class="hover:text-white transition-colors">Documentation</a>
54
+ <a href="#" class="hover:text-white transition-colors">API</a>
55
+ </div>
56
+ </nav>
57
+ </header>
58
+
59
+ <!-- Main Content -->
60
+ <main class="max-w-4xl mx-auto px-4 py-12 pb-24 flex-grow">
61
+ <div class="text-center mb-16">
62
+ <h2 class="text-3xl md:text-4xl font-bold mb-4 text-gray-100">
63
+ Deepfake Detection
64
+ </h2>
65
+ <p class="text-lg text-gray-400 max-w-2xl mx-auto">
66
+ Upload image, video, or PDF for forensic analysis using advanced AI models.
67
+ </p>
68
+ </div>
69
+
70
+ <!-- Upload Section -->
71
+ <div id="uploadSection" class="result-card rounded-2xl p-8 md:p-12 mb-12 shadow-xl">
72
+ <!-- Upload Dropzone -->
73
+ <div id="uploadArea" class="upload-area rounded-xl p-12 text-center cursor-pointer mb-8 hover:border-gray-400 transition-colors"
74
+ onclick="document.getElementById('fileInput').click()">
75
+ <i class="fas fa-cloud-arrow-up text-4xl text-gray-500 mb-6"></i>
76
+ <h3 class="text-xl font-semibold mb-3 text-gray-200">Drop file here or click to browse</h3>
77
+ <p class="text-gray-500 mb-8">Supports JPG, PNG, MP4, PDF (max 50MB)</p>
78
+ <div class="flex justify-center space-x-6 text-sm text-gray-500">
79
+ <span><i class="fas fa-image mr-1"></i> Image</span>
80
+ <span><i class="fas fa-video mr-1"></i> Video</span>
81
+ <span><i class="fas fa-file-pdf mr-1"></i> PDF</span>
82
+ </div>
83
+ </div>
84
+ <input type="file" id="fileInput" accept="image/*,video/*,.pdf" class="hidden">
85
+
86
+ <!-- Preview (conditionally shown) -->
87
+ <div id="previewSection" class="hidden mb-8 p-6 bg-gray-900/50 rounded-xl">
88
+ <div class="flex flex-col lg:flex-row gap-6 items-start">
89
+ <img id="previewImg" class="flex-1 max-h-80 lg:max-h-96 object-contain rounded-lg shadow-md mx-auto lg:mx-0" alt="File preview">
90
+ <div id="fileInfo" class="flex-1 min-w-0"></div>
91
+ </div>
92
+ </div>
93
+
94
+ <!-- Analyze Button (conditionally shown) -->
95
+ <button id="analyzeBtn" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-medium py-4 px-8 rounded-xl text-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed">
96
+ Analyze for Deepfakes
97
+ </button>
98
+ </div>
99
+
100
+ <!-- Progress Section -->
101
+ <div id="progressSection" class="hidden result-card rounded-2xl p-12 mb-12">
102
+ <div class="text-center mb-8">
103
+ <div class="w-12 h-12 border-4 border-blue-500/20 border-t-blue-500 rounded-full animate-spin mx-auto mb-6"></div>
104
+ <p class="text-2xl font-semibold text-gray-100">Analyzing file</p>
105
+ </div>
106
+ <div class="w-full bg-gray-800 rounded-lg h-2.5 overflow-hidden">
107
+ <div id="progressBar" class="progress-bar bg-blue-600 h-2.5 w-0"></div>
108
+ </div>
109
+ <p id="progressText" class="text-center text-sm text-gray-500 mt-3">0%</p>
110
+ </div>
111
+
112
+ <!-- Results Section -->
113
+ <div id="resultsSection" class="hidden">
114
+ <div class="result-card rounded-2xl p-8 md:p-12">
115
+ <div class="flex flex-col lg:flex-row gap-12">
116
+ <div class="flex-1">
117
+ <div class="flex items-start mb-8">
118
+ <div id="resultIcon" class="w-12 h-12 rounded-lg flex items-center justify-center text-xl font-bold mr-4 flex-shrink-0 mt-1" style="min-width: 48px;"></div>
119
+ <div>
120
+ <h3 id="resultTitle" class="text-2xl font-bold text-gray-100"></h3>
121
+ <p id="resultSubtitle" class="text-gray-400 mt-1"></p>
122
+ </div>
123
+ </div>
124
+ <div class="space-y-6">
125
+ <div>
126
+ <label class="text-sm font-medium text-gray-400 mb-2 block">Confidence</label>
127
+ <div class="w-full bg-gray-800 rounded-lg h-8 relative overflow-hidden">
128
+ <div id="confidenceBar" class="confidence-fill absolute inset-0" style="width: 0%"></div>
129
+ <div id="confidenceText" class="absolute inset-0 flex items-center justify-center font-semibold text-sm bg-transparent z-10"></div>
130
+ </div>
131
+ </div>
132
+ <div class="grid grid-cols-2 gap-4">
133
+ <div class="text-center p-5 bg-gray-900/50 rounded-xl border border-gray-700">
134
+ <div id="scoreReal" class="text-3xl font-bold text-green-400 mb-1">92%</div>
135
+ <p class="text-gray-500 text-sm">Authentic</p>
136
+ </div>
137
+ <div class="text-center p-5 bg-gray-900/50 rounded-xl border border-gray-700">
138
+ <div id="scoreFake" class="text-3xl font-bold text-red-400 mb-1">8%</div>
139
+ <p class="text-gray-500 text-sm">Fabricated</p>
140
+ </div>
141
+ </div>
142
+ </div>
143
+ </div>
144
+ <div class="flex-1 lg:min-h-[300px]">
145
+ <div id="analysisDetails" class="space-y-3 text-sm text-gray-400 mb-8"></div>
146
+ <div class="border-t border-gray-800 pt-8 flex flex-col sm:flex-row gap-4">
147
+ <button class="flex-1 sm:flex-none bg-gray-700 hover:bg-gray-600 text-white font-medium py-3 px-6 rounded-xl border border-gray-600 transition-colors">
148
+ <i class="fas fa-download mr-2"></i>
149
+ Download Report
150
+ </button>
151
+ <button class="flex-1 sm:flex-none bg-blue-600 hover:bg-blue-700 text-white font-medium py-3 px-6 rounded-xl transition-colors">
152
+ <i class="fas fa-share mr-2"></i>
153
+ Share
154
+ </button>
155
+ </div>
156
+ </div>
157
+ </div>
158
+ </div>
159
+ </div>
160
+ </main>
161
+
162
+ <!-- Footer -->
163
+ <footer class="border-t border-gray-800 p-8 text-center text-xs text-gray-500 bg-gray-900/50">
164
+ DeepTrust Forensics | Advanced AI Detection | 2026
165
+ </footer>
166
+
167
+ <script>
168
+ const uploadArea = document.getElementById('uploadArea');
169
+ const fileInput = document.getElementById('fileInput');
170
+ const analyzeBtn = document.getElementById('analyzeBtn');
171
+
172
+ // ---------------- DRAG & DROP ----------------
173
+ ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
174
+ uploadArea.addEventListener(eventName, e => {
175
+ e.preventDefault();
176
+ e.stopPropagation();
177
+ });
178
+ });
179
+
180
+ ['dragenter', 'dragover'].forEach(eventName => {
181
+ uploadArea.addEventListener(eventName, () => {
182
+ uploadArea.classList.add('dragover');
183
+ });
184
+ });
185
+
186
+ ['dragleave', 'drop'].forEach(eventName => {
187
+ uploadArea.addEventListener(eventName, () => {
188
+ uploadArea.classList.remove('dragover');
189
+ });
190
+ });
191
+
192
+ uploadArea.addEventListener('drop', e => {
193
+ handleFiles(e.dataTransfer.files);
194
+ });
195
+
196
+ fileInput.addEventListener('change', e => {
197
+ handleFiles(e.target.files);
198
+ });
199
+
200
+ // ---------------- FILE HANDLING ----------------
201
+ function handleFiles(files) {
202
+ if (!files.length) return;
203
+
204
+ const file = files[0];
205
+
206
+ if (file.size > 50 * 1024 * 1024) {
207
+ alert('File size exceeds 50MB limit.');
208
+ return;
209
+ }
210
+
211
+ showPreview(file);
212
+ }
213
+
214
+ function showPreview(file) {
215
+ const previewSection = document.getElementById('previewSection');
216
+ const previewImg = document.getElementById('previewImg');
217
+ const fileInfo = document.getElementById('fileInfo');
218
+
219
+ const url = URL.createObjectURL(file);
220
+ previewImg.src = url;
221
+ previewImg.style.display = 'block';
222
+
223
+ fileInfo.innerHTML = `
224
+ <div class="space-y-2">
225
+ <div class="flex items-center text-sm">
226
+ <i class="fas fa-image mr-2 text-blue-400"></i>
227
+ <span class="font-semibold text-gray-200 truncate">${file.name}</span>
228
+ </div>
229
+ <p class="text-gray-500 text-sm">${formatBytes(file.size)}</p>
230
+ </div>
231
+ `;
232
+
233
+ previewSection.classList.remove('hidden');
234
+ analyzeBtn.classList.remove('hidden');
235
+ }
236
+
237
+ function formatBytes(bytes) {
238
+ if (bytes === 0) return '0 B';
239
+ const sizes = ['B', 'KB', 'MB'];
240
+ const i = Math.floor(Math.log(bytes) / Math.log(1024));
241
+ return (bytes / Math.pow(1024, i)).toFixed(1) + ' ' + sizes[i];
242
+ }
243
+
244
+ // ---------------- ANALYZE ----------------
245
+ analyzeBtn.addEventListener('click', async () => {
246
+ const file = fileInput.files[0];
247
+ if (!file) return;
248
+
249
+ document.getElementById('uploadArea').style.display = 'none';
250
+ document.getElementById('previewSection').style.display = 'none';
251
+ analyzeBtn.style.display = 'none';
252
+ document.getElementById('progressSection').classList.remove('hidden');
253
+
254
+ let progress = 0;
255
+ const interval = setInterval(() => {
256
+ progress += 10;
257
+ if (progress > 95) progress = 95;
258
+ document.getElementById('progressBar').style.width = progress + '%';
259
+ document.getElementById('progressText').textContent = progress + '%';
260
+ }, 300);
261
+
262
+ const formData = new FormData();
263
+ formData.append("file", file);
264
+
265
+ try {
266
+ const response = await fetch("http://127.0.0.1:8000/analyze", {
267
+ method: "POST",
268
+ body: formData
269
+ });
270
+
271
+ if (!response.ok) {
272
+ throw new Error("Server returned error");
273
+ }
274
+
275
+ const data = await response.json();
276
+
277
+ clearInterval(interval);
278
+ document.getElementById('progressSection').classList.add('hidden');
279
+
280
+ showResults(
281
+ data.authenticity > 50,
282
+ data.authenticity,
283
+ Math.round(data.authenticity),
284
+ Math.round(data.fake)
285
+ );
286
+
287
+ } catch (error) {
288
+ clearInterval(interval);
289
+ document.getElementById('progressSection').classList.add('hidden');
290
+ console.error(error);
291
+ alert("Analysis failed. Check backend.");
292
+ }
293
+ });
294
+
295
+ // ---------------- RESULTS ----------------
296
+ function showResults(isAuthentic, confidence, realScore, fakeScore) {
297
+ const section = document.getElementById('resultsSection');
298
+ section.classList.remove('hidden');
299
+ section.scrollIntoView({ behavior: 'smooth' });
300
+
301
+ const iconEl = document.getElementById('resultIcon');
302
+ const titleEl = document.getElementById('resultTitle');
303
+ const subtitleEl = document.getElementById('resultSubtitle');
304
+ const confBar = document.getElementById('confidenceBar');
305
+ const confText = document.getElementById('confidenceText');
306
+
307
+ if (isAuthentic) {
308
+ iconEl.innerHTML = '<i class="fas fa-check text-green-500"></i>';
309
+ titleEl.textContent = 'Authentic Media Detected';
310
+ subtitleEl.textContent = 'No synthetic manipulation found.';
311
+ confBar.style.background = '#10b981';
312
+ } else {
313
+ iconEl.innerHTML = '<i class="fas fa-triangle-exclamation text-orange-500"></i>';
314
+ titleEl.textContent = 'Potential Deepfake Detected';
315
+ subtitleEl.textContent = 'Signs of fabrication detected.';
316
+ confBar.style.background = '#f59e0b';
317
+ }
318
+
319
+ confText.textContent = confidence.toFixed(1) + '%';
320
+ confBar.style.width = confidence + '%';
321
+
322
+ document.getElementById('scoreReal').textContent = realScore + '%';
323
+ document.getElementById('scoreFake').textContent = fakeScore + '%';
324
+ }
325
+ </script>
326
+ </body>
327
+ </html>
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ fastapi
2
+ uvicorn
3
+ transformers
4
+ torch
5
+ pillow
6
+ python-multipart
server.py ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi.staticfiles import StaticFiles
2
+ from fastapi.responses import FileResponse
3
+ from transformers import AutoImageProcessor, AutoModelForImageClassification
4
+ from PIL import Image
5
+ import torch
6
+ from fastapi import FastAPI, UploadFile, File
7
+ from fastapi.middleware.cors import CORSMiddleware
8
+ import shutil
9
+ import os
10
+ import io
11
+ model_name = "buildborderless/CommunityForensics-DeepfakeDet-ViT"
12
+
13
+ processor = AutoImageProcessor.from_pretrained(model_name)
14
+ model = AutoModelForImageClassification.from_pretrained(model_name)
15
+ model.eval()
16
+
17
+ app = FastAPI()
18
+ app.mount("/static", StaticFiles(directory="static"), name="static")
19
+
20
+ @app.get("/")
21
+ def read_root():
22
+ return FileResponse("static/index.html")
23
+
24
+ app.add_middleware(
25
+ CORSMiddleware,
26
+ allow_origins=["*"],
27
+ allow_methods=["*"],
28
+ allow_headers=["*"],
29
+ )
30
+
31
+
32
+
33
+ @app.post("/analyze")
34
+ async def analyze(file: UploadFile = File(...)):
35
+
36
+ if not file.content_type.startswith("image/"):
37
+ return {"error": "Only image files are supported."}
38
+
39
+ contents = await file.read()
40
+ image = Image.open(io.BytesIO(contents)).convert("RGB")
41
+
42
+ inputs = processor(
43
+ images=image,
44
+ return_tensors="pt",
45
+ size={"height": 384, "width": 384}
46
+ )
47
+ with torch.no_grad():
48
+ outputs = model(**inputs)
49
+
50
+ logits = outputs.logits
51
+ probs = torch.softmax(logits, dim=1)
52
+
53
+ deepfake_prob = probs[0][1].item()
54
+
55
+ authenticity = (1 - deepfake_prob) * 100
56
+ fake = deepfake_prob * 100
57
+
58
+ return {
59
+ "authenticity": round(authenticity, 2),
60
+ "fake": round(fake, 2)
61
+ }