Commit ·
0b774dc
1
Parent(s): cbfd21c
result dict
Browse files
app.py
CHANGED
|
@@ -9,7 +9,7 @@ from src.detection import load_detector, detect
|
|
| 9 |
from fastapi import FastAPI, Request
|
| 10 |
from fastapi.responses import JSONResponse
|
| 11 |
|
| 12 |
-
import requests
|
| 13 |
|
| 14 |
DETECTOR_MODEL = "models/detector_quantized.onnx"
|
| 15 |
LIVENESS_MODEL = "models/best_model_quantized.onnx"
|
|
@@ -50,34 +50,101 @@ FIXED_THRESHOLD = 0.4
|
|
| 50 |
FIXED_MARGIN = 5
|
| 51 |
FIXED_BBOX_EXPANSION = 1.5
|
| 52 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
def predict_liveness(image_in, threshold=FIXED_THRESHOLD, margin=FIXED_MARGIN, bbox_expansion_factor=FIXED_BBOX_EXPANSION):
|
| 54 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
|
| 56 |
-
# image_in is RGB from Gradio [web:122]
|
| 57 |
image_bgr = cv2.cvtColor(image_in, cv2.COLOR_RGB2BGR)
|
| 58 |
if image_bgr is None:
|
| 59 |
-
return
|
| 60 |
|
|
|
|
| 61 |
if face_detector is None or liveness_session is None:
|
| 62 |
-
return
|
| 63 |
|
| 64 |
-
#
|
| 65 |
p = max(1e-6, min(1 - 1e-6, float(threshold)))
|
| 66 |
logit_threshold = float(np.log(p / (1 - p)))
|
| 67 |
|
| 68 |
-
#
|
| 69 |
det_bgr, scale = resize_for_detection(image_bgr, max_side=1280)
|
| 70 |
det_rgb = cv2.cvtColor(det_bgr, cv2.COLOR_BGR2RGB)
|
| 71 |
faces = detect(det_rgb, face_detector, margin=int(margin))
|
| 72 |
|
| 73 |
if not faces:
|
| 74 |
-
return
|
| 75 |
|
| 76 |
inv_scale = 1.0 / scale if scale != 0 else 1.0
|
| 77 |
image_rgb = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2RGB)
|
| 78 |
|
| 79 |
face_crops = []
|
| 80 |
meta = []
|
|
|
|
| 81 |
for face in faces:
|
| 82 |
det_conf = face.get("confidence", None)
|
| 83 |
det_conf = round(float(det_conf), 2) if det_conf is not None else None
|
|
@@ -97,19 +164,20 @@ def predict_liveness(image_in, threshold=FIXED_THRESHOLD, margin=FIXED_MARGIN, b
|
|
| 97 |
})
|
| 98 |
|
| 99 |
if not face_crops:
|
| 100 |
-
return
|
| 101 |
|
| 102 |
predictions = infer(face_crops, liveness_session, input_name, model_img_size=128)
|
| 103 |
if not predictions:
|
| 104 |
-
return
|
| 105 |
|
| 106 |
out_faces = []
|
| 107 |
for m, pred in zip(meta, predictions):
|
| 108 |
-
r = process_with_logits(pred, logit_threshold)
|
| 109 |
out_faces.append({**m, "liveness": r})
|
| 110 |
|
|
|
|
| 111 |
best = max(out_faces, key=lambda d: (d["det_confidence"] or 0.0))
|
| 112 |
-
return
|
| 113 |
|
| 114 |
demo = gr.Interface(
|
| 115 |
fn=predict_liveness,
|
|
@@ -125,31 +193,5 @@ demo = gr.Interface(
|
|
| 125 |
|
| 126 |
|
| 127 |
|
| 128 |
-
app: FastAPI = demo.app # Gradio's FastAPI app
|
| 129 |
-
|
| 130 |
-
@app.post("/api/predict_liveness_json")
|
| 131 |
-
async def predict_liveness_json(req: Request):
|
| 132 |
-
payload = await req.json()
|
| 133 |
-
|
| 134 |
-
# Expect a public URL (simplest for Postman). Example:
|
| 135 |
-
# {"image_url": "https://....jpg"}
|
| 136 |
-
image_url = payload.get("image_url")
|
| 137 |
-
if not image_url:
|
| 138 |
-
return JSONResponse(status_code=400, content={"error": "image_url is required"})
|
| 139 |
-
|
| 140 |
-
# download -> decode to image -> run predict_liveness
|
| 141 |
-
|
| 142 |
-
r = requests.get(image_url, timeout=20)
|
| 143 |
-
img_arr = np.frombuffer(r.content, dtype=np.uint8)
|
| 144 |
-
bgr = cv2.imdecode(img_arr, cv2.IMREAD_COLOR)
|
| 145 |
-
if bgr is None:
|
| 146 |
-
return JSONResponse(status_code=400, content={"error": "invalid image_url"})
|
| 147 |
-
|
| 148 |
-
# convert BGR->RGB because your predict_liveness expects Gradio-style RGB input
|
| 149 |
-
rgb = cv2.cvtColor(bgr, cv2.COLOR_BGR2RGB)
|
| 150 |
-
|
| 151 |
-
result = predict_liveness(rgb) # returns dict
|
| 152 |
-
return JSONResponse(content=result)
|
| 153 |
-
|
| 154 |
if __name__ == "__main__":
|
| 155 |
demo.launch()
|
|
|
|
| 9 |
from fastapi import FastAPI, Request
|
| 10 |
from fastapi.responses import JSONResponse
|
| 11 |
|
| 12 |
+
# import requests
|
| 13 |
|
| 14 |
DETECTOR_MODEL = "models/detector_quantized.onnx"
|
| 15 |
LIVENESS_MODEL = "models/best_model_quantized.onnx"
|
|
|
|
| 50 |
FIXED_MARGIN = 5
|
| 51 |
FIXED_BBOX_EXPANSION = 1.5
|
| 52 |
|
| 53 |
+
# def predict_liveness(image_in, threshold=FIXED_THRESHOLD, margin=FIXED_MARGIN, bbox_expansion_factor=FIXED_BBOX_EXPANSION):
|
| 54 |
+
# # image_bgr is a numpy array from gr.Image(type="numpy")
|
| 55 |
+
|
| 56 |
+
# # image_in is RGB from Gradio [web:122]
|
| 57 |
+
# image_bgr = cv2.cvtColor(image_in, cv2.COLOR_RGB2BGR)
|
| 58 |
+
# if image_bgr is None:
|
| 59 |
+
# return {"faces": [], "result": spoof_default_result(), "error": "No image provided"}
|
| 60 |
+
|
| 61 |
+
# if face_detector is None or liveness_session is None:
|
| 62 |
+
# return {"faces": [], "result": spoof_default_result(), "error": "Models not loaded"}
|
| 63 |
+
|
| 64 |
+
# # threshold -> logit threshold (same as demo.py)
|
| 65 |
+
# p = max(1e-6, min(1 - 1e-6, float(threshold)))
|
| 66 |
+
# logit_threshold = float(np.log(p / (1 - p)))
|
| 67 |
+
|
| 68 |
+
# # detect on resized
|
| 69 |
+
# det_bgr, scale = resize_for_detection(image_bgr, max_side=1280)
|
| 70 |
+
# det_rgb = cv2.cvtColor(det_bgr, cv2.COLOR_BGR2RGB)
|
| 71 |
+
# faces = detect(det_rgb, face_detector, margin=int(margin))
|
| 72 |
+
|
| 73 |
+
# if not faces:
|
| 74 |
+
# return {"faces": [], "result": spoof_default_result()}
|
| 75 |
+
|
| 76 |
+
# inv_scale = 1.0 / scale if scale != 0 else 1.0
|
| 77 |
+
# image_rgb = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2RGB)
|
| 78 |
+
|
| 79 |
+
# face_crops = []
|
| 80 |
+
# meta = []
|
| 81 |
+
# for face in faces:
|
| 82 |
+
# det_conf = face.get("confidence", None)
|
| 83 |
+
# det_conf = round(float(det_conf), 2) if det_conf is not None else None
|
| 84 |
+
|
| 85 |
+
# bbox_orig = scale_bbox_to_original(face["bbox"], inv_scale)
|
| 86 |
+
# x, y, w, h = bbox_orig["x"], bbox_orig["y"], bbox_orig["width"], bbox_orig["height"]
|
| 87 |
+
|
| 88 |
+
# try:
|
| 89 |
+
# face_crop = crop(image_rgb, (x, y, x + w, y + h), float(bbox_expansion_factor))
|
| 90 |
+
# except Exception:
|
| 91 |
+
# continue
|
| 92 |
+
|
| 93 |
+
# face_crops.append(face_crop)
|
| 94 |
+
# meta.append({
|
| 95 |
+
# "bbox": {"x": int(x), "y": int(y), "width": int(w), "height": int(h)},
|
| 96 |
+
# "det_confidence": det_conf
|
| 97 |
+
# })
|
| 98 |
+
|
| 99 |
+
# if not face_crops:
|
| 100 |
+
# return {"faces": [], "result": spoof_default_result()}
|
| 101 |
+
|
| 102 |
+
# predictions = infer(face_crops, liveness_session, input_name, model_img_size=128)
|
| 103 |
+
# if not predictions:
|
| 104 |
+
# return {"faces": [], "result": spoof_default_result()}
|
| 105 |
+
|
| 106 |
+
# out_faces = []
|
| 107 |
+
# for m, pred in zip(meta, predictions):
|
| 108 |
+
# r = process_with_logits(pred, logit_threshold) # already rounded (your change)
|
| 109 |
+
# out_faces.append({**m, "liveness": r})
|
| 110 |
+
|
| 111 |
+
# best = max(out_faces, key=lambda d: (d["det_confidence"] or 0.0))
|
| 112 |
+
# return best["liveness"]
|
| 113 |
+
|
| 114 |
+
|
| 115 |
def predict_liveness(image_in, threshold=FIXED_THRESHOLD, margin=FIXED_MARGIN, bbox_expansion_factor=FIXED_BBOX_EXPANSION):
|
| 116 |
+
"""Return only the liveness result dict under all conditions."""
|
| 117 |
+
|
| 118 |
+
# Convert RGB to BGR
|
| 119 |
+
if image_in is None:
|
| 120 |
+
return spoof_default_result()
|
| 121 |
|
|
|
|
| 122 |
image_bgr = cv2.cvtColor(image_in, cv2.COLOR_RGB2BGR)
|
| 123 |
if image_bgr is None:
|
| 124 |
+
return spoof_default_result()
|
| 125 |
|
| 126 |
+
# Ensure models are loaded
|
| 127 |
if face_detector is None or liveness_session is None:
|
| 128 |
+
return spoof_default_result()
|
| 129 |
|
| 130 |
+
# Convert threshold to logit
|
| 131 |
p = max(1e-6, min(1 - 1e-6, float(threshold)))
|
| 132 |
logit_threshold = float(np.log(p / (1 - p)))
|
| 133 |
|
| 134 |
+
# Detect faces
|
| 135 |
det_bgr, scale = resize_for_detection(image_bgr, max_side=1280)
|
| 136 |
det_rgb = cv2.cvtColor(det_bgr, cv2.COLOR_BGR2RGB)
|
| 137 |
faces = detect(det_rgb, face_detector, margin=int(margin))
|
| 138 |
|
| 139 |
if not faces:
|
| 140 |
+
return spoof_default_result()
|
| 141 |
|
| 142 |
inv_scale = 1.0 / scale if scale != 0 else 1.0
|
| 143 |
image_rgb = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2RGB)
|
| 144 |
|
| 145 |
face_crops = []
|
| 146 |
meta = []
|
| 147 |
+
|
| 148 |
for face in faces:
|
| 149 |
det_conf = face.get("confidence", None)
|
| 150 |
det_conf = round(float(det_conf), 2) if det_conf is not None else None
|
|
|
|
| 164 |
})
|
| 165 |
|
| 166 |
if not face_crops:
|
| 167 |
+
return spoof_default_result()
|
| 168 |
|
| 169 |
predictions = infer(face_crops, liveness_session, input_name, model_img_size=128)
|
| 170 |
if not predictions:
|
| 171 |
+
return spoof_default_result()
|
| 172 |
|
| 173 |
out_faces = []
|
| 174 |
for m, pred in zip(meta, predictions):
|
| 175 |
+
r = process_with_logits(pred, logit_threshold)
|
| 176 |
out_faces.append({**m, "liveness": r})
|
| 177 |
|
| 178 |
+
# Return only the liveness dict of the best face
|
| 179 |
best = max(out_faces, key=lambda d: (d["det_confidence"] or 0.0))
|
| 180 |
+
return best["liveness"]
|
| 181 |
|
| 182 |
demo = gr.Interface(
|
| 183 |
fn=predict_liveness,
|
|
|
|
| 193 |
|
| 194 |
|
| 195 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 196 |
if __name__ == "__main__":
|
| 197 |
demo.launch()
|