Spaces:
Sleeping
Sleeping
| import numpy as np | |
| from scipy.spatial import distance as dist | |
| def euclidean_dist(p1, p2): | |
| return dist.euclidean(p1, p2) | |
| def extract_features(landmarks): | |
| """ | |
| Extracts geometric features from 68-point facial landmarks. | |
| Indices based on dlib/OpenCV LBF standard. | |
| Key Points: | |
| - Jaw: 0-16 (8 is chin) | |
| - Eyebrows: 17-21 (left), 22-26 (right) | |
| - Nose: 27-35 (27 is bridge top) | |
| - Eyes: 36-41 (left), 42-47 (right) | |
| """ | |
| landmarks = np.array(landmarks) | |
| # defined points | |
| jaw_left = landmarks[4] | |
| jaw_right = landmarks[12] | |
| chin = landmarks[8] | |
| temple_left = landmarks[0] | |
| temple_right = landmarks[16] | |
| brow_left = landmarks[19] | |
| brow_right = landmarks[24] | |
| # Measurements | |
| jaw_width = euclidean_dist(jaw_left, jaw_right) | |
| face_width = euclidean_dist(temple_left, temple_right) # Cheekbone/Temple width | |
| brow_mid = (brow_left + brow_right) / 2.0 | |
| face_length = euclidean_dist(chin, brow_mid) | |
| # Approximations | |
| forehead_width = face_width * 0.9 # Hard to measure with 68 points, assume slightly narrower than temples | |
| # Ratios | |
| lw_ratio = face_length / face_width if face_width > 0 else 0 | |
| jaw_ratio = jaw_width / face_width if face_width > 0 else 0 | |
| forehead_ratio = forehead_width / face_width if face_width > 0 else 0 # Will be constant-ish with this heuristic | |
| return { | |
| "lw_ratio": lw_ratio, | |
| "jaw_ratio": jaw_ratio, | |
| "forehead_ratio": forehead_ratio | |
| } | |