File size: 1,514 Bytes
a5a6a2e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2abac5a
 
a5a6a2e
 
 
 
 
2abac5a
 
a5a6a2e
 
 
 
 
 
 
 
 
 
 
 
 
 
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
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
    }