SmartFace Embedded Toolkit  4.2.1
Loading...
Searching...
No Matches
example_face_liveness.cpp

Example of use of sfe_face library

#include "sfe_toolkit/sfe_core.h"
#include "sfe_toolkit/sfe_face.h"
#include <iomanip>
#include <regex>
#include <sstream>
#include <vector>
#include "annotate.hpp"
#include "solvers.h"
#include "utils.hpp"
#include <unordered_map>
std::string image_probe = "assets/face/images/obiwan0.png";
std::string solver_face_detect = SOLVER_FACE_DETECT;
std::string solver_face_landmarks = SOLVER_FACE_LANDMARKS;
std::string solver_face_liveness = SOLVER_FACE_LIVENESS;
const auto SOLVER_PARAMETERS = std::vector<SFESolverParameter>{};
size_t face_size_min = 28;
size_t face_size_max = 170;
float detection_threshold = 0.1f;
SFEImage &image, const size_t face_size_min,
const size_t face_size_max,
size_t &recommended_width,
size_t &recommended_height) {
// If the face detection solver requires static input (filename contains width
// and height), we need to prepare the input image accordingly Typically the
// format is: face_detect_accurate_mask_w1920h1080_op11.onnxrt.solver NOTE:
// This can be hardcoded into the application, or we can parse the width and
// height from the solver filename
// Try to use regex capture to extract width and height from solver name
std::smatch width_height;
if (std::regex_match(solver_face_detect, width_height,
std::regex(".*w([0-9]+)h([0-9]+).*"))) {
recommended_width = std::stoi(width_height[1]);
recommended_height = std::stoi(width_height[2]);
} else {
// Solvers without width and height in name can accept dynamic input
// image size. For best results we recommend to scale the input image to
// a resolution recommended by the sfeFaceDetectInputSize function
// Use sfeFaceDetectInputSize to get recommended input image dimensions
// for given min_face_size/max_face_size
SFEDetectionInputSize input_size{};
image,
SFEFaceDetectionAccuracyType::SFE_FACE_DETECT_ACCURACY_TYPE_ACCURATE,
face_size_min, face_size_max, &input_size);
utils::checkError(error);
recommended_width = input_size.width;
recommended_height = input_size.height;
};
}
inline void printFaceDetectionInfo(SFEDetection &detected_face) {
std::cout << "SFEDetection{ " << std::endl;
std::cout << " confidence: " << detected_face.confidence << std::endl;
std::cout << " SFEBoundingBox{ " << std::endl;
std::cout << " x: " << detected_face.bounding_box.x << std::endl;
std::cout << " y: " << detected_face.bounding_box.y << std::endl;
std::cout << " width: " << detected_face.bounding_box.width << std::endl;
std::cout << " height: " << detected_face.bounding_box.height << std::endl;
std::cout << " }" << std::endl;
std::cout << " } " << std::endl;
std::cout << std::endl;
}
inline void prinFaceAreaInfo(SFEFaceArea &face_area) {
std::cout << "SFEFaceArea{ " << std::endl;
std::cout << " size: " << face_area.size << " [px]" << std::endl;
std::cout << " area: " << face_area.area << std::endl;
std::cout << " area_in_image: " << face_area.area_in_image << std::endl;
std::cout << "}" << std::endl;
std::cout << std::endl;
}
inline void printFaceHeadPose(SFEFaceHeadPose &head_pose) {
std::cout << "SFEFaceHeadPose{" << std::endl;
std::cout << " pitch: " << head_pose.pitch << std::endl;
std::cout << " yaw: " << head_pose.yaw << std::endl;
std::cout << " roll: " << head_pose.roll << std::endl;
std::cout << "} " << std::endl;
std::cout << std::endl;
}
inline void printFaceSize(size_t face_size) {
std::cout << "Face size: " << face_size << " [px]" << std::endl;
std::cout << std::endl;
}
inline void printMaskConfidence(float mask_confidence) {
std::cout << "Mask confidence: " << mask_confidence << std::endl;
std::cout << std::endl;
}
inline void
std::cout << "SFEFaceQualityAttributes{" << std::endl;
std::cout << " sharpness: " << quality_attributes.sharpness << std::endl;
std::cout << " brightness: " << quality_attributes.brightness << std::endl;
std::cout << " contrast: " << quality_attributes.contrast << std::endl;
std::cout << " unique_intensity_levels: "
<< quality_attributes.unique_intensity_levels << std::endl;
std::cout << "} " << std::endl;
std::cout << std::endl;
}
void printHelp() {
std::cout << "Help: Usage of the program." << std::endl;
std::cout << "Options:" << std::endl;
std::cout << "-h: Display help." << std::endl;
std::cout << "-p: Probe image file." << std::endl;
std::cout << "-d: Path to detector solver." << std::endl;
std::cout << "-l: Path to landmarks solver." << std::endl;
std::cout << "-i: Path to liveness solver." << std::endl;
std::cout << "-t: Face detection threshold. <0,1>" << std::endl;
std::cout << "-m: Minimal face size in pixels to detect." << std::endl;
std::cout << "-x: Max face size in pixels to detect." << std::endl;
}
int main(int argc, char *argv[]) {
{ // STAGE: 0 Parse command line arguments
// Map to store argument values
std::unordered_map<std::string, std::string> args;
// Parse command line arguments
for (int i = 1; i < argc; ++i) {
std::string arg = argv[i];
if (arg[0] == '-') {
if (arg == "-h") {
return 0;
}
// Check if there's a next argument and it isn't another option
if (i + 1 < argc && argv[i + 1][0] != '-') {
args[arg] = argv[++i];
} else {
std::cerr << "Option " << arg << " requires a value." << std::endl;
return 1;
}
} else {
std::cerr << "Unknown option: " << arg << std::endl;
return 1;
}
}
// Assign values to variables based on parsed arguments
if (args.count("-p"))
image_probe = args["-p"];
if (args.count("-d"))
solver_face_detect = args["-d"];
if (args.count("-l"))
solver_face_landmarks = args["-l"];
if (args.count("-i"))
solver_face_liveness = args["-i"];
if (args.count("-t"))
detection_threshold = std::stof(args["-t"]);
if (args.count("-m"))
face_size_min = std::stoi(args["-m"]);
if (args.count("-x"))
face_size_max = std::stoi(args["-x"]);
utils::printFormatted("PARAMETERS");
// Print resource names
std::cout << "Probe image: " << image_probe << std::endl;
// Print solver names
std::cout << "Face detection solver: " << solver_face_detect << std::endl;
std::cout << "Face landmarks solver: " << solver_face_landmarks
<< std::endl;
std::cout << "Face liveness solver: " << solver_face_liveness << std::endl;
// Print other options
std::cout << "Min face size [px]: " << face_size_min << std::endl;
std::cout << "Max face size [px]: " << face_size_max << std::endl;
std::cout << "Detection threshold: " << detection_threshold << std::endl;
}
utils::printToolkitInfo();
SFEError error{};
SFESolver detector_solver{};
DEFER(sfeSolverFree(detector_solver));
SFESolver landmarks_solver{};
DEFER(sfeSolverFree(landmarks_solver));
SFESolver liveness_solver{};
DEFER(sfeSolverFree(liveness_solver));
{ // STAGE 1: loading solvers
utils::printFormatted("LOADING solvers");
// Initialize face detection solver
error = sfeSolverCreate(
SOLVER_PARAMETERS.size(), &detector_solver);
utils::checkError(error);
// Initialize landmarks detection solver
error = sfeSolverCreate(
SOLVER_PARAMETERS.size(), &landmarks_solver);
utils::checkError(error);
// Initialize face liveness solver
error = sfeSolverCreate(
SOLVER_PARAMETERS.size(), &liveness_solver);
utils::checkError(error);
}
SFEImage image{};
DEFER(sfeImageFree(image));
SFEImage resized_image{};
DEFER(sfeImageFree(resized_image));
{ // STAGE 2: Load image
utils::printFormatted("FACE DETECTION");
// Load image data from file
auto image_data = utils::readFile(image_probe);
// Decode image from data
error = sfeImageDecode(image_data.data(), image_data.size(), &image);
utils::checkError(error);
size_t recommended_width{};
size_t recommended_height{};
// Calculate optimal input image size for face detection. Image will be
// resized to recommended size for optimal performance.
face_size_max, recommended_width,
recommended_height);
// Resize image to recommended size
// NOTE: This function will resize the image without preserving the aspect
// ratio of the image.
error = sfeImageResize(image, recommended_width, recommended_height,
&resized_image);
utils::checkError(error);
}
SFEDetection detected_face = {};
{ // STAGE 3: Detect face in the image
size_t detection_count = 1;
// Detect face in the image
error = sfeDetect(detector_solver, resized_image, detection_threshold,
&detected_face, &detection_count);
utils::checkError(error);
if (detection_count == 0) {
std::cout << "No face detected in the probe image." << std::endl;
return 0;
} else {
std::cout << "Found " << detection_count
<< " face(s) in the probe image. Using the face with highest "
"confidence."
<< std::endl;
}
}
std::vector<SFEFaceLandmarks> landmarks(SFE_FACE_LANDMARK_COUNT);
{ // STAGE 4: Get face landmarks
// Use landmarks detection solver to get relevant landmarks for
// the detected face
error = sfeFaceLandmarks(landmarks_solver, image, &detected_face,
landmarks.data());
utils::checkError(error);
}
SFEFaceLiveness liveness = {};
{ // STAGE 5: Liveness check
utils::printFormatted("LIVENESS CHECK");
error = sfeFaceLivenessPassive(liveness_solver, image, landmarks.data(),
&liveness);
utils::checkError(error);
}
{ // STAGE 6: Print the liveness score
std::cout << "Liveness score is " << liveness.score;
// Recommended threshold for liveness detection, see EER threshold for distant fast mode in the documentation
static const float LIVENESS_THRESHOLD = 0.81f;
if (liveness.score < LIVENESS_THRESHOLD) {
std::cout << " which is below " << LIVENESS_THRESHOLD
<< " threshold. Face is spoof." << std::endl;
} else {
std::cout << " which is above " << LIVENESS_THRESHOLD
<< " threshold. Face is genuine." << std::endl;
}
}
// Optional face attributes to check for further analysis. See the
// documentation for more information.
float face_size = 0;
float mask_confidence;
SFEFaceArea face_area = {};
SFEFaceHeadPose head_pose{};
SFEFaceQualityAttributes quality_attributes{};
{ // STAGE 6: (optional) Face attributes
utils::printFormatted("FACE ATTRIBUTES");
error = sfeFaceSize(image, landmarks.data(), &face_size);
utils::checkError(error);
error = sfeFaceMaskConfidence(landmarks.data(), &mask_confidence);
utils::checkError(error);
error = sfeFaceArea(image, landmarks.data(), &face_area);
utils::checkError(error);
error = sfeFaceHeadPose(image, landmarks.data(), &head_pose);
utils::checkError(error);
error =
sfeFaceQualityAttributes(image, landmarks.data(), &quality_attributes);
utils::checkError(error);
}
{ // STAGE 8: Print the face attributes
printFaceDetectionInfo(detected_face);
printFaceSize(face_size);
printMaskConfidence(mask_confidence);
prinFaceAreaInfo(face_area);
printFaceHeadPose(head_pose);
printFaceQualityAttributes(quality_attributes);
}
{ // STAGE 9: Annotate the image
// Render bounding box
std::stringstream label;
label << "Face" << std::fixed << std::setprecision(2)
<< " d:" << detected_face.confidence << " m:" << mask_confidence
<< " l:" << liveness.score;
annotate::labelBox(image, label.str(), detected_face.bounding_box, annotate::WHITE,
annotate::GREEN);
// Render landmarks
for (auto &landmark : landmarks) {
auto x = landmark.x * image.width;
auto y = landmark.y * image.height;
annotate::circle(image, x, y, 3, annotate::YELLOW);
}
// Save annotated image
size_t size = image.width * image.height * 3;
auto png_file = std::vector<unsigned char>(size);
error = sfeImageEncode(image, SFE_IMAGE_FORMAT_PNG, png_file.data(), &size);
utils::checkError(error);
png_file.resize(size);
utils::saveFile("face_liveness.png", png_file);
std::cout << std::endl;
std::cout << "Annotated image saved to face_liveness.png" << std::endl;
}
utils::printFormatted("FINISHED");
}
void printHelp()
void getRecommendedImageSize(const std::string &solver_face_detect, SFEImage &image, const size_t face_size_min, const size_t face_size_max, size_t &recommended_width, size_t &recommended_height)
Get recommended image size for face detection.
std::string solver_face_detect
Solvers to use in example, the defaults are filled in by CMake.
std::string image_probe
float detection_threshold
const auto SOLVER_PARAMETERS
size_t face_size_max
size_t face_size_min
Required size of the face to be detected.
std::string solver_face_landmarks
void printMaskConfidence(float mask_confidence)
std::string solver_face_liveness
void printFaceSize(size_t face_size)
void printFaceHeadPose(SFEFaceHeadPose &head_pose)
void prinFaceAreaInfo(SFEFaceArea &face_area)
void printFaceQualityAttributes(SFEFaceQualityAttributes &quality_attributes)
void printFaceDetectionInfo(SFEDetection &detected_face)
void sfeSolverFree(SFESolver solver)
Free memory associated with SFESolver.
SFEError sfeImageResize(SFEImageView image, size_t width, size_t height, SFEImage *out_image)
Resize image.
void * SFESolver
Solver provides an abstract interface over inference models and engines.
Definition sfe_core.h:102
void * SFEError
Error type used to hold optional error message.
Definition sfe_core.h:97
void sfeImageFree(SFEImage image)
Free memory associated with SFEImage.
@ SFE_IMAGE_FORMAT_PNG
Portable Network Graphics format.
Definition sfe_core.h:53
SFEError sfeImageEncode(SFEImageView image, SFEImageFormat image_format, unsigned char *out_data, size_t *in_out_data_len)
Encode SFEImage into a buffer with specified image_format.
SFEError sfeSolverCreate(const char *solver_file, const SFESolverParameter *solver_parameters, size_t solver_parameters_count, SFESolver *out_solver)
Create new solver from solver file.
SFEError sfeImageDecode(const unsigned char *data, size_t data_len, SFEImage *out_image)
Decode SFEImage from raw image data of various formats. Eg. PNG, JPEG ..
SFEError sfeDetect(SFESolver solver, SFEImageView image, float threshold, SFEDetection *out_detections, size_t *in_out_detection_count)
Detect objects in the source image using unified detection API.
SFEError sfeFaceArea(SFEImageView image, SFEFaceLandmarks face_landmarks[SFE_FACE_LANDMARK_COUNT], SFEFaceArea *out_area)
Get face area.
SFEError sfeFaceSize(SFEImageView image, SFEFaceLandmarks face_landmarks[SFE_FACE_LANDMARK_COUNT], float *face_size)
Get the face size in pixels from the face landmarks and the source image. Face size is defined as a m...
SFEError sfeFaceDetectInputSize(SFEImageView image, SFEFaceDetectionAccuracyType detection_mode, size_t min_face_size, size_t max_face_size, SFEDetectionInputSize *out_input_size)
Calculation of recommended input image width and height according to desired minimal and maximal size...
SFEError sfeFaceMaskConfidence(const SFEFaceLandmarks face_landmarks[SFE_FACE_LANDMARK_COUNT], float *out_mask_confidence)
Get confidence from given landmarks if the face is wearing a face mask.
#define SFE_FACE_LANDMARK_COUNT
Definition sfe_face.h:17
SFEError sfeFaceLandmarks(SFESolver solver, SFEImageView image, const SFEDetection *detection, SFEFaceLandmarks out_face_landmarks[SFE_FACE_LANDMARK_COUNT])
Detect 23 landmarks of the face detected in the area of source image marked with detection.
SFEError sfeFaceLivenessPassive(SFESolver solver, SFEImageView image, SFEFaceLandmarks face_landmarks[SFE_FACE_LANDMARK_COUNT], SFEFaceLiveness *out_liveness)
Passive liveness score calculation.
SFEError sfeFaceHeadPose(SFEImageView image, SFEFaceLandmarks face_landmarks[SFE_FACE_LANDMARK_COUNT], SFEFaceHeadPose *out_head_pose)
Calculate angle rotations of head towards camera reference frame from given landmarks.
SFEError sfeFaceQualityAttributes(SFEImageView image, SFEFaceLandmarks face_landmarks[SFE_FACE_LANDMARK_COUNT], SFEFaceQualityAttributes *out_quality_attributes)
Calculate face image quality attributes from given source image and landmarks data.
float y
Y coordinate of the top left corner of the bounding box relative to source image size - range <0,...
float width
Width of the bounding box relative to source image size - range <0,1>
float x
X coordinate of the top left corner of the bounding box relative to source image size - range <0,...
float height
Height of the bounding box relative to source image size - range <0,1>
Core detection - tagged union containing all detection types.
SFEBoundingBox bounding_box
Bounding box (common for all detection types)
float confidence
Detection confidence - range <0,1> (common for all detection types)
Detection input size struct.
Definition sfe_face.h:59
Face area struct.
Definition sfe_face.h:237
float area_in_image
size of face area intersected with the whole image and relative to the whole image; value in range <0...
Definition sfe_face.h:244
float area
size of face area relative to the whole image; value in range <0,1>
Definition sfe_face.h:241
float size
absolute face size
Definition sfe_face.h:239
Face head pose struct containing angle rotations of head.
Definition sfe_face.h:259
float yaw
Face attribute representing angle rotation of head towards camera reference frame around Y-axis as pe...
Definition sfe_face.h:265
float pitch
Face attribute representing angle rotation of head towards camera reference frame around X-axis as pe...
Definition sfe_face.h:262
float roll
Face attribute representing angle rotation of head towards camera reference frame around Z-axis as pe...
Definition sfe_face.h:268
Face liveness struct.
Definition sfe_face.h:220
float score
Normalized passive liveness score - range <0,1>
Definition sfe_face.h:222
Face quality attributes struct.
Definition sfe_face.h:283
float unique_intensity_levels
Normalized face attribute for evaluating whether an area of face has appropriate number of unique int...
Definition sfe_face.h:305
float sharpness
Normalized face attribute for evaluating whether an area of face image is not blurred....
Definition sfe_face.h:288
float brightness
Normalized face attribute for evaluating whether an area of face is correctly exposed....
Definition sfe_face.h:293
float contrast
Normalized face attribute for evaluating whether an area of face is contrast enough....
Definition sfe_face.h:299
Raw owned raster image representation, HWC|BGR order.
Definition sfe_core.h:70
size_t width
Width of the image in pixels.
Definition sfe_core.h:72
size_t height
Height of the image in pixels.
Definition sfe_core.h:74