Stream images

Stream JPEG frames from a camera or a video and receive, for each frame, the faces detected in it and their expression measurements. ## Sending frames Send each frame as one binary message containing a complete JPEG. Every frame gets exactly one reply: a `measurement.result`, or an `error`. - **Configuration:** send `session.update` before your first frame. A `session.update` after your first frame ends the session with `config_invalid`. - **Rejected frames:** a frame that is empty, is not a JPEG, or is over the pixel limit gets `invalid_image_frame`. A frame over 2MB ends the session with `message_too_large`. - **Send rate:** send frames as they are captured or played, up to 3 per second. A frame sent faster can get `rate_limited` and is not measured. From a live camera, drop it and send the next frame; from a recording, wait briefly and resend it. To measure a recorded video faster than it plays, send its frames to the video file endpoint. - **Under load:** replies slow down; a frame within the send rate is never rejected because the server is busy. - **Failures:** a frame whose measurement fails gets `internal_error`; resend it if you need its result. Five failures in a row end the session. ## Matching results to frames Replies arrive in the order you sent the frames, so pair them by position. - Keep a queue of the frames you have sent. Remove the oldest for each `measurement.result`, and for each `error` with code `invalid_image_frame`, `rate_limited` or `internal_error`. - Other errors do not consume a frame: they answer a JSON message you sent, or end the session. - Frames sent before `session.close` receive their replies before `session.closed`. - `frame_id` counts results, not frames. It does not advance for a frame that received an `error`, so do not use it as an index into your own frames. ## Detection and measurement The server detects the faces in each frame and lists them in `faces`. It measures the largest of them, up to the limit below, and lists those first, largest first. - A face is skipped, and is not listed, if its detection confidence is below `face.threshold` or its bounding box is under `face.min_size` pixels on its shorter side. - Each measured face carries its bounding box, its detection confidence, a `face_id`, and two sets of scores: `expressions` for emotional expressions such as amusement or interest, and `descriptions` for visible facial actions such as a smile or a jaw drop. - The faces that were not measured follow, most confident first, up to 32 faces in all. Each carries its bounding box and detection confidence, with `face_id`, `expressions` and `descriptions` null. - A frame with no faces is a normal result: a `measurement.result` with an empty `faces` array. - `face_id` tracks a face across frames in the session. A face normally keeps its ID while it is measured, and can get it back after it leaves the view or is no longer among the largest faces. IDs restart in every session and do not identify a person. ## Limits | Limit | Value | |---|---| | Message size | 2MB | | Pixels per frame | 8,294,400 (the area of a 3840x2160 image) | | Send rate | 3 frames per second | | Faces measured per frame | 2 by default, the largest; contact support to raise it | | Faces listed per frame | 32 | | Consecutive measurement failures | 5, after which the session ends | ## Message flow A session of four frames, the third a PNG sent by mistake. The rejected frame gets no `frame_id`, so the last result is `frame_id` 2. In `session.closed`, `received.frames` counts only the 3 frames accepted for measurement, and `produced.measurements` counts faces, so it is 4. | Direction | Message | |---|---| | receive | `session.created` | | send | `session.update` (if you change the configuration) | | receive | `session.updated` (if you sent `session.update`) | | send | frame | | receive | `measurement.result` (`frame_id` 0, face 0) | | send | frame | | receive | `measurement.result` (`frame_id` 1, faces 0 and 1) | | send | frame (a PNG, not a JPEG) | | receive | `error` (`invalid_image_frame`) | | send | frame | | receive | `measurement.result` (`frame_id` 2, face 1) | | send | `session.close` | | receive | `session.closed` (`received.frames` 3, `produced.measurements` 4) |