What every metric measures, the values it produces, and how to interpret them. Metric semantics are identical on all platforms β only the identifier spelling differs (see the per-platform API reference for names). Enum names below use the C++ spelling.
Reading metric values
Metrics are delivered per tracked face, per processed frame, through your listener. Three value shapes appear throughout the SDK:
- Continuous scores (0β100). Most expressions and emotions. The value expresses the degree of evidence that the facial action or emotion is present β 0 means absent, 100 means unmistakably present at full intensity. Values rise and fall smoothly as the expression forms and fades; there is no built-in notion of “detected” below 100, so you choose the threshold that fits your application (see best practices).
- Binary events (0 or 100).
BLINK, andYAWN. The SDK has already applied a decision threshold (and, where noted, temporal filtering), so the value simply toggles between “not occurring” (0) and “occurring” (100). - Physical measurements. Head pose in degrees, distances and coordinates in pixels. These are signed geometric quantities, not scores.
Baseline mode. Detectors constructed with BaselineMode::ON (C++ and
Python) score most expressions and emotions against a rolling-average baseline of the
individual’s recent facial behavior, so those metrics reflect change from that person’s
neutral rather than absolute intensity. This suits long sessions with one person (adapting away
resting facial features); the default OFF computes each frame statically and is the
right choice for comparing values across people.
Some metrics are the same in both modes β they have a single implementation and
no baseline variant: BLINK, BLINK_RATE, ATTENTION, every
measurement metric, and every
image quality metric.
Seven metrics require BaselineMode::ON and are otherwise absent
entirely β not zero, not NaN, simply missing from the results, so a lookup finds no
entry:
SPEAKING,YAWN,PAINCONVERSATIONAL_ENGAGEMENT,CONVERSATIONAL_VALENCEADAPTIVE_ENGAGEMENT,ADAPTIVE_VALENCE
They are defined only relative to an individual’s own baseline, so there is no static form to
report. If you need any of them, construct the detector with BaselineMode::ON.
Choosing features β and what it costs
Metric families are gated by features, and the enabled feature set directly controls how much CPU each frame costs: the SDK activates only the classifier models required by the features you enable, and skips the rest entirely. Enabling less genuinely computes less.
What is enabled by default
Nothing. A newly constructed detector has no features enabled, and
start() throws an exception (“at least one Feature must be enabled before calling
the start method”) if you start it that way. You must enable at least one feature. The
tracking, measurement, and image-quality metrics have no feature flag of their own β they activate
automatically alongside the first feature you enable and are delivered for every tracked face.
The three features
| Feature | Unlocks | Relative cost per face |
|---|---|---|
Feature::FACES | face detection and tracking only β tracking, measurement, and quality metrics | Cheapest: no expression or emotion models run at all. Use it when you only need presence,
head pose, or face position. (C++ and Python; the Android API exposes only
EXPRESSIONS and EMOTIONS.) |
Feature::EXPRESSIONS | all expression metrics, including the behavior signals and pain models | Runs the full expression classifier set β the bulk of the SDK’s per-face analysis cost. |
Feature::EMOTIONS | all emotion metrics | Emotions are computed from the expression classifiers, so enabling
EMOTIONS runs most of that machinery internally even with
EXPRESSIONS disabled (it does skip the dedicated pain and behavior-signal
models). See the note below. |
Enabling and disabling
Enable features on the detector before start(); you can also toggle them later β
changes take effect from the next processed frame. With SyncFrameDetector, toggle
between process() calls; with the asynchronous FrameDetector, prefer
configuring the feature set before start(), since processing runs on a background
thread.
// Cheapest configuration: tracking only (presence, bounding box, head pose, quality).
detector.enable(Feature::FACES);
// Add analysis families only when you need them:
detector.enable(Feature::EXPRESSIONS);
detector.enable({Feature::EXPRESSIONS, Feature::EMOTIONS}); // or several at once
// Drop a family you no longer need β e.g. between videos, or when your app
// leaves the screen that uses it. Takes effect from the next frame:
detector.disable(Feature::EMOTIONS);
// Introspection:
bool haveExpressions = detector.isEnabled(Feature::EXPRESSIONS);
std::set<Feature> enabled = detector.getEnabledFeatures();# Cheapest configuration: tracking only (presence, bounding box, head pose, quality).
detector.enable_feature(af.Feature.faces)
# Add analysis families only when you need them:
detector.enable_feature(af.Feature.expressions)
detector.enable_features({af.Feature.expressions, af.Feature.emotions})
# Drop a family you no longer need. Takes effect from the next frame:
detector.disable_feature(af.Feature.emotions)
# Introspection:
have_expressions = detector.is_enabled(af.Feature.expressions)
enabled = detector.get_enabled_features()// Android exposes EXPRESSIONS and EMOTIONS (no FACES-only mode).
detector.enable(Feature.EXPRESSIONS);
detector.enable(Arrays.asList(Feature.EXPRESSIONS, Feature.EMOTIONS));
// Drop a family you no longer need. Takes effect from the next frame:
detector.disable(Feature.EMOTIONS);
// Introspection:
boolean haveExpressions = detector.isEnabled(Feature.EXPRESSIONS);Does disabling features help performance?
Yes β with one structural caveat. What each configuration actually computes:
FACESonly skips every expression and emotion model. This is the configuration to use for presence/head-pose applications, and the savings are large.- Disabling
EMOTIONSwhile keepingEXPRESSIONSsaves a little: the emotion metrics are lightweight combinations of the expression classifier outputs, which still run. - Disabling
EXPRESSIONSwhile keepingEMOTIONSsaves little compute: the emotion metrics still require most expression classifiers internally (though the dedicated pain and behavior-signal models are skipped). It mainly changes what is reported. - Face detection and tracking always run while the detector is started β that cost is independent of the feature set.
Individual metrics inside a family (say, a single Action Unit) cannot be toggled through the
public API β the feature families are the granularity. For further performance control, the two
biggest levers are elsewhere: maxNumFaces (analysis cost scales with the number of
tracked faces) and, on the asynchronous FrameDetector, processFrameRate
(process fewer frames per second).
Face tracking metrics
Always delivered for every tracked face, regardless of which features are enabled.
| Metric | Values | What it represents |
|---|---|---|
Face ID (getId()) | integer, from 0 | A tracking identifier assigned when a face is first detected and kept stable for as long as
the face remains tracked. IDs start at 0 and count up as new faces appear.
If tracking is lost, the SDK holds the ID briefly and gives it back to the same face if it reappears β within about 5 seconds, in roughly the same place (the new bounding box must overlap the old one by at least a quarter). A face that returns later than that, or in a clearly different position, is treated as new and gets a new ID. You will see onFaceLost and then onFaceFound for the
same ID when a face is recovered this way, so do not assume an ID is finished the
first time it is reported lost.
Recovery is purely positional β the SDK performs no face recognition and compares no facial appearance. One consequence matters in practice: if a different person moves into that position within the recovery window, they can inherit the previous person’s ID. Use the ID to group metrics over time, never to establish identity. reset() discards all tracking state, after which IDs begin again at 0. |
Confidence (getConfidence()) | 0β100 | How certain the tracker is that this really is a face. Values near 100 indicate a clear, well-tracked face; low values occur with occlusion, extreme head angles, poor lighting, or very small faces. Consider ignoring or down-weighting metric readings from faces below a confidence of ~50, since expression estimates degrade along with tracking quality. |
Bounding box (getBoundingBox()) | 2 points, pixels | The axis-aligned rectangle enclosing the face, returned as its top-left and bottom-right corners in image coordinates (origin at the image’s top-left, x right, y down). |
Face points (getFacePoints()) | 4 landmarks, pixels | Key facial landmarks in image coordinates: OUTER_RIGHT_EYE,
OUTER_LEFT_EYE (the outer corners of the eyes), NOSE_TIP, and
CHIN_TIP. Right/left are from the person’s perspective. Useful for
overlays and for sanity-checking tracking. |
Read
Tracking metrics are delivered for every tracked face. Read them from the
Face objects handed to your per-frame results callback:
// In FaceListener::onFaceResults(faces, frame):
for (const auto& entry : faces)
{
const Face& face = entry.second;
float confidence = face.getConfidence(); // 0-100
auto box = face.getBoundingBox(); // {top-left, bottom-right}, pixels
auto points = face.getFacePoints(); // std::map<FacePoint, Point>
}# In FaceListener.results_updated(faces, frame):
for face_id, face in faces.items():
confidence = face.get_confidence() # 0-100
box = face.get_bounding_box() # [top-left, bottom-right], pixels
points = face.get_face_points() # {af.FacePoint: af.Point}// In ImageListener.onImageResults(faces, frame):
for (Face face : faces.values()) {
float confidence = face.getConfidence(); // 0-100
List<Point> box = face.getBoundingBox(); // top-left, bottom-right
Map<Face.FacePoint, Point> points = face.getFacePoints();
}Expression metrics
Expression metrics measure the movement of facial muscles. Most correspond to Action Units (AUs) from the Facial Action Coding System (FACS), the standard taxonomy of facial muscle movements β each is estimated by a classifier trained on that specific action. A score of 0 means the action is absent; 100 means it is present at full intensity. Expressions are independent: several can (and usually do) score high at once.
Enabled with Feature::EXPRESSIONS.
Enable & read
Enable the feature before start(); read the values from
getExpressions() in your per-frame results callback β one map entry per expression
metric listed below:
detector.enable(Feature::EXPRESSIONS); // before start()
// In FaceListener::onFaceResults(faces, frame):
auto expressions = face.getExpressions(); // std::map<Expression, float>
float smile = expressions[Expression::SMILE]; // 0-100
float blinkRate = expressions[Expression::BLINK_RATE]; // NaN for the first 10 sdetector.enable_features({af.Feature.expressions}) # before start()
# In FaceListener.results_updated(faces, frame):
expressions = face.get_expressions() # dict {af.Expression: float}
smile = expressions[af.Expression.smile] # 0-100
blink_rate = expressions[af.Expression.blink_rate] # NaN for the first 10 sdetector.enable(Arrays.asList(Feature.EXPRESSIONS)); // before start()
// In ImageListener.onImageResults(faces, frame):
Map<Face.Expression, Float> expressions = face.getExpressions();
Float smile = expressions.get(Face.Expression.SMILE); // 0-100
Float blinkRate = expressions.get(Face.Expression.BLINK_RATE); // NaN for the first 10 sFACS Action Unit reference
Every expression metric in this guide is named after an Action Unit β a single facial muscle movement in the Facial Action Coding System.
Brow and forehead
| Reference | Action Unit | What moves | SDK metric |
|---|---|---|---|
| AU 1Inner Brow Raiser | The inner ends of the eyebrows lift, creasing the middle of the forehead. | INNER_BROW_RAISE | |
| AU 1Inner Brow Raisersecond view | The same action at lower intensity β useful for seeing how little movement still codes as AU 1. | INNER_BROW_RAISE | |
| AU 2Outer Brow Raiser | The outer ends of the eyebrows lift, arching them upward. | BROW_RAISE | |
| AU 2Outer Brow Raiserright side only | One brow only. FACS codes left and right separately; the SDK reports a single combined intensity, so a one-sided raise scores lower than a symmetric one. | BROW_RAISE | |
| AU 4Brow Lowerer | The eyebrows pull down and together; vertical creases form between them. | BROW_FURROW |
Eyes and eyelids
| Reference | Action Unit | What moves | SDK metric |
|---|---|---|---|
| AU 5Upper Lid Raiser | The upper eyelids pull up, exposing more of the eyeball. | EYE_WIDEN | |
| AU 5Upper Lid Raisersecond view | The same action framed wider, with the brows staying still. | EYE_WIDEN | |
| AU 6Cheek Raiser | The cheeks lift and the skin at the outer eye corners bunches into crow’s feet. | CHEEK_RAISE | |
| AU 7Lid Tightener | The eyelids narrow and tighten without closing. | LID_TIGHTEN | |
| AU 43Eyes Closed | The eyelids close and remain closed. | EYE_CLOSURE | |
| AU 45Blink | The eyelids close and reopen in one rapid movement. | BLINK |
Nose and upper lip
| Reference | Action Unit | What moves | SDK metric |
|---|---|---|---|
| AU 9Nose Wrinkler | The skin along the bridge of the nose draws up and wrinkles. | NOSE_WRINKLE | |
| AU 9Nose Wrinklerwith AU 4 and AU 10 | The natural disgust cluster: nose wrinkle with lowered brows and a raised upper lip. Several SDK metrics rise together here. | NOSE_WRINKLE | |
| AU 10Upper Lip Raiserwith AU 25 | The upper lip pulls straight up, deepening the fold beside the nose. Shown with the lips parted, as the action is hard to perform alone. | UPPER_LIP_RAISE |
Lip corners and cheeks
| Reference | Action Unit | What moves | SDK metric |
|---|---|---|---|
| AU 12Lip Corner Puller | The lip corners pull up and back toward the ears β the core smile action. | SMILE | |
| AU 12Lip Corner Pullersecond view | The same action at higher intensity. Performed on one side only, AU 12 is what SMIRK measures. | SMILE | |
| AU 14Dimpler | The lip corners pull inward and tighten, dimpling the cheeks. | DIMPLER | |
| AU 15Lip Corner Depressor | The lip corners pull straight down into a frown. | LIP_CORNER_DEPRESSOR | |
| AU 15Lip Corner Depressorsecond view | The same action with the chin relaxed, so the downward pull is easier to isolate. | LIP_CORNER_DEPRESSOR |
Lips and jaw
| Reference | Action Unit | What moves | SDK metric |
|---|---|---|---|
| AU 17Chin Raiser | The chin pushes upward, forcing the lower lip up with it. | CHIN_RAISE | |
| AU 18Lip Puckererwith AU 22 and AU 25, trace | The lips push forward and draw together into a kiss shape. | LIP_PUCKER | |
| AU 20Lip Stretcher | The lip corners pull sideways, flattening and widening the lips. | LIP_STRETCH | |
| AU 24Lip Pressor | The lips press firmly together without rolling inward. | LIP_PRESS | |
| AU 25Lips Part | The lips separate while the jaw stays largely still. | MOUTH_OPEN | |
| AU 26Jaw Drop | The jaw lowers so the mouth hangs open β a larger movement than AU 25. | JAW_DROP | |
| AU 26Jaw Dropwith AU 25 | Jaw drop with the lips parted, the ordinary open-mouth combination. | JAW_DROP | |
| AU 28Lip Suckwith AU 26 | The lips roll inward over the teeth. | LIP_SUCK |
Head position
| Reference | Action Unit | What moves | SDK metric |
|---|---|---|---|
| AU 51Head Turn Left | The head rotates toward the person’s left. Turning away from the camera is also what lowers ATTENTION. | YAW | |
| AU 52Head Turn Right | The head rotates toward the person’s right. | YAW | |
| AU 53Head Up | The head tips back so the chin rises. | PITCH | |
| AU 54Head Down | The head tips forward so the chin lowers. | PITCH | |
| AU 55Head Tilt Left | The head tilts sideways toward the left shoulder. | ROLL | |
| AU 56Head Tilt Right | The head tilts sideways toward the right shoulder. | ROLL | |
| AU 57Head Forward | The head moves toward the camera. The SDK has no forward/back angle; distance shows up as a larger interocular distance. | INTEROCULAR_DISTANCE | |
| AU 58Head Back | The head moves away from the camera, shrinking the interocular distance. | INTEROCULAR_DISTANCE |
Brow and forehead
| Metric | FACS | Range | What it represents |
|---|---|---|---|
INNER_BROW_RAISE | AU 1 | 0β100 | The inner corners of the eyebrows lift (the “worried” brow shape). A component of surprise, sadness, and fear. |
BROW_RAISE | AU 2 | 0β100 | The outer portions of the eyebrows lift, arching the brows. Typical of surprise and heightened interest. |
BROW_FURROW | AU 4 | 0β100 | The eyebrows lower and draw together, creating vertical wrinkles between them. Associated with concentration, confusion, anger, and negative valence generally. |
Eyes and cheeks
| Metric | FACS | Range | What it represents |
|---|---|---|---|
EYE_WIDEN | AU 5 | 0β100 | The upper eyelids raise, exposing more of the eye. Seen in surprise, fear, and alertness. |
CHEEK_RAISE | AU 6 | 0β100 | The cheeks lift and the outer eye corners crinkle (“crow’s feet”). Together with a smile it distinguishes a genuine (Duchenne) smile β it is a key positive-valence signal. |
LID_TIGHTEN | AU 7 | 0β100 | The eyelids tighten and narrow without closing (squinting). Seen in anger, scrutiny, and pain expressions. |
EYE_CLOSURE | AU 43 | 0β100 | Both eyelids are closed. Sustained high values indicate closed eyes (sleep, long blinks);
brief spikes are blinks β use BLINK/BLINK_RATE for those. |
BLINK | AU 45 | 0 / 100 | Fires (100) on the frames where a blink β a rapid close-and-reopen of both eyes β is occurring, and returns to 0 in between. |
BLINK_RATE | β | blinks/minute | The recent blink frequency, expressed as blinks per minute over a rolling window. Reports NaN during the first 10 seconds after a face is found, while the window fills. Typical resting adult rates are roughly 8β21 blinks/min; sustained changes can indicate fatigue or cognitive load. |
Nose and upper lip
| Metric | FACS | Range | What it represents |
|---|---|---|---|
NOSE_WRINKLE | AU 9 | 0β100 | The skin along the nose wrinkles as the nose scrunches upward. The signature action of disgust. |
UPPER_LIP_RAISE | AU 10 | 0β100 | The upper lip pulls upward, often exposing the upper teeth. Associated with disgust and contempt-like sneers. |
Mouth and jaw
| Metric | FACS | Range | What it represents |
|---|---|---|---|
SMILE | AU 12 | 0β100 | The lip corners pull up and outward toward the ears β the core smile action and the strongest single indicator of positive valence and joy. |
SMIRK | unilateral AU 12 | 0β100 | An asymmetric smile: one lip corner pulled up while the other is not. A key component of contempt. |
DIMPLER | AU 14 | 0β100 | The lip corners tighten and pull inward, dimpling the cheeks. Often accompanies skepticism or suppressed reactions. |
LIP_CORNER_DEPRESSOR | AU 15 | 0β100 | The lip corners pull downward into a frown. A primary sadness and negative-valence signal. |
CHIN_RAISE | AU 17 | 0β100 | The chin boss pushes upward, pressing the lower lip up (the “pout” base). Appears in sadness, doubt, and disgust. |
LIP_PUCKER | AU 18 | 0β100 | The lips push forward and pucker, as in a kiss shape. Can indicate doubt or deliberation. |
LIP_STRETCH | AU 20 | 0β100 | The lip corners stretch horizontally outward, flattening the lips. A component of fear expressions. |
LIP_PRESS | AU 24 | 0β100 | The lips press firmly together while remaining closed. Associated with suppressed anger, determination, or restraint. |
MOUTH_OPEN | AU 25 | 0β100 | The lips part. Rises during speech, surprise, laughter, and relaxed-jaw states. |
JAW_DROP | AU 26 | 0β100 | The jaw lowers so the mouth hangs open β a larger movement than MOUTH_OPEN.
Typical of surprise and yawning. |
LIP_SUCK | AU 28 | 0β100 | The lips roll inward and are sucked into the mouth. Often signals hesitation, nervousness, or self-restraint. |
Behavior signals
| Metric | Range | What it represents |
|---|---|---|
ATTENTION | 0β100 | How much the person’s visual focus is directed toward the camera, estimated from head orientation. 100 means facing the camera directly; the score drops as the head turns away. Place the camera near what the person should attend to (screen, road, display) for this metric to represent attention to that target. Head orientation is what drives this metric β see AU 51/AU 52 (head turn) in the Action Unit reference. |
SPEAKING | 0β100 | The likelihood that the person is actively speaking, estimated from characteristic mouth movement patterns over a short time window (roughly the last second). Use it to gate other metrics if speech-related mouth motion would confound them. |
YAWN | 0 / 100 | Fires (100) while a yawn is detected β a wide, sustained jaw opening with the
characteristic yawn mouth shape β and returns to 0 otherwise. A classic drowsiness
indicator, especially combined with BLINK_RATE and EYE_CLOSURE. |
PAIN | 0β100 | The model’s continuous estimate that the person is expressing pain. The scale is anchored so that 50 corresponds to the model’s decision threshold: above 50, pain is more likely present than not. Values map monotonically to the model’s raw probability, so you can apply your own cutoff (e.g. a stricter 70) or track trends over time. |
Emotion metrics
Emotion metrics are computed from combinations of the expression metrics above, following the EMFACS mapping between facial action units and emotional expressions. Each score expresses the degree of evidence for that emotional expression: 0 = absent, 100 = fully present. Because they share underlying facial actions, related emotions can rise together β treat them as a profile, not as mutually exclusive classes.
Enabled with Feature::EMOTIONS.
Enable & read
Enable the feature before start(); read the values from getEmotions()
in your per-frame results callback β one map entry per emotion metric listed below:
detector.enable(Feature::EMOTIONS); // before start()
// In FaceListener::onFaceResults(faces, frame):
auto emotions = face.getEmotions(); // std::map<Emotion, float>
float joy = emotions[Emotion::JOY]; // 0-100
float valence = emotions[Emotion::VALENCE]; // -100 .. +100detector.enable_features({af.Feature.emotions}) # before start()
# In FaceListener.results_updated(faces, frame):
emotions = face.get_emotions() # dict {af.Emotion: float}
joy = emotions[af.Emotion.joy] # 0-100
valence = emotions[af.Emotion.valence] # -100 .. +100detector.enable(Arrays.asList(Feature.EMOTIONS)); // before start()
// In ImageListener.onImageResults(faces, frame):
Map<Face.Emotion, Float> emotions = face.getEmotions();
Float joy = emotions.get(Face.Emotion.JOY); // 0-100
Float valence = emotions.get(Face.Emotion.VALENCE); // -100 .. +100These metrics measure facial expression of emotion β what the face shows β not the person’s private internal state. Context (task, culture, conversation) determines how expression relates to experience; interpret accordingly.
The seven expressed emotions + neutral
| Metric | Range | What it represents Β· key contributing facial actions |
|---|---|---|
JOY | 0β100 | An expression of happiness or amusement. Driven by the smile (AU 12) together with cheek raising (AU 6) β the genuine-smile combination. |
SURPRISE | 0β100 | A startled, astonished expression: raised inner and outer brows (AU 1 + 2), widened eyes (AU 5), and a dropped jaw (AU 26). |
ANGER | 0β100 | A hostile, displeased expression: furrowed brows (AU 4), widened/tightened eyes (AU 5 + 7), raised upper lip (AU 10), raised chin (AU 17), and pressed lips (AU 24). A concurrent smile counts strongly against it. |
SADNESS | 0β100 | A sorrowful, dejected expression: raised inner brows (AU 1), furrowed brows (AU 4), downturned lip corners (AU 15), and a raised chin (AU 17). |
FEAR | 0β100 | A frightened, alarmed expression: raised and drawn-together brows (AU 1 + 2 + 4), widened eyes (AU 5), horizontally stretched lips (AU 20), and a parted mouth (AU 25). |
DISGUST | 0β100 | A repulsed expression: a wrinkled nose (AU 9), raised upper lip (AU 10), raised chin (AU 17), and parted lips (AU 25). |
CONTEMPT | 0β100 | A scornful, dismissive expression whose hallmark is asymmetry: the unilateral smirk, typically with some brow furrow and without a full symmetric smile. |
NEUTRAL | 0β100 | The degree to which the face shows no emotional expression β high when all expression metrics are quiet. Useful as a denominator or rest-state check. |
Composite and contextual measures
| Metric | Range | What it represents |
|---|---|---|
VALENCE | β100 β +100 | The overall positivity or negativity of the expression on a single bipolar scale. Positive evidence comes from the smile and cheek raise; negative evidence from actions such as inner brow raise, brow furrow, nose wrinkle, lip corner depressor, chin raise, and lip press. 0 is neutral; β100 is strongly negative, +100 strongly positive. This is often the single most useful summary metric. |
ENGAGEMENT | 0β100 | Overall facial expressiveness β how much the face is doing, regardless of whether it is positive or negative. Computed from the collective strength of the observed facial actions. High engagement with negative valence is a very different state than high engagement with positive valence; read the two together. |
CONVERSATIONAL_VALENCE | β100 β +100 | Valence adjusted for conversational settings, where speech constantly moves the mouth and
would otherwise distort expression readings. Prefer it over VALENCE when
analyzing people who are talking (calls, interviews, meetings). |
CONVERSATIONAL_ENGAGEMENT | 0β100 | Engagement adjusted for conversational settings, discounting routine speech-related mouth movement so the score reflects expressive engagement beyond merely talking. |
ADAPTIVE_VALENCE | β100 β +100 | Valence measured relative to a rolling baseline of the individual’s own recent expression, rather than an absolute scale. Adapts away resting facial features (e.g. naturally downturned lips), making it better for detecting changes in one person over a session and less suitable for comparing absolute levels across people. |
ADAPTIVE_ENGAGEMENT | 0β100 | Engagement measured relative to the individual’s rolling baseline β highlights deviations from that person’s typical expressiveness. |
SENTIMENTALITY | 0β100 | A tender, touched, “moved” emotional expression β the state evoked by nostalgic or heartwarming content. Detected from the characteristic co-occurrence of positive (smile-related) and sadness-related facial actions. |
CONFUSION | 0β100 | A puzzled expression: primarily the furrowed, drawn-together brow with associated eye narrowing β the face of someone struggling to parse what they see or hear. Useful for usability studies and content testing. |
Measurement metrics
Physical, signed quantities β delivered whenever a face is tracked (no feature flag needed). Head pose angles are in degrees, relative to a reference position of the face looking straight at the camera (pitch/yaw/roll = 0/0/0). Directions are from the person’s perspective, not the camera’s.
| Metric | Unit | What it represents |
|---|---|---|
PITCH | degrees | Head nod (rotation about the ear-to-ear axis). Positive = head tilted up (looking toward the ceiling), negative = tilted down. Sustained negative pitch is a common component of drowsiness (“head nodding”) and of looking at a phone. |
YAW | degrees | Head turn (rotation about the vertical axis). Positive = head turned to the person’s right, negative = to their left. Large |yaw| means the SDK sees the face in profile β expression accuracy degrades beyond roughly Β±30β40Β°. |
ROLL | degrees | Head tilt (rotation about the nose axis, like tilting the head toward a shoulder). Positive = tilted toward the person’s left shoulder, negative = toward their right. |
INTEROCULAR_DISTANCE | pixels | The distance between the two outer eye corner landmarks. Because eye spacing is roughly constant across adults, this is an excellent proxy for the face’s size in the image β and therefore for the person’s distance from the camera. It is also a quality gate: very small values (face far away / low resolution) mean less reliable expression metrics. As a rule of thumb, aim for at least ~30 pixels. |
Read
Measurements are delivered whenever a face is tracked. Read them from
getMeasurements() in your per-frame results callback:
// In FaceListener::onFaceResults(faces, frame):
auto measurements = face.getMeasurements(); // std::map<Measurement, float>
float yaw = measurements[Measurement::YAW]; // degrees
float iod = measurements[Measurement::INTEROCULAR_DISTANCE]; // pixels# In FaceListener.results_updated(faces, frame):
measurements = face.get_measurements() # dict {af.Measurement: float}
yaw = measurements[af.Measurement.yaw] # degrees
iod = measurements[af.Measurement.interocular_distance] # pixels// In ImageListener.onImageResults(faces, frame):
Map<Face.Measurement, Float> measurements = face.getMeasurements();
Float yaw = measurements.get(Face.Measurement.YAW); // degrees
Float iod = measurements.get(Face.Measurement.INTEROCULAR_DISTANCE); // pixelsImage quality metrics
| Metric | Range | What it represents |
|---|---|---|
BRIGHTNESS | 0β100 | The average brightness of the face region (not the whole image). 0 is completely dark, 100 completely washed out; mid-range values indicate well-exposed input. Use it to detect capture problems at the source β backlighting, lens obstruction, night-time cabins β and to qualify sessions before trusting the expression metrics. |
Read
Quality metrics are delivered whenever a face is tracked. Read them from
getQualities() in your per-frame results callback:
// In FaceListener::onFaceResults(faces, frame):
auto qualities = face.getQualities(); // std::map<Quality, float>
float brightness = qualities[Quality::BRIGHTNESS]; // 0-100# In FaceListener.results_updated(faces, frame):
brightness = face.get_qualities()[af.Quality.brightness] # 0-100// In ImageListener.onImageResults(faces, frame):
Float brightness = face.getQualities().get(Face.Quality.BRIGHTNESS); // 0-100Interpretation best practices
- Choose thresholds per use case. For continuous 0β100 scores, 50 is a sensible starting point for “present”, but the right cutoff depends on your costs: use higher thresholds (70β90) when false positives are expensive, lower ones (20β30) when you must not miss subtle expressions.
- Aggregate over time. Single-frame values are noisy by nature. For most decisions, use a rolling statistic β e.g. the mean over the last second, or “above threshold for N consecutive frames” β rather than reacting to individual frames. (The binary event metrics already do this internally.)
- Gate on tracking quality. Discard or down-weight readings when
getConfidence()is low, |yaw|/|pitch| are extreme, interocular distance is small, orBRIGHTNESSis at either extreme β expression estimates degrade with tracking quality. - Mind speech. Talking moves the mouth constantly. When subjects speak, prefer
CONVERSATIONAL_VALENCE/CONVERSATIONAL_ENGAGEMENT, or gate mouth-based expressions on theSPEAKINGmetric. - Read profiles, not single labels. Emotions share facial actions and are not mutually exclusive. Comparing the full emotion profile (or simply valence + engagement) is more robust than picking the single highest emotion.