ismot pyesonekyaw commited on
Commit
2f99bb4
·
0 Parent(s):

Duplicate from pyesonekyaw/faceforgerydetection

Browse files

Co-authored-by: Pye Sone Kyaw <[email protected]>

.gitattributes ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar 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
31
+ *.xz 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/94_0.9485_val.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
Examples/Fake/fake1.png ADDED
Examples/Fake/fake2.png ADDED
Examples/Fake/fake3.png ADDED
Examples/Fake/fake4.png ADDED
Examples/Fake/fake5.png ADDED
Examples/Fake/fake6.png ADDED
Examples/Fake/fake7.png ADDED
Examples/Fake1.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:57f42fe00febf36d3c5afb8a6caa7a7969b6fb6ec68799c5980cd994d611e28d
3
+ size 5237358
Examples/Real/real1.png ADDED
Examples/Real/real2.png ADDED
Examples/Real/real3.png ADDED
Examples/Real/real4.png ADDED
Examples/Real/real5.png ADDED
Examples/Real/real6.png ADDED
Examples/Real1.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3aa4657ca2627ed201577c61071d01729d9731c7fdcd8963eee816bb2a3420b4
3
+ size 2090471
Examples/ReferenceVideos.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ Trying To Create an AI Tom Scott (on a $100 budget) - https://www.youtube.com/watch?v=xCn03u_Jvuo
2
+ It’s Getting Harder to Spot a Deep Fake Video - https://www.youtube.com/watch?v=gLoI9hAX9dw
3
+ Robot Linus Reviews a Keyboard - Deepfake - Cleave Truly Ergonomic - https://www.youtube.com/watch?v=34AmKPJNfCg
4
+ Don't drop your internet! - https://www.youtube.com/watch?v=csnnfJi1j2g
5
+ Bail Reform: Last Week Tonight with John Oliver (HBO) - https://www.youtube.com/watch?v=xQLqIWbc9VM
Examples/multiple.png ADDED
README.md ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Generalizable Face Forgery Detection with Self-Blended Consistency Learning
3
+ emoji: 🐰
4
+ colorFrom: gray
5
+ colorTo: pink
6
+ sdk: gradio
7
+ sdk_version: 3.9
8
+ app_file: app.py
9
+ pinned: false
10
+ license: mit
11
+ duplicated_from: pyesonekyaw/faceforgerydetection
12
+ ---
13
+
14
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
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)
Scripts/sbi_generator.py ADDED
@@ -0,0 +1,217 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from imutils import face_utils
2
+ import numpy as np
3
+ import random
4
+ import albumentations as alb
5
+ from .DeepFakeMask import dfl_full, extended, components, facehull
6
+ import cv2
7
+
8
+ def IoUfrom2bboxes(boxA, boxB):
9
+ xA = max(boxA[0], boxB[0])
10
+ yA = max(boxA[1], boxB[1])
11
+ xB = min(boxA[2], boxB[2])
12
+ yB = min(boxA[3], boxB[3])
13
+ interArea = max(0, xB - xA + 1) * max(0, yB - yA + 1)
14
+ boxAArea = (boxA[2] - boxA[0] + 1) * (boxA[3] - boxA[1] + 1)
15
+ boxBArea = (boxB[2] - boxB[0] + 1) * (boxB[3] - boxB[1] + 1)
16
+ iou = interArea / float(boxAArea + boxBArea - interArea)
17
+ return iou
18
+
19
+ def reorder_landmark(landmark):
20
+ landmark_add = np.zeros((13, 2))
21
+ for idx, idx_l in enumerate([77, 75, 76, 68, 69, 70, 71, 80, 72, 73, 79, 74, 78]):
22
+ landmark_add[idx] = landmark[idx_l]
23
+ landmark[68:] = landmark_add
24
+ return landmark
25
+
26
+ def get_dlib_landmarks(inp, dlib_face_detector, dlib_face_predictor):
27
+ faces = dlib_face_detector(inp, 1)
28
+ if len(faces)==0:
29
+ raise Exception("No faces detected")
30
+ landmarks=[]
31
+ size_list=[]
32
+ for face_idx in range(len(faces)):
33
+ landmark = dlib_face_predictor(inp, faces[face_idx])
34
+ landmark = face_utils.shape_to_np(landmark)
35
+ x0,y0=landmark[:,0].min(),landmark[:,1].min()
36
+ x1,y1=landmark[:,0].max(),landmark[:,1].max()
37
+ face_s=(x1-x0)*(y1-y0)
38
+ size_list.append(face_s)
39
+ landmarks.append(landmark)
40
+ landmarks=np.concatenate(landmarks).reshape((len(size_list),)+landmark.shape)
41
+ landmarks=landmarks[np.argsort(np.array(size_list))[::-1]]
42
+ return landmarks
43
+
44
+ def get_retina_bbox(inp,face_detector):
45
+ faces = face_detector.predict_jsons(inp)
46
+ landmarks=[]
47
+ size_list=[]
48
+ for face_idx in range(len(faces)):
49
+
50
+ x0,y0,x1,y1=faces[face_idx]['bbox']
51
+ landmark=np.array([[x0,y0],[x1,y1]]+faces[face_idx]['landmarks'])
52
+ face_s=(x1-x0)*(y1-y0)
53
+ size_list.append(face_s)
54
+ landmarks.append(landmark)
55
+ landmarks=np.concatenate(landmarks).reshape((len(size_list),)+landmark.shape)
56
+ landmarks=landmarks[np.argsort(np.array(size_list))[::-1]]
57
+
58
+ return landmarks
59
+
60
+ def random_get_hull(landmark,img, face_region):
61
+ face_region = int(face_region)
62
+ if face_region == 1:
63
+ mask = dfl_full(landmarks=landmark.astype('int32'),face=img, channels=3).mask
64
+ elif face_region == 2:
65
+ mask = extended(landmarks=landmark.astype('int32'),face=img, channels=3).mask
66
+ elif face_region == 3:
67
+ mask = components(landmarks=landmark.astype('int32'),face=img, channels=3).mask
68
+ else:
69
+ mask = facehull(landmarks=landmark.astype('int32'),face=img, channels=3).mask
70
+ return mask/255
71
+
72
+ class RandomDownScale(alb.core.transforms_interface.ImageOnlyTransform):
73
+ def apply(self,img,**params):
74
+ return self.randomdownscale(img)
75
+
76
+ def randomdownscale(self,img):
77
+ keep_ratio=True
78
+ keep_input_shape=True
79
+ H,W,C=img.shape
80
+ ratio_list=[2,4]
81
+ r=ratio_list[np.random.randint(len(ratio_list))]
82
+ img_ds=cv2.resize(img,(int(W/r),int(H/r)),interpolation=cv2.INTER_NEAREST)
83
+ if keep_input_shape:
84
+ img_ds=cv2.resize(img_ds,(W,H),interpolation=cv2.INTER_LINEAR)
85
+
86
+ return img_ds
87
+
88
+ def get_source_transforms():
89
+ return alb.Compose([
90
+ alb.Compose([
91
+ alb.RGBShift((-20, 20), (-20, 20), (-20, 20), p=0.3),
92
+ alb.HueSaturationValue(
93
+ hue_shift_limit=(-0.3, 0.3), sat_shift_limit=(-0.3, 0.3), val_shift_limit=(-0.3, 0.3), p=1),
94
+ alb.RandomBrightnessContrast(
95
+ brightness_limit=(-0.1, 0.1), contrast_limit=(-0.1, 0.1), p=1),
96
+ ], p=1),
97
+
98
+ alb.OneOf([
99
+ RandomDownScale(p=1),
100
+ alb.Sharpen(alpha=(0.2, 0.5), lightness=(0.5, 1.0), p=1),
101
+ ], p=1),
102
+
103
+ ], p=1.)
104
+
105
+ def randaffine(img, mask):
106
+ f = alb.Affine(
107
+ translate_percent={'x': (-0.03, 0.03), 'y': (-0.015, 0.015)},
108
+ scale=[0.95, 1/0.95],
109
+ fit_output=False,
110
+ p=1)
111
+
112
+ g = alb.ElasticTransform(
113
+ alpha=50,
114
+ sigma=7,
115
+ alpha_affine=0,
116
+ p=1,
117
+ )
118
+
119
+ transformed = f(image=img, mask=mask)
120
+ img = transformed['image']
121
+
122
+ mask = transformed['mask']
123
+ transformed = g(image=img, mask=mask)
124
+ mask = transformed['mask']
125
+ return img, mask
126
+
127
+ def get_blend_mask(mask):
128
+ H,W=mask.shape
129
+ size_h=np.random.randint(192,257)
130
+ size_w=np.random.randint(192,257)
131
+ mask=cv2.resize(mask,(size_w,size_h))
132
+ kernel_1=random.randrange(5,26,2)
133
+ kernel_1=(kernel_1,kernel_1)
134
+ kernel_2=random.randrange(5,26,2)
135
+ kernel_2=(kernel_2,kernel_2)
136
+
137
+ mask_blured = cv2.GaussianBlur(mask, kernel_1, 0)
138
+ mask_blured = mask_blured/(mask_blured.max())
139
+ mask_blured[mask_blured<1]=0
140
+
141
+ mask_blured = cv2.GaussianBlur(mask_blured, kernel_2, np.random.randint(5,46))
142
+ mask_blured = mask_blured/(mask_blured.max())
143
+ mask_blured = cv2.resize(mask_blured,(W,H))
144
+ return mask_blured.reshape((mask_blured.shape+(1,)))
145
+
146
+
147
+ def dynamic_blend(source,target,mask,blending_type, mixup_ratio=[0.25,0.5,0.75,1,1,1]):
148
+ """Performs dynamic blending of source and target, using the mask as the blending region
149
+
150
+ Args:
151
+ source: source image
152
+ target: target image
153
+ mask: mask image
154
+
155
+ Returns:
156
+ img_blended: blended image
157
+ mask_blurred: augmented mask used for blending
158
+ """
159
+
160
+ mask_blured = get_blend_mask(mask)
161
+ mask_blured_copy = mask_blured.copy()
162
+
163
+ if blending_type == "Poisson":
164
+ # Poisson blending
165
+ b_mask = (mask_blured_copy * 255).astype(np.uint8)
166
+ l, t, w, h = cv2.boundingRect(b_mask)
167
+ center = (int(l + w / 2), int(t + h / 2))
168
+ img_blended = cv2.seamlessClone(source, target, b_mask, center, cv2.NORMAL_CLONE)
169
+ else:
170
+ # Mix up blending
171
+ blend_list=mixup_ratio
172
+ blend_ratio = blend_list[np.random.randint(len(blend_list))]
173
+
174
+ mask_blured_copy = mask_blured.copy()
175
+ mask_blured_copy*=blend_ratio
176
+
177
+ img_blended=(mask_blured_copy * source + (1 - mask_blured_copy) * target)
178
+
179
+ return img_blended,mask_blured
180
+
181
+ def get_transforms():
182
+ return alb.Compose([
183
+
184
+ alb.RGBShift((-20, 20), (-20, 20), (-20, 20), p=0.3),
185
+ alb.HueSaturationValue(
186
+ hue_shift_limit=(-0.3, 0.3), sat_shift_limit=(-0.3, 0.3), val_shift_limit=(-0.3, 0.3), p=0.3),
187
+ alb.RandomBrightnessContrast(
188
+ brightness_limit=(-0.3, 0.3), contrast_limit=(-0.3, 0.3), p=0.3),
189
+ alb.ImageCompression(quality_lower=40, quality_upper=100, p=0.5),
190
+
191
+ ],
192
+ additional_targets={f'image1': 'image'},
193
+ p=1.)
194
+
195
+
196
+ def self_blending(img, landmark, blending_type, face_region):
197
+ if np.random.rand() < 0.25:
198
+ landmark = landmark[:68]
199
+ mask = random_get_hull(landmark, img, face_region)
200
+ if mask.shape[-1] == 3:
201
+ mask = mask[:, :, 0]
202
+
203
+ mask_copy = mask
204
+
205
+ source_transforms = get_source_transforms()
206
+ source = img.copy()
207
+ source = source_transforms(image=source.astype(np.uint8))['image']
208
+
209
+ source_before_affine_transforms, mask_before_affine_transforms = source, mask
210
+ source, mask = randaffine(source, mask)
211
+ source_after_affine_transforms, mask_after_affine_transforms = source, mask
212
+
213
+ img_blended, mask = dynamic_blend(source, img, mask, blending_type)
214
+ img_blended = img_blended.astype(np.uint8)
215
+ img = img.astype(np.uint8)
216
+
217
+ return img, img_blended, mask, mask_copy, source_before_affine_transforms, mask_before_affine_transforms, source_after_affine_transforms, mask_after_affine_transforms
Weights/94_0.9485_val.tar ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5c100e98694faa776fcf71990377f9c4eca46568417339c06843cdcf2a78d35d
3
+ size 141291061
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/README.md ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Faceforgerydetection
3
+ emoji: 💩
4
+ colorFrom: gray
5
+ colorTo: pink
6
+ sdk: gradio
7
+ sdk_version: 3.9
8
+ app_file: app.py
9
+ pinned: false
10
+ license: mit
11
+ ---
12
+
13
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
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
app.py ADDED
@@ -0,0 +1,387 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import warnings
2
+ import cv2
3
+ import dlib
4
+ from pytorch_grad_cam.utils.image import show_cam_on_image
5
+ from pytorch_grad_cam.utils.model_targets import ClassifierOutputTarget
6
+ import gradio as gr
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
+ from Scripts.sbi_generator import (IoUfrom2bboxes, get_dlib_landmarks,
15
+ get_retina_bbox, get_transforms,
16
+ reorder_landmark, self_blending)
17
+
18
+ warnings.filterwarnings('ignore')
19
+
20
+
21
+ # Model Initialization
22
+ device = torch.device('cpu')
23
+
24
+ sbcl = create_model("Weights/94_0.9485_val.tar")
25
+ sbi = create_model("Weights/FFc23.tar")
26
+
27
+ # Face Detector Initialization
28
+ face_detector = get_model("resnet50_2020-07-20", max_size=1024, device=device)
29
+ face_detector.eval()
30
+
31
+ # Grad-CAM Initialization
32
+ cam_sbi = create_cam(sbi)
33
+ cam_sbcl = create_cam(sbcl)
34
+ targets = [ClassifierOutputTarget(1)]
35
+
36
+ # Examples
37
+ examples = ["Examples/Fake/fake1.png", "Examples/Real/real1.png", "Examples/Real/real2.png", "Examples/Fake/fake3.png", "Examples/Real/real3.png",
38
+ "Examples/Fake/fake4.png", "Examples/Real/real4.png", "Examples/Fake/fake5.png", "Examples/Fake/fake6.png", "Examples/Fake/fake7.png", ]
39
+ examples_videos = ['Examples/Fake1.mp4', 'Examples/Real1.mp4']
40
+ examples_sbi = ["Examples/Fake/fake1.png", "Examples/Real/real1.png", "Examples/Real/real2.png", "Examples/Fake/fake3.png", "Examples/Real/real3.png",
41
+ "Examples/Fake/fake4.png", "Examples/Fake/fake5.png", ]
42
+
43
+ # dlib Models
44
+ dlib_face_detector = dlib.get_frontal_face_detector()
45
+ dlib_face_predictor = dlib.shape_predictor(
46
+ 'Weights/shape_predictor_81_face_landmarks.dat')
47
+
48
+
49
+ def generate_sbi(inp, blending_type, face_region):
50
+ """
51
+ Visualizes the different steps in the self-blended image generation process for both RGB image and mask
52
+ """
53
+ # Getting face bboxes and landmarks
54
+ landmark = get_dlib_landmarks(
55
+ inp, dlib_face_detector, dlib_face_predictor)[0]
56
+ bbox_lm = np.array([landmark[:, 0].min(), landmark[:, 1].min(),
57
+ landmark[:, 0].max(), landmark[:, 1].max()])
58
+ bboxes = get_retina_bbox(inp, face_detector)[:2]
59
+
60
+ # Reducing bboxes to just one if multiple
61
+ iou_max = -1
62
+ for i in range(len(bboxes)):
63
+ iou = IoUfrom2bboxes(bbox_lm, bboxes[i].flatten())
64
+ if iou_max < iou:
65
+ bbox = bboxes[i]
66
+ iou_max = iou
67
+
68
+ # Input cropping
69
+ landmarks = reorder_landmark(landmark)
70
+ img, landmarks, bbox, __ = crop_face(
71
+ inp, landmarks, bbox, margin=True, crop_by_bbox=False)
72
+ cropped_input_face = img
73
+
74
+ # Blending
75
+ img_r_before_both_transforms, img_f_before_both_transforms, mask, mask_original, source_before_affine_transforms, _, source_after_affine_transforms, mask_after_affine_transforms = self_blending(
76
+ img.copy(), landmark.copy(), blending_type, face_region)
77
+
78
+ # Post-blending transforms
79
+ transformed = get_transforms()(image=img_f_before_both_transforms.astype(
80
+ 'uint8'), image1=img_r_before_both_transforms.astype('uint8'))
81
+ img_f_after_both_transforms, img_r_after_both_transforms = transformed[
82
+ 'image'], transformed['image1']
83
+
84
+ # Crop and resize the faces
85
+ img_f, _, __, ___, y0_new, y1_new, x0_new, x1_new = crop_face(
86
+ img_f_after_both_transforms, landmark, bbox, margin=False, crop_by_bbox=True, abs_coord=True, phase='train')
87
+ img_r = img_r_after_both_transforms[y0_new:y1_new, x0_new:x1_new]
88
+ img_f, img_r = cv2.resize(img_f, (380, 380), interpolation=cv2.INTER_LINEAR), cv2.resize(
89
+ img_r, (380, 380), interpolation=cv2.INTER_LINEAR)
90
+
91
+ # Mask operations
92
+ mask, mask_original = cv2.cvtColor(mask, cv2.COLOR_GRAY2RGB), cv2.cvtColor(
93
+ mask_original, cv2.COLOR_GRAY2RGB)
94
+ mask_after_affine_transforms = cv2.cvtColor(
95
+ mask_after_affine_transforms, cv2.COLOR_GRAY2RGB)
96
+ return cropped_input_face, img_r_before_both_transforms, img_f_before_both_transforms, img_r_after_both_transforms, img_f_after_both_transforms,\
97
+ img_r, img_f, mask, mask_original, source_before_affine_transforms, source_after_affine_transforms, mask_after_affine_transforms
98
+
99
+
100
+ def generate_ca(inp):
101
+ """
102
+ Applies consistency augmentations to the given input face
103
+ """
104
+ try:
105
+ face = extract_face(inp, face_detector)[0].transpose(1, 2, 0)
106
+ except:
107
+ raise Exception("No faces detected")
108
+ randomErasing, randomCropping, dfdc = get_augs("REAlbu"), get_augs("RandCropAlbu"), get_augs("DFDCAlbu")
109
+ return face, randomErasing(image=face)['image'], randomCropping(image=face)['image'], dfdc(image=face)['image']
110
+
111
+
112
+ def predict_image(inp, model):
113
+ """
114
+ Performs inference for a given input image and returns the prediction and CAM image.
115
+ """
116
+ face_list = extract_face(inp, face_detector)
117
+
118
+ if len(face_list) == 0:
119
+ return {'No face detected!': 1}, None, None
120
+
121
+ with torch.no_grad():
122
+ img = torch.tensor(face_list).to(device).float()/255
123
+
124
+ if model == "Self-Blended Images":
125
+ pred = sbi(img).softmax(1)[:, 1].cpu().data.numpy().tolist()[0]
126
+ else:
127
+ pred = sbcl(img).softmax(1)[:, 1].cpu().data.numpy().tolist()[0]
128
+
129
+ confidences = {'Real': 1-pred, 'Fake': pred}
130
+
131
+ if model == "Self-Blended Images":
132
+ grayscale_cam = cam_sbi(
133
+ input_tensor=img, targets=targets, aug_smooth=True)
134
+ else:
135
+ grayscale_cam = cam_sbcl(
136
+ input_tensor=img, targets=targets, aug_smooth=True)
137
+ grayscale_cam = grayscale_cam[0, :]
138
+ cam_image = show_cam_on_image(face_list[0].transpose(
139
+ 1, 2, 0)/255, grayscale_cam, use_rgb=True)
140
+
141
+ return confidences, cam_image
142
+
143
+
144
+ def predict_video(inp, model):
145
+ """
146
+ Performs inference for a given input video and returns the prediction and CAM image of the frame with the highest fake probability.
147
+ """
148
+ face_list, idx_list = extract_frames(inp, 10, face_detector)
149
+
150
+ with torch.no_grad():
151
+ img = torch.tensor(face_list).to(device).float()/255
152
+ if model == "Self-Blended Images":
153
+ pred = sbi(img).softmax(1)[:, 1]
154
+ else:
155
+ pred = sbcl(img).softmax(1)[:, 1]
156
+
157
+ pred_list = []
158
+ idx_img = -1
159
+ for i in range(len(pred)):
160
+ if idx_list[i] != idx_img:
161
+ pred_list.append([])
162
+ idx_img = idx_list[i]
163
+ pred_list[-1].append(pred[i].item())
164
+ pred_res = np.zeros(len(pred_list))
165
+ for i in range(len(pred_res)):
166
+ pred_res[i] = max(pred_list[i])
167
+ pred = pred_res.mean()
168
+
169
+ most_fake = np.argmax(pred_res)
170
+ if model == "Self-Blended Images":
171
+ grayscale_cam = cam_sbi(input_tensor=img[most_fake].unsqueeze(
172
+ 0), targets=targets, aug_smooth=True)
173
+ else:
174
+ grayscale_cam = cam_sbcl(input_tensor=img[most_fake].unsqueeze(
175
+ 0), targets=targets, aug_smooth=True)
176
+ grayscale_cam = grayscale_cam[0, :]
177
+ cam_image = show_cam_on_image(face_list[most_fake].transpose(
178
+ 1, 2, 0)/255, grayscale_cam, use_rgb=True)
179
+
180
+ return {'Real': 1-pred, 'Fake': pred}, cam_image
181
+
182
+
183
+ with gr.Blocks(title="Self-Blended Consistency Learning", css="#custom_header {min-height: 3rem} #custom_title {min-height: 3rem; text-align: center}") as demo:
184
+ gr.Markdown("# Face Forgery Detector", elem_id="custom_title")
185
+ gr.Markdown("Gradio Demo for 'Face Forgery Detection with Self-Blended Consistency Learning'. To use it, simply upload your image, or click one of the examples to load them. Paper to be available on ArXiv in the near future.", elem_id="custom_title")
186
+
187
+ with gr.Tab("Image Inference"):
188
+ with gr.Row():
189
+ with gr.Column():
190
+ with gr.Box():
191
+ gr.Markdown("## Inputs", elem_id="custom_header")
192
+ input_image = gr.Image(label="Input Image")
193
+ input_image.style(height=240)
194
+ model_selection = gr.inputs.Radio(['Self-Blended Images', 'Self-Blended Consistency Learning'],
195
+ type="value", default='Self-Blended Consistency Learning', label='Model')
196
+ btn = gr.Button(value="Submit")
197
+ btn.style(full_width=True)
198
+ with gr.Column():
199
+ with gr.Box():
200
+ gr.Markdown("## Outputs", elem_id="custom_header")
201
+ output_image = gr.Image(label="GradCAM Image")
202
+ output_image.style(height=240)
203
+ label_probs = gr.outputs.Label()
204
+ gr.Examples(
205
+ examples=examples,
206
+ inputs=input_image,
207
+ outputs=output_image,
208
+ fn=predict_image,
209
+ cache_examples=False,
210
+ )
211
+ with gr.Tab("Video Inference"):
212
+ with gr.Row():
213
+ with gr.Column():
214
+ with gr.Box():
215
+ gr.Markdown("## Inputs", elem_id="custom_header")
216
+ input_video = gr.Video(label="Input Video")
217
+ input_video.style(height=240)
218
+ model_selection_video = gr.inputs.Radio(
219
+ ['Self-Blended Images', 'Self-Blended Consistency Learning'], type="value", default='Self-Blended Consistency Learning', label='Model')
220
+ btn_video = gr.Button(value="Submit")
221
+ btn_video.style(full_width=True)
222
+
223
+ with gr.Column():
224
+ with gr.Box():
225
+ gr.Markdown("## Outputs", elem_id="custom_header")
226
+ output_image_video = gr.Image(label="GradCAM Image")
227
+ output_image_video.style(height=240)
228
+ label_probs_video = gr.outputs.Label()
229
+ gr.Examples(
230
+ examples=examples_videos,
231
+ inputs=input_video,
232
+ outputs=output_image_video,
233
+ fn=predict_video,
234
+ cache_examples=False,
235
+ )
236
+
237
+ with gr.Tab("SBI Generator"):
238
+ gr.Markdown("Input an image with a face to visualize the steps involved in the self-blended image (SBI) generation. Values for augmentations are randomly chosen. Blending type and face region can be varied. \
239
+ This process is a slightly modified version of the process from 'Detecting Deepfakes with Self-Blended Images (CVPR 2022)'", elem_id="custom_header")
240
+ with gr.Row():
241
+ with gr.Column():
242
+ with gr.Box():
243
+ gr.Markdown("## Inputs", elem_id="custom_header")
244
+ input_image_sbi = gr.Image(label="Input Image")
245
+ input_image_sbi.style(height=240)
246
+ btn_sbi = gr.Button(value="Submit")
247
+ btn_sbi.style(full_width=True)
248
+ with gr.Row():
249
+ blending_type = gr.Radio(
250
+ ["Poisson", "Mixup"], label="Blending Type", value="Poisson", interactive=True)
251
+ face_region = gr.Radio(
252
+ ["1", "2", "3", "4"], label="Face Region", value="1", interactive=True)
253
+ gr.Examples(
254
+ examples=examples_sbi,
255
+ inputs=input_image_sbi,
256
+ fn=generate_sbi,
257
+ cache_examples=False,
258
+ )
259
+ with gr.Row():
260
+ with gr.Box():
261
+ with gr.Column():
262
+ gr.Markdown("# Self-Blended Image Generation",
263
+ elem_id="custom_header")
264
+
265
+ with gr.Box():
266
+ gr.Markdown("## Step 1", elem_id="custom_header")
267
+ gr.Markdown(
268
+ "Using facial landmarks models, obtain face bounding box and facial landmarks to crop face and produce mask.", elem_id="custom_header")
269
+ with gr.Row():
270
+ cropped_input_face = gr.Image(
271
+ label="Input face after cropping")
272
+ cropped_input_face.style(height=240)
273
+ mask_original = gr.Image(label="Original mask")
274
+ mask_original.style(height=240)
275
+ gr.Markdown("The cropped input face is duplicated to become a 'source' face and a 'target' face. Eventually, the source face will be blended onto the target face after augmentations done below.", elem_id="custom_header")
276
+
277
+ with gr.Box():
278
+ gr.Markdown("## Step 2", elem_id="custom_header")
279
+ gr.Markdown("Apply source-target augmentations",
280
+ elem_id="custom_header")
281
+ with gr.Row():
282
+ source_before_affine_transforms = gr.Image(
283
+ label="Source face after source-target augmentations")
284
+ source_before_affine_transforms.style(height=240)
285
+ gr.Markdown("In this case, the source-target augmentations are applied to the source image for straight-forward visualization. In actual training,\
286
+ the augmentations are applied to either source or target face with 1:1 probability. Augmentations applied here \
287
+ include RGBShift, HueSaturationValue, RandomBrightnessContrast, RandomDownScale, Sharpen from Albumentations.")
288
+
289
+ with gr.Box():
290
+ gr.Markdown("## Step 3", elem_id="custom_header")
291
+ gr.Markdown(
292
+ "Apply affine/elastic augmentations to augmented source image/mask", elem_id="custom_header")
293
+ with gr.Row():
294
+ source_after_affine_transforms = gr.Image(
295
+ label="Source face after affine augmentations")
296
+ source_after_affine_transforms.style(height=240)
297
+
298
+ mask_after_affine_transforms = gr.Image(
299
+ label="Mask after elastic augmentations")
300
+ mask_after_affine_transforms.style(height=240)
301
+
302
+ with gr.Box():
303
+ gr.Markdown("## Step 4", elem_id="custom_header")
304
+ gr.Markdown(
305
+ "Apply smoothing augmentations to mask for gentle blending", elem_id="custom_header")
306
+ mask = gr.Image(label="Mask after Gaussian smoothing")
307
+ mask.style(height=240)
308
+
309
+ with gr.Box():
310
+ gr.Markdown("## Step 5", elem_id="custom_header")
311
+ gr.Markdown(
312
+ "Perform blending (based on chosen blending option) to produce self-blended fake", elem_id="custom_header")
313
+ with gr.Row():
314
+ img_r_before_both_transforms = gr.Image(
315
+ label="Real face (for comparison)")
316
+ img_r_before_both_transforms.style(height=240)
317
+
318
+ img_f_before_both_transforms = gr.Image(
319
+ label="Self-blended fake face")
320
+ img_f_before_both_transforms.style(height=240)
321
+
322
+ with gr.Box():
323
+ gr.Markdown("## Step 6", elem_id="custom_header")
324
+ gr.Markdown(
325
+ "Apply the same randomly chosen augmentations to both real and self-blended fake", elem_id="custom_header")
326
+ with gr.Row():
327
+ img_r_after_both_transforms = gr.Image(
328
+ label="Real face after augmentations")
329
+ img_r_after_both_transforms.style(height=240)
330
+
331
+ img_f_after_both_transforms = gr.Image(
332
+ label="Self-blended fake face after augmentations")
333
+ img_f_after_both_transforms.style(height=240)
334
+ gr.Markdown(
335
+ "Augmentations applied here include RGBShift, HueSaturationValue, RandomBrightnessContrast, ImageCompression from Albumentations.")
336
+
337
+ with gr.Box():
338
+ gr.Markdown("## Step 7", elem_id="custom_header")
339
+ gr.Markdown(
340
+ "Crop real and self-blended fake to only have the faces", elem_id="custom_header")
341
+ with gr.Row():
342
+ output_r = gr.Image(label="Final real face")
343
+ output_r.style(height=240)
344
+
345
+ output_f = gr.Image(label="Final SBI face")
346
+ output_f.style(height=240)
347
+ gr.Markdown(
348
+ "These are the images that are eventually fed into the model for training", elem_id="custom_header")
349
+
350
+ with gr.Tab("Consistency Augmentations"):
351
+ gr.Markdown("Input an image with a face to visualize the consistency augmentations. Values for augmentations are randomly chosen.", elem_id="custom_header")
352
+ with gr.Row():
353
+ with gr.Box():
354
+ gr.Markdown("## Input", elem_id="custom_header")
355
+ input_image_ca = gr.Image(label="Input Image")
356
+ input_image_ca.style(height=240)
357
+ btn_ca = gr.Button(value="Submit")
358
+ btn_ca.style(full_width=True)
359
+ gr.Examples(
360
+ examples=examples_sbi,
361
+ inputs=input_image_ca,
362
+ fn=generate_ca,
363
+ cache_examples=False,
364
+ )
365
+ with gr.Row():
366
+ with gr.Box():
367
+ with gr.Row():
368
+ og = gr.Image(label="Cropped Face (No augs)")
369
+ og.style(height=240)
370
+ re = gr.Image(label="Random Erasing")
371
+ re.style(height=240)
372
+ rc = gr.Image(label="Random Cropping")
373
+ rc.style(height=240)
374
+ dfdc = gr.Image(label="DFDC Selimsef")
375
+ dfdc.style(height=240)
376
+
377
+ btn.click(predict_image, inputs=[input_image, model_selection], outputs=[
378
+ label_probs, output_image])
379
+ btn_video.click(predict_video, inputs=[input_video, model_selection_video], outputs=[
380
+ label_probs_video, output_image_video])
381
+ btn_sbi.click(generate_sbi, inputs=[input_image_sbi, blending_type, face_region], outputs=[cropped_input_face, img_r_before_both_transforms, img_f_before_both_transforms,
382
+ img_r_after_both_transforms, img_f_after_both_transforms, output_r, output_f, mask,
383
+ mask_original, source_before_affine_transforms, source_after_affine_transforms, mask_after_affine_transforms])
384
+ btn_ca.click(generate_ca, inputs=[
385
+ input_image_ca], outputs=[og, re, rc, dfdc])
386
+ if __name__ == "__main__":
387
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ dlib
2
+ retinaface_pytorch
3
+ imutils
4
+ numpy
5
+ grad-cam
6
+ gradio
7
+ opencv-python
8
+ efficientnet_pytorch