SmartFace Embedded Toolkit  4.2.1
Loading...
Searching...
No Matches
example_face_identify_entity.cpp
Go to the documentation of this file.
1
5#include "sfe_toolkit/sfe_core.h"
6#include "sfe_toolkit/sfe_face.h"
7
8#include <regex>
9#include <vector>
10
11#include "solvers.h"
12#include "utils.hpp"
13#include <unordered_map>
14
15std::string gallery = "./assets/face/entities/";
16
17std::string image_probe = "assets/face/images/obiwan0.png";
18
20std::string solver_face_detect = SOLVER_FACE_DETECT;
21std::string solver_face_landmarks = SOLVER_FACE_LANDMARKS;
22std::string solver_face_template = SOLVER_FACE_EXTRACTION;
23
24const auto SOLVER_PARAMETERS = std::vector<SFESolverParameter>{};
25
27size_t face_size_min = 28;
28size_t face_size_max = 170;
29
32
40 SFEImage &image, const size_t face_size_min,
41 const size_t face_size_max,
42 size_t &recommended_width,
43 size_t &recommended_height) {
44
45 // If the face detection solver requires static input (filename contains width and height),
46 // we need to prepare the input image accordingly Typically the format is:
47 // face_detect_accurate_mask_w1920h1080_op11.onnxrt.solver
48 // NOTE: This can
49 // be hardcoded into the application, or we can parse the width and height
50 // from the solver filename
51
52 // Try to use regex capture to extract width and height from solver name
53 std::smatch width_height;
54 if (std::regex_match(solver_face_detect, width_height,
55 std::regex(".*w([0-9]+)h([0-9]+).*"))) {
56 recommended_width = std::stoi(width_height[1]);
57 recommended_height = std::stoi(width_height[2]);
58 } else {
59 // Solvers without width and height in name can accept dynamic input
60 // image size. For best results we recommend to scale the input image to
61 // a resolution recommended by the sfeFaceDetectInputSize function
62
63 // Use sfeFaceDetectInputSize to get recommended input image dimensions
64 // for given min_face_size/max_face_size
65 SFEDetectionInputSize input_size{};
66 SFEError error = sfeFaceDetectInputSize(image, SFEFaceDetectionAccuracyType::SFE_FACE_DETECT_ACCURACY_TYPE_ACCURATE, face_size_min,
67 face_size_max, &input_size);
68 utils::checkError(error);
69 recommended_width = input_size.width;
70 recommended_height = input_size.height;
71 };
72}
73
75 SFESolver &detector_solver,
76 SFESolver &landmarks_solver,
77 SFESolver &template_solver) {
78
79 SFEImage image{};
80 DEFER(sfeImageFree(image));
81 SFEImage resized_image{};
82 DEFER(sfeImageFree(resized_image));
83 SFEError error{};
84 { // STAGE 1 Load image
85 // Load image data from file
86 auto image_data = utils::readFile(image_path);
87
88 // Decode image from data
89 error = sfeImageDecode(image_data.data(), image_data.size(), &image);
90 utils::checkError(error);
91
92 size_t recommended_width{};
93 size_t recommended_height{};
94
95 // Calculate optimal input image size for face detection. Image will be
96 // resized to recommended size for optimal performance.
98 face_size_max, recommended_width,
99 recommended_height);
100
101 // Resize image to recommended size
102 // NOTE: This function will resize the image without preserving the aspect
103 // ratio of the image.
104
105 error = sfeImageResize(image, recommended_width, recommended_height,
106 &resized_image);
107 utils::checkError(error);
108 }
109
110 SFEDetection detected_face = {};
111 size_t detection_count = 1;
112 { // STAGE 3: Detect face in the image
113
114 // Detect face in the image
115 error = sfeDetect(detector_solver, resized_image, detection_threshold,
116 &detected_face, &detection_count);
117 utils::checkError(error);
118
119 if (detection_count == 0) {
120 throw std::runtime_error("No face detected in the image.");
121 }
122 }
123
124 std::vector<SFEFaceLandmarks> landmarks(SFE_FACE_LANDMARK_COUNT);
125 { // STAGE 4: Get face landmarks
126
127 // Use landmarks detection solver to get relevant landmarks for
128 // the detected face
129 error = sfeFaceLandmarks(landmarks_solver, image, &detected_face,
130 landmarks.data());
131 utils::checkError(error);
132 }
133
134 SFEFaceTemplate face_template = {};
135 { // STAGE 5: Extract probe face template
136 // Use template extraction solver to create new face
137 // template
138 error =
139 sfeFaceTemplateExtract(template_solver, image, &detected_face,
140 landmarks.data(), &face_template);
141 utils::checkError(error);
142 }
143 return face_template;
144}
145
146void printHelp() {
147 std::cout << "Help: Usage of the program." << std::endl;
148 std::cout << "Options:" << std::endl;
149 std::cout << "-h: Display help." << std::endl;
150 std::cout << "-p: Probe image file." << std::endl;
151 std::cout << "-g: Path to folder with entities." << std::endl;
152 std::cout << "-d: Path to detector solver." << std::endl;
153 std::cout << "-l: Path to landmarks solver." << std::endl;
154 std::cout << "-e: Path to extraction solver." << std::endl;
155 std::cout << "-t: Detection threshold. <0,1>" << std::endl;
156 std::cout << "-i: Identification threshold. <0,1>" << std::endl;
157 std::cout << "-m: Minimal face size in pixels to detect." << std::endl;
158 std::cout << "-x: Max face size in pixels to detect." << std::endl;
159}
160
161int main(int argc, char *argv[]) {
162
163 { // STAGE 0: Parse command line arguments
164
165 // Map to store argument values
166 std::unordered_map<std::string, std::string> args;
167
168 // Parse command line arguments
169 for (int i = 1; i < argc; ++i) {
170 std::string arg = argv[i];
171 if (arg[0] == '-') {
172 if (arg == "-h") {
173 printHelp();
174 return 0;
175 }
176 // Check if there's a next argument and it isn't another option
177 if (i + 1 < argc && argv[i + 1][0] != '-') {
178 args[arg] = argv[++i];
179 } else {
180 std::cerr << "Option " << arg << " requires a value." << std::endl;
181 return 1;
182 }
183 } else {
184 std::cerr << "Unknown option: " << arg << std::endl;
185 return 1;
186 }
187 }
188
189 // Assign values to variables based on parsed arguments
190 if (args.count("-p"))
191 image_probe = args["-p"];
192 if (args.count("-g"))
193 gallery = args["-g"];
194 if (args.count("-d"))
195 solver_face_detect = args["-d"];
196 if (args.count("-l"))
197 solver_face_landmarks = args["-l"];
198 if (args.count("-e"))
199 solver_face_template = args["-e"];
200 if (args.count("-t"))
201 detection_threshold = std::stof(args["-t"]);
202 if (args.count("-i"))
203 identification_threshold = std::stof(args["-i"]);
204 if (args.count("-m"))
205 face_size_min = std::stoi(args["-m"]);
206 if (args.count("-x"))
207 face_size_max = std::stoi(args["-x"]);
208
209 utils::printFormatted("EXAMPLE PARAMETERS");
210 // Print resource names
211 std::cout << "Probe image: " << image_probe << std::endl;
212 std::cout << "Entities folder: " << gallery << std::endl;
213
214 // Print solver names
215 std::cout << "Face detection solver: " << solver_face_detect << std::endl;
216 std::cout << "Face landmarks solver: " << solver_face_landmarks
217 << std::endl;
218 std::cout << "Face template solver: " << solver_face_template << std::endl;
219
220 // Print other options
221 std::cout << "Min face size [px]: " << face_size_min << std::endl;
222 std::cout << "Max face size [px]: " << face_size_max << std::endl;
223 std::cout << "Detection threshold: " << detection_threshold << std::endl;
224 std::cout << "Identification threshold: " << identification_threshold
225 << std::endl;
226 }
227
228 utils::printToolkitInfo();
229
230 SFEError error{};
231
232 SFESolver detector_solver{};
233 DEFER(sfeSolverFree(detector_solver));
234 SFESolver landmarks_solver{};
235 DEFER(sfeSolverFree(landmarks_solver));
236 SFESolver template_solver{};
237 DEFER(sfeSolverFree(template_solver));
238 { // STAGE 0: loading solvers
239 // Initialize face detection solver
240 error = sfeSolverCreate(
242 SOLVER_PARAMETERS.size(), &detector_solver);
243 utils::checkError(error);
244
245 // Initialize landmarks detection solver
246 error = sfeSolverCreate(
248 SOLVER_PARAMETERS.size(), &landmarks_solver);
249 utils::checkError(error);
250
251 // Initialize template extraction solver
252 error = sfeSolverCreate(
254 SOLVER_PARAMETERS.size(), &template_solver);
255 utils::checkError(error);
256 }
257
258 SFEFaceTemplate probe_face_template;
259 { // STAGE 1: Extract probe face template
260 utils::printFormatted("PROBE TEMPLATE EXTRACTION");
261 probe_face_template = extractTemplate(image_probe, detector_solver,
262 landmarks_solver, template_solver);
263
264 std::cout << "Probe face template extracted." << std::endl;
265 }
266
267 std::vector<SFEFaceTemplate> gallery_face_templates = {};
268 std::vector<SFEEntity> enitity_pairs = {};
269 { // STAGE 2: Extract templates for each image in entity folder and associate them
270 // with entity(UUID)
271
272 // Entities(UUID) tells the ownership of the templates in the gallery.
273 // For example:
274 // UUID1 -> template1
275 // UUID1 -> template2
276 // UUID2 -> template3
277 // UUID2 -> template4
278 // ...
279 // UUUIDN -> templateN
280
281 utils::printFormatted("ENTITIES TEMPLATE EXTRACTION");
282
283 // Get folder names from entities_gallery
284 auto folder_names = utils::getFiles(gallery);
285
286 for (auto folder_name : folder_names) {
287 if (folder_name.find(".jpg") != std::string::npos ||
288 folder_name.find(".png") != std::string::npos) {
289 continue;
290 }
291
292 // Genereate UUID for each entity.
293 auto entity = utils::generateEntity();
294
295 auto entity_folder = gallery + folder_name + "/";
296
297 std::cout << "Folder: " << entity_folder << ", entity UUID: ["
298 << static_cast<int>(entity.uuid[0]) << ", "
299 << static_cast<int>(entity.uuid[1]) << "..."
300 << static_cast<int>(entity.uuid[15]) << "]" << std::endl;
301
302 // Get image names from folder
303 auto image_names = utils::getFiles(entity_folder);
304
305 // Extract template for each image in the folder
306 for (auto image_name : image_names) {
307
308 auto image_path = entity_folder + image_name;
309
310 auto face_template = extractTemplate(
311 image_path, detector_solver, landmarks_solver, template_solver);
312
313 gallery_face_templates.push_back(face_template);
314 enitity_pairs.push_back(entity);
315 }
316 }
317 }
318
320 size_t candidate_count = 1;
321 std::vector<SFEEntityIdentificationCandidate> results(candidate_count);
322 int best_candidate_index = -1;
323 { // STAGE 3: Identification with entities.
324 // Probe template is matched against every template in the gallery.
325 // Function returns entity(UUID) which owns the best matching template.
326
327 utils::printFormatted("1:N IDENTIFICATION WITH ENTITIES");
329 &probe_face_template, gallery_face_templates.data(),
330 enitity_pairs.data(), gallery_face_templates.size(),
331 identification_threshold, results.data(), &candidate_count, 4);
332 utils::checkError(error);
333
334 if (candidate_count == 0) {
335 std::cout << "No candidates found. Exiting.." << std::endl;
336 return 0;
337 }
338
339 std::cout << std::endl;
340 std::cout << "Found " << results.size() << " candidates " << std::endl;
341 for (int i = 0; i < results.size(); i++) {
342 std::cout << "Index: " << i << ", Score: " << results[i].score
343 << std::endl;
344 std::cout << "Entity UUID: ["
345 << static_cast<int>(results[i].entity.uuid[0]) << ", "
346 << static_cast<int>(results[i].entity.uuid[1]) << "..."
347 << static_cast<int>(results[i].entity.uuid[15]) << "]"
348 << std::endl;
349 }
350 }
352
353 utils::printFormatted("FINISHED");
354}
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
uint8_t data[SFE_FACE_TEMPLATE_SIZE]
Definition sfe_face.h:108
Raw owned raster image representation, HWC|BGR order.
Definition sfe_core.h:70