Spaces:
Sleeping
Sleeping
Vinh.Vu commited on
Commit Β·
75fc585
1
Parent(s): c9d361f
Add Technology page
Browse files- .gitignore +1 -0
- App/static/app.jsx +286 -8
- App/static/images.png +3 -0
- App/static/style.css +52 -0
- App/templates/index.html +1 -1
.gitignore
CHANGED
|
@@ -137,3 +137,4 @@ mtcnn/
|
|
| 137 |
/prepared_dataset
|
| 138 |
/App/uploads
|
| 139 |
archive.zip
|
|
|
|
|
|
| 137 |
/prepared_dataset
|
| 138 |
/App/uploads
|
| 139 |
archive.zip
|
| 140 |
+
App/diag_log.txt
|
App/static/app.jsx
CHANGED
|
@@ -9,16 +9,25 @@ const STATUS_MESSAGES = {
|
|
| 9 |
function barClass(s) { return s > 0.8 ? 'bar-green' : s > 0.2 ? 'bar-orange' : 'bar-red'; }
|
| 10 |
function scoreClass(s) { return s > 0.8 ? 'score-green' : s > 0.2 ? 'score-orange' : 'score-red'; }
|
| 11 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
/* ββ Navbar ββ */
|
| 13 |
-
function Navbar() {
|
| 14 |
return (
|
| 15 |
<header className="navbar">
|
| 16 |
-
<div className="logo"><span>
|
| 17 |
<nav>
|
| 18 |
-
<a href="/" className=
|
| 19 |
-
<a href="#">
|
| 20 |
-
<a href="#">Technology</a>
|
| 21 |
-
<a href="#">FAQ</a>
|
| 22 |
</nav>
|
| 23 |
</header>
|
| 24 |
);
|
|
@@ -146,6 +155,262 @@ function ResultsPanel({ data }) {
|
|
| 146 |
);
|
| 147 |
}
|
| 148 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 149 |
/* ββ Main App ββ */
|
| 150 |
function App() {
|
| 151 |
const [file, setFile] = useState(null);
|
|
@@ -214,13 +479,20 @@ function App() {
|
|
| 214 |
}
|
| 215 |
};
|
| 216 |
|
|
|
|
|
|
|
| 217 |
return (
|
| 218 |
<>
|
| 219 |
-
<Navbar />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 220 |
|
| 221 |
<section className="hero">
|
| 222 |
<div className="hero-left">
|
| 223 |
-
<h1 className="hero-title">
|
| 224 |
<p className="hero-desc">
|
| 225 |
Free deepfake detection tool for videos. Upload a video and get
|
| 226 |
per-face authenticity scores in seconds. AI-powered synthetic face detection.
|
|
@@ -237,6 +509,10 @@ function App() {
|
|
| 237 |
{error && <div className="error-box">{error}</div>}
|
| 238 |
</div>
|
| 239 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 240 |
</section>
|
| 241 |
|
| 242 |
<VideoComparison
|
|
@@ -245,6 +521,8 @@ function App() {
|
|
| 245 |
isProcessing={submitting}
|
| 246 |
/>
|
| 247 |
<ResultsPanel data={result} />
|
|
|
|
|
|
|
| 248 |
</>
|
| 249 |
);
|
| 250 |
}
|
|
|
|
| 9 |
function barClass(s) { return s > 0.8 ? 'bar-green' : s > 0.2 ? 'bar-orange' : 'bar-red'; }
|
| 10 |
function scoreClass(s) { return s > 0.8 ? 'score-green' : s > 0.2 ? 'score-orange' : 'score-red'; }
|
| 11 |
|
| 12 |
+
/* ββ Simple hash-based router ββ */
|
| 13 |
+
function useHashRoute() {
|
| 14 |
+
const [route, setRoute] = useState(window.location.hash || '#/');
|
| 15 |
+
useEffect(() => {
|
| 16 |
+
const onHash = () => setRoute(window.location.hash || '#/');
|
| 17 |
+
window.addEventListener('hashchange', onHash);
|
| 18 |
+
return () => window.removeEventListener('hashchange', onHash);
|
| 19 |
+
}, []);
|
| 20 |
+
return route;
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
/* ββ Navbar ββ */
|
| 24 |
+
function Navbar({ route }) {
|
| 25 |
return (
|
| 26 |
<header className="navbar">
|
| 27 |
+
<div className="logo"><span>AI</span>-Deepfake Video Detection</div>
|
| 28 |
<nav>
|
| 29 |
+
<a href="#/" className={route === '#/' || route === '' ? 'active' : ''}>Product</a>
|
| 30 |
+
<a href="#/technology" className={route === '#/technology' ? 'active' : ''}>Technology</a>
|
|
|
|
|
|
|
| 31 |
</nav>
|
| 32 |
</header>
|
| 33 |
);
|
|
|
|
| 155 |
);
|
| 156 |
}
|
| 157 |
|
| 158 |
+
/* ββ Step Diagram SVG ββ */
|
| 159 |
+
function StepDiagram({ number, title, color }) {
|
| 160 |
+
return (
|
| 161 |
+
<svg width="80" height="80" viewBox="0 0 80 80" className="step-icon">
|
| 162 |
+
<circle cx="40" cy="40" r="36" fill="none" stroke={color} strokeWidth="2.5" />
|
| 163 |
+
<text x="40" y="46" textAnchor="middle" fill={color} fontSize="28" fontWeight="700">{number}</text>
|
| 164 |
+
</svg>
|
| 165 |
+
);
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
/* ββ Pipeline Flow Arrow ββ */
|
| 169 |
+
function FlowArrow() {
|
| 170 |
+
return (
|
| 171 |
+
<div className="flow-arrow">
|
| 172 |
+
<svg width="40" height="40" viewBox="0 0 40 40">
|
| 173 |
+
<path d="M10 20 L28 20 M22 13 L30 20 L22 27" fill="none" stroke="#6c8cff" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"/>
|
| 174 |
+
</svg>
|
| 175 |
+
</div>
|
| 176 |
+
);
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
/* ββ Technology Page ββ */
|
| 180 |
+
function TechnologyPage() {
|
| 181 |
+
const steps = [
|
| 182 |
+
{
|
| 183 |
+
number: 1,
|
| 184 |
+
title: 'Video to Frames',
|
| 185 |
+
color: '#6c8cff',
|
| 186 |
+
file: '00-convert_video_to_image.py',
|
| 187 |
+
description: 'Raw training videos are split into individual image frames. One frame is extracted per second of video using OpenCV. Each frame is automatically scaled based on its resolution to normalize image sizes across the dataset.',
|
| 188 |
+
details: [
|
| 189 |
+
'Reads MP4 videos from the FaceForensics++ dataset',
|
| 190 |
+
'Extracts 1 frame per second (at the video\'s native frame rate)',
|
| 191 |
+
'Auto-scales: 2Γ for small frames (<300px), 0.5Γ for HD, 0.33Γ for Full HD+',
|
| 192 |
+
'Saves frames as individual JPG images organized by video',
|
| 193 |
+
],
|
| 194 |
+
diagram: (
|
| 195 |
+
<svg viewBox="0 0 400 120" className="step-illustration">
|
| 196 |
+
<rect x="10" y="15" width="90" height="90" rx="8" fill="#1a1a2e" stroke="#6c8cff" strokeWidth="1.5"/>
|
| 197 |
+
<polygon points="40,35 40,80 70,57" fill="#6c8cff" opacity="0.8"/>
|
| 198 |
+
<text x="55" y="112" textAnchor="middle" fill="#888" fontSize="11">Video</text>
|
| 199 |
+
<path d="M115 60 L155 60" stroke="#6c8cff" strokeWidth="2" markerEnd="url(#arrow1)"/>
|
| 200 |
+
<defs><marker id="arrow1" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto"><path d="M0,0 L10,5 L0,10 z" fill="#6c8cff"/></marker></defs>
|
| 201 |
+
{[0,1,2,3,4].map(i => (
|
| 202 |
+
<g key={i}>
|
| 203 |
+
<rect x={165 + i*46} y={20 + (i%2)*15} width="38" height="38" rx="4" fill="#1a1a2e" stroke="#4caf50" strokeWidth="1"/>
|
| 204 |
+
<text x={184 + i*46} y={44 + (i%2)*15} textAnchor="middle" fill="#4caf50" fontSize="9">F{i+1}</text>
|
| 205 |
+
</g>
|
| 206 |
+
))}
|
| 207 |
+
<text x="300" y="112" textAnchor="middle" fill="#888" fontSize="11">Extracted Frames (1/sec)</text>
|
| 208 |
+
</svg>
|
| 209 |
+
),
|
| 210 |
+
},
|
| 211 |
+
{
|
| 212 |
+
number: 2,
|
| 213 |
+
title: 'Face Detection & Cropping',
|
| 214 |
+
color: '#ff9800',
|
| 215 |
+
file: '01-crop_faces_with_mtcnn.py',
|
| 216 |
+
description: 'MTCNN (Multi-task Cascaded Convolutional Network) scans each extracted frame to detect faces. Detected faces are cropped with a 30% margin around the bounding box to preserve context like hair and jawline, which helps the model detect manipulation artifacts.',
|
| 217 |
+
details: [
|
| 218 |
+
'Uses MTCNN deep learning face detector for accurate face localization',
|
| 219 |
+
'Filters low-confidence detections (>95% threshold for multi-face frames)',
|
| 220 |
+
'Adds 30% margin around each face bounding box',
|
| 221 |
+
'Crops and saves individual face images for training',
|
| 222 |
+
],
|
| 223 |
+
diagram: (
|
| 224 |
+
<svg viewBox="0 0 400 120" className="step-illustration">
|
| 225 |
+
<rect x="10" y="10" width="100" height="100" rx="6" fill="#1a1a2e" stroke="#444" strokeWidth="1"/>
|
| 226 |
+
<circle cx="60" cy="45" r="15" fill="none" stroke="#ff9800" strokeWidth="1.5" strokeDasharray="3,2"/>
|
| 227 |
+
<circle cx="60" cy="42" r="6" fill="#ff9800" opacity="0.4"/>
|
| 228 |
+
<ellipse cx="60" cy="55" rx="10" ry="6" fill="#ff9800" opacity="0.3"/>
|
| 229 |
+
<rect x="35" y="25" width="50" height="50" rx="4" fill="none" stroke="#ff9800" strokeWidth="2"/>
|
| 230 |
+
<text x="60" y="112" textAnchor="middle" fill="#888" fontSize="11">Frame + Detection</text>
|
| 231 |
+
<path d="M125 55 L165 55" stroke="#ff9800" strokeWidth="2" markerEnd="url(#arrow2)"/>
|
| 232 |
+
<defs><marker id="arrow2" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto"><path d="M0,0 L10,5 L0,10 z" fill="#ff9800"/></marker></defs>
|
| 233 |
+
<rect x="175" y="20" width="70" height="70" rx="8" fill="#1a1a2e" stroke="#ff9800" strokeWidth="2"/>
|
| 234 |
+
<circle cx="210" cy="45" r="12" fill="none" stroke="#ff9800" strokeWidth="1.5"/>
|
| 235 |
+
<circle cx="210" cy="42" r="5" fill="#ff9800" opacity="0.5"/>
|
| 236 |
+
<ellipse cx="210" cy="52" rx="8" ry="5" fill="#ff9800" opacity="0.4"/>
|
| 237 |
+
<text x="210" y="112" textAnchor="middle" fill="#888" fontSize="11">Cropped Face (+30% margin)</text>
|
| 238 |
+
<text x="340" y="40" fill="#ff9800" fontSize="11" fontWeight="600">β 95%+ confidence</text>
|
| 239 |
+
<text x="340" y="58" fill="#888" fontSize="10">30% margin padding</text>
|
| 240 |
+
<text x="340" y="76" fill="#888" fontSize="10">Context preserved</text>
|
| 241 |
+
</svg>
|
| 242 |
+
),
|
| 243 |
+
},
|
| 244 |
+
{
|
| 245 |
+
number: 3,
|
| 246 |
+
title: 'Dataset Preparation',
|
| 247 |
+
color: '#4caf50',
|
| 248 |
+
file: '02-prepare_fake_real_dataset.py',
|
| 249 |
+
description: 'Cropped face images are organized into "real" and "fake" categories based on FaceForensics++ metadata. Small or corrupted images (<90px) are filtered out. The dataset is then split into training (80%), validation (10%), and test (10%) sets using stratified splitting.',
|
| 250 |
+
details: [
|
| 251 |
+
'Labels faces as REAL or FAKE using FaceForensics++ CSV metadata',
|
| 252 |
+
'Filters out low-quality images smaller than 90Γ90 pixels',
|
| 253 |
+
'Balances fake samples across manipulation methods (Deepfakes, Face2Face, FaceSwap, NeuralTextures, FaceShifter)',
|
| 254 |
+
'Splits into train/val/test (80/10/10) with stratified random sampling',
|
| 255 |
+
],
|
| 256 |
+
diagram: (
|
| 257 |
+
<svg viewBox="0 0 400 120" className="step-illustration">
|
| 258 |
+
<g>
|
| 259 |
+
<rect x="10" y="15" width="55" height="40" rx="4" fill="rgba(76,175,80,0.15)" stroke="#4caf50" strokeWidth="1.5"/>
|
| 260 |
+
<text x="37" y="39" textAnchor="middle" fill="#4caf50" fontSize="11" fontWeight="600">REAL</text>
|
| 261 |
+
<rect x="10" y="65" width="55" height="40" rx="4" fill="rgba(244,67,54,0.15)" stroke="#f44336" strokeWidth="1.5"/>
|
| 262 |
+
<text x="37" y="89" textAnchor="middle" fill="#f44336" fontSize="11" fontWeight="600">FAKE</text>
|
| 263 |
+
</g>
|
| 264 |
+
<path d="M80 55 L120 55" stroke="#4caf50" strokeWidth="2" markerEnd="url(#arrow3)"/>
|
| 265 |
+
<defs><marker id="arrow3" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto"><path d="M0,0 L10,5 L0,10 z" fill="#4caf50"/></marker></defs>
|
| 266 |
+
<rect x="130" y="8" width="100" height="28" rx="4" fill="rgba(76,175,80,0.1)" stroke="#4caf50" strokeWidth="1"/>
|
| 267 |
+
<text x="180" y="26" textAnchor="middle" fill="#4caf50" fontSize="10">Train 80%</text>
|
| 268 |
+
<rect x="130" y="44" width="100" height="28" rx="4" fill="rgba(255,152,0,0.1)" stroke="#ff9800" strokeWidth="1"/>
|
| 269 |
+
<text x="180" y="62" textAnchor="middle" fill="#ff9800" fontSize="10">Val 10%</text>
|
| 270 |
+
<rect x="130" y="80" width="100" height="28" rx="4" fill="rgba(108,140,255,0.1)" stroke="#6c8cff" strokeWidth="1"/>
|
| 271 |
+
<text x="180" y="98" textAnchor="middle" fill="#6c8cff" fontSize="10">Test 10%</text>
|
| 272 |
+
<text x="310" y="30" fill="#888" fontSize="10">β Min 90Γ90px filter</text>
|
| 273 |
+
<text x="310" y="50" fill="#888" fontSize="10">β Stratified split</text>
|
| 274 |
+
<text x="310" y="70" fill="#888" fontSize="10">β Multi-method balance</text>
|
| 275 |
+
<text x="310" y="90" fill="#888" fontSize="10">β CSV metadata labels</text>
|
| 276 |
+
</svg>
|
| 277 |
+
),
|
| 278 |
+
},
|
| 279 |
+
{
|
| 280 |
+
number: 4,
|
| 281 |
+
title: 'CNN Training (EfficientNetB0)',
|
| 282 |
+
color: '#f44336',
|
| 283 |
+
file: '03-train_cnn.py',
|
| 284 |
+
description: 'A two-phase transfer learning approach trains an EfficientNetB0-based classifier. Phase 1 freezes the pre-trained ImageNet backbone and trains only the classification head. Phase 2 unfreezes the entire network for fine-tuning with a very low learning rate, achieving ~92% accuracy.',
|
| 285 |
+
details: [
|
| 286 |
+
'EfficientNetB0 backbone pre-trained on ImageNet (224Γ224 input)',
|
| 287 |
+
'Phase 1: Frozen base, train head only (lr=1e-3, up to 15 epochs)',
|
| 288 |
+
'Phase 2: Full fine-tuning (lr=1e-5, up to 30 epochs)',
|
| 289 |
+
'Data augmentation: rotation, flip, zoom, shift, brightness',
|
| 290 |
+
'Class weight balancing for imbalanced datasets',
|
| 291 |
+
'Callbacks: EarlyStopping, ReduceLROnPlateau, ModelCheckpoint',
|
| 292 |
+
'Output: binary sigmoid β score > 0.5 = REAL, β€ 0.5 = FAKE',
|
| 293 |
+
],
|
| 294 |
+
diagram: (
|
| 295 |
+
<svg viewBox="0 0 400 140" className="step-illustration">
|
| 296 |
+
<rect x="10" y="30" width="70" height="80" rx="6" fill="#1a1a2e" stroke="#f44336" strokeWidth="1.5"/>
|
| 297 |
+
<text x="45" y="55" textAnchor="middle" fill="#f44336" fontSize="9" fontWeight="600">224Γ224</text>
|
| 298 |
+
<text x="45" y="70" textAnchor="middle" fill="#888" fontSize="8">Face Input</text>
|
| 299 |
+
<text x="45" y="95" textAnchor="middle" fill="#666" fontSize="8">preprocess</text>
|
| 300 |
+
<text x="45" y="106" textAnchor="middle" fill="#666" fontSize="8">[-1, 1]</text>
|
| 301 |
+
<path d="M90 70 L115 70" stroke="#f44336" strokeWidth="1.5" markerEnd="url(#arrow4)"/>
|
| 302 |
+
<rect x="120" y="15" width="100" height="110" rx="6" fill="#1a1a2e" stroke="#ff9800" strokeWidth="1.5"/>
|
| 303 |
+
<text x="170" y="35" textAnchor="middle" fill="#ff9800" fontSize="10" fontWeight="600">EfficientNetB0</text>
|
| 304 |
+
<text x="170" y="52" textAnchor="middle" fill="#888" fontSize="8">(ImageNet weights)</text>
|
| 305 |
+
{[0,1,2,3].map(i => (
|
| 306 |
+
<rect key={i} x="135" y={60 + i*14} width="70" height="10" rx="2" fill={`rgba(255,152,0,${0.15 + i*0.1})`} stroke="#ff9800" strokeWidth="0.5"/>
|
| 307 |
+
))}
|
| 308 |
+
<defs><marker id="arrow4" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto"><path d="M0,0 L10,5 L0,10 z" fill="#f44336"/></marker></defs>
|
| 309 |
+
<path d="M230 70 L255 70" stroke="#f44336" strokeWidth="1.5" markerEnd="url(#arrow4)"/>
|
| 310 |
+
<rect x="260" y="25" width="80" height="90" rx="6" fill="#1a1a2e" stroke="#6c8cff" strokeWidth="1.5"/>
|
| 311 |
+
<text x="300" y="45" textAnchor="middle" fill="#6c8cff" fontSize="9" fontWeight="600">Head</text>
|
| 312 |
+
<text x="300" y="62" textAnchor="middle" fill="#888" fontSize="7">GlobalAvgPool</text>
|
| 313 |
+
<text x="300" y="74" textAnchor="middle" fill="#888" fontSize="7">BatchNorm</text>
|
| 314 |
+
<text x="300" y="86" textAnchor="middle" fill="#888" fontSize="7">Dense(256)+Dropout</text>
|
| 315 |
+
<text x="300" y="98" textAnchor="middle" fill="#888" fontSize="7">Dense(1, sigmoid)</text>
|
| 316 |
+
<path d="M350 70 L375 70" stroke="#4caf50" strokeWidth="1.5" markerEnd="url(#arrow5)"/>
|
| 317 |
+
<defs><marker id="arrow5" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto"><path d="M0,0 L10,5 L0,10 z" fill="#4caf50"/></marker></defs>
|
| 318 |
+
<text x="388" y="65" fill="#4caf50" fontSize="11" fontWeight="700">0β1</text>
|
| 319 |
+
<text x="388" y="80" fill="#888" fontSize="8">Score</text>
|
| 320 |
+
</svg>
|
| 321 |
+
),
|
| 322 |
+
},
|
| 323 |
+
];
|
| 324 |
+
|
| 325 |
+
return (
|
| 326 |
+
<section className="tech-page">
|
| 327 |
+
<div className="tech-hero">
|
| 328 |
+
<h1 className="tech-title">How It Works</h1>
|
| 329 |
+
<p className="tech-subtitle">
|
| 330 |
+
Our deepfake detection pipeline processes videos through four stages β from raw video
|
| 331 |
+
to a trained AI model that scores each face for authenticity.
|
| 332 |
+
</p>
|
| 333 |
+
</div>
|
| 334 |
+
|
| 335 |
+
{/* Pipeline overview */}
|
| 336 |
+
<div className="pipeline-overview">
|
| 337 |
+
{steps.map((step, i) => (
|
| 338 |
+
<React.Fragment key={step.number}>
|
| 339 |
+
<div className="pipeline-step-mini">
|
| 340 |
+
<StepDiagram number={step.number} title={step.title} color={step.color} />
|
| 341 |
+
<span style={{ color: step.color, fontWeight: 600, fontSize: 13 }}>{step.title}</span>
|
| 342 |
+
</div>
|
| 343 |
+
{i < steps.length - 1 && <FlowArrow />}
|
| 344 |
+
</React.Fragment>
|
| 345 |
+
))}
|
| 346 |
+
</div>
|
| 347 |
+
|
| 348 |
+
{/* Detailed steps */}
|
| 349 |
+
{steps.map((step) => (
|
| 350 |
+
<div className="tech-step" key={step.number}>
|
| 351 |
+
<div className="tech-step-header">
|
| 352 |
+
<StepDiagram number={step.number} title={step.title} color={step.color} />
|
| 353 |
+
<div>
|
| 354 |
+
<h2 className="tech-step-title" style={{ color: step.color }}>
|
| 355 |
+
Step {step.number}: {step.title}
|
| 356 |
+
</h2>
|
| 357 |
+
<code className="tech-file">{step.file}</code>
|
| 358 |
+
</div>
|
| 359 |
+
</div>
|
| 360 |
+
<p className="tech-step-desc">{step.description}</p>
|
| 361 |
+
<div className="tech-step-diagram">
|
| 362 |
+
{step.diagram}
|
| 363 |
+
</div>
|
| 364 |
+
<ul className="tech-step-details">
|
| 365 |
+
{step.details.map((d, i) => <li key={i}>{d}</li>)}
|
| 366 |
+
</ul>
|
| 367 |
+
</div>
|
| 368 |
+
))}
|
| 369 |
+
|
| 370 |
+
{/* Inference section */}
|
| 371 |
+
<div className="tech-step">
|
| 372 |
+
<div className="tech-step-header">
|
| 373 |
+
<svg width="80" height="80" viewBox="0 0 80 80" className="step-icon">
|
| 374 |
+
<circle cx="40" cy="40" r="36" fill="none" stroke="#6c8cff" strokeWidth="2.5"/>
|
| 375 |
+
<text x="40" y="46" textAnchor="middle" fill="#6c8cff" fontSize="22" fontWeight="700">βΆ</text>
|
| 376 |
+
</svg>
|
| 377 |
+
<div>
|
| 378 |
+
<h2 className="tech-step-title" style={{ color: '#6c8cff' }}>
|
| 379 |
+
Real-Time Inference
|
| 380 |
+
</h2>
|
| 381 |
+
<code className="tech-file">App/app.py</code>
|
| 382 |
+
</div>
|
| 383 |
+
</div>
|
| 384 |
+
<p className="tech-step-desc">
|
| 385 |
+
When you upload a video, the app uses YOLOv8 for fast face detection on each frame,
|
| 386 |
+
then feeds cropped faces through the trained EfficientNetB0 model. Each face gets an
|
| 387 |
+
authenticity score (0 = fake, 1 = real), and the processed video shows bounding boxes
|
| 388 |
+
with per-face REAL/FAKE labels overlaid in real time.
|
| 389 |
+
</p>
|
| 390 |
+
<div className="tech-step-diagram">
|
| 391 |
+
<svg viewBox="0 0 400 80" className="step-illustration">
|
| 392 |
+
<rect x="5" y="15" width="65" height="50" rx="6" fill="#1a1a2e" stroke="#6c8cff" strokeWidth="1.5"/>
|
| 393 |
+
<text x="37" y="44" textAnchor="middle" fill="#6c8cff" fontSize="9" fontWeight="600">Upload</text>
|
| 394 |
+
<path d="M80 40 L105 40" stroke="#6c8cff" strokeWidth="1.5" markerEnd="url(#arrowI)"/>
|
| 395 |
+
<rect x="110" y="15" width="65" height="50" rx="6" fill="#1a1a2e" stroke="#ff9800" strokeWidth="1.5"/>
|
| 396 |
+
<text x="142" y="38" textAnchor="middle" fill="#ff9800" fontSize="9" fontWeight="600">YOLOv8</text>
|
| 397 |
+
<text x="142" y="50" textAnchor="middle" fill="#888" fontSize="7">Face Detect</text>
|
| 398 |
+
<path d="M185 40 L210 40" stroke="#ff9800" strokeWidth="1.5" markerEnd="url(#arrowI)"/>
|
| 399 |
+
<rect x="215" y="15" width="65" height="50" rx="6" fill="#1a1a2e" stroke="#f44336" strokeWidth="1.5"/>
|
| 400 |
+
<text x="247" y="38" textAnchor="middle" fill="#f44336" fontSize="8" fontWeight="600">EfficientNet</text>
|
| 401 |
+
<text x="247" y="50" textAnchor="middle" fill="#888" fontSize="7">Predict</text>
|
| 402 |
+
<path d="M290 40 L315 40" stroke="#4caf50" strokeWidth="1.5" markerEnd="url(#arrowI)"/>
|
| 403 |
+
<rect x="320" y="15" width="70" height="50" rx="6" fill="#1a1a2e" stroke="#4caf50" strokeWidth="1.5"/>
|
| 404 |
+
<text x="355" y="38" textAnchor="middle" fill="#4caf50" fontSize="9" fontWeight="600">REAL</text>
|
| 405 |
+
<text x="355" y="50" textAnchor="middle" fill="#f44336" fontSize="9" fontWeight="600">FAKE</text>
|
| 406 |
+
<defs><marker id="arrowI" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto"><path d="M0,0 L10,5 L0,10 z" fill="#6c8cff"/></marker></defs>
|
| 407 |
+
</svg>
|
| 408 |
+
</div>
|
| 409 |
+
</div>
|
| 410 |
+
</section>
|
| 411 |
+
);
|
| 412 |
+
}
|
| 413 |
+
|
| 414 |
/* ββ Main App ββ */
|
| 415 |
function App() {
|
| 416 |
const [file, setFile] = useState(null);
|
|
|
|
| 479 |
}
|
| 480 |
};
|
| 481 |
|
| 482 |
+
const route = useHashRoute();
|
| 483 |
+
|
| 484 |
return (
|
| 485 |
<>
|
| 486 |
+
<Navbar route={route} />
|
| 487 |
+
|
| 488 |
+
{route === '#/technology' ? (
|
| 489 |
+
<TechnologyPage />
|
| 490 |
+
) : (
|
| 491 |
+
<>
|
| 492 |
|
| 493 |
<section className="hero">
|
| 494 |
<div className="hero-left">
|
| 495 |
+
<h1 className="hero-title">AI-Deepfake Video Detection</h1>
|
| 496 |
<p className="hero-desc">
|
| 497 |
Free deepfake detection tool for videos. Upload a video and get
|
| 498 |
per-face authenticity scores in seconds. AI-powered synthetic face detection.
|
|
|
|
| 509 |
{error && <div className="error-box">{error}</div>}
|
| 510 |
</div>
|
| 511 |
|
| 512 |
+
<div className="hero-right">
|
| 513 |
+
<img src="/static/images.png" alt="AI Deepfake Detection" className="hero-image" />
|
| 514 |
+
</div>
|
| 515 |
+
|
| 516 |
</section>
|
| 517 |
|
| 518 |
<VideoComparison
|
|
|
|
| 521 |
isProcessing={submitting}
|
| 522 |
/>
|
| 523 |
<ResultsPanel data={result} />
|
| 524 |
+
</>
|
| 525 |
+
)}
|
| 526 |
</>
|
| 527 |
);
|
| 528 |
}
|
App/static/images.png
ADDED
|
Git LFS Details
|
App/static/style.css
CHANGED
|
@@ -41,6 +41,11 @@ input[type="file"] { display: none; }
|
|
| 41 |
.video-preview video {
|
| 42 |
max-width: 100%; max-height: 400px; border-radius: 12px; border: 1px solid #1e1e35; background: #111122;
|
| 43 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
.preview-placeholder {
|
| 45 |
width: 100%; max-width: 460px; height: 280px; border-radius: 12px; background: #111122;
|
| 46 |
border: 1px solid #1e1e35; display: flex; align-items: center; justify-content: center; color: #333; font-size: 48px;
|
|
@@ -89,7 +94,54 @@ input[type="file"] { display: none; }
|
|
| 89 |
@media (max-width: 768px) {
|
| 90 |
.hero { flex-direction: column; padding: 0 20px; margin-top: 30px; gap: 30px; }
|
| 91 |
.hero-left { max-width: 100%; } .hero-title { font-size: 32px; }
|
|
|
|
| 92 |
.results-section { padding: 0 20px; } .results-panel { padding: 20px; }
|
| 93 |
.navbar { padding: 14px 20px; } .navbar nav a { margin-left: 16px; font-size: 13px; }
|
| 94 |
.compare-grid { flex-direction: column; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
}
|
|
|
|
| 41 |
.video-preview video {
|
| 42 |
max-width: 100%; max-height: 400px; border-radius: 12px; border: 1px solid #1e1e35; background: #111122;
|
| 43 |
}
|
| 44 |
+
.hero-right { flex: 1; display: flex; justify-content: center; align-items: stretch; }
|
| 45 |
+
.hero-image {
|
| 46 |
+
width: 100%; height: 100%; border-radius: 14px; border: 1px solid #1e1e35;
|
| 47 |
+
object-fit: cover; box-shadow: 0 8px 32px rgba(108,140,255,0.10);
|
| 48 |
+
}
|
| 49 |
.preview-placeholder {
|
| 50 |
width: 100%; max-width: 460px; height: 280px; border-radius: 12px; background: #111122;
|
| 51 |
border: 1px solid #1e1e35; display: flex; align-items: center; justify-content: center; color: #333; font-size: 48px;
|
|
|
|
| 94 |
@media (max-width: 768px) {
|
| 95 |
.hero { flex-direction: column; padding: 0 20px; margin-top: 30px; gap: 30px; }
|
| 96 |
.hero-left { max-width: 100%; } .hero-title { font-size: 32px; }
|
| 97 |
+
.hero-right { display: none; }
|
| 98 |
.results-section { padding: 0 20px; } .results-panel { padding: 20px; }
|
| 99 |
.navbar { padding: 14px 20px; } .navbar nav a { margin-left: 16px; font-size: 13px; }
|
| 100 |
.compare-grid { flex-direction: column; }
|
| 101 |
+
.pipeline-overview { flex-direction: column; align-items: center; }
|
| 102 |
+
.flow-arrow { transform: rotate(90deg); }
|
| 103 |
+
.tech-step-header { flex-direction: column; align-items: center; text-align: center; }
|
| 104 |
+
.tech-page { padding: 0 16px; }
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
/* ββ Technology Page ββ */
|
| 108 |
+
.tech-page { max-width: 900px; margin: 0 auto; padding: 0 40px 60px; }
|
| 109 |
+
.tech-hero { text-align: center; margin: 50px 0 40px; }
|
| 110 |
+
.tech-title { font-size: 42px; font-weight: 800; color: #fff; margin-bottom: 14px; }
|
| 111 |
+
.tech-subtitle { font-size: 15px; color: #999; line-height: 1.7; max-width: 600px; margin: 0 auto; }
|
| 112 |
+
|
| 113 |
+
.pipeline-overview {
|
| 114 |
+
display: flex; align-items: center; justify-content: center; gap: 12px;
|
| 115 |
+
margin-bottom: 50px; flex-wrap: wrap;
|
| 116 |
+
}
|
| 117 |
+
.pipeline-step-mini {
|
| 118 |
+
display: flex; flex-direction: column; align-items: center; gap: 8px; text-align: center; width: 110px;
|
| 119 |
+
}
|
| 120 |
+
.flow-arrow { display: flex; align-items: center; color: #6c8cff; }
|
| 121 |
+
|
| 122 |
+
.tech-step {
|
| 123 |
+
background: #111122; border: 1px solid #1e1e35; border-radius: 14px;
|
| 124 |
+
padding: 30px 36px; margin-bottom: 28px;
|
| 125 |
+
}
|
| 126 |
+
.tech-step-header { display: flex; align-items: center; gap: 20px; margin-bottom: 16px; }
|
| 127 |
+
.tech-step-title { font-size: 22px; font-weight: 700; margin-bottom: 4px; }
|
| 128 |
+
.tech-file {
|
| 129 |
+
font-size: 12px; color: #6c8cff; background: rgba(108,140,255,0.1);
|
| 130 |
+
padding: 3px 10px; border-radius: 4px; font-family: 'Consolas', monospace;
|
| 131 |
+
}
|
| 132 |
+
.tech-step-desc { font-size: 14px; color: #aaa; line-height: 1.7; margin-bottom: 20px; }
|
| 133 |
+
.tech-step-diagram {
|
| 134 |
+
background: #0b0b1a; border: 1px solid #1a1a2e; border-radius: 10px;
|
| 135 |
+
padding: 20px; margin-bottom: 20px; text-align: center; overflow-x: auto;
|
| 136 |
+
}
|
| 137 |
+
.step-illustration { max-width: 100%; height: auto; }
|
| 138 |
+
.step-icon { flex-shrink: 0; }
|
| 139 |
+
.tech-step-details {
|
| 140 |
+
list-style: none; padding: 0;
|
| 141 |
+
}
|
| 142 |
+
.tech-step-details li {
|
| 143 |
+
font-size: 13px; color: #bbb; padding: 6px 0 6px 20px; position: relative; line-height: 1.5;
|
| 144 |
+
}
|
| 145 |
+
.tech-step-details li::before {
|
| 146 |
+
content: 'β'; position: absolute; left: 0; color: #6c8cff; font-weight: 700;
|
| 147 |
}
|
App/templates/index.html
CHANGED
|
@@ -3,7 +3,7 @@
|
|
| 3 |
<head>
|
| 4 |
<meta charset="UTF-8">
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
-
<title>
|
| 7 |
<script src="https://unpkg.com/react@18/umd/react.production.min.js" crossorigin></script>
|
| 8 |
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js" crossorigin></script>
|
| 9 |
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
|
|
|
|
| 3 |
<head>
|
| 4 |
<meta charset="UTF-8">
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>AI-Deepfake Video Detection</title>
|
| 7 |
<script src="https://unpkg.com/react@18/umd/react.production.min.js" crossorigin></script>
|
| 8 |
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js" crossorigin></script>
|
| 9 |
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
|