Files
insightface/cpp-package/inspireface/cpp/sample/api/sample_cpp_multithreading.cpp
2025-08-08 13:26:10 +08:00

294 lines
9.4 KiB
C++

/**
* Created by Jingyu Yan
* @date 2025-07-13
*
* Multi-threading example: One thread performs face tracking and stores tokens,
* another thread monitors tokens and extracts face features.
*/
#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include <chrono>
#include <memory>
#include <inspireface.h>
// Thread-safe token storage using C++ containers
class ThreadSafeTokenStorage {
private:
std::vector<HFFaceBasicToken> tokens_;
mutable std::mutex mutex_; // Make mutex mutable for const member functions
std::condition_variable cv_;
std::atomic<bool> stop_flag_{false};
public:
// Add a token to the storage
void addToken(const HFFaceBasicToken& token) {
std::lock_guard<std::mutex> lock(mutex_);
// Copy token data
HInt32 tokenSize;
HFGetFaceBasicTokenSize(&tokenSize);
char* tokenBuffer = new char[tokenSize];
HFCopyFaceBasicToken(token, tokenBuffer, tokenSize);
// Create copied token
HFFaceBasicToken copiedToken;
copiedToken.size = tokenSize;
copiedToken.data = tokenBuffer;
tokens_.push_back(copiedToken);
cv_.notify_one();
}
// Get the last token and remove it
bool getLastToken(HFFaceBasicToken& token) {
std::unique_lock<std::mutex> lock(mutex_);
if (tokens_.empty()) {
return false;
}
token = tokens_.back();
tokens_.pop_back();
return true;
}
// Wait for a token with timeout
bool waitForToken(HFFaceBasicToken& token, int timeout_ms = 1000) {
std::unique_lock<std::mutex> lock(mutex_);
if (cv_.wait_for(lock, std::chrono::milliseconds(timeout_ms),
[this] { return !tokens_.empty() || stop_flag_.load(); })) {
if (!tokens_.empty()) {
token = tokens_.back();
tokens_.pop_back();
return true;
}
}
return false;
}
// Check if there are tokens available
bool hasTokens() const {
std::lock_guard<std::mutex> lock(mutex_);
return !tokens_.empty();
}
// Get token count
size_t getTokenCount() const {
std::lock_guard<std::mutex> lock(mutex_);
return tokens_.size();
}
// Stop the threads
void stop() {
stop_flag_.store(true);
cv_.notify_all();
}
// Check if should stop
bool shouldStop() const {
return stop_flag_.load();
}
// Cleanup allocated memory
void cleanup() {
std::lock_guard<std::mutex> lock(mutex_);
for (auto& token : tokens_) {
if (token.data != nullptr) {
delete[] static_cast<char*>(token.data);
}
}
tokens_.clear();
}
~ThreadSafeTokenStorage() {
cleanup();
}
};
// Face tracking thread function
void faceTrackingThread(ThreadSafeTokenStorage& tokenStorage,
HFSession session,
const std::string& imagePath,
int maxFrames = 100) {
std::cout << "Face tracking thread started" << std::endl;
// Load image using C API
HFImageBitmap imageBitmap = {0};
HResult ret = HFCreateImageBitmapFromFilePath(imagePath.c_str(), 3, &imageBitmap);
if (ret != HSUCCEED) {
std::cerr << "Failed to create image bitmap: " << ret << std::endl;
return;
}
// Create image stream
HFImageStream stream;
ret = HFCreateImageStreamFromImageBitmap(imageBitmap, HF_CAMERA_ROTATION_0, &stream);
if (ret != HSUCCEED) {
std::cerr << "Failed to create image stream: " << ret << std::endl;
HFReleaseImageBitmap(imageBitmap);
return;
}
int frameCount = 0;
while (!tokenStorage.shouldStop() && frameCount < maxFrames) {
// Perform face detection and tracking using C API
HFMultipleFaceData multipleFaceData = {0};
ret = HFExecuteFaceTrack(session, stream, &multipleFaceData);
if (ret == HSUCCEED && multipleFaceData.detectedNum > 0) {
// Add tokens to storage
for (int i = 0; i < multipleFaceData.detectedNum; i++) {
tokenStorage.addToken(multipleFaceData.tokens[i]);
std::cout << "Frame " << frameCount << ": Added token for face "
<< multipleFaceData.trackIds[i] << std::endl;
}
}
frameCount++;
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Simulate frame processing
}
// Cleanup
HFReleaseImageStream(stream);
HFReleaseImageBitmap(imageBitmap);
std::cout << "Face tracking thread finished after " << frameCount << " frames" << std::endl;
}
// Face feature extraction thread function
void featureExtractionThread(ThreadSafeTokenStorage& tokenStorage,
HFSession session,
const std::string& imagePath) {
std::cout << "Feature extraction thread started" << std::endl;
// Load image using C API (same as tracking thread)
HFImageBitmap imageBitmap = {0};
HResult ret = HFCreateImageBitmapFromFilePath(imagePath.c_str(), 3, &imageBitmap);
if (ret != HSUCCEED) {
std::cerr << "Failed to create image bitmap: " << ret << std::endl;
return;
}
// Create image stream
HFImageStream stream;
ret = HFCreateImageStreamFromImageBitmap(imageBitmap, HF_CAMERA_ROTATION_0, &stream);
if (ret != HSUCCEED) {
std::cerr << "Failed to create image stream: " << ret << std::endl;
HFReleaseImageBitmap(imageBitmap);
return;
}
int extractedCount = 0;
while (!tokenStorage.shouldStop()) {
HFFaceBasicToken token;
// Wait for token with timeout
if (tokenStorage.waitForToken(token, 2000)) {
// Extract face feature using C API
HFFaceFeature feature;
ret = HFCreateFaceFeature(&feature);
if (ret == HSUCCEED) {
ret = HFFaceFeatureExtractTo(session, stream, token, feature);
if (ret == HSUCCEED) {
extractedCount++;
std::cout << "Extracted feature " << extractedCount
<< " (feature size: " << feature.size << ")" << std::endl;
// Print first few feature values as example
std::cout << "Feature values: ";
for (int i = 0; i < std::min(5, feature.size); i++) {
std::cout << feature.data[i] << " ";
}
std::cout << "..." << std::endl;
} else {
std::cerr << "Feature extraction failed with error: " << ret << std::endl;
}
HFReleaseFaceFeature(&feature);
}
// Clean up token memory
if (token.data != nullptr) {
delete[] static_cast<char*>(token.data);
}
} else {
std::cout << "No tokens available, waiting..." << std::endl;
}
}
// Cleanup
HFReleaseImageStream(stream);
HFReleaseImageBitmap(imageBitmap);
std::cout << "Feature extraction thread finished, extracted "
<< extractedCount << " features" << std::endl;
}
int main(int argc, char** argv) {
if (argc != 3) {
std::cout << "Usage: " << argv[0] << " <model_path> <image_path>" << std::endl;
return -1;
}
std::string modelPath = argv[1];
std::string imagePath = argv[2];
// Initialize InspireFace using C API
HResult ret = HFLaunchInspireFace(modelPath.c_str());
if (ret != HSUCCEED) {
std::cerr << "Failed to launch InspireFace: " << ret << std::endl;
return -1;
}
// Create session using C API
HOption option = HF_ENABLE_FACE_RECOGNITION;
HFSession session;
ret = HFCreateInspireFaceSessionOptional(option, HF_DETECT_MODE_LIGHT_TRACK, 10, -1, -1, &session);
if (ret != HSUCCEED) {
std::cerr << "Failed to create session: " << ret << std::endl;
return -1;
}
// Set session parameters using C API
HFSessionSetTrackPreviewSize(session, 640);
// Create thread-safe token storage
ThreadSafeTokenStorage tokenStorage;
// Start face tracking thread
std::thread trackingThread(faceTrackingThread, std::ref(tokenStorage), session, imagePath, 50);
// Start feature extraction thread
std::thread extractionThread(featureExtractionThread, std::ref(tokenStorage), session, imagePath);
// Main thread: monitor and print statistics
std::cout << "Main thread: Monitoring token storage..." << std::endl;
for (int i = 0; i < 30; i++) {
std::this_thread::sleep_for(std::chrono::milliseconds(500));
size_t tokenCount = tokenStorage.getTokenCount();
std::cout << "Token storage status: " << tokenCount << " tokens available" << std::endl;
}
// Stop threads
std::cout << "Stopping threads..." << std::endl;
tokenStorage.stop();
// Wait for threads to finish
trackingThread.join();
extractionThread.join();
// Cleanup using C API
HFReleaseInspireFaceSession(session);
std::cout << "All threads finished successfully" << std::endl;
return 0;
}