face_verification_core 0.1.0

Cross-platform on-device face liveness and verification core.
Documentation
use serde::{Deserialize, Serialize};

use crate::{
    validate_pose, FrameAnalysis, LivenessChallenge, PoseCheck, VerificationThresholds,
    FRAMES_OK_TO_CAPTURE,
};

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum SessionDecision {
    Waiting { check: PoseCheck, valid_frames: u8 },
    CaptureReady { check: PoseCheck },
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct VerificationSession {
    challenge: LivenessChallenge,
    thresholds: VerificationThresholds,
    current_step: usize,
    valid_frames: u8,
}

impl VerificationSession {
    pub fn new(challenge: LivenessChallenge, thresholds: VerificationThresholds) -> Self {
        Self {
            challenge,
            thresholds,
            current_step: 0,
            valid_frames: 0,
        }
    }

    pub fn current_step(&self) -> usize {
        self.current_step
    }

    pub fn advance_step(&mut self) {
        self.current_step += 1;
        self.valid_frames = 0;
    }

    pub fn tick(&mut self, frame: &FrameAnalysis, _timestamp_ms: u64) -> SessionDecision {
        let check = self
            .challenge
            .steps
            .get(self.current_step)
            .map(|step| validate_pose(step, frame, &self.challenge, &self.thresholds))
            .unwrap_or_else(|| PoseCheck::fail("No hay más pasos en el reto."));

        if !check.valid {
            self.valid_frames = 0;
            return SessionDecision::Waiting {
                check,
                valid_frames: 0,
            };
        }

        self.valid_frames = self.valid_frames.saturating_add(1);
        if self.valid_frames >= FRAMES_OK_TO_CAPTURE {
            self.valid_frames = 0;
            SessionDecision::CaptureReady { check }
        } else {
            SessionDecision::Waiting {
                check,
                valid_frames: self.valid_frames,
            }
        }
    }
}