spideyhead commited on
Commit
eb864ee
·
verified ·
1 Parent(s): 24bb2b8

Upload 3 files

Browse files
Files changed (3) hide show
  1. Dockerfile +26 -0
  2. main.py +158 -0
  3. requirements.txt +9 -0
Dockerfile ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9-slim
2
+
3
+ # Install system dependencies required by OpenCV
4
+ RUN apt-get update && apt-get install -y \
5
+ libglib2.0-0 \
6
+ libsm6 \
7
+ libxext6 \
8
+ libxrender-dev \
9
+ && rm -rf /var/lib/apt/lists/*
10
+
11
+ # Set up a non-root user for Hugging Face Spaces
12
+ RUN useradd -m -u 1000 user
13
+ USER user
14
+ ENV PATH="/home/user/.local/bin:$PATH"
15
+
16
+ WORKDIR /app
17
+
18
+ # Copy requirements and install
19
+ COPY --chown=user requirements.txt .
20
+ RUN pip install --no-cache-dir -r requirements.txt
21
+
22
+ # Copy the app
23
+ COPY --chown=user . .
24
+
25
+ # Run the app on port 7860 (Hugging Face Spaces default)
26
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
main.py ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import cv2
3
+ import torch
4
+ import shutil
5
+ from fastapi import FastAPI, UploadFile, File
6
+ from fastapi.middleware.cors import CORSMiddleware
7
+ from transformers import AutoImageProcessor, AutoModelForImageClassification
8
+ from facenet_pytorch import MTCNN
9
+ from PIL import Image
10
+
11
+ app = FastAPI()
12
+
13
+ # Enable CORS for the React frontend
14
+ app.add_middleware(
15
+ CORSMiddleware,
16
+ allow_origins=["*"],
17
+ allow_credentials=True,
18
+ allow_methods=["*"],
19
+ allow_headers=["*"],
20
+ )
21
+
22
+ # Initialize Models globally so they load once on startup
23
+ print("Loading MTCNN Face Detector...")
24
+ mtcnn = MTCNN(keep_all=False, select_largest=True, post_process=False)
25
+
26
+ print("Loading Hugging Face Deepfake Detector...")
27
+ model_name = "dima806/deepfake_vs_real_image_detection"
28
+ processor = AutoImageProcessor.from_pretrained(model_name)
29
+ model = AutoModelForImageClassification.from_pretrained(model_name)
30
+
31
+ # Ensure temp directory exists
32
+ os.makedirs("temp", exist_ok=True)
33
+
34
+ def extract_faces_from_video(video_path, max_frames=8):
35
+ """Extracts a limited number of frames and crops the face from each."""
36
+ cap = cv2.VideoCapture(video_path)
37
+ frames_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
38
+
39
+ if frames_count == 0:
40
+ return []
41
+
42
+ # Calculate step to get evenly spaced frames
43
+ step = max(1, frames_count // max_frames)
44
+
45
+ faces = []
46
+ current_frame = 0
47
+
48
+ while cap.isOpened() and len(faces) < max_frames:
49
+ cap.set(cv2.CAP_PROP_POS_FRAMES, current_frame)
50
+ ret, frame = cap.read()
51
+
52
+ if not ret:
53
+ break
54
+
55
+ # Convert BGR to RGB for MTCNN and PIL
56
+ frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
57
+ pil_img = Image.fromarray(frame_rgb)
58
+
59
+ # Detect and crop face
60
+ boxes, _ = mtcnn.detect(pil_img)
61
+ if boxes is not None and len(boxes) > 0:
62
+ box = boxes[0]
63
+
64
+ # Add 30% padding around the face. Deepfake models need to see the jawline and background boundaries!
65
+ w = box[2] - box[0]
66
+ h = box[3] - box[1]
67
+ pad_w = int(w * 0.3)
68
+ pad_h = int(h * 0.3)
69
+
70
+ # Ensure box coordinates are within image bounds with padding
71
+ x1 = max(0, int(box[0]) - pad_w)
72
+ y1 = max(0, int(box[1]) - pad_h)
73
+ x2 = min(pil_img.width, int(box[2]) + pad_w)
74
+ y2 = min(pil_img.height, int(box[3]) + pad_h)
75
+
76
+ if x2 > x1 and y2 > y1:
77
+ face_crop = pil_img.crop((x1, y1, x2, y2))
78
+ faces.append(face_crop)
79
+
80
+ current_frame += step
81
+
82
+ cap.release()
83
+ return faces
84
+
85
+ @app.post("/api/analyze")
86
+ async def analyze_video(file: UploadFile = File(...)):
87
+ print(f"Received file: {file.filename}")
88
+
89
+ # Save the uploaded file temporarily
90
+ temp_video_path = os.path.join("temp", file.filename)
91
+ with open(temp_video_path, "wb") as buffer:
92
+ shutil.copyfileobj(file.file, buffer)
93
+
94
+ try:
95
+ # Extract faces
96
+ print("Extracting faces from video...")
97
+ faces = extract_faces_from_video(temp_video_path, max_frames=6)
98
+
99
+ if not faces:
100
+ return {
101
+ "isFake": False,
102
+ "confidence": 0,
103
+ "explanation": "Could not detect any faces in the video. Ensure the subject's face is clearly visible.",
104
+ "details": []
105
+ }
106
+
107
+ print(f"Extracted {len(faces)} faces. Running inference...")
108
+
109
+ # Prepare for model
110
+ inputs = processor(images=faces, return_tensors="pt")
111
+
112
+ # Run inference
113
+ with torch.no_grad():
114
+ outputs = model(**inputs)
115
+
116
+ # Apply softmax to get probabilities
117
+ probabilities = torch.nn.functional.softmax(outputs.logits, dim=-1)
118
+
119
+ # For dima806/deepfake_vs_real_image_detection, Fake is index 1 and Real is index 0
120
+ fake_idx = 1
121
+
122
+ # Get average probability for 'Fake' class across all analyzed frames
123
+ avg_fake_prob = probabilities[:, fake_idx].mean().item()
124
+
125
+ is_fake = avg_fake_prob > 0.5
126
+
127
+ # Calculate confidence score based on the chosen class
128
+ if is_fake:
129
+ confidence = round(avg_fake_prob * 100, 2)
130
+ else:
131
+ confidence = round((1.0 - avg_fake_prob) * 100, 2)
132
+
133
+ explanation = "Our AI detected significant spatial artifacts and inconsistencies consistent with synthetic generation or facial manipulation." if is_fake else "No significant manipulation artifacts were detected. The spatial integrity and facial rendering are consistent with genuine media."
134
+
135
+ print(f"Result: isFake={is_fake}, confidence={confidence}%")
136
+
137
+ return {
138
+ "isFake": is_fake,
139
+ "confidence": confidence,
140
+ "explanation": explanation,
141
+ "details": [
142
+ {"title": "Face Detection", "desc": f"Analyzed {len(faces)} key frames evenly distributed across the video."},
143
+ {"title": "Spatial Analysis", "desc": "Evaluated using a Vision Transformer (ViT) deep learning architecture."}
144
+ ]
145
+ }
146
+
147
+ except Exception as e:
148
+ print(f"Error analyzing video: {e}")
149
+ return {"error": str(e)}
150
+
151
+ finally:
152
+ # Clean up temp file
153
+ if os.path.exists(temp_video_path):
154
+ os.remove(temp_video_path)
155
+
156
+ @app.get("/")
157
+ def health_check():
158
+ return {"status": "Backend is running!"}
requirements.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ fastapi
2
+ uvicorn
3
+ python-multipart
4
+ opencv-python-headless
5
+ torch
6
+ torchvision
7
+ transformers
8
+ facenet-pytorch
9
+ Pillow