| from src.geometry import FaceGeometry | |
| def extract_features(landmarks, hairline_y=None): | |
| geo = FaceGeometry(landmarks, hairline_y=hairline_y) | |
| thirds = geo.facial_thirds_ratio() | |
| if thirds: | |
| u = thirds["upper"] | |
| m = thirds["middle"] | |
| l = thirds["lower"] | |
| max_dev = max(abs(u - 0.333), abs(m - 0.333), abs(l - 0.333)) | |
| if max_dev < 0.05: | |
| thirds_balance = 0.0 | |
| elif max_dev < 0.10: | |
| thirds_balance = 0.5 | |
| else: | |
| thirds_balance = 1.0 | |
| else: | |
| u = m = l = 0.33 | |
| thirds_balance = 0.0 | |
| return { | |
| "face_ratio": geo.face_ratio(), | |
| "jaw_ratio": geo.jaw_ratio(), | |
| "jaw_to_height": geo.jaw_to_height(), | |
| "eye_ratio": geo.eye_ratio(), | |
| "eye_height": geo.eye_height(), | |
| "lip_ratio": geo.lip_ratio(), | |
| "nose_position": geo.nose_position_ratio(), | |
| "lower_face_ratio": geo.lower_face_ratio(), | |
| "chin_prominence": geo.chin_prominence(), | |
| "symmetry": geo.symmetry_score(), | |
| "upper_third": thirds["upper"] if thirds else 0.33, | |
| "middle_third": thirds["middle"] if thirds else 0.33, | |
| "lower_third": thirds["lower"] if thirds else 0.33, | |
| "upper_third": u, | |
| "middle_third": m, | |
| "lower_third": l, | |
| "mid_lower_ratio": geo.middle_lower_ratio(), | |
| "thirds_balance": thirds_balance, | |
| } |