Files
Immich/machine-learning/immich_ml/models/facial_recognition/detection.py
T
MertandGitHub 23f1f87dd0 feat(ml): optimized model inference (#31750)
* add immich_model

* graph per shape

* decouple ocr from ort

* prepared graphs

* use f16 for accelerators

* preload all shapes

* warm-up pass after preload

* keep precision for fp32 graphs

* ensure padding for text recognition

* optimize postprocessing for fp16 outputs

* handle raw ctc logits

* use f16 output for rknn

* mmap token embedding

* read device metadata
2026-09-25 14:56:44 -04:00

39 lines
1.5 KiB
Python

import numpy as np
from immich_model.constants import FACE_DETECTION_SIZE as DET_SIZE
from numpy.typing import NDArray
from immich_ml.models.base import InferenceModel
from immich_ml.models.transforms import decode_pil, letterbox, normalize, widen
from immich_ml.schemas import FaceDetectionOutput, ModelTask, ModelType, Shape
from immich_ml.sessions.policy import ShapePolicy
from ._ops import decode_scrfd, nms
class FaceDetector(InferenceModel):
depends = []
identity = (ModelType.DETECTION, ModelTask.FACIAL_RECOGNITION)
shape_policy = ShapePolicy(dims=(Shape(batch=1, height=DET_SIZE, width=DET_SIZE),))
def _predict(self, inputs: NDArray[np.uint8] | bytes, minScore: float) -> FaceDetectionOutput:
session = self.session.for_shape(self.shape_policy.dims[0])
canvas, scale = letterbox(decode_pil(inputs), DET_SIZE)
blob: NDArray[np.float32] | NDArray[np.uint8] = (
canvas[None]
if session.normalizes_input
else normalize(canvas.astype(np.float32), 127.5, 128).transpose(2, 0, 1)[None]
)
heads = [widen(head) for head in session.run(None, {session.get_inputs()[0].name: blob})]
scores, boxes, kps = decode_scrfd(heads, DET_SIZE)
candidates = scores >= minScore
scores, boxes, kps = scores[candidates], boxes[candidates] / scale, kps[candidates] / scale
keep = nms(boxes, scores)
return {
"boxes": boxes[keep].round(),
"scores": scores[keep],
"landmarks": kps[keep].reshape(-1, 5, 2),
}