Download clean/image/effort/DeepfakeBench/training/demo.py from deepsafe/model-code: direct link, hf CLI and curl.
- Browser
- Download file 8.9 kB
-
https://huggingface.co/deepsafe/model-code/resolve/main/clean/image/effort/DeepfakeBench/training/demo.py
- Command line
-
hf download hf://deepsafe/model-code/clean/image/effort/DeepfakeBench/training/demo.py
-
curl -L -o demo.py https://huggingface.co/deepsafe/model-code/resolve/main/clean/image/effort/DeepfakeBench/training/demo.py
8.9 kB
| import numpy as np | |
| import cv2 | |
| import random | |
| import yaml | |
| import pickle | |
| from tqdm import tqdm | |
| from PIL import Image as pil_image | |
| import dlib | |
| import torch | |
| import torch.nn as nn | |
| import torch.nn.parallel | |
| import torch.nn.functional as F | |
| import torch.utils.data | |
| from torchvision import transforms | |
| from trainer.trainer import Trainer | |
| from detectors import DETECTOR | |
| from collections import defaultdict | |
| from PIL import Image as pil_image | |
| from imutils import face_utils | |
| from skimage import transform as trans | |
| import torchvision.transforms as T | |
| import os | |
| from os.path import join | |
| from typing import Tuple, List | |
| from pathlib import Path | |
| """ | |
| Usage: | |
| python infer.py \ | |
| --detector_config ./training/config/detector/effort.yaml \ | |
| --weights ../../DeepfakeBenchv2/training/weights/easy_clipl14_cdf.pth \ | |
| --image ./id9_id6_0009.jpg \ | |
| --landmark_model ../../DeepfakeBenchv2/preprocessing/dlib_tools/shape_predictor_81_face_landmarks.dat | |
| """ | |
| import argparse | |
| device = torch.device("cuda" if torch.cuda.is_available() else "cpu") | |
| def inference(model, data_dict): | |
| data, label = data_dict['image'], data_dict['label'] | |
| # move data to GPU | |
| data_dict['image'], data_dict['label'] = data.to(device), label.to(device) | |
| predictions = model(data_dict, inference=True) | |
| return predictions | |
| # preprocess the input image --> cropped face, resize = 256, adding a dimension of batch (output shape: 1x3x256x256) | |
| def get_keypts(image, face, predictor, face_detector): | |
| # detect the facial landmarks for the selected face | |
| shape = predictor(image, face) | |
| # select the key points for the eyes, nose, and mouth | |
| leye = np.array([shape.part(37).x, shape.part(37).y]).reshape(-1, 2) | |
| reye = np.array([shape.part(44).x, shape.part(44).y]).reshape(-1, 2) | |
| nose = np.array([shape.part(30).x, shape.part(30).y]).reshape(-1, 2) | |
| lmouth = np.array([shape.part(49).x, shape.part(49).y]).reshape(-1, 2) | |
| rmouth = np.array([shape.part(55).x, shape.part(55).y]).reshape(-1, 2) | |
| pts = np.concatenate([leye, reye, nose, lmouth, rmouth], axis=0) | |
| return pts | |
| def extract_aligned_face_dlib(face_detector, predictor, image, res=224, mask=None): | |
| def img_align_crop(img, landmark=None, outsize=None, scale=1.3, mask=None): | |
| """ | |
| align and crop the face according to the given bbox and landmarks | |
| landmark: 5 key points | |
| """ | |
| M = None | |
| target_size = [112, 112] | |
| dst = np.array([ | |
| [30.2946, 51.6963], | |
| [65.5318, 51.5014], | |
| [48.0252, 71.7366], | |
| [33.5493, 92.3655], | |
| [62.7299, 92.2041]], dtype=np.float32) | |
| if target_size[1] == 112: | |
| dst[:, 0] += 8.0 | |
| dst[:, 0] = dst[:, 0] * outsize[0] / target_size[0] | |
| dst[:, 1] = dst[:, 1] * outsize[1] / target_size[1] | |
| target_size = outsize | |
| margin_rate = scale - 1 | |
| x_margin = target_size[0] * margin_rate / 2. | |
| y_margin = target_size[1] * margin_rate / 2. | |
| # move | |
| dst[:, 0] += x_margin | |
| dst[:, 1] += y_margin | |
| # resize | |
| dst[:, 0] *= target_size[0] / (target_size[0] + 2 * x_margin) | |
| dst[:, 1] *= target_size[1] / (target_size[1] + 2 * y_margin) | |
| src = landmark.astype(np.float32) | |
| # use skimage tranformation | |
| tform = trans.SimilarityTransform() | |
| tform.estimate(src, dst) | |
| M = tform.params[0:2, :] | |
| # M: use opencv | |
| # M = cv2.getAffineTransform(src[[0,1,2],:],dst[[0,1,2],:]) | |
| img = cv2.warpAffine(img, M, (target_size[1], target_size[0])) | |
| if outsize is not None: | |
| img = cv2.resize(img, (outsize[1], outsize[0])) | |
| if mask is not None: | |
| mask = cv2.warpAffine(mask, M, (target_size[1], target_size[0])) | |
| mask = cv2.resize(mask, (outsize[1], outsize[0])) | |
| return img, mask | |
| else: | |
| return img | |
| # Image size | |
| height, width = image.shape[:2] | |
| # Convert to rgb | |
| rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) | |
| # Detect with dlib | |
| faces = face_detector(rgb, 1) | |
| if len(faces): | |
| # For now only take the biggest face | |
| face = max(faces, key=lambda rect: rect.width() * rect.height()) | |
| # Get the landmarks/parts for the face in box d only with the five key points | |
| landmarks = get_keypts(rgb, face, predictor, face_detector) | |
| # Align and crop the face | |
| cropped_face = img_align_crop(rgb, landmarks, outsize=(res, res), mask=mask) | |
| cropped_face = cv2.cvtColor(cropped_face, cv2.COLOR_RGB2BGR) | |
| # Extract the all landmarks from the aligned face | |
| face_align = face_detector(cropped_face, 1) | |
| landmark = predictor(cropped_face, face_align[0]) | |
| landmark = face_utils.shape_to_np(landmark) | |
| return cropped_face, landmark,face | |
| else: | |
| return None, None | |
| def load_detector(detector_cfg: str, weights: str): | |
| with open(detector_cfg, "r") as f: | |
| cfg = yaml.safe_load(f) | |
| model_cls = DETECTOR[cfg["model_name"]] | |
| model = model_cls(cfg).to(device) | |
| ckpt = torch.load(weights, map_location=device) | |
| state = ckpt.get("state_dict", ckpt) | |
| state = {k.replace("module.", ""): v for k, v in state.items()} | |
| model.load_state_dict(state, strict=False) # FIXME ⚠ | |
| model.eval() | |
| print("[✓] Detector loaded.") | |
| return model | |
| def preprocess_face(img_bgr: np.ndarray): | |
| """BGR → tensor""" | |
| img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) | |
| img_rgb = cv2.resize(img_rgb, (224, 224), interpolation=cv2.INTER_LINEAR) | |
| transform = T.Compose([ | |
| T.ToTensor(), | |
| T.Normalize([0.48145466, 0.4578275, 0.40821073], | |
| [0.26862954, 0.26130258, 0.27577711]), | |
| ]) | |
| return transform(pil_image.fromarray(img_rgb)).unsqueeze(0) # 1×3×H×W | |
| def infer_single_image( | |
| img_bgr: np.ndarray, | |
| face_detector, | |
| landmark_predictor, | |
| model, | |
| ) -> Tuple[int, float]: | |
| """Return (cls_out, prob)""" | |
| if face_detector is None or landmark_predictor is None: | |
| face_aligned = img_bgr | |
| else: | |
| face_aligned, _, _ = extract_aligned_face_dlib( | |
| face_detector, landmark_predictor, img_bgr, res=224 | |
| ) | |
| face_tensor = preprocess_face(face_aligned).to(device) | |
| data = {"image": face_tensor, "label": torch.tensor([0]).to(device)} | |
| preds = inference(model, data) | |
| cls_out = preds["cls"].squeeze().cpu().numpy() # 0/1 | |
| prob = preds["prob"].squeeze().cpu().numpy() # prob | |
| return cls_out, prob | |
| IMG_EXTS = {'.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.webp'} | |
| def collect_image_paths(path_str: str) -> List[Path]: | |
| p = Path(path_str) | |
| if not p.exists(): | |
| raise FileNotFoundError(f"[Error] Path does not exist: {path_str}") | |
| if p.is_file(): | |
| if p.suffix.lower() not in IMG_EXTS: | |
| raise ValueError(f"[Error] Invalid image format: {p.name}") | |
| return [p] | |
| img_list = [fp for fp in p.iterdir() if fp.is_file() and fp.suffix.lower() in IMG_EXTS] | |
| if not img_list: | |
| raise RuntimeError(f"[Error] No valid image files found in directory: {path_str}") | |
| return sorted(img_list) | |
| def parse_args(): | |
| p = argparse.ArgumentParser( | |
| description="Deepfake image inference (single image version)" | |
| ) | |
| p.add_argument("--detector_config", default='training/config/detector/effort.yaml', | |
| help="YAML 配置文件路径") | |
| p.add_argument("--weights", required=True, | |
| help="Detector 预训练权重") | |
| p.add_argument("--image", required=True, | |
| help="tested image") | |
| p.add_argument("--landmark_model", default=False, | |
| help="dlib 81 landmarks dat 文件 / 如果不需要裁剪人脸就是False") | |
| return p.parse_args() | |
| def main(): | |
| args = parse_args() | |
| model = load_detector(args.detector_config, args.weights) | |
| if args.landmark_model: | |
| face_det = dlib.get_frontal_face_detector() | |
| shape_predictor = dlib.shape_predictor(args.landmark_model) | |
| else: | |
| face_det, shape_predictor = None, None | |
| img_paths = collect_image_paths(args.image) | |
| multiple = len(img_paths) > 1 | |
| if multiple: | |
| print(f"Collected {len(img_paths)} images in total,let's infer them...\n") | |
| # ---------- infer ---------- | |
| for idx, img_path in enumerate(img_paths, 1): | |
| img = cv2.imread(str(img_path)) | |
| if img is None: | |
| print(f"[Warning] loading wrong,skip: {img_path}", file=sys.stderr) | |
| continue | |
| cls, prob = infer_single_image(img, face_det, shape_predictor, model) | |
| print( | |
| f"[{idx}/{len(img_paths)}] {img_path.name:>30} | Pred Label: {cls} " | |
| f"(0=Real, 1=Fake) | Fake Prob: {prob:.4f}" | |
| ) | |
| if __name__ == "__main__": | |
| main() | |