| import cv2 |
| import numpy as np |
| import gradio as gr |
|
|
| from src.inference import load_model, infer, process_with_logits, crop |
| from src.detection import load_detector, detect |
|
|
|
|
| from fastapi import FastAPI, Request |
| from fastapi.responses import JSONResponse |
|
|
| |
|
|
| DETECTOR_MODEL = "models/detector_quantized.onnx" |
| LIVENESS_MODEL = "models/best_model_quantized.onnx" |
|
|
| |
| face_detector = load_detector(DETECTOR_MODEL, (320, 320)) |
| liveness_session, input_name = load_model(LIVENESS_MODEL) |
|
|
| def spoof_default_result(): |
| return { |
| "is_real": False, |
| "status": "spoof", |
| "logit_diff": 0.00, |
| "real_logit": 0.00, |
| "spoof_logit": 0.00, |
| "confidence": 0.00, |
| "p_real": 0.00, |
| } |
|
|
| def resize_for_detection(image_bgr, max_side=1280): |
| h, w = image_bgr.shape[:2] |
| if max(h, w) <= max_side: |
| return image_bgr, 1.0 |
| scale = max_side / float(max(h, w)) |
| new_w, new_h = int(w * scale), int(h * scale) |
| resized = cv2.resize(image_bgr, (new_w, new_h), interpolation=cv2.INTER_AREA) |
| return resized, scale |
|
|
| def scale_bbox_to_original(bbox, inv_scale): |
| return { |
| "x": bbox["x"] * inv_scale, |
| "y": bbox["y"] * inv_scale, |
| "width": bbox["width"] * inv_scale, |
| "height": bbox["height"] * inv_scale, |
| } |
|
|
| FIXED_THRESHOLD = 0.4 |
| FIXED_MARGIN = 5 |
| FIXED_BBOX_EXPANSION = 1.5 |
|
|
| |
| |
|
|
| |
| |
| |
| |
|
|
| |
| |
|
|
| |
| |
| |
|
|
| |
| |
| |
| |
|
|
| |
| |
|
|
| |
| |
|
|
| |
| |
| |
| |
| |
|
|
| |
| |
|
|
| |
| |
| |
| |
|
|
| |
| |
| |
| |
| |
|
|
| |
| |
|
|
| |
| |
| |
|
|
| |
| |
| |
| |
|
|
| |
| |
|
|
|
|
| def predict_liveness(image_in, threshold=FIXED_THRESHOLD, margin=FIXED_MARGIN, bbox_expansion_factor=FIXED_BBOX_EXPANSION): |
| """Return only the liveness result dict under all conditions.""" |
| |
| |
| if image_in is None: |
| return spoof_default_result() |
|
|
| image_bgr = cv2.cvtColor(image_in, cv2.COLOR_RGB2BGR) |
| if image_bgr is None: |
| return spoof_default_result() |
|
|
| |
| if face_detector is None or liveness_session is None: |
| return spoof_default_result() |
|
|
| |
| p = max(1e-6, min(1 - 1e-6, float(threshold))) |
| logit_threshold = float(np.log(p / (1 - p))) |
|
|
| |
| det_bgr, scale = resize_for_detection(image_bgr, max_side=1280) |
| det_rgb = cv2.cvtColor(det_bgr, cv2.COLOR_BGR2RGB) |
| faces = detect(det_rgb, face_detector, margin=int(margin)) |
|
|
| if not faces: |
| return spoof_default_result() |
|
|
| inv_scale = 1.0 / scale if scale != 0 else 1.0 |
| image_rgb = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2RGB) |
|
|
| face_crops = [] |
| meta = [] |
|
|
| for face in faces: |
| det_conf = face.get("confidence", None) |
| det_conf = round(float(det_conf), 2) if det_conf is not None else None |
|
|
| bbox_orig = scale_bbox_to_original(face["bbox"], inv_scale) |
| x, y, w, h = bbox_orig["x"], bbox_orig["y"], bbox_orig["width"], bbox_orig["height"] |
|
|
| try: |
| face_crop = crop(image_rgb, (x, y, x + w, y + h), float(bbox_expansion_factor)) |
| except Exception: |
| continue |
|
|
| face_crops.append(face_crop) |
| meta.append({ |
| "bbox": {"x": int(x), "y": int(y), "width": int(w), "height": int(h)}, |
| "det_confidence": det_conf |
| }) |
|
|
| if not face_crops: |
| return spoof_default_result() |
|
|
| predictions = infer(face_crops, liveness_session, input_name, model_img_size=128) |
| if not predictions: |
| return spoof_default_result() |
|
|
| out_faces = [] |
| for m, pred in zip(meta, predictions): |
| r = process_with_logits(pred, logit_threshold) |
| out_faces.append({**m, "liveness": r}) |
|
|
| |
| best = max(out_faces, key=lambda d: (d["det_confidence"] or 0.0)) |
| return best["liveness"] |
|
|
| demo = gr.Interface( |
| fn=predict_liveness, |
| inputs=[ |
| gr.Image(type="numpy", label="Upload image", height=400, width=500), |
| |
| |
| |
| ], |
| outputs=gr.JSON(label="Result JSON"), |
| title="SOL9X: Face Liveness Detection", |
| ) |
|
|
|
|
|
|
| if __name__ == "__main__": |
| demo.launch() |
|
|