antispoof / spoofynet.py
Wikidepia's picture
Add demo for spoof detection
fc1a577
import cv2
import numpy as np
from onnxruntime import InferenceSession
import box_utils_numpy
from auto_rotate import align_face
def softmax(x):
e_x = np.exp(x - np.max(x))
return e_x / e_x.sum(axis=0)
def crop_square(img, size, interpolation=cv2.INTER_AREA):
h, w = img.shape[:2]
min_size = np.amin([h, w])
# Centralize and crop
crop_img = img[
int(h / 2 - min_size / 2) : int(h / 2 + min_size / 2),
int(w / 2 - min_size / 2) : int(w / 2 + min_size / 2),
]
resized = cv2.resize(crop_img, (size, size), interpolation=interpolation)
return resized
class SpoofyNet:
def __init__(self):
self.face_model = InferenceSession("models/slim-facedetect.onnx")
self.face_inputname = self.face_model.get_inputs()[0].name
self.classifier = InferenceSession("models/spoof.onnx")
def find_boxes(
self,
width,
height,
confidences,
boxes,
prob_threshold,
iou_threshold=0.3,
top_k=-1,
):
boxes = boxes[0]
confidences = confidences[0]
picked_box_probs = []
picked_labels = []
for class_index in range(1, confidences.shape[1]):
probs = confidences[:, class_index]
mask = probs > prob_threshold
probs = probs[mask]
if probs.shape[0] == 0:
continue
subset_boxes = boxes[mask, :]
box_probs = np.concatenate([subset_boxes, probs.reshape(-1, 1)], axis=1)
box_probs = box_utils_numpy.hard_nms(
box_probs,
iou_threshold=iou_threshold,
top_k=top_k,
)
picked_box_probs.append(box_probs)
picked_labels.extend([class_index] * box_probs.shape[0])
if not picked_box_probs:
return np.array([]), np.array([]), np.array([])
picked_box_probs = np.concatenate(picked_box_probs)
picked_box_probs[:, 0] *= width
picked_box_probs[:, 1] *= height
picked_box_probs[:, 2] *= width
picked_box_probs[:, 3] *= height
return (
picked_box_probs[:, :4].astype(np.int32),
np.array(picked_labels),
picked_box_probs[:, 4],
)
def tta(self, src):
horizontal_rot = cv2.rotate(src, cv2.ROTATE_180)
grayscale = cv2.cvtColor(src, cv2.COLOR_RGB2GRAY)
grayscale = cv2.cvtColor(grayscale, cv2.COLOR_GRAY2RGB)
return [src, horizontal_rot, grayscale]
def find_spoof(self, img):
ret = []
threshold = 0.6
image_mean = np.array([127, 127, 127])
image = cv2.resize(img, (320, 240))
image = (image - image_mean) / 128
image = np.transpose(image, [2, 0, 1])
image = np.expand_dims(image, axis=0)
image = image.astype(np.float32)
confidences, boxes = self.face_model.run(None, {self.face_inputname: image})
boxes, _, _ = self.find_boxes(
img.shape[1], img.shape[0], confidences, boxes, threshold
)
classify_mean, classify_std = [0.485, 0.456, 0.406], [0.229, 0.224, 0.225]
for i in range(boxes.shape[0]):
(startX, startY, endX, endY) = boxes[i, :]
face = img[startY:endY, startX:endX]
if face.size == 0:
continue
face = cv2.cvtColor(face, cv2.COLOR_BGR2RGB)
# Preprocess
face = align_face(face)
face = crop_square(face, 256)
probs_all = []
for face in self.tta(face):
# Normalize
face = face / 255.0
face = (face - classify_mean) / classify_std
face = np.transpose(face, [2, 0, 1])
face = np.expand_dims(face, axis=0)
face = face.astype(np.float32)
predicted = self.classifier.run(None, {"input": face})
predicted_id = np.argmax(predicted)
probs = softmax(predicted[0][0])
probs_all.append(probs)
final_probs = np.mean(probs_all, axis=0)
predicted_id = np.argmax(final_probs)
ret.append(
{
"coords": (startX, startY, endX, endY),
"is_real": bool(predicted_id),
"probs": final_probs[predicted_id],
}
)
return ret