Plumerai Familiar Face Identification Python API¶
This document describes the Python API for the Plumerai Familiar Face Identification software for videos.
The API¶
The Python API consists of a single class with a constructor that needs to be ran once, and a process_frame
function that needs to be executed on each input frame. Furthermore, the API provides functions to 'enroll' or 'register' new people by generating and storing a 'face embedding'.
FaceIdentification¶
init¶
Initializes a new face identification object.
This needs to be called only once at the start of the application.
Arguments:
- height:: The height of the input image in pixels.
- width:: The width of the input image in pixels.
Returns:
- Nothing.
process_frame¶
FaceIdentification.process_frame(
image, delta_t: float = 0.0
) -> tuple[ErrorCodeFamiliarFaceID, tuple[BoxPrediction, ...]]
Process a single frame from a video sequence with RGB input.
The image must have the height and width that was specified when the FaceIdentification object was created.
Arguments:
- image: A tensor of shape
(video_height, video_width, 3)
with RGB image data. It can be a Numpy array or TensorFlow, PyTorch or Jax tensor. - delta_t: The time in seconds it took between this and the previous video frame (1/fps). If left to the default of 0, then the system clock will be used to compute this value.
Returns:
- An error code of type
ErrorCodeFamiliarFaceID
and the resulting bounding-boxes found in the frame.
single_image¶
FaceIdentification.single_image(
image
) -> tuple[ErrorCodeFamiliarFaceID, tuple[BoxPrediction, ...]]
Process a single image not part of a video sequence.
This should not be used for video data. It can be used for face enrollments from a set of images. The returned box id values are not related to those returned by process_frame
or other calls to single_frame
.
Arguments:
- image: A tensor of shape
(*, *, 3)
with RGB image data. It can be a Numpy array, or TensorFlow, PyTorch or Jax tensor.
Returns:
- An error code of type
ErrorCodeFamiliarFaceID
and the resulting bounding-boxes found in the frame.
add_face_embedding¶
FaceIdentification.add_face_embedding(
face_embedding: np.ndarray, face_id: int
) -> ErrorCodeFamiliarFaceID
Add a face embedding to the face library for any process_frame
calls that follow.
A second embedding for the same face id will overwrite the previous one.
When process_frame
detects a person that matches a face, it will use the face_id
provided here in the returned BoxPrediction
.
Arguments:
- face_embedding: A NumPy face embedding as returned by
finish_face_enrollment
. If an empty NumPy array is given, the specifiedface_id
is removed. - face_id: A non-negative integer face-ID, which is not allowed to be equal to
FACE_ID_UNIDENTIFIED
orFACE_ID_NOT_IN_LIBRARY
.
Returns:
- Returns the error code "SUCCESS" on success, "INVALID_FACE_ID" or "INVALID_EMBEDDING" on failure.
remove_face_embedding¶
Remove a face embedding from the face library for any process_frame
calls that follow.
Arguments:
- face_id: An integer face-ID, previously used in
add_face_embedding
.
Returns:
- Returns the error code "SUCCESS" on success.
start_face_enrollment¶
FaceIdentification.start_face_enrollment(
region_of_interest: tuple[float, float, float, float] = (0, 0, 1, 1),
) -> ErrorCodeFamiliarFaceID
This starts the enrollment procedure for a new face.
During subsequent calls to process_frame
or single_image
, face embeddings will be computed. During enrollment there should be a single face in the image, or in the optional region specified by region_of_interest
, ideally clearly and completely visible. The enrollment procedure can be finalized by calling finish_face_enrollment
.
Arguments:
- region_of_interest: This can be set to a region of interest in the image as
y_min
,x_min
,y_max
,x_max
in relative coordinates. Only faces that have overlap with this region will be used for enrollment. The default of0, 0, 1, 1
includes the entire image.
Returns:
- Returns the error code "ENROLLMENT_IN_PROGRESS" if the enrollment procedure was started successfully, otherwise returns "ALREADY_ENROLLING".
finish_face_enrollment¶
Finalize the face enrollment procedure started by start_face_enrollment
and obtain the face embedding.
The application is responsible for storing this embedding on persistent storage and passing it to add_face_embedding
every time an instance of this class is created. The face enrollment procedure does not automatically call add_face_embedding
.
Arguments:
- None.
Returns:
- A tuple with an error code of type ErrorCodeFamiliarFaceID and the resulting face embedding vector
get_face_id¶
Retrieve the face ID that belongs to a person box.
Retrieves the face ID given a person box returned by process_frame
. This function has three possible return values:
FACE_ID_UNIDENTIFIED
when the provided box is not ofCLASS_PERSON
or when a face is not yet identified, e.g. the face is not fully visible.FACE_ID_NOT_IN_LIBRARY
when the face is visible but not found in the library, e.g. when a stranger is in view.- Any integer face-ID provided previously to
add_face_embedding
when a familiar face is identified.
This function should not be called directly after restoring from a previous state.
Arguments:
- person_box: A box returned by
process_frame
withclass_id
equal toDetectionClass::CLASS_PERSON
.
Returns:
- Returns an integer face-ID as provided when calling
add_face_embedding
when a face is identified,FACE_ID_UNIDENTIFIED
orFACE_ID_NOT_IN_LIBRARY
when it is not identified successfully, orFACE_ID_UNIDENTIFIED
when the provided box is not a valid person box.
BoxPrediction¶
A structure representing a single resulting bounding box. Coordinates are between 0 and 1, the origin is at the top-left.
class BoxPrediction(NamedTuple):
y_min: float # top coordinate between 0 and 1 in height dimension
x_min: float # left coordinate between 0 and 1 in width dimension
y_max: float # bottom coordinate between 0 and 1 in height dimension
x_max: float # right coordinate between 0 and 1 in width dimension
confidence: float # between 0 and 1, higher means more confident
id: int = 0 # the tracked identifier of this box
class_id: int = DetectionClass.CLASS_UNKNOWN # the class of the object
DetectionClass¶
ErrorCodeFamiliarFaceID¶
Error codes returned which can be returned by the API.
class ErrorCodeFamiliarFaceID(Enum):
# All went well
SUCCESS = 0
# Should not occur, contact Plumerai if this happens
INTERNAL_ERROR = -1
# The `delta_t` parameter should be >= 0
INVALID_DELTA_T = -2
# Everything OK, enrollment is in progress.
ENROLLMENT_IN_PROGRESS = -1000
# This library is not built with support for face identification.
FACE_IDENTIFICATION_DISABLED = -1001
# Called `start_face_enrollment` but enrollment is already started.
ALREADY_ENROLLING = -1002
# Last frame was ignored because multiple faces were detected within the
# specified region of interest. Try again with one face visible.
MULTIPLE_FACES_IN_VIEW = -1003
# Last frame was ignored because no face was detected, or it was not clearly
# visible, or the lighting was not good enough for enrollment. Try again with
# one face clearly visible.
NO_FACE_IN_VIEW = -1004
# Last frame was ignored because the face detection was too close to the
# edge. Too close to the edge means that the border of the face bounding-box
# is within 2% of the edge of the image. In theory, it is OK if a face is
# close to edge, however, it can also mean that a face is only partially
# visible. Thus, this check is added to prevent partial faces from being
# enrolled.
FACE_CLOSE_TO_EDGE = -1005
# Called `finish_face_enrollment` but enrollment was not started.
NOT_ENROLLING = -1006
# This is returned by `finish_face_enrollment` when the enrollment failed
# because no faces could be detected in any of the images.
ENROLLMENT_FAILED = -1007
# This is returned by `finish_face_enrollment` when a face embedding vector
# is returned but the quality of the enrollment is very low. In this case
# re-enrolllment is recommended.
LOW_QUALITY_ENROLLMENT = -1008
# Returned by `add_face_embedding` or `remove_face_embedding` when the
# provided face ID is invalid.
INVALID_FACE_ID = -1009
# Returned by `add_face_embedding` when the provided face embedding is
# invalid.
INVALID_EMBEDDING = -1010
Constants¶
If the face-identification model is not confident about a face, it will output FACE_ID_UNIDENTIFIED
. If the model is confident that a face is not in the face library, it will output FACE_ID_NOT_IN_LIBRARY
.
Example usage¶
Below is an example of using the Python API shown above.
import numpy as np
import plumerai_people_detection as ppd_api
# Settings, to be changed as needed
width = 1600 # camera image width in pixels
height = 1200 # camera image height in pixels
# Initialize the `FaceIdentification` object
ffid = ppd_api.FaceIdentification(height, width)
# ---------------------- Enrollment starting ------------------------------
error_code = ffid.start_face_enrollment()
if error_code != ppd_api.ErrorCodeFamiliarFaceID.ENROLLMENT_IN_PROGRESS:
raise RuntimeError(f"Error in 'start_face_enrollment': {error_code}")
# Enroll for 10 frames (just an example, more frames is better)
for t in range(10):
# Some example input here, normally this is where camera data is acquired
image = np.zeros((height, width, 3), dtype=np.uint8)
# Process the frame. If the enrollment frames come from a video source,
# then use 'process_frame' instead.
error_code, predictions = ffid.single_image(image)
if error_code != ppd_api.ErrorCodeFamiliarFaceID.SUCCESS:
raise RuntimeError(f"Error in 'single_image': {error_code}")
# Finish enrollment
error_code, embedding = ffid.finish_face_enrollment()
if error_code != ppd_api.ErrorCodeFamiliarFaceID.SUCCESS:
raise RuntimeError(f"Error in 'finish_face_enrollment': {error_code}")
# Add the embedding to the library with face id '1'.
error_code = ffid.add_face_embedding(embedding, face_id=1)
if error_code != ppd_api.ErrorCodeFamiliarFaceID.SUCCESS:
raise RuntimeError(f"Error in 'add_face_embedding': {error_code}")
# ---------------------- Enrollment finished ------------------------------
# Loop over frames in a video stream (example: 10 frames)
for t in range(10):
# Some example input here, normally this is where camera data is acquired
image = np.zeros((height, width, 3), dtype=np.uint8)
# The time between two video frames in seconds. In this example we assume
# a constant frame rate of 30 fps, but variable rates are supported.
delta_t = 1. / 30.
# Process the frame
error_code, predictions = ffid.process_frame(image, delta_t)
if error_code != ppd_api.ErrorCodeFamiliarFaceID.SUCCESS:
raise RuntimeError(f"Error in 'process_frame': {error_code}")
# Display the results
for p in predictions:
if p.class_id != ppd_api.DetectionClass.CLASS_PERSON:
continue
face_id = ffid.get_face_id(p)
print(
f"Box #{p.id} with face ID {face_id} with confidence {p.confidence} "
f"@(x,y)->({p.x_min:.2f},{p.y_min:.2f})-({p.x_max:.2f},{p.y_max:.2f})"
)
if len(predictions) == 0:
print("No bounding boxes found in this frame")