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

Example of use of sfe_face library

#include "sfe_toolkit/sfe_core.h"
#include "sfe_toolkit/sfe_face.h"
#include <regex>
#include <vector>
#include "solvers.h"
#include "utils.hpp"
#include <unordered_map>
std::string gallery = "./assets/face/entities/";
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_template = SOLVER_FACE_EXTRACTION;
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{};
SFEError error = sfeFaceDetectInputSize(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;
};
}
SFESolver &detector_solver,
SFESolver &landmarks_solver,
SFESolver &template_solver) {
SFEImage image{};
DEFER(sfeImageFree(image));
SFEImage resized_image{};
DEFER(sfeImageFree(resized_image));
SFEError error{};
{ // STAGE 1 Load image
// Load image data from file
auto image_data = utils::readFile(image_path);
// 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 = {};
size_t detection_count = 1;
{ // STAGE 3: Detect face in the image
// Detect face in the image
error = sfeDetect(detector_solver, resized_image, detection_threshold,
&detected_face, &detection_count);
utils::checkError(error);
if (detection_count == 0) {
throw std::runtime_error("No face detected in the image.");
}
}
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);
}
SFEFaceTemplate face_template = {};
{ // STAGE 5: Extract probe face template
// Use template extraction solver to create new face
// template
error =
sfeFaceTemplateExtract(template_solver, image, &detected_face,
landmarks.data(), &face_template);
utils::checkError(error);
}
return face_template;
}
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 << "-g: Path to folder with entities." << std::endl;
std::cout << "-d: Path to detector solver." << std::endl;
std::cout << "-l: Path to landmarks solver." << std::endl;
std::cout << "-e: Path to extraction solver." << std::endl;
std::cout << "-t: Detection threshold. <0,1>" << std::endl;
std::cout << "-i: Identification 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("-g"))
gallery = args["-g"];
if (args.count("-d"))
solver_face_detect = args["-d"];
if (args.count("-l"))
solver_face_landmarks = args["-l"];
if (args.count("-e"))
solver_face_template = args["-e"];
if (args.count("-t"))
detection_threshold = std::stof(args["-t"]);
if (args.count("-i"))
identification_threshold = std::stof(args["-i"]);
if (args.count("-m"))
face_size_min = std::stoi(args["-m"]);
if (args.count("-x"))
face_size_max = std::stoi(args["-x"]);
utils::printFormatted("EXAMPLE PARAMETERS");
// Print resource names
std::cout << "Probe image: " << image_probe << std::endl;
std::cout << "Entities folder: " << gallery << 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 template solver: " << solver_face_template << 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;
std::cout << "Identification threshold: " << identification_threshold
<< std::endl;
}
utils::printToolkitInfo();
SFEError error{};
SFESolver detector_solver{};
DEFER(sfeSolverFree(detector_solver));
SFESolver landmarks_solver{};
DEFER(sfeSolverFree(landmarks_solver));
SFESolver template_solver{};
DEFER(sfeSolverFree(template_solver));
{ // STAGE 0: 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 template extraction solver
error = sfeSolverCreate(
SOLVER_PARAMETERS.size(), &template_solver);
utils::checkError(error);
}
SFEFaceTemplate probe_face_template;
{ // STAGE 1: Extract probe face template
utils::printFormatted("PROBE TEMPLATE EXTRACTION");
probe_face_template = extractTemplate(image_probe, detector_solver,
landmarks_solver, template_solver);
std::cout << "Probe face template extracted." << std::endl;
}
std::vector<SFEFaceTemplate> gallery_face_templates = {};
std::vector<SFEEntity> enitity_pairs = {};
{ // STAGE 2: Extract templates for each image in entity folder and associate them
// with entity(UUID)
// Entities(UUID) tells the ownership of the templates in the gallery.
// For example:
// UUID1 -> template1
// UUID1 -> template2
// UUID2 -> template3
// UUID2 -> template4
// ...
// UUUIDN -> templateN
utils::printFormatted("ENTITIES TEMPLATE EXTRACTION");
// Get folder names from entities_gallery
auto folder_names = utils::getFiles(gallery);
for (auto folder_name : folder_names) {
if (folder_name.find(".jpg") != std::string::npos ||
folder_name.find(".png") != std::string::npos) {
continue;
}
// Genereate UUID for each entity.
auto entity = utils::generateEntity();
auto entity_folder = gallery + folder_name + "/";
std::cout << "Folder: " << entity_folder << ", entity UUID: ["
<< static_cast<int>(entity.uuid[0]) << ", "
<< static_cast<int>(entity.uuid[1]) << "..."
<< static_cast<int>(entity.uuid[15]) << "]" << std::endl;
// Get image names from folder
auto image_names = utils::getFiles(entity_folder);
// Extract template for each image in the folder
for (auto image_name : image_names) {
auto image_path = entity_folder + image_name;
auto face_template = extractTemplate(
image_path, detector_solver, landmarks_solver, template_solver);
gallery_face_templates.push_back(face_template);
enitity_pairs.push_back(entity);
}
}
}
size_t candidate_count = 1;
std::vector<SFEEntityIdentificationCandidate> results(candidate_count);
int best_candidate_index = -1;
{ // STAGE 3: Identification with entities.
// Probe template is matched against every template in the gallery.
// Function returns entity(UUID) which owns the best matching template.
utils::printFormatted("1:N IDENTIFICATION WITH ENTITIES");
&probe_face_template, gallery_face_templates.data(),
enitity_pairs.data(), gallery_face_templates.size(),
identification_threshold, results.data(), &candidate_count, 4);
utils::checkError(error);
if (candidate_count == 0) {
std::cout << "No candidates found. Exiting.." << std::endl;
return 0;
}
std::cout << std::endl;
std::cout << "Found " << results.size() << " candidates " << std::endl;
for (int i = 0; i < results.size(); i++) {
std::cout << "Index: " << i << ", Score: " << results[i].score
<< std::endl;
std::cout << "Entity UUID: ["
<< static_cast<int>(results[i].entity.uuid[0]) << ", "
<< static_cast<int>(results[i].entity.uuid[1]) << "..."
<< static_cast<int>(results[i].entity.uuid[15]) << "]"
<< std::endl;
}
}
utils::printFormatted("FINISHED");
}
void printHelp()
float identification_threshold
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
std::string solver_face_template
std::string gallery
SFEFaceTemplate extractTemplate(std::string image_path, SFESolver &detector_solver, SFESolver &landmarks_solver, SFESolver &template_solver)
std::string image_path
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.
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 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...
#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 sfeFaceEntityIdentify(SFEFaceTemplate *probe_face_template, SFEFaceTemplate *templates_gallery, SFEEntity *entities_gallery, size_t gallery_size, float matching_score_threshold, SFEEntityIdentificationCandidate *out_candidates, size_t *in_out_candidates_count, size_t thread_count)
Get an ordered array of SFEEntityIdentificationCandidate from tested template best matches of probe t...
SFEError sfeFaceTemplateExtract(SFESolver solver, SFEImageView image, const SFEDetection *detection, const SFEFaceLandmarks face_landmarks[SFE_FACE_LANDMARK_COUNT], SFEFaceTemplate *out_face_template)
Extract template from source image and given face landmarks.
Core detection - tagged union containing all detection types.
Detection input size struct.
Definition sfe_face.h:59
Face template struct.
Definition sfe_face.h:107
Raw owned raster image representation, HWC|BGR order.
Definition sfe_core.h:70