Real-time facial expression and emotion analysis, on device. The Affectiva Vision SDK detects and tracks human faces in video frames and images, and estimates facial expressions, emotions, head pose, and image quality β with all processing running locally on the CPU. No cloud connection, no GPU, and no external model files.
What the SDK provides
- Face detection and tracking β finds faces in each frame and assigns a stable identifier per face so metrics can be followed over time.
- Facial expressions β 20+ facial-action metrics (smile, brow raise, eye closure, blink and blink rate, yawn, attention, speaking, β¦).
- Emotions β estimates of joy, anger, sadness, surprise, fear, disgust, contempt and neutrality, along with composite measures such as valence and engagement.
- Measurements β head pose (pitch, yaw, roll, in degrees) and interocular distance (in pixels).
- Face points β pixel coordinates of key landmarks (outer eye corners, nose tip, chin tip) and a per-face bounding box.
- Image quality β a brightness metric for verifying capture conditions.
Metrics overview
Metrics are delivered per tracked face, per processed frame. The table below is a summary β the Metrics guide explains every individual metric in detail: what it represents, the facial actions behind it, its value range, and how to interpret it. Identifier spellings per platform are in the API reference.
| Family | Examples | Value range | Enabled by |
|---|---|---|---|
| Expressions | smile, brow furrow, eye closure, blink, yawn, attention, speaking, pain | 0β100 (degree of expression). Event-like metrics (BLINK, YAWN) report 0 or 100; BLINK_RATE is blinks/minute;
PAIN is a continuous 0β100 score | Feature::EXPRESSIONS |
| Emotions | joy, anger, sadness, surprise, valence, engagement, neutral | 0β100, except VALENCE variants: β100 (negative) to +100 (positive) | Feature::EMOTIONS |
| Measurements | pitch, yaw, roll, interocular distance | degrees (0/0/0 = facing camera); pixels for interocular distance | always delivered with tracked faces |
| Face points | outer eye corners, nose tip, chin tip, bounding box | pixel coordinates | always delivered with tracked faces |
| Quality | brightness | 0β100 | always delivered with tracked faces |
| Face confidence | Face::getConfidence() | 0β100 (confidence in the presence of the face) | always delivered with tracked faces |
How it works
Your application owns the camera or video source. Each captured image is wrapped in a
Frame (raw pixels + timestamp) and pushed to a Detector. The detector runs
the embedded neural networks and delivers results through your listener as a map of tracked
Face objects.
Your application
camera / video / image source
β
Frame
pixels, color format, rotation, timestamp (ms)
β
Detector
FrameDetector (async)
SyncFrameDetector (sync)
β
FaceListener
map<FaceId, Face> per processed frame
The same five-step flow applies on every platform:
- Construct a detector β
SyncFrameDetectorto process every frame on your thread, orFrameDetectorfor asynchronous, real-time processing that may drop frames to keep up. - Enable the features you need (
EXPRESSIONS,EMOTIONS). Enabling only what you use saves CPU. - Register your listener to receive results.
- Start the detector with your license key.
- Process frames with strictly increasing timestamps; read metrics in your listener callback. Stop the detector when finished.
The same flow in every language
#include <SyncFrameDetector.h>
#include <FaceListener.h>
using namespace affdex::vision;
SyncFrameDetector detector; // 1. construct
detector.enable({Feature::EXPRESSIONS, Feature::EMOTIONS}); // 2. enable
MyListener listener; // your FaceListener subclass
detector.setFaceListener(&listener); // 3. listen
detector.start("YOUR-LICENSE-KEY"); // 4. start
Frame frame(0, 640, 480, pixels, Frame::ColorFormat::BGR);
detector.process(frame); // 5. process
detector.stop();import affvisionpy as af
detector = af.SyncFrameDetector() # 1. construct
detector.enable_features({af.Feature.expressions, # 2. enable
af.Feature.emotions})
listener = Listener() # your af.FaceListener subclass
detector.set_face_listener(listener) # 3. listen
detector.start("YOUR-LICENSE-KEY") # 4. start
frame = af.Frame(0, 640, 480, pixels,
af.Frame.ColorFormat.bgr, af.Frame.Rotation.upright)
detector.process(frame) # 5. process
detector.stop()FrameDetector detector = new FrameDetector(context); // 1. construct
detector.enable(Arrays.asList(Feature.EXPRESSIONS, // 2. enable
Feature.EMOTIONS));
detector.setImageListener(resultsListener); // 3. listen (results on Android)
detector.start(licenseKey); // 4. start
Frame frame = new Frame(640, 480, nv21,
Frame.ColorFormat.YUV_NV21, timestampMs);
detector.process(frame); // 5. process
detector.stop();
detector.dispose(); // release native resourcesSupported platforms
| Platform | Architecture | Toolchain | Package | Language APIs |
|---|---|---|---|---|
| Ubuntu 24.04 | x86_64 | GCC 13 | shared library (.so) + headers; Python wheel | C++17, Python 3.12 |
| Windows 10 / 11 | x64 | Visual Studio 2022 (MSVC) | .dll / .lib + headers; Python wheel | C++17, Python 3.13 |
| Android 8.0+ (API 26) | arm64-v8a | NDK 24 / Clang | AAR (com.affectiva.android:emotion-sdk) | Java |
- Android packages target 64-bit ARM devices only (
arm64-v8a); 32-bit devices and x86 emulator images are not supported. The native libraries are aligned for 16 KB memory pages, as required by recent Android releases. - For platforms not listed here (for example macOS or iOS), contact your iMotions / Affectiva representative.
Hardware requirements
The SDK’s inference runs entirely on the CPU β no GPU, NPU, or special
accelerator is required on any supported platform. Processing is multithreaded: by default the SDK
uses number of cores β 1 worker threads, and every detector constructor accepts a
maxThreads parameter to bound this.
Camera and input
- Input is raw frame data from a webcam, phone camera, or decoded video stream. The SDK does not open cameras itself; your application supplies pixel buffers.
- Accepted pixel formats: RGB, BGR, RGBA, BGRA, YUV (NV21, I420, YUY2), and 8-bit grayscale (grayscale: C++/Python only).
- Rotated inputs (90Β°/180Β°/270Β°) are handled by declaring the rotation on the
Frameβ useful for portrait-mounted cameras and phones. - The default processing rate is 30 fps and is configurable per detector. Higher input rates are simply downsampled by the asynchronous detector.
Licensing
A valid license key is required to use the SDK. The key is checked when a detector starts, and is resolved in this order:
- The string passed to the detector’s
start(license)method, if non-empty. - Otherwise, the
AFFECTIVA_LICENSE_KEYenvironment variable.
If neither is provided, start() throws an exception. To obtain a license key or
discuss licensing terms, contact sales@affectiva.com.
Frequently asked questions
Is inference real-time or batch?
Real-time, per frame. Your application pushes frames and receives metrics through its listener as
each frame is processed. Two detector types cover both use cases:
FrameDetector processes asynchronously with real-time semantics (it drops frames rather
than fall behind live input), while SyncFrameDetector processes every frame
synchronously on the calling thread β the natural choice for offline analysis of recorded
video, where dropping frames is not acceptable. The default processing rate is 30 fps and is
configurable per detector.
What is the per-frame latency?
There is no single figure: per-frame cost depends on the CPU, the number of tracked faces, and
which features are enabled. The SDK is designed to keep pace with live camera input at the default
30 fps on hardware meeting the hardware requirements. To measure for
your deployment, run SyncFrameDetector on your target hardware and time
process() β and enable only the features you need, which directly reduces per-frame
cost.
How many concurrent streams can one instance handle?
One detector processes one stream. To analyze multiple streams in the same process, create one
detector per stream and use the maxThreads constructor parameter to partition CPU cores
between them (by default a detector uses cores β 1 threads). The practical stream count is
bounded by CPU cores; validate on your target hardware.
Does the SDK run on device, or does it call back to a server?
Everything runs on device. Inference executes locally on the CPU, the neural-network models are compiled into the library, and the SDK makes no network calls β no frames, metrics, or telemetry ever leave the machine. It works fully offline.
Which languages and platforms are supported, and which are first-class?
C++ is the core API and the most complete surface. The Python and Android (Java) APIs are bindings over the same native engine, so metric values are identical across platforms; minor API-surface differences are noted in the API reference. Supported targets are listed under Supported platforms: Linux (x86_64 and aarch64), Windows 10/11, and Android 8.0+ (arm64-v8a). macOS and iOS are not currently offered β contact your iMotions / Affectiva representative.
What does a reference implementation look like?
Each quick start contains a complete, runnable program β from project setup through processing video and reading metrics β and the integration itself is five calls: construct, enable features, register a listener, start, process. The SDK is a library linked into your application, not a hosted API, so most integration effort typically goes into your own camera or video plumbing rather than the SDK itself.
How are model updates delivered?
Models ship inside the SDK library and are versioned with it. Updating models means adopting a new SDK release on your own schedule β there are no forced redeploys, no runtime model downloads, and no hot-swapping. This is deliberate: deployments are reproducible and fully offline, and the models in the version you validated are the models that run in production.
What observability hooks does the SDK expose?
The SDK is a passive library: it emits no telemetry and has no built-in exporters for tools like
Prometheus, CloudWatch, or Datadog. Instrument it from your application β listener callbacks are a
natural point to count processed frames, faces, and callback-to-callback timing β and surface those
measurements through your existing monitoring stack. Errors are reported as exceptions
(asynchronous errors via ProcessStatusListener::onProcessingException; see the
API reference for per-platform equivalents).
Get started
Metrics guide
Every metric explained in detail β what it represents, its facial-action basis, value ranges, and interpretation guidance.
C++ quick start
Link the native library, process video frames, and read metrics with a
FaceListener.
Python quick start
Install the affvisionpy wheel and analyze webcam or video input with OpenCV.
Android quick start
Add the AAR to your Gradle project and process camera frames on device.
API reference
Every public class, method, and enum β documented per platform: C++, Python, and Android.