Sefik Ilkin Serengil 429c6b8f67 pylint exceptions
2024-06-07 20:37:48 +01:00

224 lines
7.4 KiB
Python

# Minivision's Silent-Face-Anti-Spoofing Repo licensed under Apache License 2.0
# Ref: github.com/minivision-ai/Silent-Face-Anti-Spoofing/blob/master/src/model_lib/MiniFASNet.py
# built-in dependencies
from typing import Union
# 3rd party dependencies
import cv2
import numpy as np
# project dependencies
from deepface.commons import folder_utils, file_utils, logger as log
logger = log.get_singletonish_logger()
# pylint: disable=line-too-long, too-few-public-methods
class Fasnet:
"""
Mini Face Anti Spoofing Net Library from repo: github.com/minivision-ai/Silent-Face-Anti-Spoofing
"""
def __init__(self):
# pytorch is an opitonal dependency, enforce it to be installed if class imported
try:
import torch
except Exception as err:
raise ValueError(
"You must install torch with `pip install pytorch` command to use face anti spoofing module"
) from err
home = folder_utils.get_deepface_home()
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
self.device = device
# download pre-trained models if not installed yet
file_utils.download_external_file(
file_name="2.7_80x80_MiniFASNetV2.pth",
exact_file_path=f"{home}/.deepface/weights/2.7_80x80_MiniFASNetV2.pth",
url="https://github.com/minivision-ai/Silent-Face-Anti-Spoofing/raw/master/resources/anti_spoof_models/2.7_80x80_MiniFASNetV2.pth",
)
file_utils.download_external_file(
file_name="4_0_0_80x80_MiniFASNetV1SE.pth",
exact_file_path=f"{home}/.deepface/weights/4_0_0_80x80_MiniFASNetV1SE.pth",
url="https://github.com/minivision-ai/Silent-Face-Anti-Spoofing/raw/master/resources/anti_spoof_models/4_0_0_80x80_MiniFASNetV1SE.pth",
)
# guarantees Fasnet imported and torch installed
from deepface.spoofmodels import FasNetBackbone
# Fasnet will use 2 distinct models to predict, then it will find the sum of predictions
# to make a final prediction
first_model = FasNetBackbone.MiniFASNetV2(conv6_kernel=(5, 5)).to(device)
second_model = FasNetBackbone.MiniFASNetV1SE(conv6_kernel=(5, 5)).to(device)
# load model weight for first model
state_dict = torch.load(
f"{home}/.deepface/weights/2.7_80x80_MiniFASNetV2.pth", map_location=device
)
keys = iter(state_dict)
first_layer_name = keys.__next__()
if first_layer_name.find("module.") >= 0:
from collections import OrderedDict
new_state_dict = OrderedDict()
for key, value in state_dict.items():
name_key = key[7:]
new_state_dict[name_key] = value
first_model.load_state_dict(new_state_dict)
else:
first_model.load_state_dict(state_dict)
# load model weight for second model
state_dict = torch.load(
f"{home}/.deepface/weights/4_0_0_80x80_MiniFASNetV1SE.pth", map_location=device
)
keys = iter(state_dict)
first_layer_name = keys.__next__()
if first_layer_name.find("module.") >= 0:
from collections import OrderedDict
new_state_dict = OrderedDict()
for key, value in state_dict.items():
name_key = key[7:]
new_state_dict[name_key] = value
second_model.load_state_dict(new_state_dict)
else:
second_model.load_state_dict(state_dict)
# evaluate models
_ = first_model.eval()
_ = second_model.eval()
self.first_model = first_model
self.second_model = second_model
def analyze(self, img: np.ndarray, facial_area: Union[list, tuple]):
"""
Analyze a given image spoofed or not
Args:
img (np.ndarray): pre loaded image
facial_area (list or tuple): facial rectangle area coordinates with x, y, w, h respectively
Returns:
result (tuple): a result tuple consisting of is_real and score
"""
import torch
import torch.nn.functional as F
x, y, w, h = facial_area
first_img = crop(img, (x, y, w, h), 2.7, 80, 80)
second_img = crop(img, (x, y, w, h), 4, 80, 80)
test_transform = Compose(
[
ToTensor(),
]
)
first_img = test_transform(first_img)
first_img = first_img.unsqueeze(0).to(self.device)
second_img = test_transform(second_img)
second_img = second_img.unsqueeze(0).to(self.device)
with torch.no_grad():
first_result = self.first_model.forward(first_img)
first_result = F.softmax(first_result).cpu().numpy()
second_result = self.second_model.forward(second_img)
second_result = F.softmax(second_result).cpu().numpy()
prediction = np.zeros((1, 3))
prediction += first_result
prediction += second_result
label = np.argmax(prediction)
is_real = True if label == 1 else False # pylint: disable=simplifiable-if-expression
score = prediction[0][label] / 2
return is_real, score
# subsdiary classes and functions
def to_tensor(pic):
"""Convert a ``numpy.ndarray`` to tensor.
See ``ToTensor`` for more details.
Args:
pic (PIL Image or numpy.ndarray): Image to be converted to tensor.
Returns:
Tensor: Converted image.
"""
import torch
# handle numpy array
# IR image channel=1: modify by lzc --> 20190730
if pic.ndim == 2:
pic = pic.reshape((pic.shape[0], pic.shape[1], 1))
img = torch.from_numpy(pic.transpose((2, 0, 1)))
# backward compatibility
# return img.float().div(255) modify by zkx
return img.float()
class Compose:
def __init__(self, transforms):
self.transforms = transforms
def __call__(self, img):
for t in self.transforms:
img = t(img)
return img
class ToTensor:
def __call__(self, pic):
return to_tensor(pic)
def _get_new_box(src_w, src_h, bbox, scale):
x = bbox[0]
y = bbox[1]
box_w = bbox[2]
box_h = bbox[3]
scale = min(
(src_h - 1) / box_h, min((src_w - 1) / box_w, scale)
) # pylint: disable=nested-min-max
new_width = box_w * scale
new_height = box_h * scale
center_x, center_y = box_w / 2 + x, box_h / 2 + y
left_top_x = center_x - new_width / 2
left_top_y = center_y - new_height / 2
right_bottom_x = center_x + new_width / 2
right_bottom_y = center_y + new_height / 2
if left_top_x < 0:
right_bottom_x -= left_top_x
left_top_x = 0
if left_top_y < 0:
right_bottom_y -= left_top_y
left_top_y = 0
if right_bottom_x > src_w - 1:
left_top_x -= right_bottom_x - src_w + 1
right_bottom_x = src_w - 1
if right_bottom_y > src_h - 1:
left_top_y -= right_bottom_y - src_h + 1
right_bottom_y = src_h - 1
return int(left_top_x), int(left_top_y), int(right_bottom_x), int(right_bottom_y)
def crop(org_img, bbox, scale, out_w, out_h):
src_h, src_w, _ = np.shape(org_img)
left_top_x, left_top_y, right_bottom_x, right_bottom_y = _get_new_box(src_w, src_h, bbox, scale)
img = org_img[left_top_y : right_bottom_y + 1, left_top_x : right_bottom_x + 1]
dst_img = cv2.resize(img, (out_w, out_h))
return dst_img