Spaces:
Sleeping
Sleeping
ender
commited on
Commit
·
ed84fba
1
Parent(s):
8126add
HF Ready
Browse files- .gitattributes +8 -1
- Examples/Fake/Fake1.PNG +0 -0
- Examples/Fake/Fake2.PNG +0 -0
- Examples/Fake/Fake3.PNG +0 -0
- Examples/Fake1.mp4 +3 -0
- Examples/Real/Real1.PNG +3 -0
- Examples/Real/Real2.PNG +3 -0
- Examples/Real1.mp4 +3 -0
- README.md +3 -4
- Scripts/DeepFakeMask.py +149 -0
- Scripts/__init__.py +0 -0
- Scripts/ca_generator.py +32 -0
- Scripts/model.py +34 -0
- Scripts/preprocess.py +149 -0
- Weights/FFc23.tar +3 -0
- Weights/shape_predictor_81_face_landmarks.dat +3 -0
- Weights/weights.tar +3 -0
- app.py +189 -0
- requirements.txt +10 -0
.gitattributes
CHANGED
@@ -25,7 +25,6 @@
|
|
25 |
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
26 |
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
27 |
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
28 |
-
*.tar filter=lfs diff=lfs merge=lfs -text
|
29 |
*.tflite filter=lfs diff=lfs merge=lfs -text
|
30 |
*.tgz filter=lfs diff=lfs merge=lfs -text
|
31 |
*.wasm filter=lfs diff=lfs merge=lfs -text
|
@@ -33,3 +32,11 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
26 |
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
27 |
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
|
|
28 |
*.tflite filter=lfs diff=lfs merge=lfs -text
|
29 |
*.tgz filter=lfs diff=lfs merge=lfs -text
|
30 |
*.wasm filter=lfs diff=lfs merge=lfs -text
|
|
|
32 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
33 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
34 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
35 |
+
Weights/Weights.tar filter=lfs diff=lfs merge=lfs -text
|
36 |
+
Weights/FFc23.tar filter=lfs diff=lfs merge=lfs -text
|
37 |
+
Examples/Fake1.mp4 filter=lfs diff=lfs merge=lfs -text
|
38 |
+
Examples/Real1.mp4 filter=lfs diff=lfs merge=lfs -text
|
39 |
+
*.dat filter=lfs diff=lfs merge=lfs -text
|
40 |
+
Weights/weights.tar filter=lfs diff=lfs merge=lfs -text
|
41 |
+
Examples/Real/Real1.PNG filter=lfs diff=lfs merge=lfs -text
|
42 |
+
Examples/Real/Real2.PNG filter=lfs diff=lfs merge=lfs -text
|
Examples/Fake/Fake1.PNG
ADDED
|
Examples/Fake/Fake2.PNG
ADDED
|
Examples/Fake/Fake3.PNG
ADDED
|
Examples/Fake1.mp4
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:ac9f6fde4b2e56822230d969085867e45cecf53daba7f964f340d2f669121cf1
|
3 |
+
size 3319584
|
Examples/Real/Real1.PNG
ADDED
|
Git LFS Details
|
Examples/Real/Real2.PNG
ADDED
|
Git LFS Details
|
Examples/Real1.mp4
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:cd8f1fc5cb478949a1b0c8c790351d45b35112c2c8b1a9272a507bed955565ae
|
3 |
+
size 344095
|
README.md
CHANGED
@@ -1,12 +1,11 @@
|
|
1 |
---
|
2 |
title: DFDetection
|
3 |
-
emoji:
|
4 |
colorFrom: blue
|
5 |
colorTo: green
|
6 |
sdk: gradio
|
7 |
-
sdk_version: 4.
|
8 |
app_file: app.py
|
9 |
pinned: false
|
|
|
10 |
---
|
11 |
-
|
12 |
-
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
1 |
---
|
2 |
title: DFDetection
|
3 |
+
emoji: 📚
|
4 |
colorFrom: blue
|
5 |
colorTo: green
|
6 |
sdk: gradio
|
7 |
+
sdk_version: 4.29.0
|
8 |
app_file: app.py
|
9 |
pinned: false
|
10 |
+
license: mit
|
11 |
---
|
|
|
|
Scripts/DeepFakeMask.py
ADDED
@@ -0,0 +1,149 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import logging
|
2 |
+
import cv2
|
3 |
+
import numpy as np
|
4 |
+
|
5 |
+
logger = logging.getLogger(__name__) # pylint: disable=invalid-name
|
6 |
+
|
7 |
+
class Mask():
|
8 |
+
""" Parent class for masks
|
9 |
+
the output mask will be <mask_type>.mask
|
10 |
+
channels: 1, 3 or 4:
|
11 |
+
1 - Returns a single channel mask
|
12 |
+
3 - Returns a 3 channel mask
|
13 |
+
4 - Returns the original image with the mask in the alpha channel """
|
14 |
+
|
15 |
+
def __init__(self, landmarks, face, channels=4):
|
16 |
+
logger.info("Initializing %s: (face_shape: %s, channels: %s, landmarks: %s)",
|
17 |
+
self.__class__.__name__, face.shape, channels, landmarks)
|
18 |
+
self.landmarks = landmarks
|
19 |
+
self.face = face
|
20 |
+
self.channels = channels
|
21 |
+
|
22 |
+
mask = self.build_mask()
|
23 |
+
self.mask = self.merge_mask(mask)
|
24 |
+
logger.info("Initialized %s", self.__class__.__name__)
|
25 |
+
|
26 |
+
def build_mask(self):
|
27 |
+
""" Override to build the mask """
|
28 |
+
raise NotImplementedError
|
29 |
+
|
30 |
+
def merge_mask(self, mask):
|
31 |
+
""" Return the mask in requested shape """
|
32 |
+
logger.info("mask_shape: %s", mask.shape)
|
33 |
+
assert self.channels in (1, 3, 4), "Channels should be 1, 3 or 4"
|
34 |
+
assert mask.shape[2] == 1 and mask.ndim == 3, "Input mask be 3 dimensions with 1 channel"
|
35 |
+
|
36 |
+
if self.channels == 3:
|
37 |
+
retval = np.tile(mask, 3)
|
38 |
+
elif self.channels == 4:
|
39 |
+
retval = np.concatenate((self.face, mask), -1)
|
40 |
+
else:
|
41 |
+
retval = mask
|
42 |
+
|
43 |
+
logger.info("Final mask shape: %s", retval.shape)
|
44 |
+
return retval
|
45 |
+
|
46 |
+
|
47 |
+
class dfl_full(Mask): # pylint: disable=invalid-name
|
48 |
+
""" DFL facial mask """
|
49 |
+
def build_mask(self):
|
50 |
+
mask = np.zeros(self.face.shape[0:2] + (1, ), dtype=np.float32)
|
51 |
+
|
52 |
+
nose_ridge = (self.landmarks[27:31], self.landmarks[33:34])
|
53 |
+
jaw = (self.landmarks[0:17],
|
54 |
+
self.landmarks[48:68],
|
55 |
+
self.landmarks[0:1],
|
56 |
+
self.landmarks[8:9],
|
57 |
+
self.landmarks[16:17])
|
58 |
+
eyes = (self.landmarks[17:27],
|
59 |
+
self.landmarks[0:1],
|
60 |
+
self.landmarks[27:28],
|
61 |
+
self.landmarks[16:17],
|
62 |
+
self.landmarks[33:34])
|
63 |
+
parts = [jaw, nose_ridge, eyes]
|
64 |
+
|
65 |
+
for item in parts:
|
66 |
+
merged = np.concatenate(item)
|
67 |
+
cv2.fillConvexPoly(mask, cv2.convexHull(merged), 255.) # pylint: disable=no-member
|
68 |
+
return mask
|
69 |
+
|
70 |
+
|
71 |
+
class components(Mask): # pylint: disable=invalid-name
|
72 |
+
""" Component model mask """
|
73 |
+
def build_mask(self):
|
74 |
+
mask = np.zeros(self.face.shape[0:2] + (1, ), dtype=np.float32)
|
75 |
+
|
76 |
+
r_jaw = (self.landmarks[0:9], self.landmarks[17:18])
|
77 |
+
l_jaw = (self.landmarks[8:17], self.landmarks[26:27])
|
78 |
+
r_cheek = (self.landmarks[17:20], self.landmarks[8:9])
|
79 |
+
l_cheek = (self.landmarks[24:27], self.landmarks[8:9])
|
80 |
+
nose_ridge = (self.landmarks[19:25], self.landmarks[8:9],)
|
81 |
+
r_eye = (self.landmarks[17:22],
|
82 |
+
self.landmarks[27:28],
|
83 |
+
self.landmarks[31:36],
|
84 |
+
self.landmarks[8:9])
|
85 |
+
l_eye = (self.landmarks[22:27],
|
86 |
+
self.landmarks[27:28],
|
87 |
+
self.landmarks[31:36],
|
88 |
+
self.landmarks[8:9])
|
89 |
+
nose = (self.landmarks[27:31], self.landmarks[31:36])
|
90 |
+
parts = [r_jaw, l_jaw, r_cheek, l_cheek, nose_ridge, r_eye, l_eye, nose]
|
91 |
+
|
92 |
+
for item in parts:
|
93 |
+
merged = np.concatenate(item)
|
94 |
+
cv2.fillConvexPoly(mask, cv2.convexHull(merged), 255.) # pylint: disable=no-member
|
95 |
+
return mask
|
96 |
+
|
97 |
+
|
98 |
+
class extended(Mask): # pylint: disable=invalid-name
|
99 |
+
""" Extended mask
|
100 |
+
Based on components mask. Attempts to extend the eyebrow points up the forehead
|
101 |
+
"""
|
102 |
+
def build_mask(self):
|
103 |
+
mask = np.zeros(self.face.shape[0:2] + (1, ), dtype=np.float32)
|
104 |
+
|
105 |
+
landmarks = self.landmarks.copy()
|
106 |
+
# mid points between the side of face and eye point
|
107 |
+
ml_pnt = (landmarks[36] + landmarks[0]) // 2
|
108 |
+
mr_pnt = (landmarks[16] + landmarks[45]) // 2
|
109 |
+
|
110 |
+
# mid points between the mid points and eye
|
111 |
+
ql_pnt = (landmarks[36] + ml_pnt) // 2
|
112 |
+
qr_pnt = (landmarks[45] + mr_pnt) // 2
|
113 |
+
|
114 |
+
# Top of the eye arrays
|
115 |
+
bot_l = np.array((ql_pnt, landmarks[36], landmarks[37], landmarks[38], landmarks[39]))
|
116 |
+
bot_r = np.array((landmarks[42], landmarks[43], landmarks[44], landmarks[45], qr_pnt))
|
117 |
+
|
118 |
+
# Eyebrow arrays
|
119 |
+
top_l = landmarks[17:22]
|
120 |
+
top_r = landmarks[22:27]
|
121 |
+
|
122 |
+
# Adjust eyebrow arrays
|
123 |
+
landmarks[17:22] = top_l + ((top_l - bot_l) // 2)
|
124 |
+
landmarks[22:27] = top_r + ((top_r - bot_r) // 2)
|
125 |
+
|
126 |
+
r_jaw = (landmarks[0:9], landmarks[17:18])
|
127 |
+
l_jaw = (landmarks[8:17], landmarks[26:27])
|
128 |
+
r_cheek = (landmarks[17:20], landmarks[8:9])
|
129 |
+
l_cheek = (landmarks[24:27], landmarks[8:9])
|
130 |
+
nose_ridge = (landmarks[19:25], landmarks[8:9],)
|
131 |
+
r_eye = (landmarks[17:22], landmarks[27:28], landmarks[31:36], landmarks[8:9])
|
132 |
+
l_eye = (landmarks[22:27], landmarks[27:28], landmarks[31:36], landmarks[8:9])
|
133 |
+
nose = (landmarks[27:31], landmarks[31:36])
|
134 |
+
parts = [r_jaw, l_jaw, r_cheek, l_cheek, nose_ridge, r_eye, l_eye, nose]
|
135 |
+
|
136 |
+
for item in parts:
|
137 |
+
merged = np.concatenate(item)
|
138 |
+
cv2.fillConvexPoly(mask, cv2.convexHull(merged), 255.) # pylint: disable=no-member
|
139 |
+
return mask
|
140 |
+
|
141 |
+
|
142 |
+
class facehull(Mask): # pylint: disable=invalid-name
|
143 |
+
""" Basic face hull mask """
|
144 |
+
def build_mask(self):
|
145 |
+
mask = np.zeros(self.face.shape[0:2] + (1, ), dtype=np.float32)
|
146 |
+
hull = cv2.convexHull( # pylint: disable=no-member
|
147 |
+
np.array(self.landmarks).reshape((-1, 2)))
|
148 |
+
cv2.fillConvexPoly(mask, hull, 255.0, lineType=cv2.LINE_AA) # pylint: disable=no-member
|
149 |
+
return mask
|
Scripts/__init__.py
ADDED
File without changes
|
Scripts/ca_generator.py
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import albumentations as alb
|
2 |
+
from albumentations.pytorch import ToTensorV2
|
3 |
+
import cv2
|
4 |
+
|
5 |
+
def get_augs(name):
|
6 |
+
IMG_SIZE = 380
|
7 |
+
if name == "REAlbu":
|
8 |
+
return alb.Compose([
|
9 |
+
alb.HorizontalFlip(),
|
10 |
+
alb.CoarseDropout(max_holes = 1, min_height=int(IMG_SIZE*0.02), max_height=int(IMG_SIZE*0.2), min_width=int(IMG_SIZE*0.02), max_width=int(IMG_SIZE*0.2), p=1),
|
11 |
+
])
|
12 |
+
elif name == "RandCropAlbu":
|
13 |
+
return alb.Compose([
|
14 |
+
alb.HorizontalFlip(),
|
15 |
+
alb.RandomResizedCrop(height = IMG_SIZE, width = IMG_SIZE, scale=(1/1.3, 1.0), ratio=(0.9,1.1)),
|
16 |
+
])
|
17 |
+
elif name == "DFDCAlbu":
|
18 |
+
return alb.Compose([
|
19 |
+
alb.ImageCompression(quality_lower=60, quality_upper=100, p=0.5),
|
20 |
+
alb.GaussNoise(p=0.1),
|
21 |
+
alb.GaussianBlur(blur_limit=3, p=0.05),
|
22 |
+
alb.HorizontalFlip(),
|
23 |
+
alb.OneOf([
|
24 |
+
alb.LongestMaxSize(max_size=IMG_SIZE, interpolation=cv2.INTER_CUBIC),
|
25 |
+
alb.LongestMaxSize(max_size=IMG_SIZE, interpolation=cv2.INTER_AREA),
|
26 |
+
alb.LongestMaxSize(max_size=IMG_SIZE, interpolation=cv2.INTER_LINEAR)
|
27 |
+
], p=1.0),
|
28 |
+
alb.PadIfNeeded(min_height=IMG_SIZE, min_width=IMG_SIZE, border_mode=cv2.BORDER_CONSTANT),
|
29 |
+
alb.OneOf([alb.RandomBrightnessContrast(), alb.FancyPCA(), alb.HueSaturationValue()], p=0.7),
|
30 |
+
alb.ToGray(p=0.2),
|
31 |
+
alb.ShiftScaleRotate(shift_limit=0.1, scale_limit=0.2, rotate_limit=10, border_mode=cv2.BORDER_CONSTANT, p=0.5),
|
32 |
+
])
|
Scripts/model.py
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import torch
|
2 |
+
from torch import nn
|
3 |
+
from efficientnet_pytorch import EfficientNet
|
4 |
+
from pytorch_grad_cam import GradCAMElementWise
|
5 |
+
from pytorch_grad_cam.utils.model_targets import ClassifierOutputTarget
|
6 |
+
|
7 |
+
|
8 |
+
class Detector(nn.Module):
|
9 |
+
def __init__(self):
|
10 |
+
super(Detector, self).__init__()
|
11 |
+
self.net=EfficientNet.from_pretrained("efficientnet-b4",advprop=True,num_classes=2)
|
12 |
+
|
13 |
+
def forward(self,x):
|
14 |
+
x=self.net(x)
|
15 |
+
return x
|
16 |
+
|
17 |
+
|
18 |
+
def create_model(path="Weights/94_0.9485_val.tar", device=torch.device('cpu')):
|
19 |
+
model=Detector()
|
20 |
+
model=model.to(device)
|
21 |
+
if device == torch.device('cpu'):
|
22 |
+
cnn_sd=torch.load(path, map_location=torch.device('cpu') )["model"]
|
23 |
+
else:
|
24 |
+
cnn_sd=torch.load(path)["model"]
|
25 |
+
model.load_state_dict(cnn_sd)
|
26 |
+
model.eval()
|
27 |
+
return model
|
28 |
+
|
29 |
+
def create_cam(model):
|
30 |
+
target_layers = [model.net._blocks[-1]]
|
31 |
+
targets = [ClassifierOutputTarget(1)]
|
32 |
+
cam_algorithm = GradCAMElementWise
|
33 |
+
cam = cam_algorithm(model=model,target_layers=target_layers,use_cuda=False)
|
34 |
+
return cam
|
Scripts/preprocess.py
ADDED
@@ -0,0 +1,149 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import numpy as np
|
2 |
+
import cv2
|
3 |
+
from tqdm import tqdm
|
4 |
+
|
5 |
+
def extract_frames(filename,num_frames,model,image_size=(380,380)):
|
6 |
+
cap_org = cv2.VideoCapture(filename)
|
7 |
+
|
8 |
+
if not cap_org.isOpened():
|
9 |
+
print(f'Cannot open: {filename}')
|
10 |
+
# sys.exit()
|
11 |
+
return []
|
12 |
+
|
13 |
+
croppedfaces=[]
|
14 |
+
idx_list=[]
|
15 |
+
frame_count_org = int(cap_org.get(cv2.CAP_PROP_FRAME_COUNT))
|
16 |
+
|
17 |
+
frame_idxs = np.linspace(0, frame_count_org - 1, num_frames, endpoint=True, dtype=int)
|
18 |
+
for cnt_frame in range(frame_count_org):
|
19 |
+
ret_org, frame_org = cap_org.read()
|
20 |
+
height,width=frame_org.shape[:-1]
|
21 |
+
if not ret_org:
|
22 |
+
tqdm.write('Frame read {} Error! : {}'.format(cnt_frame,os.path.basename(filename)))
|
23 |
+
break
|
24 |
+
|
25 |
+
if cnt_frame not in frame_idxs:
|
26 |
+
continue
|
27 |
+
|
28 |
+
frame = cv2.cvtColor(frame_org, cv2.COLOR_BGR2RGB)
|
29 |
+
|
30 |
+
faces = model.predict_jsons(frame)
|
31 |
+
try:
|
32 |
+
if len(faces)==0:
|
33 |
+
tqdm.write('No faces in {}:{}'.format(cnt_frame,os.path.basename(filename)))
|
34 |
+
continue
|
35 |
+
|
36 |
+
size_list=[]
|
37 |
+
croppedfaces_temp=[]
|
38 |
+
idx_list_temp=[]
|
39 |
+
|
40 |
+
for face_idx in range(len(faces)):
|
41 |
+
x0,y0,x1,y1=faces[face_idx]['bbox']
|
42 |
+
bbox=np.array([[x0,y0],[x1,y1]])
|
43 |
+
croppedfaces_temp.append(cv2.resize(crop_face(frame,None,bbox,False,crop_by_bbox=True,only_img=True,phase='test'),dsize=image_size).transpose((2,0,1)))
|
44 |
+
idx_list_temp.append(cnt_frame)
|
45 |
+
size_list.append((x1-x0)*(y1-y0))
|
46 |
+
|
47 |
+
max_size=max(size_list)
|
48 |
+
croppedfaces_temp=[f for face_idx,f in enumerate(croppedfaces_temp) if size_list[face_idx]>=max_size/2]
|
49 |
+
idx_list_temp=[f for face_idx,f in enumerate(idx_list_temp) if size_list[face_idx]>=max_size/2]
|
50 |
+
croppedfaces+=croppedfaces_temp
|
51 |
+
idx_list+=idx_list_temp
|
52 |
+
except Exception as e:
|
53 |
+
print(f'error in {cnt_frame}:{filename}')
|
54 |
+
print(e)
|
55 |
+
continue
|
56 |
+
cap_org.release()
|
57 |
+
|
58 |
+
|
59 |
+
|
60 |
+
return croppedfaces,idx_list
|
61 |
+
|
62 |
+
def extract_face(frame,model,image_size=(380,380)):
|
63 |
+
|
64 |
+
|
65 |
+
faces = model.predict_jsons(frame)
|
66 |
+
|
67 |
+
if len(faces[0]['bbox'])==0:
|
68 |
+
return []
|
69 |
+
croppedfaces=[]
|
70 |
+
for face_idx in range(len(faces)):
|
71 |
+
x0,y0,x1,y1=faces[face_idx]['bbox']
|
72 |
+
bbox=np.array([[x0,y0],[x1,y1]])
|
73 |
+
croppedfaces.append(cv2.resize(crop_face(frame,None,bbox,False,crop_by_bbox=True,only_img=True,phase='test'),dsize=image_size).transpose((2,0,1)))
|
74 |
+
|
75 |
+
return croppedfaces
|
76 |
+
|
77 |
+
|
78 |
+
def crop_face(img,landmark=None,bbox=None,margin=False,crop_by_bbox=True,abs_coord=False,only_img=False,phase='train'):
|
79 |
+
assert phase in ['train','val','test']
|
80 |
+
|
81 |
+
#crop face------------------------------------------
|
82 |
+
H,W=len(img),len(img[0])
|
83 |
+
|
84 |
+
assert landmark is not None or bbox is not None
|
85 |
+
|
86 |
+
H,W=len(img),len(img[0])
|
87 |
+
|
88 |
+
if crop_by_bbox:
|
89 |
+
x0,y0=bbox[0]
|
90 |
+
x1,y1=bbox[1]
|
91 |
+
w=x1-x0
|
92 |
+
h=y1-y0
|
93 |
+
w0_margin=w/4#0#np.random.rand()*(w/8)
|
94 |
+
w1_margin=w/4
|
95 |
+
h0_margin=h/4#0#np.random.rand()*(h/5)
|
96 |
+
h1_margin=h/4
|
97 |
+
else:
|
98 |
+
x0,y0=landmark[:68,0].min(),landmark[:68,1].min()
|
99 |
+
x1,y1=landmark[:68,0].max(),landmark[:68,1].max()
|
100 |
+
w=x1-x0
|
101 |
+
h=y1-y0
|
102 |
+
w0_margin=w/8#0#np.random.rand()*(w/8)
|
103 |
+
w1_margin=w/8
|
104 |
+
h0_margin=h/2#0#np.random.rand()*(h/5)
|
105 |
+
h1_margin=h/5
|
106 |
+
|
107 |
+
|
108 |
+
|
109 |
+
if margin:
|
110 |
+
w0_margin*=4
|
111 |
+
w1_margin*=4
|
112 |
+
h0_margin*=2
|
113 |
+
h1_margin*=2
|
114 |
+
elif phase=='train':
|
115 |
+
w0_margin*=(np.random.rand()*0.6+0.2)#np.random.rand()
|
116 |
+
w1_margin*=(np.random.rand()*0.6+0.2)#np.random.rand()
|
117 |
+
h0_margin*=(np.random.rand()*0.6+0.2)#np.random.rand()
|
118 |
+
h1_margin*=(np.random.rand()*0.6+0.2)#np.random.rand()
|
119 |
+
else:
|
120 |
+
w0_margin*=0.5
|
121 |
+
w1_margin*=0.5
|
122 |
+
h0_margin*=0.5
|
123 |
+
h1_margin*=0.5
|
124 |
+
|
125 |
+
y0_new=max(0,int(y0-h0_margin))
|
126 |
+
y1_new=min(H,int(y1+h1_margin)+1)
|
127 |
+
x0_new=max(0,int(x0-w0_margin))
|
128 |
+
x1_new=min(W,int(x1+w1_margin)+1)
|
129 |
+
|
130 |
+
img_cropped=img[y0_new:y1_new,x0_new:x1_new]
|
131 |
+
if landmark is not None:
|
132 |
+
landmark_cropped=np.zeros_like(landmark)
|
133 |
+
for i,(p,q) in enumerate(landmark):
|
134 |
+
landmark_cropped[i]=[p-x0_new,q-y0_new]
|
135 |
+
else:
|
136 |
+
landmark_cropped=None
|
137 |
+
if bbox is not None:
|
138 |
+
bbox_cropped=np.zeros_like(bbox)
|
139 |
+
for i,(p,q) in enumerate(bbox):
|
140 |
+
bbox_cropped[i]=[p-x0_new,q-y0_new]
|
141 |
+
else:
|
142 |
+
bbox_cropped=None
|
143 |
+
|
144 |
+
if only_img:
|
145 |
+
return img_cropped
|
146 |
+
if abs_coord:
|
147 |
+
return img_cropped,landmark_cropped,bbox_cropped,(y0-y0_new,x0-x0_new,y1_new-y1,x1_new-x1),y0_new,y1_new,x0_new,x1_new
|
148 |
+
else:
|
149 |
+
return img_cropped,landmark_cropped,bbox_cropped,(y0-y0_new,x0-x0_new,y1_new-y1,x1_new-x1)
|
Weights/FFc23.tar
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:2b05c2ba36ccb9e9f4f9e1aae9acd443ae2e6400ce725f56104fdb175d3c3267
|
3 |
+
size 141290933
|
Weights/shape_predictor_81_face_landmarks.dat
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:8cae4375589dd915d9a0a881101bed1bbb4e9887e35e63b024388f1ca25ff869
|
3 |
+
size 19743860
|
Weights/weights.tar
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:5c100e98694faa776fcf71990377f9c4eca46568417339c06843cdcf2a78d35d
|
3 |
+
size 141291061
|
app.py
ADDED
@@ -0,0 +1,189 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import warnings
|
3 |
+
import cv2
|
4 |
+
import dlib
|
5 |
+
from pytorch_grad_cam.utils.image import show_cam_on_image
|
6 |
+
from pytorch_grad_cam.utils.model_targets import ClassifierOutputTarget
|
7 |
+
import numpy as np
|
8 |
+
import torch
|
9 |
+
from retinaface.pre_trained_models import get_model
|
10 |
+
|
11 |
+
from Scripts.model import create_cam, create_model
|
12 |
+
from Scripts.preprocess import crop_face, extract_face, extract_frames
|
13 |
+
from Scripts.ca_generator import get_augs
|
14 |
+
|
15 |
+
warnings.filterwarnings('ignore')
|
16 |
+
|
17 |
+
|
18 |
+
|
19 |
+
device = torch.device('cpu')
|
20 |
+
|
21 |
+
sbcl = create_model("Weights/weights.tar")
|
22 |
+
|
23 |
+
|
24 |
+
face_detector = get_model("resnet50_2020-07-20", max_size=1024, device=device)
|
25 |
+
face_detector.eval()
|
26 |
+
|
27 |
+
cam_sbcl = create_cam(sbcl)
|
28 |
+
targets = [ClassifierOutputTarget(1)]
|
29 |
+
|
30 |
+
# Examples
|
31 |
+
examples = ["Examples/Fake/Fake1.PNG", "Examples/Real/Real1.PNG", "Examples/Real/Real2.PNG", "Examples/Fake/Fake3.PNG",
|
32 |
+
"Examples/Fake/Fake2.PNG", ]
|
33 |
+
examples_videos = ['Examples/Fake1.mp4', 'Examples/Real1.mp4']
|
34 |
+
|
35 |
+
# dlib Models
|
36 |
+
dlib_face_detector = dlib.get_frontal_face_detector()
|
37 |
+
dlib_face_predictor = dlib.shape_predictor(
|
38 |
+
'Weights/shape_predictor_81_face_landmarks.dat')
|
39 |
+
|
40 |
+
|
41 |
+
def predict_image(inp):
|
42 |
+
|
43 |
+
face_list = extract_face(inp, face_detector)
|
44 |
+
|
45 |
+
if len(face_list) == 0:
|
46 |
+
return {'No face detected!': 1}, None
|
47 |
+
|
48 |
+
with torch.no_grad():
|
49 |
+
img = torch.tensor(face_list).to(device).float()/255
|
50 |
+
pred = sbcl(img).softmax(1)[:, 1].cpu().data.numpy().tolist()[0]
|
51 |
+
confidences = {'Real': 1-pred, 'Fake': pred}
|
52 |
+
|
53 |
+
grayscale_cam = cam_sbcl(input_tensor=img, targets=targets, aug_smooth=True)
|
54 |
+
grayscale_cam = grayscale_cam[0, :]
|
55 |
+
cam_image = show_cam_on_image(face_list[0].transpose(1, 2, 0)/255, grayscale_cam, use_rgb=True)
|
56 |
+
|
57 |
+
return confidences, cam_image
|
58 |
+
|
59 |
+
|
60 |
+
def predict_video(inp):
|
61 |
+
|
62 |
+
face_list, idx_list = extract_frames(inp, 10, face_detector)
|
63 |
+
|
64 |
+
with torch.no_grad():
|
65 |
+
img = torch.tensor(face_list).to(device).float()/255
|
66 |
+
pred = sbcl(img).softmax(1)[:, 1]
|
67 |
+
|
68 |
+
pred_list = []
|
69 |
+
idx_img = -1
|
70 |
+
for i in range(len(pred)):
|
71 |
+
if idx_list[i] != idx_img:
|
72 |
+
pred_list.append([])
|
73 |
+
idx_img = idx_list[i]
|
74 |
+
pred_list[-1].append(pred[i].item())
|
75 |
+
pred_res = np.zeros(len(pred_list))
|
76 |
+
for i in range(len(pred_res)):
|
77 |
+
pred_res[i] = max(pred_list[i])
|
78 |
+
pred = pred_res.mean()
|
79 |
+
|
80 |
+
most_fake = np.argmax(pred_res)
|
81 |
+
grayscale_cam = cam_sbcl(input_tensor=img[most_fake].unsqueeze(0), targets=targets, aug_smooth=True)
|
82 |
+
grayscale_cam = grayscale_cam[0, :]
|
83 |
+
cam_image = show_cam_on_image(face_list[most_fake].transpose(1, 2, 0)/255, grayscale_cam, use_rgb=True)
|
84 |
+
|
85 |
+
return {'Real': 1-pred, 'Fake': pred}, cam_image
|
86 |
+
|
87 |
+
|
88 |
+
|
89 |
+
with gr.Blocks(title="Deepfake Detection CL", theme='upsatwal/mlsc_tiet', css="""
|
90 |
+
@import url('https://fonts.googleapis.com/css?family=Source+Code+Pro:200');
|
91 |
+
#custom_header {
|
92 |
+
min-height: 3rem;
|
93 |
+
background-image: url('https://static.pexels.com/photos/414171/pexels-photo-414171.jpeg');
|
94 |
+
background-size: cover;
|
95 |
+
background-position: top;
|
96 |
+
color: white;
|
97 |
+
text-align: center;
|
98 |
+
padding: 0.5rem;
|
99 |
+
font-family: 'Source Code Pro', monospace;
|
100 |
+
text-transform: uppercase;
|
101 |
+
}
|
102 |
+
#custom_header:hover {
|
103 |
+
-webkit-animation: slidein 10s;
|
104 |
+
animation: slidein 10s;
|
105 |
+
-webkit-animation-fill-mode: forwards;
|
106 |
+
animation-fill-mode: forwards;
|
107 |
+
-webkit-animation-iteration-count: infinite;
|
108 |
+
animation-iteration-count: infinite;
|
109 |
+
-webkit-animation-direction: alternate;
|
110 |
+
animation-direction: alternate;
|
111 |
+
}
|
112 |
+
@-webkit-keyframes slidein {
|
113 |
+
from {
|
114 |
+
background-position: top;
|
115 |
+
background-size: 3000px;
|
116 |
+
}
|
117 |
+
to {
|
118 |
+
background-position: -100px 0px;
|
119 |
+
background-size: 2750px;
|
120 |
+
}
|
121 |
+
}
|
122 |
+
@keyframes slidein {
|
123 |
+
from {
|
124 |
+
background-position: top;
|
125 |
+
background-size: 3000px;
|
126 |
+
}
|
127 |
+
to {
|
128 |
+
background-position: -100px 0px;
|
129 |
+
background-size: 2750px;
|
130 |
+
}
|
131 |
+
}
|
132 |
+
#custom_title {
|
133 |
+
min-height: 3rem;
|
134 |
+
text-align: center;
|
135 |
+
}
|
136 |
+
.full-width {
|
137 |
+
width: 100%;
|
138 |
+
}
|
139 |
+
.full-width:hover {
|
140 |
+
background: rgba(75, 75, 250, 0.3);
|
141 |
+
color: white;
|
142 |
+
}
|
143 |
+
""") as demo:
|
144 |
+
|
145 |
+
with gr.Tab("Image"):
|
146 |
+
with gr.Row():
|
147 |
+
with gr.Column():
|
148 |
+
with gr.Group():
|
149 |
+
gr.Markdown("## Deepfake Detection", elem_id="custom_header")
|
150 |
+
input_image = gr.Image(label="Input Image", height=240)
|
151 |
+
btn = gr.Button(value="Submit", variant="primary", elem_classes="full-width")
|
152 |
+
with gr.Column():
|
153 |
+
with gr.Group():
|
154 |
+
gr.Markdown("## Result", elem_id="custom_header")
|
155 |
+
output_image = gr.Image(label="GradCAM Image", height=240)
|
156 |
+
label_probs = gr.Label()
|
157 |
+
gr.Examples(
|
158 |
+
examples=examples,
|
159 |
+
inputs=input_image,
|
160 |
+
outputs=output_image,
|
161 |
+
fn=predict_image,
|
162 |
+
cache_examples=False,
|
163 |
+
)
|
164 |
+
btn.click(predict_image, inputs=input_image, outputs=[label_probs, output_image], api_name="/predict_image")
|
165 |
+
|
166 |
+
with gr.Tab("Video"):
|
167 |
+
with gr.Row():
|
168 |
+
with gr.Column():
|
169 |
+
with gr.Group():
|
170 |
+
gr.Markdown("## Deepfake Detection", elem_id="custom_header")
|
171 |
+
input_video = gr.Video(label="Input Video", height=240)
|
172 |
+
btn_video = gr.Button(value="Submit", variant="primary", elem_classes="full-width")
|
173 |
+
|
174 |
+
with gr.Column():
|
175 |
+
with gr.Group():
|
176 |
+
gr.Markdown("## Result", elem_id="custom_header")
|
177 |
+
output_image_video = gr.Image(label="GradCAM", height=240)
|
178 |
+
label_probs_video = gr.Label()
|
179 |
+
gr.Examples(
|
180 |
+
examples=examples_videos,
|
181 |
+
inputs=input_video,
|
182 |
+
outputs=output_image_video,
|
183 |
+
fn=predict_video,
|
184 |
+
cache_examples=False,
|
185 |
+
)
|
186 |
+
btn_video.click(predict_video, inputs=input_video, outputs=[label_probs_video, output_image_video], api_name="/predict_video")
|
187 |
+
|
188 |
+
if __name__ == "__main__":
|
189 |
+
demo.launch()
|
requirements.txt
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
dlib
|
2 |
+
retinaface_pytorch
|
3 |
+
imutils
|
4 |
+
numpy
|
5 |
+
grad-cam==1.4.8
|
6 |
+
gradio
|
7 |
+
opencv-python
|
8 |
+
efficientnet_pytorch
|
9 |
+
httpx
|
10 |
+
ffmpeg
|