face_verification_core 0.2.0

Cross-platform on-device face liveness and verification core.
Documentation
use face_verification_core::{
    verify_captured_photos, BoundingBox, CapturedPhotoAnalysis, FaceAnalysis, FrameAnalysis,
    HandAnalysis, Landmarks, LivenessChallenge, Point, TouchTarget, VerificationThresholds,
};

fn main() {
    let challenge = LivenessChallenge::six_step(3, TouchTarget::Nose);
    let photos = mock_photos();
    let result = verify_captured_photos(&photos, &challenge, &VerificationThresholds::default());

    println!("ok: {}", result.ok);
    println!("estimated_age: {:?}", result.estimated_age);
    println!(
        "embedding_len: {:?}",
        result.embedding.as_ref().map(Vec::len)
    );
    println!("error: {:?}", result.error);
}

fn mock_photos() -> Vec<CapturedPhotoAnalysis> {
    let yaws = [0.0, 0.0, 0.2, -0.2, 0.0, 0.0];
    let embeddings = [
        vec![0.0, 0.0, 0.0],
        vec![0.04, 0.0, 0.0],
        vec![0.08, 0.0, 0.0],
        vec![0.12, 0.0, 0.0],
        vec![0.16, 0.0, 0.0],
        vec![0.20, 0.0, 0.0],
    ];

    yaws.into_iter()
        .enumerate()
        .map(|(index, yaw)| {
            let mut frame = mock_frame(yaw);
            frame.face.embedding = Some(embeddings[index].clone());
            frame.face.age = Some(if index == 0 { 29.0 } else { 31.0 });
            frame.hands = vec![HandAnalysis {
                extended_fingers: Some(3),
                finger_tips: vec![Point { x: 50.0, y: 50.0 }],
            }];
            CapturedPhotoAnalysis {
                hash: format!("mock-photo-{index}"),
                frame,
                nsfw: None,
            }
        })
        .collect()
}

fn mock_frame(yaw: f32) -> FrameAnalysis {
    FrameAnalysis {
        face: FaceAnalysis {
            detected: true,
            bounding_box: Some(BoundingBox {
                x: 0.35,
                y: 0.25,
                width: 0.30,
                height: 0.35,
            }),
            landmarks: Some(landmarks_for_yaw(yaw)),
            embedding: Some(vec![0.0, 0.0, 0.0]),
            smile_score: Some(0.8),
            age: Some(30.0),
        },
        hands: vec![],
    }
}

fn landmarks_for_yaw(yaw: f32) -> Landmarks {
    let mut points = vec![Point { x: 0.0, y: 0.0 }; 68];
    let left = Point { x: 0.0, y: 50.0 };
    let right = Point { x: 100.0, y: 50.0 };
    let d_left = 50.0 * (1.0 + yaw);
    let nose_x = left.x + d_left;
    points[0] = left;
    points[2] = Point { x: 20.0, y: 50.0 };
    points[14] = Point { x: 80.0, y: 50.0 };
    points[16] = right;
    points[30] = Point { x: nose_x, y: 50.0 };
    Landmarks { points }
}