SmartFace Embedded Toolkit  4.2.1
Loading...
Searching...
No Matches
example_face_track.cpp File Reference
#include "sfe_toolkit/sfe_core.h"
#include "sfe_toolkit/sfe_face.h"
#include <ostream>
#include <regex>
#include <vector>
#include <iomanip>
#include <sstream>
#include "annotate.hpp"
#include "solvers.h"
#include "utils.hpp"
#include <unordered_map>

Go to the source code of this file.

Functions

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.
 
void printHelp ()
 
std::vector< SFEDetection > detectFaces (SFEImage &image, SFESolver &detector_solver)
 Detect faces in the image.
 
std::ostream & operator<< (std::ostream &os, const SFEEntity &entity)
 Print UUID.
 
std::ostream & operator<< (std::ostream &os, const SFETrackedState &state)
 Print tracked face state.
 
std::ostream & operator<< (std::ostream &os, const SFETracked &tracked)
 Print tracked face.
 
template<typename T >
std::ostream & operator<< (std::ostream &os, const std::vector< T > &vec)
 Print a container of printable objects.
 
void frame (std::vector< SFEDetection > &detections, SFETracker tracker)
 Print out the results of the face tracking.
 
int main (int argc, char *argv[])
 Main function is used to parse command line arguments.
 

Variables

std::string image_probe = "assets/face/images/obiwan0.png"
 
std::string solver_face_detect = SOLVER_FACE_DETECT
 Solvers to use in example, the defaults are filled in by CMake.
 
const auto SOLVER_PARAMETERS = std::vector<SFESolverParameter>{}
 
size_t face_size_min = 28
 Required size of the face to be detected.
 
size_t face_size_max = 170
 
float detection_threshold = 0.1f
 

Function Documentation

◆ detectFaces()

std::vector< SFEDetection > detectFaces ( SFEImage &  image,
SFESolver &  detector_solver 
)

Detect faces in the image.

Examples
example_face_track.cpp.

Definition at line 86 of file example_face_track.cpp.

87 {
88
89 SFEImage resized_image{};
90 DEFER(sfeImageFree(resized_image));
91 SFEError error{};
92 { // STAGE 1 Load image
93 size_t recommended_width{};
94 size_t recommended_height{};
95
96 // Calculate optimal input image size for face detection. Image will be
97 // resized to recommended size for optimal performance.
99 face_size_max, recommended_width,
100 recommended_height);
101
102 // Resize image to recommended size
103 // NOTE: This function will resize the image without preserving the aspect
104 // ratio of the image.
105
106 error = sfeImageResize(image, recommended_width, recommended_height,
107 &resized_image);
108 utils::checkError(error);
109 }
110
111 size_t detection_count = 10;
112 std::vector<SFEDetection> detected_faces(detection_count);
113 { // STAGE 2: Detect face in the image
114
115 // Detect faces in the image
116 error = sfeDetect(detector_solver, resized_image, detection_threshold,
117 detected_faces.data(), &detection_count);
118 utils::checkError(error);
119
120 if (detection_count == 0) {
121 std::ostringstream oss;
122 oss << "Error: No face detected in the image ";
123 throw std::runtime_error(oss.str());
124 }
125 detected_faces.resize(detection_count);
126
127 std::cout << "Detected " << detection_count << " faces in the image."
128 << std::endl;
129 }
130 return detected_faces;
131}
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.
float detection_threshold
size_t face_size_max
size_t face_size_min
Required size of the face to be detected.
SFEError sfeImageResize(SFEImageView image, size_t width, size_t height, SFEImage *out_image)
Resize image.
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 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.
Raw owned raster image representation, HWC|BGR order.
Definition sfe_core.h:70

◆ frame()

void frame ( std::vector< SFEDetection > &  detections,
SFETracker  tracker 
)

Print out the results of the face tracking.

Examples
example_face_track.cpp.

Definition at line 185 of file example_face_track.cpp.

185 {
186 size_t reserved_size = 10;
187 size_t tracked_faces_count = reserved_size;
188 size_t lost_faces_count = reserved_size;
189 size_t removed_faces_count = reserved_size;
190
191 std::vector<SFETracked> tracked_faces(tracked_faces_count);
192 std::vector<SFEEntity> lost_faces(lost_faces_count);
193 std::vector<SFEEntity> removed_faces(removed_faces_count);
194
195 // Update tracker with detected faces
196 SFEError error =
197 sfeDetectionTrackerUpdate(tracker, detections.data(), detections.size(),
198 tracked_faces.data(), &tracked_faces_count);
199 utils::checkError(error);
200 tracked_faces.resize(tracked_faces_count);
201
202 // Get lost faces
203 error = sfeDetectionTrackerLost(tracker, lost_faces.data(), &lost_faces_count);
204 utils::checkError(error);
205 lost_faces.resize(lost_faces_count);
206
207 // Get removed faces
208 error = sfeDetectionTrackerRemoved(tracker, removed_faces.data(),
209 &removed_faces_count);
210 utils::checkError(error);
211 removed_faces.resize(removed_faces_count);
212
213 // Print out frame results
214 std::cout << "Tracked" << std::endl;
215 std::cout << tracked_faces;
216 std::cout << "Lost" << std::endl;
217 std::cout << lost_faces;
218 std::cout << "Removed" << std::endl;
219 std::cout << removed_faces;
220}
SFEError sfeDetectionTrackerUpdate(SFETracker tracker, const SFEDetection *detections, size_t detections_count, SFETracked *out_tracked, size_t *in_out_tracked_count)
Update the tracker.
SFEError sfeDetectionTrackerRemoved(SFETracker tracker, SFEEntity *out_entities, size_t *in_out_entities_count)
Get removed entities after update.
SFEError sfeDetectionTrackerLost(SFETracker tracker, SFEEntity *out_entities, size_t *in_out_entities_count)
Get lost entities after update.

◆ getRecommendedImageSize()

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.

Parameters
solver_face_detectname of the face detection solver
face_size_minDesired minimal size of detected face
face_size_maxDesired maximal size of detected face
recommended_widthRecommended image width
recommended_heightRecommended image height

Definition at line 38 of file example_face_track.cpp.

42 {
43
44 // If the face detection solver requires static input (filename contains width
45 // and height), we need to prepare the input image accordingly Typically the
46 // format is: face_detect_accurate_mask_w1920h1080_op11.onnxrt.solver NOTE:
47 // This can be hardcoded into the application, or we can parse the width and
48 // height from the solver filename
49
50 // Try to use regex capture to extract width and height from solver name
51 std::smatch width_height;
52 if (std::regex_match(solver_face_detect, width_height,
53 std::regex(".*w([0-9]+)h([0-9]+).*"))) {
54 recommended_width = std::stoi(width_height[1]);
55 recommended_height = std::stoi(width_height[2]);
56 } else {
57 // Solvers without width and height in name can accept dynamic input
58 // image size. For best results we recommend to scale the input image to
59 // a resolution recommended by the sfeFaceDetectInputSize function
60
61 // Use sfeFaceDetectInputSize to get recommended input image dimensions
62 // for given min_face_size/max_face_size
63 SFEDetectionInputSize input_size{};
65 image,
66 SFEFaceDetectionAccuracyType::SFE_FACE_DETECT_ACCURACY_TYPE_ACCURATE,
67 face_size_min, face_size_max, &input_size);
68 utils::checkError(error);
69 recommended_width = input_size.width;
70 recommended_height = input_size.height;
71 };
72}
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...
Detection input size struct.
Definition sfe_face.h:59

◆ main()

int main ( int  argc,
char *  argv[] 
)

Main function is used to parse command line arguments.

Definition at line 223 of file example_face_track.cpp.

223 {
224
225 { // STAGE 0: Parse command line arguments
226 // Map to store argument values
227 std::unordered_map<std::string, std::string> args;
228
229 // Parse command line arguments
230 for (int i = 1; i < argc; ++i) {
231 std::string arg = argv[i];
232 if (arg[0] == '-') {
233 if (arg == "-h") {
234 printHelp();
235 return 0;
236 }
237 // Check if there's a next argument and it isn't another option
238 if (i + 1 < argc && argv[i + 1][0] != '-') {
239 args[arg] = argv[++i];
240 } else {
241 std::cerr << "Option " << arg << " requires a value." << std::endl;
242 return 1;
243 }
244 } else {
245 std::cerr << "Unknown option: " << arg << std::endl;
246 return 1;
247 }
248 }
249
250 // Assign values to variables based on parsed arguments
251 if (args.count("-p"))
252 image_probe = args["-p"];
253 if (args.count("-d"))
254 solver_face_detect = args["-d"];
255 if (args.count("-t"))
256 detection_threshold = std::stof(args["-t"]);
257 if (args.count("-m"))
258 face_size_min = std::stoi(args["-m"]);
259 if (args.count("-x"))
260 face_size_max = std::stoi(args["-x"]);
261
262 utils::printFormatted("EXAMPLE PARAMETERS");
263 // Print resource names
264 std::cout << "Probe image: " << image_probe << std::endl;
265
266 // Print solver names
267 std::cout << "Face detection solver: " << solver_face_detect << std::endl;
268
269 // Print other options
270 std::cout << "Min face size [px]: " << face_size_min << std::endl;
271 std::cout << "Max face size [px]: " << face_size_max << std::endl;
272 std::cout << "Detection threshold: " << detection_threshold << std::endl;
273 }
274
275 utils::printToolkitInfo();
276
277 SFEError error{};
278
279 SFESolver detector_solver{};
280 DEFER(sfeSolverFree(detector_solver));
281 { // STAGE 1: loading solvers
282 utils::printFormatted("LOADING SOLVERS");
283 // Initialize face detection solver
284 error = sfeSolverCreate(
286 SOLVER_PARAMETERS.size(), &detector_solver);
287 utils::checkError(error);
288 }
289
290 std::vector<SFEDetection> detected_faces;
291 { // STAGE 2: Detect faces
292 utils::printFormatted("DETECT FACES");
293
294 SFEImage image{};
295 DEFER(sfeImageFree(image));
296 { // STAGE 2.1: Load image
297 // Load image data from file
298 auto image_data = utils::readFile(image_probe);
299
300 // Decode image from data
301 error = sfeImageDecode(image_data.data(), image_data.size(), &image);
302 utils::checkError(error);
303 }
304
305 SFEDetection detected_face = {};
306 { // STAGE 2.2: Detect face in the image
307 // Detect a face in the image
308 detected_faces = detectFaces(image, detector_solver);
309 }
310 }
311
312 SFETracker tracker{};
313 DEFER(sfeDetectionTrackerFree(tracker));
314 { // STAGE 3: Track faces
315 utils::printFormatted("TRACK FACES");
316 error = sfeDetectionTrackerCreate(0.3f, 0.1f, 0.3f, 0.1f, 1, &tracker);
317 utils::checkError(error);
318
319 // We will be using the current detected faces and simulate detection across
320 // 4 frames In a real application, you would call detectFaces() for each
321 // frame and pass the detected faces to the tracker
322
323 // First frame with detected faces, all should track as NEW
324 utils::printFormatted("FRAME 1");
325 frame(detected_faces, tracker);
326
327 // Second frame with the same UUIDs, all should track as TRACKED
328 utils::printFormatted("FRAME 2");
329 frame(detected_faces, tracker);
330
331 // Simulate a lost detection
332 detected_faces.clear();
333
334 // We should see the lost UUIDs
335 utils::printFormatted("FRAME 3");
336 frame(detected_faces, tracker);
337
338 // We should see the removed UUIDs
339 utils::printFormatted("FRAME 4");
340 frame(detected_faces, tracker);
341 }
342 utils::printFormatted("FINISHED");
343}
void printHelp()
std::string image_probe
const auto SOLVER_PARAMETERS
std::vector< SFEDetection > detectFaces(SFEImage &image, SFESolver &detector_solver)
Detect faces in the image.
void frame(std::vector< SFEDetection > &detections, SFETracker tracker)
Print out the results of the face tracking.
void sfeSolverFree(SFESolver solver)
Free memory associated with SFESolver.
SFEError sfeDetectionTrackerCreate(float new_track_threshold, float track_high_threshold, float track_low_threshold, float match_threshold, uint64_t max_time_lost, SFETracker *out_tracker)
Create a new tracker.
void * SFESolver
Solver provides an abstract interface over inference models and engines.
Definition sfe_core.h:102
SFEError sfeSolverCreate(const char *solver_file, const SFESolverParameter *solver_parameters, size_t solver_parameters_count, SFESolver *out_solver)
Create new solver from solver file.
void sfeDetectionTrackerFree(SFETracker tracker)
Free the tracker.
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 ..
void * SFETracker
Tracker is a ByteTrack implementation for multi-modal tracking across multiple frames.
Definition sfe_core.h:286
Core detection - tagged union containing all detection types.

◆ operator<<() [1/4]

std::ostream & operator<< ( std::ostream &  os,
const SFEEntity &  entity 
)

Print UUID.

Examples
example_face_track.cpp.

Definition at line 134 of file example_face_track.cpp.

134 {
135 for (size_t i = 0; i < 16; ++i) {
136 os << std::hex << std::setw(2) << std::setfill('0')
137 << static_cast<int>(entity.uuid[i]);
138 if (i == 3 || i == 5 || i == 7 || i == 9) {
139 os << '-';
140 }
141 }
142 os << std::dec;
143 return os;
144}
uint8_t uuid[16]
Definition sfe_core.h:115

◆ operator<<() [2/4]

std::ostream & operator<< ( std::ostream &  os,
const SFETracked &  tracked 
)

Print tracked face.

Definition at line 169 of file example_face_track.cpp.

169 {
170 os << "Face ID: " << tracked.id << " UUID: " << tracked.uuid
171 << " State: " << tracked.state;
172 return os;
173}
uint64_t id
Tracked ID.
Definition sfe_core.h:301
SFEEntity uuid
UUID.
Definition sfe_core.h:303
enum SFETrackedState state
Tracking state.
Definition sfe_core.h:305

◆ operator<<() [3/4]

std::ostream & operator<< ( std::ostream &  os,
const SFETrackedState &  state 
)

Print tracked face state.

Definition at line 147 of file example_face_track.cpp.

147 {
148 switch (state) {
150 os << "NEW";
151 break;
153 os << "TRACKED";
154 break;
156 os << "LOST";
157 break;
159 os << "REMOVED";
160 break;
161 default:
162 os << "UNKNOWN";
163 break;
164 }
165 return os;
166}
@ SFE_TRACKED_STATE_REMOVED
Definition sfe_core.h:293
@ SFE_TRACKED_STATE_LOST
Definition sfe_core.h:292
@ SFE_TRACKED_STATE_TRACKED
Definition sfe_core.h:291
@ SFE_TRACKED_STATE_NEW
Definition sfe_core.h:290

◆ operator<<() [4/4]

template<typename T >
std::ostream & operator<< ( std::ostream &  os,
const std::vector< T > &  vec 
)

Print a container of printable objects.

Definition at line 177 of file example_face_track.cpp.

177 {
178 for (auto &item : vec) {
179 os << item << std::endl;
180 }
181 return os;
182}

◆ printHelp()

void printHelp ( )

Definition at line 74 of file example_face_track.cpp.

74 {
75 std::cout << "Help: Usage of the program." << std::endl;
76 std::cout << "Options:" << std::endl;
77 std::cout << "-h: Display help." << std::endl;
78 std::cout << "-p: Probe image file." << std::endl;
79 std::cout << "-d: Path to detector solver." << std::endl;
80 std::cout << "-t: Detection threshold. <0,1>" << std::endl;
81 std::cout << "-m: Minimal face size in pixels to detect." << std::endl;
82 std::cout << "-x: Max face size in pixels to detect." << std::endl;
83}

Variable Documentation

◆ detection_threshold

float detection_threshold = 0.1f

Definition at line 30 of file example_face_track.cpp.

◆ face_size_max

size_t face_size_max = 170

Definition at line 28 of file example_face_track.cpp.

◆ face_size_min

size_t face_size_min = 28

Required size of the face to be detected.

Definition at line 27 of file example_face_track.cpp.

◆ image_probe

std::string image_probe = "assets/face/images/obiwan0.png"

Definition at line 19 of file example_face_track.cpp.

◆ solver_face_detect

std::string solver_face_detect = SOLVER_FACE_DETECT

Solvers to use in example, the defaults are filled in by CMake.

Definition at line 22 of file example_face_track.cpp.

◆ SOLVER_PARAMETERS

const auto SOLVER_PARAMETERS = std::vector<SFESolverParameter>{}

Definition at line 24 of file example_face_track.cpp.