Metrics Guide

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, and YAWN. 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, PAIN
  • CONVERSATIONAL_ENGAGEMENT, CONVERSATIONAL_VALENCE
  • ADAPTIVE_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

FeatureUnlocksRelative cost per face
Feature::FACESface detection and tracking only β€” tracking, measurement, and quality metricsCheapest: 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::EXPRESSIONSall expression metrics, including the behavior signals and pain modelsRuns the full expression classifier set β€” the bulk of the SDK’s per-face analysis cost.
Feature::EMOTIONSall emotion metricsEmotions 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:

  • FACES only skips every expression and emotion model. This is the configuration to use for presence/head-pose applications, and the savings are large.
  • Disabling EMOTIONS while keeping EXPRESSIONS saves a little: the emotion metrics are lightweight combinations of the expression classifier outputs, which still run.
  • Disabling EXPRESSIONS while keeping EMOTIONS saves 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.

MetricValuesWhat it represents
Face ID (getId())integer, from 0A 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–100How 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, pixelsThe 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, pixelsKey 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 s
detector.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 s
detector.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 s

FACS 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

ReferenceAction UnitWhat movesSDK metric
AU 1Inner Brow RaiserThe inner ends of the eyebrows lift, creasing the middle of the forehead.INNER_BROW_RAISE
AU 1Inner Brow Raisersecond viewThe same action at lower intensity β€” useful for seeing how little movement still codes as AU 1.INNER_BROW_RAISE
AU 2Outer Brow RaiserThe outer ends of the eyebrows lift, arching them upward.BROW_RAISE
AU 2Outer Brow Raiserright side onlyOne 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 LowererThe eyebrows pull down and together; vertical creases form between them.BROW_FURROW

Eyes and eyelids

ReferenceAction UnitWhat movesSDK metric
AU 5Upper Lid RaiserThe upper eyelids pull up, exposing more of the eyeball.EYE_WIDEN
AU 5Upper Lid Raisersecond viewThe same action framed wider, with the brows staying still.EYE_WIDEN
AU 6Cheek RaiserThe cheeks lift and the skin at the outer eye corners bunches into crow’s feet.CHEEK_RAISE
AU 7Lid TightenerThe eyelids narrow and tighten without closing.LID_TIGHTEN
AU 43Eyes ClosedThe eyelids close and remain closed.EYE_CLOSURE
AU 45BlinkThe eyelids close and reopen in one rapid movement.BLINK

Nose and upper lip

ReferenceAction UnitWhat movesSDK metric
AU 9Nose WrinklerThe skin along the bridge of the nose draws up and wrinkles.NOSE_WRINKLE
AU 9Nose Wrinklerwith AU 4 and AU 10The 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 25The 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

ReferenceAction UnitWhat movesSDK metric
AU 12Lip Corner PullerThe lip corners pull up and back toward the ears β€” the core smile action.SMILE
AU 12Lip Corner Pullersecond viewThe same action at higher intensity. Performed on one side only, AU 12 is what SMIRK measures.SMILE
AU 14DimplerThe lip corners pull inward and tighten, dimpling the cheeks.DIMPLER
AU 15Lip Corner DepressorThe lip corners pull straight down into a frown.LIP_CORNER_DEPRESSOR
AU 15Lip Corner Depressorsecond viewThe same action with the chin relaxed, so the downward pull is easier to isolate.LIP_CORNER_DEPRESSOR

Lips and jaw

ReferenceAction UnitWhat movesSDK metric
AU 17Chin RaiserThe chin pushes upward, forcing the lower lip up with it.CHIN_RAISE
AU 18Lip Puckererwith AU 22 and AU 25, traceThe lips push forward and draw together into a kiss shape.LIP_PUCKER
AU 20Lip StretcherThe lip corners pull sideways, flattening and widening the lips.LIP_STRETCH
AU 24Lip PressorThe lips press firmly together without rolling inward.LIP_PRESS
AU 25Lips PartThe lips separate while the jaw stays largely still.MOUTH_OPEN
AU 26Jaw DropThe jaw lowers so the mouth hangs open β€” a larger movement than AU 25.JAW_DROP
AU 26Jaw Dropwith AU 25Jaw drop with the lips parted, the ordinary open-mouth combination.JAW_DROP
AU 28Lip Suckwith AU 26The lips roll inward over the teeth.LIP_SUCK

Head position

ReferenceAction UnitWhat movesSDK metric
AU 51Head Turn LeftThe head rotates toward the person’s left. Turning away from the camera is also what lowers ATTENTION.YAW
AU 52Head Turn RightThe head rotates toward the person’s right.YAW
AU 53Head UpThe head tips back so the chin rises.PITCH
AU 54Head DownThe head tips forward so the chin lowers.PITCH
AU 55Head Tilt LeftThe head tilts sideways toward the left shoulder.ROLL
AU 56Head Tilt RightThe head tilts sideways toward the right shoulder.ROLL
AU 57Head ForwardThe head moves toward the camera. The SDK has no forward/back angle; distance shows up as a larger interocular distance.INTEROCULAR_DISTANCE
AU 58Head BackThe head moves away from the camera, shrinking the interocular distance.INTEROCULAR_DISTANCE

Brow and forehead

MetricFACSRangeWhat it represents
INNER_BROW_RAISEAU 10–100The inner corners of the eyebrows lift (the “worried” brow shape). A component of surprise, sadness, and fear.
BROW_RAISEAU 20–100The outer portions of the eyebrows lift, arching the brows. Typical of surprise and heightened interest.
BROW_FURROWAU 40–100The eyebrows lower and draw together, creating vertical wrinkles between them. Associated with concentration, confusion, anger, and negative valence generally.

Eyes and cheeks

MetricFACSRangeWhat it represents
EYE_WIDENAU 50–100The upper eyelids raise, exposing more of the eye. Seen in surprise, fear, and alertness.
CHEEK_RAISEAU 60–100The 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_TIGHTENAU 70–100The eyelids tighten and narrow without closing (squinting). Seen in anger, scrutiny, and pain expressions.
EYE_CLOSUREAU 430–100Both eyelids are closed. Sustained high values indicate closed eyes (sleep, long blinks); brief spikes are blinks β€” use BLINK/BLINK_RATE for those.
BLINKAU 450 / 100Fires (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/minuteThe 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

MetricFACSRangeWhat it represents
NOSE_WRINKLEAU 90–100The skin along the nose wrinkles as the nose scrunches upward. The signature action of disgust.
UPPER_LIP_RAISEAU 100–100The upper lip pulls upward, often exposing the upper teeth. Associated with disgust and contempt-like sneers.

Mouth and jaw

MetricFACSRangeWhat it represents
SMILEAU 120–100The lip corners pull up and outward toward the ears β€” the core smile action and the strongest single indicator of positive valence and joy.
SMIRKunilateral AU 120–100An asymmetric smile: one lip corner pulled up while the other is not. A key component of contempt.
DIMPLERAU 140–100The lip corners tighten and pull inward, dimpling the cheeks. Often accompanies skepticism or suppressed reactions.
LIP_CORNER_DEPRESSORAU 150–100The lip corners pull downward into a frown. A primary sadness and negative-valence signal.
CHIN_RAISEAU 170–100The chin boss pushes upward, pressing the lower lip up (the “pout” base). Appears in sadness, doubt, and disgust.
LIP_PUCKERAU 180–100The lips push forward and pucker, as in a kiss shape. Can indicate doubt or deliberation.
LIP_STRETCHAU 200–100The lip corners stretch horizontally outward, flattening the lips. A component of fear expressions.
LIP_PRESSAU 240–100The lips press firmly together while remaining closed. Associated with suppressed anger, determination, or restraint.
MOUTH_OPENAU 250–100The lips part. Rises during speech, surprise, laughter, and relaxed-jaw states.
JAW_DROPAU 260–100The jaw lowers so the mouth hangs open β€” a larger movement than MOUTH_OPEN. Typical of surprise and yawning.
LIP_SUCKAU 280–100The lips roll inward and are sucked into the mouth. Often signals hesitation, nervousness, or self-restraint.

Behavior signals

MetricRangeWhat it represents
ATTENTION0–100How 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.
SPEAKING0–100The 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.
YAWN0 / 100Fires (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.
PAIN0–100The 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 .. +100
detector.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 .. +100
detector.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 .. +100

These 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

MetricRangeWhat it represents Β· key contributing facial actions
JOY0–100An expression of happiness or amusement. Driven by the smile (AU 12) together with cheek raising (AU 6) β€” the genuine-smile combination.
SURPRISE0–100A startled, astonished expression: raised inner and outer brows (AU 1 + 2), widened eyes (AU 5), and a dropped jaw (AU 26).
ANGER0–100A 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.
SADNESS0–100A sorrowful, dejected expression: raised inner brows (AU 1), furrowed brows (AU 4), downturned lip corners (AU 15), and a raised chin (AU 17).
FEAR0–100A 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).
DISGUST0–100A repulsed expression: a wrinkled nose (AU 9), raised upper lip (AU 10), raised chin (AU 17), and parted lips (AU 25).
CONTEMPT0–100A scornful, dismissive expression whose hallmark is asymmetry: the unilateral smirk, typically with some brow furrow and without a full symmetric smile.
NEUTRAL0–100The 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

MetricRangeWhat it represents
VALENCEβˆ’100 – +100The 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.
ENGAGEMENT0–100Overall 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 – +100Valence 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_ENGAGEMENT0–100Engagement adjusted for conversational settings, discounting routine speech-related mouth movement so the score reflects expressive engagement beyond merely talking.
ADAPTIVE_VALENCEβˆ’100 – +100Valence 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_ENGAGEMENT0–100Engagement measured relative to the individual’s rolling baseline β€” highlights deviations from that person’s typical expressiveness.
SENTIMENTALITY0–100A 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.
CONFUSION0–100A 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.

MetricUnitWhat it represents
PITCHdegreesHead 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.
YAWdegreesHead 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Β°.
ROLLdegreesHead 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_DISTANCEpixelsThe 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);  // pixels

Image quality metrics

MetricRangeWhat it represents
BRIGHTNESS0–100The 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-100

Interpretation 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, or BRIGHTNESS is 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 the SPEAKING metric.
  • 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.