Spaces:
Running
Running
Anish commited on
Commit ·
cd801e4
1
Parent(s): f49287c
[Updated Features] > Updated explainer and attribution with a new model, and support for better heatmap.
Browse files
backend/app/ai/attribution.py
CHANGED
|
@@ -1,56 +1,83 @@
|
|
| 1 |
import torch
|
| 2 |
import cv2
|
| 3 |
import numpy as np
|
| 4 |
-
import timm
|
| 5 |
from PIL import Image
|
| 6 |
-
from
|
|
|
|
| 7 |
|
| 8 |
-
|
| 9 |
-
model.eval()
|
| 10 |
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
mean=[0.485, 0.456, 0.406],
|
| 16 |
-
std=[0.229, 0.224, 0.225]
|
| 17 |
-
)
|
| 18 |
-
])
|
| 19 |
|
| 20 |
def generate_attribution(image_path: str, save_path: str) -> dict:
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import torch
|
| 2 |
import cv2
|
| 3 |
import numpy as np
|
|
|
|
| 4 |
from PIL import Image
|
| 5 |
+
from transformers import AutoImageProcessor, AutoModelForImageClassification
|
| 6 |
+
import logging
|
| 7 |
|
| 8 |
+
logger = logging.getLogger(__name__)
|
|
|
|
| 9 |
|
| 10 |
+
model_id = "prithivMLmods/Deep-Fake-Detector-Model"
|
| 11 |
+
processor = AutoImageProcessor.from_pretrained(model_id)
|
| 12 |
+
model = AutoModelForImageClassification.from_pretrained(model_id)
|
| 13 |
+
model.eval()
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
|
| 15 |
def generate_attribution(image_path: str, save_path: str) -> dict:
|
| 16 |
+
try:
|
| 17 |
+
img = Image.open(image_path).convert("RGB")
|
| 18 |
+
original_cv = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
|
| 19 |
+
|
| 20 |
+
inputs = processor(images=img, return_tensors="pt")
|
| 21 |
+
x = inputs["pixel_values"]
|
| 22 |
+
|
| 23 |
+
target_layer = model.vision_model.encoder.layers[-1].layer_norm1
|
| 24 |
+
|
| 25 |
+
activations = []
|
| 26 |
+
gradients = []
|
| 27 |
+
|
| 28 |
+
def forward_hook(module, args, output):
|
| 29 |
+
activations.append(output.detach())
|
| 30 |
+
|
| 31 |
+
def backward_hook(module, grad_input, grad_output):
|
| 32 |
+
gradients.append(grad_output[0].detach())
|
| 33 |
+
|
| 34 |
+
handle_forward = target_layer.register_forward_hook(forward_hook)
|
| 35 |
+
handle_backward = target_layer.register_full_backward_hook(backward_hook)
|
| 36 |
+
|
| 37 |
+
outputs = model(x)
|
| 38 |
+
logits = outputs.logits
|
| 39 |
+
target_class = logits.argmax(dim=-1).item()
|
| 40 |
+
|
| 41 |
+
score = logits[0, target_class]
|
| 42 |
+
model.zero_grad()
|
| 43 |
+
score.backward()
|
| 44 |
+
|
| 45 |
+
handle_forward.remove()
|
| 46 |
+
handle_backward.remove()
|
| 47 |
+
|
| 48 |
+
act = activations[0][0]
|
| 49 |
+
grad = gradients[0][0]
|
| 50 |
+
weights = torch.mean(grad, dim=0)
|
| 51 |
+
|
| 52 |
+
cam = torch.zeros(act.shape[0])
|
| 53 |
+
for i in range(act.shape[1]):
|
| 54 |
+
cam += weights[i] * act[:, i]
|
| 55 |
+
|
| 56 |
+
cam = cam.numpy()
|
| 57 |
+
cam = cam.reshape(14, 14)
|
| 58 |
+
|
| 59 |
+
cam = np.maximum(cam, 0)
|
| 60 |
+
cam = cam - np.min(cam)
|
| 61 |
+
if np.max(cam) != 0:
|
| 62 |
+
cam = cam / np.max(cam)
|
| 63 |
+
|
| 64 |
+
cam_resized = cv2.resize(cam, (img.width, img.height), interpolation=cv2.INTER_CUBIC)
|
| 65 |
+
|
| 66 |
+
heatmap_uint8 = np.uint8(255 * cam_resized)
|
| 67 |
+
heatmap_colored = cv2.applyColorMap(heatmap_uint8, cv2.COLORMAP_INFERNO)
|
| 68 |
+
overlay = cv2.addWeighted(original_cv, 0.6, heatmap_colored, 0.4, 0)
|
| 69 |
+
|
| 70 |
+
cv2.imwrite(save_path, overlay)
|
| 71 |
+
|
| 72 |
+
y, x_coord = np.unravel_index(np.argmax(cam_resized), cam_resized.shape)
|
| 73 |
+
|
| 74 |
+
return {
|
| 75 |
+
"heatmap_path": save_path,
|
| 76 |
+
"top_regions": [
|
| 77 |
+
{"x": int(x_coord), "y": int(y), "importance": float(np.max(cam_resized))}
|
| 78 |
+
]
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
except Exception as e:
|
| 82 |
+
logger.error(f"Failed to generate attribution map: {str(e)}")
|
| 83 |
+
raise
|
backend/app/ai/explainer.py
CHANGED
|
@@ -2,38 +2,75 @@ import torch
|
|
| 2 |
import numpy as np
|
| 3 |
import cv2
|
| 4 |
from PIL import Image
|
| 5 |
-
|
| 6 |
-
import
|
| 7 |
|
| 8 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
model.eval()
|
| 10 |
|
| 11 |
def generate_heatmap(image_path: str, save_path: str):
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
|
| 39 |
-
|
|
|
|
|
|
|
|
|
| 2 |
import numpy as np
|
| 3 |
import cv2
|
| 4 |
from PIL import Image
|
| 5 |
+
from transformers import AutoImageProcessor, AutoModelForImageClassification
|
| 6 |
+
import logging
|
| 7 |
|
| 8 |
+
logger = logging.getLogger(__name__)
|
| 9 |
+
|
| 10 |
+
model_id = "prithivMLmods/Deep-Fake-Detector-Model"
|
| 11 |
+
processor = AutoImageProcessor.from_pretrained(model_id)
|
| 12 |
+
model = AutoModelForImageClassification.from_pretrained(model_id)
|
| 13 |
model.eval()
|
| 14 |
|
| 15 |
def generate_heatmap(image_path: str, save_path: str):
|
| 16 |
+
try:
|
| 17 |
+
img = Image.open(image_path).convert("RGB")
|
| 18 |
+
original_cv = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
|
| 19 |
+
|
| 20 |
+
inputs = processor(images=img, return_tensors="pt")
|
| 21 |
+
x = inputs["pixel_values"]
|
| 22 |
+
|
| 23 |
+
target_layer = model.vision_model.encoder.layers[-1].layer_norm1
|
| 24 |
+
|
| 25 |
+
activations = []
|
| 26 |
+
gradients = []
|
| 27 |
+
|
| 28 |
+
def forward_hook(module, args, output):
|
| 29 |
+
activations.append(output.detach())
|
| 30 |
+
|
| 31 |
+
def backward_hook(module, grad_input, grad_output):
|
| 32 |
+
gradients.append(grad_output[0].detach())
|
| 33 |
+
|
| 34 |
+
handle_forward = target_layer.register_forward_hook(forward_hook)
|
| 35 |
+
handle_backward = target_layer.register_full_backward_hook(backward_hook)
|
| 36 |
+
|
| 37 |
+
outputs = model(x)
|
| 38 |
+
logits = outputs.logits
|
| 39 |
+
target_class = logits.argmax(dim=-1).item()
|
| 40 |
+
score = logits[0, target_class]
|
| 41 |
+
|
| 42 |
+
model.zero_grad()
|
| 43 |
+
score.backward()
|
| 44 |
+
|
| 45 |
+
handle_forward.remove()
|
| 46 |
+
handle_backward.remove()
|
| 47 |
+
|
| 48 |
+
act = activations[0][0]
|
| 49 |
+
grad = gradients[0][0]
|
| 50 |
+
weights = torch.mean(grad, dim=0)
|
| 51 |
+
|
| 52 |
+
cam = torch.zeros(act.shape[0])
|
| 53 |
+
for i in range(act.shape[1]):
|
| 54 |
+
cam += weights[i] * act[:, i]
|
| 55 |
+
|
| 56 |
+
cam = cam.numpy()
|
| 57 |
+
cam = cam.reshape(14, 14)
|
| 58 |
+
|
| 59 |
+
cam = np.maximum(cam, 0)
|
| 60 |
+
cam = cam - np.min(cam)
|
| 61 |
+
if np.max(cam) != 0:
|
| 62 |
+
cam = cam / np.max(cam)
|
| 63 |
+
|
| 64 |
+
heatmap_resized = cv2.resize(cam, (img.width, img.height), interpolation=cv2.INTER_CUBIC)
|
| 65 |
+
|
| 66 |
+
heatmap_uint8 = np.uint8(255 * heatmap_resized)
|
| 67 |
+
heatmap_colored = cv2.applyColorMap(heatmap_uint8, cv2.COLORMAP_INFERNO)
|
| 68 |
+
|
| 69 |
+
overlay = cv2.addWeighted(original_cv, 0.6, heatmap_colored, 0.4, 0)
|
| 70 |
+
cv2.imwrite(save_path, overlay)
|
| 71 |
+
|
| 72 |
+
return save_path
|
| 73 |
|
| 74 |
+
except Exception as e:
|
| 75 |
+
logger.error(f"Failed to generate heatmap: {str(e)}")
|
| 76 |
+
raise
|
backend/app/ai/video/frame_detector.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
| 1 |
import torch
|
| 2 |
-
|
| 3 |
-
from torchvision import models, transforms
|
| 4 |
from PIL import Image
|
| 5 |
import numpy as np
|
| 6 |
import logging
|
|
@@ -12,23 +11,15 @@ class FrameDetector:
|
|
| 12 |
self.device = torch.device("cpu")
|
| 13 |
|
| 14 |
try:
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
param.requires_grad = False
|
| 19 |
-
|
| 20 |
-
num_ftrs = self.model.classifier[1].in_features
|
| 21 |
-
self.model.classifier[1] = nn.Linear(num_ftrs, 1)
|
| 22 |
-
|
| 23 |
self.model = self.model.to(self.device)
|
| 24 |
self.model.eval()
|
| 25 |
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
|
| 30 |
-
])
|
| 31 |
-
logger.info("EfficientNet-B0 CPU Inference Engine Initialized Successfully.")
|
| 32 |
|
| 33 |
except Exception as e:
|
| 34 |
logger.error(f"FATAL: Could not boot AI model: {str(e)}")
|
|
@@ -45,13 +36,16 @@ class FrameDetector:
|
|
| 45 |
rgb_frame = frame[:, :, ::-1]
|
| 46 |
pil_image = Image.fromarray(rgb_frame)
|
| 47 |
|
| 48 |
-
|
| 49 |
|
| 50 |
with torch.no_grad():
|
| 51 |
-
|
| 52 |
-
|
|
|
|
|
|
|
|
|
|
| 53 |
|
| 54 |
-
return
|
| 55 |
|
| 56 |
except Exception as e:
|
| 57 |
logger.error(f"AI Frame Prediction Crashed: {str(e)}")
|
|
|
|
| 1 |
import torch
|
| 2 |
+
from transformers import AutoImageProcessor, AutoModelForImageClassification
|
|
|
|
| 3 |
from PIL import Image
|
| 4 |
import numpy as np
|
| 5 |
import logging
|
|
|
|
| 11 |
self.device = torch.device("cpu")
|
| 12 |
|
| 13 |
try:
|
| 14 |
+
model_id = "prithivMLmods/Deep-Fake-Detector-Model"
|
| 15 |
+
self.processor = ViTImageProcessor.from_pretrained(model_id)
|
| 16 |
+
self.model = ViTForImageClassification.from_pretrained(model_id)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
self.model = self.model.to(self.device)
|
| 18 |
self.model.eval()
|
| 19 |
|
| 20 |
+
logger.info(f"Loaded {model_id} CPU Inference Engine Successfully.")
|
| 21 |
+
|
| 22 |
+
self.fake_label_idx = 1 if "fake" in str(self.model.config.id2label.get(1, "").lower()) else 0
|
|
|
|
|
|
|
|
|
|
| 23 |
|
| 24 |
except Exception as e:
|
| 25 |
logger.error(f"FATAL: Could not boot AI model: {str(e)}")
|
|
|
|
| 36 |
rgb_frame = frame[:, :, ::-1]
|
| 37 |
pil_image = Image.fromarray(rgb_frame)
|
| 38 |
|
| 39 |
+
inputs = self.processor(images=pil_image, return_tensors="pt").to(self.device)
|
| 40 |
|
| 41 |
with torch.no_grad():
|
| 42 |
+
outputs = self.model(**inputs)
|
| 43 |
+
logits = outputs.logits
|
| 44 |
+
|
| 45 |
+
probs = torch.nn.functional.softmax(logits, dim=-1)
|
| 46 |
+
ai_probability = probs[0][self.fake_label_idx].item()
|
| 47 |
|
| 48 |
+
return ai_probability
|
| 49 |
|
| 50 |
except Exception as e:
|
| 51 |
logger.error(f"AI Frame Prediction Crashed: {str(e)}")
|
backend/app/ai/video/noise_entropy_detector.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import cv2
|
| 2 |
+
import numpy as np
|
| 3 |
+
import logging
|
| 4 |
+
from scipy.stats import entropy
|
| 5 |
+
from typing import Optional
|
| 6 |
+
|
| 7 |
+
logger = logging.getLogger(__name__)
|
| 8 |
+
|
| 9 |
+
def compute_noise_entropy_anomaly(frame: np.ndarray) -> Optional[float]:
|
| 10 |
+
try:
|
| 11 |
+
if frame is None or frame.size == 0:
|
| 12 |
+
return None
|
| 13 |
+
|
| 14 |
+
gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
|
| 15 |
+
gray_small = cv2.resize(gray_frame, (512, 512), interpolation=cv2.INTER_AREA)
|
| 16 |
+
|
| 17 |
+
hist = cv2.calcHist([gray_small], [0], None, [256], [0, 256])
|
| 18 |
+
hist_prob = hist.ravel() / hist.sum()
|
| 19 |
+
|
| 20 |
+
img_entropy = entropy(hist_prob, base=2)
|
| 21 |
+
|
| 22 |
+
laplacian = cv2.Laplacian(gray_small, cv2.CV_64F)
|
| 23 |
+
noise_variance = laplacian.var()
|
| 24 |
+
|
| 25 |
+
score = 0.0
|
| 26 |
+
if img_entropy < 5.5:
|
| 27 |
+
score += 0.4
|
| 28 |
+
|
| 29 |
+
if noise_variance < 50 or noise_variance > 3000:
|
| 30 |
+
score += 0.6
|
| 31 |
+
|
| 32 |
+
return float(min(score, 1.0))
|
| 33 |
+
|
| 34 |
+
except Exception as e:
|
| 35 |
+
logger.error(f"Noise/Entropy Analysis Crash: {str(e)}")
|
| 36 |
+
return 0.0
|