File size: 2,025 Bytes
c4ba2f9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | import numpy as np
from dataclasses import dataclass
from typing import Optional
@dataclass
class ValidationResult:
valid: bool
error: Optional[str] = None
def validate_landmarks(landmarks) -> ValidationResult:
if landmarks is None:
return ValidationResult(False, "No face detected")
if len(landmarks) != 478:
return ValidationResult(False, f"Expected 478 landmarks, got {len(landmarks)}")
if np.any(np.isnan(landmarks)) or np.any(np.isinf(landmarks)):
return ValidationResult(False, "Landmarks contain NaN or inf values")
return ValidationResult(True)
def validate_features(features: dict) -> ValidationResult:
EXPECTED_KEYS = {
"face_ratio", "jaw_ratio", "jaw_to_height", "eye_ratio",
"eye_height", "lip_ratio", "nose_position", "lower_face_ratio",
"chin_prominence", "symmetry", "upper_third",
"middle_third", "lower_third", "mid_lower_ratio"
}
missing = EXPECTED_KEYS - set(features.keys())
if missing:
return ValidationResult(False, f"Missing features: {missing}")
for key, value in features.items():
if not np.isfinite(value):
return ValidationResult(False, f"Feature '{key}' is {value}")
if value < 0:
return ValidationResult(False, f"Feature '{key}' is negative: {value}")
SANITY_BOUNDS = {
"face_ratio": (0.5, 2.5),
"jaw_ratio": (0.3, 1.2),
"eye_ratio": (0.2, 0.9),
"symmetry": (0.0, 1.0),
"nose_position": (0.2, 0.8),
"upper_third": (0.1, 0.6),
"middle_third": (0.1, 0.6),
"lower_third": (0.1, 0.6),
"mid_lower_ratio": (0.2, 3.0),
}
for key, (lo, hi) in SANITY_BOUNDS.items():
if key in features and not (lo <= features[key] <= hi):
return ValidationResult(
False,
f"Feature '{key}' = {features[key]:.3f} is outside expected range [{lo}, {hi}]"
)
return ValidationResult(True) |