Merge pull request #2764 from tunmx/dev/up121

Update inspireface
This commit is contained in:
Jia Guo
2025-05-22 16:20:52 +08:00
committed by GitHub
168 changed files with 6434 additions and 2527 deletions

View File

@@ -19,7 +19,7 @@ pack/*
.vscode/*
build_local/*
local_build/*
cpp/inspireface/information.h
cpp/inspireface/include/inspireface/information.h
cpp/inspireface/version.txt
.DS_Store
._.DS_Store

View File

@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.10)
cmake_minimum_required(VERSION 3.20)
project(InspireFace)
set(CMAKE_CXX_STANDARD 14)
@@ -7,16 +7,30 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
# Hide symbols
option(ISF_ENABLE_SYMBOL_HIDING "Enable symbol hiding." ON)
if(ISF_ENABLE_SYMBOL_HIDING)
if(NOT WIN32)
set(CMAKE_C_VISIBILITY_PRESET hidden)
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
set(CMAKE_VISIBILITY_INLINES_HIDDEN YES)
endif()
else()
set(CMAKE_C_VISIBILITY_PRESET default)
set(CMAKE_CXX_VISIBILITY_PRESET default)
set(CMAKE_VISIBILITY_INLINES_HIDDEN NO)
endif()
# Current version
set(INSPIRE_FACE_VERSION_MAJOR 1)
set(INSPIRE_FACE_VERSION_MINOR 2)
set(INSPIRE_FACE_VERSION_PATCH 0)
set(INSPIRE_FACE_VERSION_PATCH 1)
# Converts the version number to a string
string(CONCAT INSPIRE_FACE_VERSION_MAJOR_STR ${INSPIRE_FACE_VERSION_MAJOR})
string(CONCAT INSPIRE_FACE_VERSION_MINOR_STR ${INSPIRE_FACE_VERSION_MINOR})
string(CONCAT INSPIRE_FACE_VERSION_PATCH_STR ${INSPIRE_FACE_VERSION_PATCH})
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cpp/inspireface/information.h.in ${CMAKE_CURRENT_SOURCE_DIR}/cpp/inspireface/information.h)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cpp/inspireface/information.h.in ${CMAKE_CURRENT_SOURCE_DIR}/cpp/inspireface/include/inspireface/information.h)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cpp/inspireface/version.txt.in ${CMAKE_CURRENT_SOURCE_DIR}/cpp/inspireface/version.txt)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/python/version.txt.in ${CMAKE_CURRENT_SOURCE_DIR}/python/version.txt)
@@ -40,6 +54,9 @@ else()
message(STATUS "3rdparty directory already exists")
endif()
# Install cpp api header file
option(ISF_INSTALL_CPP_HEADER "Install cpp api header file." ON)
# Set the ISF_THIRD_PARTY_DIR variable to allow it to be set externally from the command line, or use the default path if it is not set
set(ISF_THIRD_PARTY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/3rdparty" CACHE PATH "Path to the third-party libraries directory")
@@ -214,13 +231,11 @@ if(ISF_ENABLE_OPENCV)
if (ISF_BUILD_LINUX_ARM7 OR ISF_BUILD_LINUX_AARCH64)
set(DISABLE_GUI ON)
add_definitions("-DDISABLE_GUI")
# set(OpenCV_DIR ${ISF_THIRD_PARTY_DIR}/opencv/opencv-linux-armhf/share/OpenCV)
# set(OpenCV_STATIC_INCLUDE_DIR ${PATH_3RDPARTY}/opencv/opencv-linux-armhf/include/)
if (ISF_RK_DEVICE_TYPE STREQUAL "RV1109RV1126" AND ISF_ENABLE_RKNN)
# In special cases, specialize for that version
message("The OpenCV that builds the RV1109RV1126 version depends on is specialized!")
set(OpenCV_DIR ${ISF_THIRD_PARTY_DIR}/inspireface-precompile/opencv/3.4.5/opencv-linux-armhf/share/OpenCV)
set(OpenCV_STATIC_INCLUDE_DIR ${PATH_3RDPARTY}/inspireface-precompile/opencv/3.4.5/opencv-linux-armhf/include/)
set(OpenCV_DIR ${OPENCV_PRECOMPILED_DIR}/opencv/3.4.5/opencv-linux-armhf/share/OpenCV)
set(OpenCV_STATIC_INCLUDE_DIR ${OPENCV_PRECOMPILED_DIR}/opencv/3.4.5/opencv-linux-armhf/include/)
set(PLAT linux-arm7)
else()
if (VERSION_MAJOR STREQUAL "3")
@@ -231,13 +246,13 @@ if(ISF_ENABLE_OPENCV)
if(ISF_BUILD_LINUX_ARM7)
set(PLAT linux-arm7)
message("The OpenCV that builds the gnueabihf version depends on is specialized!")
set(OpenCV_DIR ${ISF_THIRD_PARTY_DIR}/inspireface-precompile/opencv/3.4.5/opencv-linux-armhf/share/OpenCV)
set(OpenCV_STATIC_INCLUDE_DIR ${PATH_3RDPARTY}/inspireface-precompile/opencv/3.4.5/opencv-linux-armhf/include/)
set(OpenCV_DIR ${OPENCV_PRECOMPILED_DIR}/opencv/3.4.5/opencv-linux-armhf/share/OpenCV)
set(OpenCV_STATIC_INCLUDE_DIR ${OPENCV_PRECOMPILED_DIR}/opencv/3.4.5/opencv-linux-armhf/include/)
elseif(ISF_BUILD_LINUX_AARCH64)
set(PLAT linux-aarch64)
message("The OpenCV that builds the aarch64 version depends on is specialized!")
set(OpenCV_DIR ${ISF_THIRD_PARTY_DIR}/inspireface-precompile/opencv/3.4.5/opencv-linux-aarch64/share/OpenCV)
set(OpenCV_STATIC_INCLUDE_DIR ${PATH_3RDPARTY}/inspireface-precompile/opencv/3.4.5/opencv-linux-aarch64/include/)
set(OpenCV_DIR ${OPENCV_PRECOMPILED_DIR}/opencv/3.4.5/opencv-linux-aarch64/share/OpenCV)
set(OpenCV_STATIC_INCLUDE_DIR ${OPENCV_PRECOMPILED_DIR}/opencv/3.4.5/opencv-linux-aarch64/include/)
endif()
endif()
else ()
@@ -350,4 +365,10 @@ if(ISF_ENABLE_APPLE_EXTENSION)
message(STATUS "\t ISF_ENABLE_APPLE_EXTENSION: ${ISF_ENABLE_APPLE_EXTENSION}")
endif()
# Install cpp api header file
if(ISF_INSTALL_CPP_HEADER)
message(STATUS "\t ISF_INSTALL_CPP_HEADER: ${ISF_INSTALL_CPP_HEADER}")
endif()
message(STATUS "\t CMAKE_INSTALL_PREFIX: ${CMAKE_INSTALL_PREFIX}")

View File

@@ -5,6 +5,8 @@
[![JitPack](https://img.shields.io/jitpack/v/github/HyperInspire/inspireface-android-sdk?style=for-the-badge&color=green&label=JitPack&logo=android)](https://jitpack.io/#HyperInspire/inspireface-android-sdk)
[![build](https://img.shields.io/github/actions/workflow/status/HyperInspire/InspireFace/release-sdks.yaml?&style=for-the-badge&label=building&logo=cmake)](https://github.com/HyperInspire/InspireFace/actions/workflows/release-sdks.yaml)
[![test](https://img.shields.io/github/actions/workflow/status/HyperInspire/InspireFace/release-sdks.yaml?&style=for-the-badge&label=testing&logo=c)](https://github.com/HyperInspire/InspireFace/actions/workflows/test_ubuntu_x86_Pikachu.yaml)
[![Document](https://img.shields.io/badge/Document-Building-blue?style=for-the-badge&logo=readthedocs)](https://doc.inspireface.online/)
InspireFace is a cross-platform face recognition SDK developed in C/C++, supporting multiple operating systems and various backend types for inference, such as CPU, GPU, and NPU.
@@ -15,8 +17,15 @@ Please contact [contact@insightface.ai](mailto:contact@insightface.ai?subject=In
<img src="images/banner.jpg" alt="banner" style="zoom:80%;" />
---
📘 [Documentation](https://doc.inspireface.online/) is a **work in progress**.
We welcome your questions💬, they help guide and accelerate its development.
## Change Logs
**`2025-04-27`** Optimize some issues and provide a stable version.
**`2025-03-16`** Acceleration using NVIDIA-GPU (**CUDA**) devices is already supported.
**`2025-03-09`** Release of android sdk in JitPack.
@@ -49,26 +58,18 @@ Please contact [contact@insightface.ai](mailto:contact@insightface.ai?subject=In
**`2024-07-02`** Fixed several bugs in the face detector with multi-level input.
**`2024-06-27`** Verified iOS usability and fixed some bugs.
**`2024-06-18`** Added face detection feature with tracking-by-detection mode.
## License
The licensing of the open-source models employed by InspireFace adheres to the same requirements as InsightFace, specifying their use solely for academic purposes and explicitly prohibiting commercial applications.
## Quick Start
For Python users on Linux and MacOS, InspireFace can be quickly installed via pip:
For Python users on **Linux and MacOS**, InspireFace can be quickly installed via pip:
```bash
pip install inspireface
```
_Windows support is **not available yet**, but will be coming soon!_
After installation, you can use inspireface like this:
```Python
@@ -176,7 +177,7 @@ The '**3rdparty**' directory already includes the MNN library and specifies a pa
### Requirements
- CMake (version 3.10 or higher)
- CMake (version 3.20 or higher)
- NDK (version 16 or higher, only required for Android) [**Optional**]
- MNN (version 1.4.0 or higher)
- C++ Compiler
@@ -210,7 +211,10 @@ After compilation, you can find the local file in the build directory, which con
inspireface-linux
├── include
│ ├── herror.h
── inspireface.h
── intypedef.h
│ ├── inspireface.h
│ ├── inspirecv/
│ └── inspireface/
└── lib
└── libInspireFace.so
```
@@ -218,6 +222,9 @@ inspireface-linux
- **libInspireFace.so**Compiled dynamic linking library.
- **inspireface.h**Header file definition.
- **herror.h**Reference error number definition.
- **intypedef.h**: Type definition file.
- **inspirecv**: Simple cv library CPP header file folder.
- **inspireface**: inspireface cpp header folder.
### Cross Compilation
Cross compilation requires you to prepare the target platform's cross-compilation toolchain on the host machine in advance. Here, compiling for Rockchip's embedded devices RV1106 is used as an example:
```bash
@@ -272,6 +279,10 @@ docker-compose up build-tensorrt-cuda12-ubuntu22
If you want to use pre-compiled libraries, you can use **[FindTensorRT.cmake](toolchain/FindTensorRT.cmake)** to create links to CUDA and TensorRT.
### React Native
For Android and iOS, in addition to the native interface, you can use the React Native library powered by Nitro Modules and JSI—providing ultra-fast, seamless bindings to the InspireFace SDK. For more details, check out the [react-native-nitro-inspire-face](https://github.com/ronickg/react-native-nitro-inspire-face) repository or the [documentation](https://ronickg.github.io/react-native-nitro-inspire-face). Author: ronickg.
### Supported Platforms and Architectures
We have completed the adaptation and testing of the software across various operating systems and CPU architectures. This includes compatibility verification for platforms such as Linux, macOS, iOS, and Android, as well as testing for specific hardware support to ensure stable operation in diverse environments.
@@ -292,10 +303,11 @@ We have completed the adaptation and testing of the software across various oper
| 12 | **iOS** | ARM | CPU/Metal/**ANE** | [![](https://img.shields.io/badge/%E2%9C%93-green)](#) | [![](https://img.shields.io/badge/%E2%9C%93-green)](#) | [![build](https://img.shields.io/github/actions/workflow/status/HyperInspire/InspireFace/release-sdks.yaml?label=✓&labelColor=success&color=success&failedLabel=✗&failedColor=critical&logo=github&logoColor=white)](https://github.com/HyperInspire/InspireFace/actions/workflows/release-sdks.yaml) |
| 13 | **Android** | ARMv7 | - | [![](https://img.shields.io/badge/%E2%9C%93-green)](#) | [![](https://img.shields.io/badge/%E2%9C%93-green)](#) | [![build](https://img.shields.io/github/actions/workflow/status/HyperInspire/InspireFace/release-sdks.yaml?label=✓&labelColor=success&color=success&failedLabel=✗&failedColor=critical&logo=github&logoColor=white)](https://github.com/HyperInspire/InspireFace/actions/workflows/release-sdks.yaml) |
| 14 | | ARMv8 | - | [![](https://img.shields.io/badge/%E2%9C%93-green)](#) | [![](https://img.shields.io/badge/%E2%9C%93-green)](#) | [![build](https://img.shields.io/github/actions/workflow/status/HyperInspire/InspireFace/release-sdks.yaml?label=✓&labelColor=success&color=success&failedLabel=✗&failedColor=critical&logo=github&logoColor=white)](https://github.com/HyperInspire/InspireFace/actions/workflows/release-sdks.yaml) |
| 15 | **Android**<sup><br/>(Rockchip) | ARMv8 | RK3566/RK3568 | [![](https://img.shields.io/badge/%E2%9C%93-green)](#) | [![](https://img.shields.io/badge/%E2%9C%93-green)](#) | [![build](https://img.shields.io/github/actions/workflow/status/HyperInspire/InspireFace/release-sdks.yaml?label=✓&labelColor=success&color=success&failedLabel=✗&failedColor=critical&logo=github&logoColor=white)](https://github.com/HyperInspire/InspireFace/actions/workflows/release-sdks.yaml) |
| 16 | | ARMv8 | RK3588 | [![](https://img.shields.io/badge/%E2%9C%93-green)](#) | [![](https://img.shields.io/badge/%E2%9C%93-green)](#) | [![build](https://img.shields.io/github/actions/workflow/status/HyperInspire/InspireFace/release-sdks.yaml?label=✓&labelColor=success&color=success&failedLabel=✗&failedColor=critical&logo=github&logoColor=white)](https://github.com/HyperInspire/InspireFace/actions/workflows/release-sdks.yaml) |
| 17 | **HarmonyOS** | ARMv8 | - | - | - | - |
| 18 | **Linux**<sup><br/>(Jetson series) | ARMv8 | Jetson series | - | - | - |
| 15 | | x86_64 | - | [![](https://img.shields.io/badge/%E2%9C%93-green)](#) | [![](https://img.shields.io/badge/%E2%9C%93-green)](#) | [![build](https://img.shields.io/github/actions/workflow/status/HyperInspire/InspireFace/release-sdks.yaml?label=✓&labelColor=success&color=success&failedLabel=✗&failedColor=critical&logo=github&logoColor=white)](https://github.com/HyperInspire/InspireFace/actions/workflows/release-sdks.yaml) |
| 16 | **Android**<sup><br/>(Rockchip) | ARMv8 | RK3566/RK3568 | [![](https://img.shields.io/badge/%E2%9C%93-green)](#) | [![](https://img.shields.io/badge/%E2%9C%93-green)](#) | [![build](https://img.shields.io/github/actions/workflow/status/HyperInspire/InspireFace/release-sdks.yaml?label=✓&labelColor=success&color=success&failedLabel=✗&failedColor=critical&logo=github&logoColor=white)](https://github.com/HyperInspire/InspireFace/actions/workflows/release-sdks.yaml) |
| 17 | | ARMv8 | RK3588 | [![](https://img.shields.io/badge/%E2%9C%93-green)](#) | [![](https://img.shields.io/badge/%E2%9C%93-green)](#) | [![build](https://img.shields.io/github/actions/workflow/status/HyperInspire/InspireFace/release-sdks.yaml?label=✓&labelColor=success&color=success&failedLabel=✗&failedColor=critical&logo=github&logoColor=white)](https://github.com/HyperInspire/InspireFace/actions/workflows/release-sdks.yaml) |
| 18 | **HarmonyOS** | ARMv8 | - | - | - | - |
| 19 | **Linux**<sup><br/>(Jetson series) | ARMv8 | Jetson series | - | - | - |
- **Device**: Some special device support, primarily focused on computing power devices.
- **Supported**: The solution has been fully developed and successfully verified on offline devices.
@@ -332,15 +344,20 @@ docker-compose up
```
## Example
### C/C++ Sample
To integrate InspireFace into a C/C++ project, you simply need to link the InspireFace library and include the appropriate header files. Below is a basic example demonstrating face detection:
### C/C++ Sample: Use the recommended CAPI interface
To integrate InspireFace into a C/C++ project, you simply need to link the InspireFace library and include the appropriate header files(We recommend using the more compatible **CAPI** headers). Below is a basic example demonstrating face detection:
```c
#include <inspireface.h>
#include <herror.h>
...
HResult ret;
// The resource file must be loaded before it can be used
ret = HFLaunchInspireFace(packPath);
if (ret != HSUCCEED) {
std::cout << "Load Resource error: " << ret << std::endl;
HFLogPrint(HF_LOG_ERROR, "Load Resource error: %d", ret);
return ret;
}
@@ -358,10 +375,11 @@ HInt32 detectPixelLevel = 160;
HFSession session = {0};
ret = HFCreateInspireFaceSessionOptional(option, detMode, maxDetectNum, detectPixelLevel, -1, &session);
if (ret != HSUCCEED) {
std::cout << "Create FaceContext error: " << ret << std::endl;
HFLogPrint(HF_LOG_ERROR, "Create FaceContext error: %d", ret);
return ret;
}
// Configure some detection parameters
HFSessionSetTrackPreviewSize(session, detectPixelLevel);
HFSessionSetFilterMinimumFacePixelSize(session, 4);
@@ -369,14 +387,14 @@ HFSessionSetFilterMinimumFacePixelSize(session, 4);
HFImageBitmap image;
ret = HFCreateImageBitmapFromFilePath(sourcePath, 3, &image);
if (ret != HSUCCEED) {
std::cout << "The source entered is not a picture or read error." << std::endl;
HFLogPrint(HF_LOG_ERROR, "The source entered is not a picture or read error.");
return ret;
}
// Prepare an image parameter structure for configuration
HFImageStream imageHandle = {0};
ret = HFCreateImageStreamFromImageBitmap(image, rotation_enum, &imageHandle);
if (ret != HSUCCEED) {
std::cout << "Create ImageStream error: " << ret << std::endl;
HFLogPrint(HF_LOG_ERROR, "Create ImageStream error: %d", ret);
return ret;
}
@@ -384,36 +402,117 @@ if (ret != HSUCCEED) {
HFMultipleFaceData multipleFaceData = {0};
ret = HFExecuteFaceTrack(session, imageHandle, &multipleFaceData);
if (ret != HSUCCEED) {
std::cout << "Execute HFExecuteFaceTrack error: " << ret << std::endl;
HFLogPrint(HF_LOG_ERROR, "Execute HFExecuteFaceTrack error: %d", ret);
return ret;
}
// Print the number of faces detected
auto faceNum = multipleFaceData.detectedNum;
std::cout << "Num of face: " << faceNum << std::endl;
HFLogPrint(HF_LOG_INFO, "Num of face: %d", faceNum);
// The memory must be freed at the end of the program
ret = HFReleaseImageBitmap(image);
if (ret != HSUCCEED) {
printf("Release image bitmap error: %lu\n", ret);
HFLogPrint(HF_LOG_ERROR, "Release image bitmap error: %d", ret);
return ret;
}
ret = HFReleaseImageStream(imageHandle);
if (ret != HSUCCEED) {
printf("Release image stream error: %lu\n", ret);
HFLogPrint(HF_LOG_ERROR, "Release image stream error: %d", ret);
}
ret = HFReleaseInspireFaceSession(session);
if (ret != HSUCCEED) {
printf("Release session error: %lu\n", ret);
HFLogPrint(HF_LOG_ERROR, "Release session error: %d", ret);
return ret;
}
...
```
For more examples, you can refer to the `cpp/sample` sub-project located in the root directory. You can compile these sample executables by enabling the `ISF_BUILD_WITH_SAMPLE` option during the compilation process.
- **More detailed cases**: [C/C++ Sample](cpp/sample/api/)
**Note**: For each error code feedback, you can click on this [link](doc/Error-Feedback-Codes.md) to view detailed explanations.
### C++ Sample: Use the C++ version of the header files
If you want to use C++ header files, then you need to enable **ISF_INSTALL_CPP_HEADER** during compilation. Executing the install command will add the C++ header files.
```c++
#include <iostream>
#include <memory>
#include <inspireface/inspireface.hpp>
...
// Set log level to info
INSPIRE_SET_LOG_LEVEL(inspire::LogLevel::ISF_LOG_INFO);
int32_t ret = 0;
// Global init(you only need to call once)
ret = INSPIREFACE_CONTEXT->Load("Pikachu");
INSPIREFACE_CHECK_MSG(ret == HSUCCEED, "Load model failed");
// Create face algorithm session
inspire::ContextCustomParameter custom_param;
custom_param.enable_recognition = true;
auto max_detect_face = 5;
auto detect_level_px = 320; // 160, 320, 640
// Create a face algorithm session
std::shared_ptr<inspire::Session> session(
inspire::Session::CreatePtr(inspire::DETECT_MODE_ALWAYS_DETECT, max_detect_face, custom_param, detect_level_px));
// Load image(default format is BGR)
inspirecv::Image image = inspirecv::Image::Create("face.jpg");
// Create frame process
inspirecv::FrameProcess process =
inspirecv::FrameProcess::Create(image, inspirecv::BGR, inspirecv::ROTATION_0);
// Detect face
std::vector<inspire::FaceTrackWrap> detect_results;
ret = session->FaceDetectAndTrack(process, detect_results);
INSPIRE_LOGI("Number of faces detected: %d", detect_results.size());
if (detect_results.size() == 0)
{
INSPIRE_LOGW("No face detected");
return -1;
}
// Copy image
inspirecv::Image image_copy = image.Clone();
// Draw face
auto thickness = 2;
for (auto &face : detect_results)
{
auto rect = session->GetFaceBoundingBox(face);
auto lmk = session->GetNumOfFaceDenseLandmark(face);
image_copy.DrawRect(rect, inspirecv::Color::Red, thickness);
for (auto &point : lmk)
{
image_copy.DrawCircle(point.As<int>(), 0, inspirecv::Color::Orange, thickness);
}
}
// Save draw image
image_copy.Write("result.jpg");
// Face Embedding extract
inspire::FaceEmbedding face_embedding;
// Extract the first face feature
ret = session->FaceFeatureExtract(process, detect_results[0], face_embedding);
INSPIRE_LOGI("Length of face embedding: %d", face_embedding.embedding.size());
...
```
Please note that the C++ interface has not been fully tested. It is recommended to use the **CAPI** interface as the primary option.
**More detailed cases**:
- [C Sample](cpp/sample/api/)
- [C/C++ Sample](cpp/sample/cpp_api/)
### Python Native Sample
The Python implementation is compiled based on InspireFace source code, and is integrated using a native interface approach.
@@ -453,7 +552,7 @@ ret = isf.reload()
assert ret, "Launch failure. Please ensure the resource path is correct."
# Optional features, loaded during session creation based on the modules specified.
opt = isf.HF_ENABLE_NONE
opt = isf.HF_ENABLE_FACE_POSE
session = isf.InspireFaceSession(opt, isf.HF_DETECT_MODE_ALWAYS_DETECT)
# Load the image using OpenCV.
@@ -488,6 +587,7 @@ We have an [Android SDK project](https://github.com/HyperInspire/inspireface-and
Precompiled library support:
- arm64-v8a
- armeabi-v7a
- x86_64
#### a. Quick to use in Android
@@ -652,7 +752,7 @@ For different scenarios, we currently provide several Packs, each containing mul
| --- | --- | --- | --- | --- |
| Pikachu | CPU | Lightweight edge-side models | Feb 20, 2025 | [Download](https://github.com/HyperInspire/InspireFace/releases/download/v1.x/Pikachu) |
| Megatron | CPU, GPU | Mobile and server models | Feb 20, 2025 | [Download](https://github.com/HyperInspire/InspireFace/releases/download/v1.x/Megatron) |
| Megatron_TRT | GPU | Cuda-based server models | Mar 16, 2025 | [Download](https://github.com/HyperInspire/InspireFace/releases/download/v1.x/Megatron_TRT) |
| Megatron_TRT | GPU | CUDA-based server models | Mar 16, 2025 | [Download](https://github.com/HyperInspire/InspireFace/releases/download/v1.x/Megatron_TRT) |
| Gundam-RV1109 | RKNPU | Supports RK1109 and RK1126 | Feb 20, 2025 | [Download](https://github.com/HyperInspire/InspireFace/releases/download/v1.x/Gundam_RV1109) |
| Gundam-RV1106 | RKNPU | Supports RV1103 and RV1106 | Feb 20, 2025 | [Download](https://github.com/HyperInspire/InspireFace/releases/download/v1.x/Gundam_RV1106) |
| Gundam-RK356X | RKNPU | Supports RK3566 and RK3568 | Feb 20, 2025 | [Download](https://github.com/HyperInspire/InspireFace/releases/download/v1.x/Gundam_RK356X) |
@@ -661,7 +761,8 @@ For different scenarios, we currently provide several Packs, each containing mul
## Short-Term Plan
- [x] Add TensorRT backend support.
- [ ] Add the RKNPU backend support for Android .
- [x] Add Add c++ style header files.
- [x] Add the RKNPU backend support for Android .
- [ ] Example app project for Android and iOS samples.
- [ ] Add the batch forward feature.

View File

@@ -25,7 +25,7 @@ public class MainActivity extends AppCompatActivity {
void test() {
InspireFaceVersion version = InspireFace.QueryInspireFaceVersion();
Log.i(TAG, "InspireFace Version: " + version.major + "." + version.minor + "." + version.patch + " " + version.information);
Log.i(TAG, "InspireFace Version: " + version.major + "." + version.minor + "." + version.patch);
String dbPath = "/storage/emulated/0/Android/data/com.example.inspireface_example/files/f.db";
FeatureHubConfiguration configuration = InspireFace.CreateFeatureHubConfiguration()
.setEnablePersistence(false)

View File

@@ -27,6 +27,7 @@ cd build/${BUILD_DIRNAME}/
# Configure the CMake build system
cmake -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DISF_BUILD_WITH_SAMPLE=OFF \
-DISF_BUILD_WITH_TEST=ON \
-DISF_ENABLE_BENCHMARK=ON \

View File

@@ -16,6 +16,7 @@ cd build/${BUILD_DIRNAME}/
# Configure the CMake build system
cmake -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DISF_BUILD_WITH_SAMPLE=OFF \
-DISF_BUILD_WITH_TEST=OFF \
-DISF_ENABLE_BENCHMARK=OFF \

View File

@@ -43,6 +43,7 @@ cd build/${BUILD_DIRNAME}/
# Configure the CMake build system
cmake -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DISF_BUILD_WITH_SAMPLE=OFF \
-DISF_BUILD_WITH_TEST=ON \
-DISF_ENABLE_BENCHMARK=ON \

View File

@@ -75,12 +75,15 @@ cd "$BUILD_DIR" || exit 1
# Run CMake configuration (adjust the options as needed)
cmake -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DISF_BUILD_WITH_SAMPLE=ON \
-DISF_BUILD_WITH_TEST=OFF \
-DISF_ENABLE_BENCHMARK=OFF \
-DISF_ENABLE_USE_LFW_DATA=OFF \
-DISF_ENABLE_TEST_EVALUATION=OFF \
-DISF_BUILD_SHARED_LIBS=ON "$SCRIPT_DIR"
-DISF_BUILD_SHARED_LIBS=ON \
-Wno-dev \
"$SCRIPT_DIR"
# Compile and install
make -j4

View File

@@ -18,7 +18,14 @@ reorganize_structure() {
done
# Find all architecture directories (e.g., arm64-v8a, armeabi-v7a)
local arch_dirs=($(find "$base_path" -maxdepth 1 -type d -name "arm*"))
local arch_dirs=()
for d in "$base_path"/*; do
[[ -d "$d" ]] || continue
name=$(basename "$d")
if [[ "$name" != "lib" && "$name" != "sample" && "$name" != "test" && "$name" != "." && "$name" != ".." ]]; then
arch_dirs+=("$d")
fi
done
for arch_dir in "${arch_dirs[@]}"; do
# Get the architecture name (e.g., arm64-v8a)
@@ -58,11 +65,22 @@ reorganize_structure() {
fi
done
# Copy include once from a valid arch
for arch_dir in "${arch_dirs[@]}"; do
if [ -d "$arch_dir/InspireFace/include" ]; then
mkdir -p "$base_path/include"
cp -r "$arch_dir/InspireFace/include/"* "$base_path/include/"
echo "Copied include from $arch_dir"
break
fi
done
# Delete the original architecture directories
for arch_dir in "${arch_dirs[@]}"; do
rm -rf "$arch_dir"
done
echo "Reorganization complete."
}
@@ -98,6 +116,7 @@ build() {
cmake ${SCRIPT_DIR} \
-G "Unix Makefiles" \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DCMAKE_C_FLAGS="-g0 ${CMAKE_C_FLAGS}" \
-DCMAKE_CXX_FLAGS="-g0 ${CMAKE_CXX_FLAGS}" \
-DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK}/build/cmake/android.toolchain.cmake \
@@ -111,7 +130,8 @@ build() {
-DISF_ENABLE_BENCHMARK=OFF \
-DISF_ENABLE_USE_LFW_DATA=OFF \
-DISF_ENABLE_TEST_EVALUATION=OFF \
-DISF_BUILD_SHARED_LIBS=ON
-DISF_BUILD_SHARED_LIBS=ON \
-Wno-dev
make -j4
make install
popd
@@ -129,6 +149,7 @@ BUILD_FOLDER_PATH="build/inspireface-android${TAG}"
build arm64-v8a 21
build armeabi-v7a 21
build x86_64 21
reorganize_structure "${BUILD_FOLDER_PATH}"

View File

@@ -99,6 +99,7 @@ build() {
cmake ${SCRIPT_DIR} \
-G "Unix Makefiles" \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DCMAKE_C_FLAGS="-g0 ${CMAKE_C_FLAGS}" \
-DCMAKE_CXX_FLAGS="-g0 ${CMAKE_CXX_FLAGS}" \
-DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK}/build/cmake/android.toolchain.cmake \
@@ -117,7 +118,8 @@ build() {
-DISF_ENABLE_BENCHMARK=OFF \
-DISF_ENABLE_USE_LFW_DATA=OFF \
-DISF_ENABLE_TEST_EVALUATION=OFF \
-DISF_BUILD_SHARED_LIBS=ON
-DISF_BUILD_SHARED_LIBS=ON \
-Wno-dev
make -j4
make install
popd

View File

@@ -38,6 +38,7 @@ cd ${BUILD_FOLDER_PATH}
# export cross_compile_toolchain=/home/jingyuyan/software/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu
cmake -DCMAKE_SYSTEM_NAME=Linux \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DCMAKE_SYSTEM_VERSION=1 \
-DCMAKE_SYSTEM_PROCESSOR=aarch64 \
-DCMAKE_C_COMPILER=$ARM_CROSS_COMPILE_TOOLCHAIN/bin/aarch64-linux-gnu-gcc \
@@ -51,6 +52,7 @@ cmake -DCMAKE_SYSTEM_NAME=Linux \
-DISF_ENABLE_BENCHMARK=OFF \
-DISF_ENABLE_USE_LFW_DATA=OFF \
-DISF_ENABLE_TEST_EVALUATION=OFF \
-Wno-dev \
-DISF_BUILD_SHARED_LIBS=ON ${SCRIPT_DIR}
make -j4

View File

@@ -38,6 +38,7 @@ cd ${BUILD_FOLDER_PATH}
# export cross_compile_toolchain=/home/jingyuyan/software/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf
cmake -DCMAKE_SYSTEM_NAME=Linux \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DCMAKE_SYSTEM_VERSION=1 \
-DCMAKE_SYSTEM_PROCESSOR=armv7 \
-DCMAKE_C_COMPILER=$ARM_CROSS_COMPILE_TOOLCHAIN/bin/arm-linux-gnueabihf-gcc \
@@ -50,6 +51,7 @@ cmake -DCMAKE_SYSTEM_NAME=Linux \
-DISF_ENABLE_BENCHMARK=OFF \
-DISF_ENABLE_USE_LFW_DATA=OFF \
-DISF_ENABLE_TEST_EVALUATION=OFF \
-Wno-dev \
-DISF_BUILD_SHARED_LIBS=ON ${SCRIPT_DIR}
make -j4

View File

@@ -46,6 +46,7 @@ cd ${BUILD_FOLDER_PATH}
cmake -DCMAKE_SYSTEM_NAME=Linux \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DCMAKE_SYSTEM_VERSION=1 \
-DCMAKE_SYSTEM_PROCESSOR=aarch64 \
-DCMAKE_C_COMPILER=$ARM_CROSS_COMPILE_TOOLCHAIN/bin/aarch64-linux-gnu-gcc \
@@ -66,7 +67,8 @@ cmake -DCMAKE_SYSTEM_NAME=Linux \
-DISF_ENABLE_BENCHMARK=OFF \
-DISF_ENABLE_USE_LFW_DATA=OFF \
-DISF_ENABLE_TEST_EVALUATION=OFF \
-DISF_BUILD_SHARED_LIBS=OFF ${SCRIPT_DIR}
-Wno-dev \
-DISF_BUILD_SHARED_LIBS=ON ${SCRIPT_DIR}
make -j4
make install

View File

@@ -78,6 +78,7 @@ cd ${BUILD_FOLDER_PATH}
cmake -DCMAKE_SYSTEM_NAME=Linux \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DCMAKE_SYSTEM_VERSION=1 \
-DCMAKE_SYSTEM_PROCESSOR=armv7 \
-DCMAKE_C_COMPILER=$ARM_CROSS_COMPILE_TOOLCHAIN/bin/arm-rockchip830-linux-uclibcgnueabihf-gcc \
@@ -98,9 +99,10 @@ cmake -DCMAKE_SYSTEM_NAME=Linux \
-DISF_ENABLE_BENCHMARK=OFF \
-DISF_ENABLE_USE_LFW_DATA=OFF \
-DISF_ENABLE_TEST_EVALUATION=OFF \
-DISF_BUILD_SHARED_LIBS=OFF ${SCRIPT_DIR}
-Wno-dev \
-DISF_BUILD_SHARED_LIBS=ON ${SCRIPT_DIR}
make -j4
# make install
make install
# move_install_files "$(pwd)"
move_install_files "$(pwd)"

View File

@@ -39,6 +39,7 @@ cd ${BUILD_FOLDER_PATH}
# export cross_compile_toolchain=/home/jingyuyan/software/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf
cmake -DCMAKE_SYSTEM_NAME=Linux \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DCMAKE_SYSTEM_VERSION=1 \
-DCMAKE_SYSTEM_PROCESSOR=armv7 \
-DCMAKE_C_COMPILER=$ARM_CROSS_COMPILE_TOOLCHAIN/bin/arm-linux-gnueabihf-gcc \
@@ -53,6 +54,7 @@ cmake -DCMAKE_SYSTEM_NAME=Linux \
-DISF_ENABLE_BENCHMARK=OFF \
-DISF_ENABLE_USE_LFW_DATA=OFF \
-DISF_ENABLE_TEST_EVALUATION=OFF \
-Wno-dev \
-DISF_BUILD_SHARED_LIBS=ON ${SCRIPT_DIR}
make -j4

View File

@@ -0,0 +1,157 @@
#!/bin/bash
# Reusable function to handle 'install' directory operations
move_install_files() {
local root_dir="$1"
local install_dir="$root_dir/install"
# Step 1: Check if the 'install' directory exists
if [ ! -d "$install_dir" ]; then
echo "Error: 'install' directory does not exist in $root_dir"
exit 1
fi
# Step 2: Delete all other files/folders except 'install'
find "$root_dir" -mindepth 1 -maxdepth 1 -not -name "install" -exec rm -rf {} +
# Step 3: Move all files from 'install' to the root directory
mv "$install_dir"/* "$root_dir" 2>/dev/null
# Step 4: Remove the empty 'install' directory
rmdir "$install_dir"
echo "Files from 'install' moved to $root_dir, and 'install' directory deleted."
}
# Define download URLs
MNN_IOS_URL="https://github.com/alibaba/MNN/releases/download/2.8.1/mnn_2.8.1_ios_armv82_cpu_metal_coreml.zip"
# Set the cache directory
MACOS_CACHE="$PWD/.macos_cache/"
# Create the directory if it does not exist
mkdir -p "${MACOS_CACHE}"
# Function to download and unzip a file if the required framework does not exist
download_and_unzip() {
local url=$1
local dir=$2
local framework_name=$3 # Name of the framework directory to check
# Check if the framework already exists
if [ ! -d "${dir}${framework_name}" ]; then
local file_name=$(basename "$url")
local full_path="${dir}${file_name}"
# Check if the zip file already exists
if [ ! -f "$full_path" ]; then
echo "Downloading ${file_name}..."
# Download the file
curl -sL "$url" -o "$full_path"
else
echo "${file_name} already downloaded. Proceeding to unzip."
fi
# Unzip the file to a temporary directory
echo "Unzipping ${file_name}..."
unzip -q "$full_path" -d "${dir}"
rm "$full_path"
# Move the framework if it's in a subdirectory specific to the iOS build
if [ "${framework_name}" == "MNN.framework" ]; then
mv "${dir}ios_build/Release-iphoneos/${framework_name}" "${dir}"
rm -rf "${dir}ios_build" # Clean up the subdirectory
fi
echo "${framework_name} has been set up."
else
echo "${framework_name} already exists in ${dir}. Skipping download and unzip."
fi
}
# Download and unzip MNN iOS package
download_and_unzip "$MNN_IOS_URL" "$MACOS_CACHE" "MNN.framework"
# Download and unzip OpenCV iOS package
if [ -n "$VERSION" ]; then
TAG="-$VERSION"
else
TAG=""
fi
TOOLCHAIN="$PWD/toolchain/ios.toolchain.cmake"
BUILD_DIR="build/inspireface-ios-coreml-arm64$TAG"
mkdir -p "$BUILD_DIR"
cd "$BUILD_DIR"
cmake \
-DIOS_3RDPARTY="${MACOS_CACHE}" \
-DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN} \
-DCMAKE_OSX_ARCHITECTURES=arm64 \
-DENABLE_BITCODE=0 \
-DIOS_DEPLOYMENT_TARGET=11.0 \
-DISF_ENABLE_APPLE_EXTENSION=ON \
-DISF_BUILD_WITH_SAMPLE=OFF \
-DISF_BUILD_WITH_TEST=OFF \
-DISF_BUILD_SHARED_LIBS=OFF \
../..
make -j8
make install
move_install_files "$(pwd)"
# Set the framework name
FRAMEWORK_NAME=InspireFace
# Specify the version of the framework
FRAMEWORK_VERSION=1.0.0
# Root build directory
BUILD_DIR="$(pwd)"
BUILD_LIB_DIR="$BUILD_DIR/InspireFace"
# Create the framework structure
FRAMEWORK_DIR=$BUILD_DIR/$FRAMEWORK_NAME.framework
mkdir -p $FRAMEWORK_DIR
mkdir -p $FRAMEWORK_DIR/Headers
mkdir -p $FRAMEWORK_DIR/Resources
# Copy the static library to the framework directory
cp $BUILD_LIB_DIR/lib/libInspireFace.a $FRAMEWORK_DIR/$FRAMEWORK_NAME
# Copy header files to the framework's Headers directory
cp $BUILD_LIB_DIR/include/*.h $FRAMEWORK_DIR/Headers/
# Create Info.plist
cat <<EOF >$FRAMEWORK_DIR/Resources/Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>$FRAMEWORK_NAME</string>
<key>CFBundleIdentifier</key>
<string>com.example.$FRAMEWORK_NAME</string>
<key>CFBundleName</key>
<string>$FRAMEWORK_NAME</string>
<key>CFBundleVersion</key>
<string>$FRAMEWORK_VERSION</string>
<key>CFBundleShortVersionString</key>
<string>$FRAMEWORK_VERSION</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
</dict>
</plist>
EOF
echo "Framework $FRAMEWORK_NAME.framework has been created at $FRAMEWORK_DIR"
cp -r $MACOS_CACHE/MNN.framework $BUILD_DIR/

View File

@@ -38,6 +38,7 @@ cd ${BUILD_FOLDER_PATH}
cmake -DCMAKE_SYSTEM_NAME=Linux \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DISF_BUILD_WITH_SAMPLE=ON \
-DISF_BUILD_WITH_TEST=ON \
-DISF_ENABLE_BENCHMARK=ON \
@@ -45,6 +46,7 @@ cmake -DCMAKE_SYSTEM_NAME=Linux \
-DISF_ENABLE_TEST_EVALUATION=OFF \
-DMNN_CUDA=ON \
-DISF_GLOBAL_INFERENCE_BACKEND_USE_MNN_CUDA=ON \
-Wno-dev \
-DISF_LINUX_MNN_CUDA=/home/tunm/softwate/MNN-2.7.2/build_cuda ${SCRIPT_DIR}
make -j4

View File

@@ -38,6 +38,7 @@ cd ${BUILD_FOLDER_PATH}
cmake -DCMAKE_SYSTEM_NAME=Linux \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DISF_BUILD_WITH_SAMPLE=ON \
-DISF_BUILD_WITH_TEST=ON \
-DISF_ENABLE_BENCHMARK=ON \
@@ -45,6 +46,7 @@ cmake -DCMAKE_SYSTEM_NAME=Linux \
-DISF_ENABLE_TEST_EVALUATION=ON \
-DMNN_CUDA=ON \
-DISF_GLOBAL_INFERENCE_BACKEND_USE_MNN_CUDA=ON \
-Wno-dev \
-DISF_LINUX_MNN_CUDA=/host/softwate/MNN-2.7.2/build_cuda ${SCRIPT_DIR}
make -j4

View File

@@ -39,10 +39,12 @@ cd ${BUILD_FOLDER_PATH}
cmake -DCMAKE_BUILD_TYPE=Release \
-DISF_BUILD_WITH_SAMPLE=OFF \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DISF_BUILD_WITH_TEST=OFF \
-DISF_ENABLE_BENCHMARK=OFF \
-DISF_ENABLE_USE_LFW_DATA=OFF \
-DISF_ENABLE_TEST_EVALUATION=OFF \
-Wno-dev \
-DISF_BUILD_SHARED_LIBS=ON ${SCRIPT_DIR}
make -j4

View File

@@ -99,12 +99,15 @@ echo "TENSORRT_ROOT: ${TENSORRT_ROOT}"
cmake \
-DCMAKE_BUILD_TYPE=Release \
-DISF_BUILD_WITH_SAMPLE=ON \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DISF_BUILD_WITH_TEST=ON \
-DISF_ENABLE_BENCHMARK=ON \
-DISF_ENABLE_USE_LFW_DATA=OFF \
-DISF_ENABLE_TEST_EVALUATION=OFF \
-DTENSORRT_ROOT=${TENSORRT_ROOT} \
-DISF_ENABLE_TENSORRT=ON ../..
-DISF_ENABLE_TENSORRT=ON \
-Wno-dev \
../..
make -j4

View File

@@ -36,13 +36,18 @@ mkdir -p ${BUILD_FOLDER_PATH}
# shellcheck disable=SC2164
cd ${BUILD_FOLDER_PATH}
cmake --version
cmake -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DISF_BUILD_WITH_SAMPLE=OFF \
-DISF_BUILD_WITH_TEST=OFF \
-DISF_ENABLE_BENCHMARK=OFF \
-DISF_ENABLE_USE_LFW_DATA=OFF \
-DISF_ENABLE_TEST_EVALUATION=OFF \
-DISF_BUILD_SHARED_LIBS=ON ${SCRIPT_DIR}
-DISF_BUILD_SHARED_LIBS=ON \
-Wno-dev \
${SCRIPT_DIR}
make -j4
make install

View File

@@ -37,12 +37,15 @@ mkdir -p ${BUILD_FOLDER_PATH}
cd ${BUILD_FOLDER_PATH}
cmake -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DISF_BUILD_WITH_SAMPLE=OFF \
-DISF_BUILD_WITH_TEST=OFF \
-DISF_ENABLE_BENCHMARK=OFF \
-DISF_ENABLE_USE_LFW_DATA=OFF \
-DISF_ENABLE_TEST_EVALUATION=OFF \
-DISF_BUILD_SHARED_LIBS=ON ${SCRIPT_DIR}
-DISF_BUILD_SHARED_LIBS=ON \
-Wno-dev \
${SCRIPT_DIR}
make -j4
make install

View File

@@ -0,0 +1,54 @@
#!/bin/bash
# Reusable function to handle 'install' directory operations
move_install_files() {
local root_dir="$1"
local install_dir="$root_dir/install"
# Step 1: Check if the 'install' directory exists
if [ ! -d "$install_dir" ]; then
echo "Error: 'install' directory does not exist in $root_dir"
exit 1
fi
# Step 2: Delete all other files/folders except 'install'
find "$root_dir" -mindepth 1 -maxdepth 1 -not -name "install" -exec rm -rf {} +
# Step 3: Move all files from 'install' to the root directory
mv "$install_dir"/* "$root_dir" 2>/dev/null
# Step 4: Remove the empty 'install' directory
rmdir "$install_dir"
echo "Files from 'install' moved to $root_dir, and 'install' directory deleted."
}
if [ -n "$VERSION" ]; then
TAG="-$VERSION"
else
TAG=""
fi
BUILD_FOLDER_PATH="build/inspireface-macos-coreml-apple-silicon-arm64${TAG}/"
SCRIPT_DIR=$(pwd) # Project dir
mkdir -p ${BUILD_FOLDER_PATH}
# shellcheck disable=SC2164
cd ${BUILD_FOLDER_PATH}
cmake -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DISF_ENABLE_APPLE_EXTENSION=ON \
-DISF_BUILD_WITH_SAMPLE=OFF \
-DISF_BUILD_WITH_TEST=OFF \
-DISF_ENABLE_BENCHMARK=OFF \
-DISF_ENABLE_USE_LFW_DATA=OFF \
-DISF_ENABLE_TEST_EVALUATION=OFF \
-DISF_BUILD_SHARED_LIBS=OFF \
-Wno-dev \
${SCRIPT_DIR}
make -j4
make install
move_install_files "$(pwd)"

View File

@@ -0,0 +1,54 @@
#!/bin/bash
# Reusable function to handle 'install' directory operations
move_install_files() {
local root_dir="$1"
local install_dir="$root_dir/install"
# Step 1: Check if the 'install' directory exists
if [ ! -d "$install_dir" ]; then
echo "Error: 'install' directory does not exist in $root_dir"
exit 1
fi
# Step 2: Delete all other files/folders except 'install'
find "$root_dir" -mindepth 1 -maxdepth 1 -not -name "install" -exec rm -rf {} +
# Step 3: Move all files from 'install' to the root directory
mv "$install_dir"/* "$root_dir" 2>/dev/null
# Step 4: Remove the empty 'install' directory
rmdir "$install_dir"
echo "Files from 'install' moved to $root_dir, and 'install' directory deleted."
}
if [ -n "$VERSION" ]; then
TAG="-$VERSION"
else
TAG=""
fi
BUILD_FOLDER_PATH="build/inspireface-macos-coreml-intel-x86-64${TAG}/"
SCRIPT_DIR=$(pwd) # Project dir
mkdir -p ${BUILD_FOLDER_PATH}
# shellcheck disable=SC2164
cd ${BUILD_FOLDER_PATH}
cmake -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DISF_ENABLE_APPLE_EXTENSION=ON \
-DISF_BUILD_WITH_SAMPLE=OFF \
-DISF_BUILD_WITH_TEST=OFF \
-DISF_ENABLE_BENCHMARK=OFF \
-DISF_ENABLE_USE_LFW_DATA=OFF \
-DISF_ENABLE_TEST_EVALUATION=OFF \
-DISF_BUILD_SHARED_LIBS=ON \
-Wno-dev \
${SCRIPT_DIR}
make -j4
make install
move_install_files "$(pwd)"

View File

@@ -37,12 +37,15 @@ mkdir -p ${BUILD_FOLDER_PATH}
cd ${BUILD_FOLDER_PATH}
cmake -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DISF_BUILD_WITH_SAMPLE=OFF \
-DISF_BUILD_WITH_TEST=OFF \
-DISF_ENABLE_BENCHMARK=OFF \
-DISF_ENABLE_USE_LFW_DATA=OFF \
-DISF_ENABLE_TEST_EVALUATION=OFF \
-DISF_BUILD_SHARED_LIBS=ON ${SCRIPT_DIR}
-DISF_BUILD_SHARED_LIBS=ON \
-Wno-dev \
${SCRIPT_DIR}
make -j4
make install

View File

@@ -37,12 +37,15 @@ mkdir -p ${BUILD_FOLDER_PATH}
cd ${BUILD_FOLDER_PATH}
cmake -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DISF_BUILD_WITH_SAMPLE=OFF \
-DISF_BUILD_WITH_TEST=OFF \
-DISF_ENABLE_BENCHMARK=OFF \
-DISF_ENABLE_USE_LFW_DATA=OFF \
-DISF_ENABLE_TEST_EVALUATION=OFF \
-DISF_BUILD_SHARED_LIBS=ON ${SCRIPT_DIR}
-DISF_BUILD_SHARED_LIBS=ON \
-Wno-dev \
${SCRIPT_DIR}
make -j4
make install

View File

@@ -37,12 +37,15 @@ mkdir -p ${BUILD_FOLDER_PATH}
cd ${BUILD_FOLDER_PATH}
cmake -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DISF_BUILD_WITH_SAMPLE=OFF \
-DISF_BUILD_WITH_TEST=OFF \
-DISF_ENABLE_BENCHMARK=OFF \
-DISF_ENABLE_USE_LFW_DATA=OFF \
-DISF_ENABLE_TEST_EVALUATION=OFF \
-DISF_BUILD_SHARED_LIBS=ON ${SCRIPT_DIR}
-DISF_BUILD_SHARED_LIBS=ON \
-Wno-dev \
${SCRIPT_DIR}
make -j4
make install

View File

@@ -37,12 +37,15 @@ mkdir -p ${BUILD_FOLDER_PATH}
cd ${BUILD_FOLDER_PATH}
cmake -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DISF_BUILD_WITH_SAMPLE=OFF \
-DISF_BUILD_WITH_TEST=OFF \
-DISF_ENABLE_BENCHMARK=OFF \
-DISF_ENABLE_USE_LFW_DATA=OFF \
-DISF_ENABLE_TEST_EVALUATION=OFF \
-DISF_BUILD_SHARED_LIBS=ON ${SCRIPT_DIR}
-DISF_BUILD_SHARED_LIBS=ON \
-Wno-dev \
${SCRIPT_DIR}
make -j4
make install

View File

@@ -1,66 +0,0 @@
#!/bin/bash
# 函数获取CUDA和Ubuntu版本标签
# 如果CUDA_TAG环境变量已设置则使用该值
# 否则自动检测CUDA和Ubuntu版本并生成标签
# 格式: cudaXX_ubuntuXX.XX
# 如果检测不到某个版本,则用"none"代替
get_cuda_ubuntu_tag() {
# 如果CUDA_TAG已设置则直接返回
if [ -n "${CUDA_TAG}" ]; then
echo "${CUDA_TAG}"
return 0
fi
# 获取CUDA版本
CUDA_VERSION="_none"
if command -v nvcc &> /dev/null; then
# 尝试从nvcc获取版本
CUDA_VERSION=$(nvcc --version 2>/dev/null | grep "release" | awk '{print $6}' | cut -d',' -f1 | tr -d '.')
if [ -z "${CUDA_VERSION}" ]; then
CUDA_VERSION="_none"
else
CUDA_VERSION="${CUDA_VERSION}"
fi
elif [ -f "/usr/local/cuda/version.txt" ]; then
# 尝试从CUDA安装目录获取版本
CUDA_VERSION=$(cat /usr/local/cuda/version.txt 2>/dev/null | grep "CUDA Version" | awk '{print $3}' | tr -d '.')
if [ -z "${CUDA_VERSION}" ]; then
CUDA_VERSION="_none"
fi
elif [ -d "/usr/local/cuda" ] && ls -l /usr/local/cuda 2>/dev/null | grep -q "cuda-"; then
# 尝试从符号链接获取版本
CUDA_LINK=$(ls -l /usr/local/cuda 2>/dev/null | grep -o "cuda-[0-9.]*" | head -n 1)
CUDA_VERSION=$(echo "${CUDA_LINK}" | cut -d'-' -f2 | tr -d '.')
if [ -z "${CUDA_VERSION}" ]; then
CUDA_VERSION="_none"
fi
fi
# 获取Ubuntu版本
UBUNTU_VERSION="_none"
if [ -f "/etc/os-release" ]; then
# 检查是否是Ubuntu
if grep -q "Ubuntu" /etc/os-release 2>/dev/null; then
UBUNTU_VERSION=$(grep "VERSION_ID" /etc/os-release 2>/dev/null | cut -d'"' -f2)
if [ -z "${UBUNTU_VERSION}" ]; then
UBUNTU_VERSION="_none"
fi
fi
elif [ -f "/etc/lsb-release" ]; then
# 尝试从lsb-release获取版本
if grep -q "Ubuntu" /etc/lsb-release 2>/dev/null; then
UBUNTU_VERSION=$(grep "DISTRIB_RELEASE" /etc/lsb-release 2>/dev/null | cut -d'=' -f2)
if [ -z "${UBUNTU_VERSION}" ]; then
UBUNTU_VERSION="_none"
fi
fi
fi
# 生成并返回标签
echo "cuda${CUDA_VERSION}_ubuntu${UBUNTU_VERSION}"
}
# 使用示例
CUDA_TAG=$(get_cuda_ubuntu_tag)
echo "Generated tag: ${CUDA_TAG}"

View File

@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.10)
cmake_minimum_required(VERSION 3.20)
project(InspireFaceSDK)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
@@ -15,7 +15,7 @@ else()
set(EXTENDED_INFORMATION "${EXTENDED_INFORMATION}@General")
endif()
set(EXTENDED_INFORMATION "${EXTENDED_INFORMATION} - Build Time: ${BUILD_TIMESTAMP}")
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/information.h.in ${CMAKE_CURRENT_SOURCE_DIR}/information.h)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/information.h.in ${CMAKE_CURRENT_SOURCE_DIR}/include/inspireface/information.h)
file(GLOB_RECURSE SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
set(SOURCE_FILES ${SOURCE_FILES} ${CMAKE_CURRENT_SOURCE_DIR}/c_api/inspireface.cc) # Add C_API file
@@ -97,7 +97,6 @@ if(ISF_ENABLE_APPLE_EXTENSION)
find_library(FOUNDATION_LIBRARY Foundation)
find_library(COREML_LIBRARY CoreML)
find_library(ACCELERATE_LIBRARY Accelerate)
find_package(OpenCV REQUIRED)
set(LINK_THIRD_LIBS ${LINK_THIRD_LIBS} ${FOUNDATION_LIBRARY} ${COREML_LIBRARY} ${ACCELERATE_LIBRARY})
# Add objective-c files
@@ -128,6 +127,9 @@ if (ISF_ENABLE_RKNN)
endif()
endif ()
# Add include directory
set(NEED_INCLUDE ${NEED_INCLUDE} ${CMAKE_CURRENT_SOURCE_DIR}/include/inspireface)
if (ISF_BUILD_LINUX_ARM7 OR ANDROID)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpu=neon")
endif()
@@ -221,7 +223,6 @@ if (ISF_ENABLE_TENSORRT)
message(STATUS "\t CUDA_RUNTIME_LIBRARY: ${CUDA_RUNTIME_LIBRARY}")
endif()
# Install lib
install(TARGETS InspireFace
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/InspireFace/lib
@@ -233,10 +234,17 @@ install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/c_api/inspireface.h DESTINATION ${CMAK
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/c_api/intypedef.h DESTINATION ${CMAKE_INSTALL_PREFIX}/InspireFace/include)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/herror.h DESTINATION ${CMAKE_INSTALL_PREFIX}/InspireFace/include)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/include/inspireface/herror.h DESTINATION ${CMAKE_INSTALL_PREFIX}/InspireFace/include)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/version.txt DESTINATION ${CMAKE_INSTALL_PREFIX}/)
if(ISF_INSTALL_CPP_HEADER)
# Install cpp api header file
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/inspireface/ DESTINATION ${CMAKE_INSTALL_PREFIX}/InspireFace/include/inspireface/)
install(DIRECTORY ${INSPIRECV_INCLUDE_PATH}/inspirecv DESTINATION ${CMAKE_INSTALL_PREFIX}/InspireFace/include/)
endif()
if (ISF_ENABLE_RKNN AND ISF_RKNPU_MAJOR STREQUAL "rknpu1")
# Install rknn 3rd lib
install(FILES ${ISF_RKNN_API_LIB}/librknn_api.so DESTINATION ${CMAKE_INSTALL_PREFIX}/InspireFace/lib)

View File

@@ -1,179 +0,0 @@
/**
* Created by Jingyu Yan
* @date 2024-10-01
*/
#include "launch.h"
#include "log.h"
#include "herror.h"
#include "isf_check.h"
#include "middleware/cuda_toolkit.h"
#if defined(ISF_ENABLE_TENSORRT)
#include "middleware/cuda_toolkit.h"
#endif
#define APPLE_EXTENSION_SUFFIX ".bundle"
namespace inspire {
std::mutex Launch::mutex_;
std::shared_ptr<Launch> Launch::instance_ = nullptr;
InspireArchive& Launch::getMArchive() {
std::lock_guard<std::mutex> lock(mutex_);
if (!m_archive_) {
throw std::runtime_error("Archive not initialized");
}
return *m_archive_;
}
std::shared_ptr<Launch> Launch::GetInstance() {
std::lock_guard<std::mutex> lock(mutex_);
if (!instance_) {
instance_ = std::shared_ptr<Launch>(new Launch());
}
return instance_;
}
int32_t Launch::Load(const std::string& path) {
std::lock_guard<std::mutex> lock(mutex_);
#if defined(ISF_ENABLE_TENSORRT)
int32_t support_cuda;
auto ret = CheckCudaUsability(&support_cuda);
if (ret != HSUCCEED) {
INSPIRE_LOGE("An error occurred while checking CUDA device support. Please ensure that your environment supports CUDA!");
return ret;
}
if (!support_cuda) {
INSPIRE_LOGE("Your environment does not support CUDA! Please ensure that your environment supports CUDA!");
return HERR_DEVICE_CUDA_NOT_SUPPORT;
}
#endif
INSPIREFACE_CHECK_MSG(os::IsExists(path), "The package path does not exist because the launch failed.");
#if defined(ISF_ENABLE_APPLE_EXTENSION)
BuildAppleExtensionPath(path);
#endif
if (!m_load_) {
try {
m_archive_ = std::make_unique<InspireArchive>();
m_archive_->ReLoad(path);
if (m_archive_->QueryStatus() == SARC_SUCCESS) {
m_load_ = true;
INSPIRE_LOGI("Successfully loaded resources");
return HSUCCEED;
} else {
m_archive_.reset();
INSPIRE_LOGE("Failed to load resources");
return HERR_ARCHIVE_LOAD_MODEL_FAILURE;
}
} catch (const std::exception& e) {
m_archive_.reset();
INSPIRE_LOGE("Exception during resource loading: %s", e.what());
return HERR_ARCHIVE_LOAD_MODEL_FAILURE;
}
} else {
INSPIRE_LOGW("There is no need to call launch more than once, as subsequent calls will not affect the initialization.");
return HSUCCEED;
}
}
int32_t Launch::Reload(const std::string& path) {
std::lock_guard<std::mutex> lock(mutex_);
INSPIREFACE_CHECK_MSG(os::IsExists(path), "The package path does not exist because the launch failed.");
#if defined(ISF_ENABLE_APPLE_EXTENSION)
BuildAppleExtensionPath(path);
#endif
try {
// Clean up existing archive if it exists
if (m_archive_) {
m_archive_.reset();
m_load_ = false;
}
// Create and load new archive
m_archive_ = std::make_unique<InspireArchive>();
m_archive_->ReLoad(path);
if (m_archive_->QueryStatus() == SARC_SUCCESS) {
m_load_ = true;
INSPIRE_LOGI("Successfully reloaded resources");
return HSUCCEED;
} else {
m_archive_.reset();
INSPIRE_LOGE("Failed to reload resources");
return HERR_ARCHIVE_LOAD_MODEL_FAILURE;
}
} catch (const std::exception& e) {
m_archive_.reset();
INSPIRE_LOGE("Exception during resource reloading: %s", e.what());
return HERR_ARCHIVE_LOAD_MODEL_FAILURE;
}
}
bool Launch::isMLoad() const {
return m_load_;
}
void Launch::Unload() {
std::lock_guard<std::mutex> lock(mutex_);
if (m_load_) {
m_archive_.reset();
m_load_ = false;
INSPIRE_LOGI("All resources have been successfully unloaded and system is reset.");
} else {
INSPIRE_LOGW("Unload called but system was not loaded.");
}
}
void Launch::SetRockchipDmaHeapPath(const std::string& path) {
m_rockchip_dma_heap_path_ = path;
}
std::string Launch::GetRockchipDmaHeapPath() const {
return m_rockchip_dma_heap_path_;
}
void Launch::ConfigurationExtensionPath(const std::string& path) {
#if defined(ISF_ENABLE_APPLE_EXTENSION)
INSPIREFACE_CHECK_MSG(os::IsDir(path), "The apple extension path is not a directory, please check.");
#endif
INSPIREFACE_CHECK_MSG(os::IsExists(path), "The extension path is not exists, please check.");
m_extension_path_ = path;
}
std::string Launch::GetExtensionPath() const {
return m_extension_path_;
}
void Launch::SetGlobalCoreMLInferenceMode(InferenceWrapper::SpecialBackend mode) {
m_global_coreml_inference_mode_ = mode;
if (m_global_coreml_inference_mode_ == InferenceWrapper::COREML_CPU) {
INSPIRE_LOGW("Global CoreML Compute Units set to CPU Only.");
} else if (m_global_coreml_inference_mode_ == InferenceWrapper::COREML_GPU) {
INSPIRE_LOGW("Global CoreML Compute Units set to CPU and GPU.");
} else if (m_global_coreml_inference_mode_ == InferenceWrapper::COREML_ANE) {
INSPIRE_LOGW("Global CoreML Compute Units set to Auto Switch (ANE, GPU, CPU).");
}
}
InferenceWrapper::SpecialBackend Launch::GetGlobalCoreMLInferenceMode() const {
return m_global_coreml_inference_mode_;
}
void Launch::BuildAppleExtensionPath(const std::string& resource_path) {
std::string basename = os::Basename(resource_path);
m_extension_path_ = os::PathJoin(os::Dirname(resource_path), basename + APPLE_EXTENSION_SUFFIX);
INSPIREFACE_CHECK_MSG(os::IsExists(m_extension_path_), "The apple extension path is not exists, please check.");
INSPIREFACE_CHECK_MSG(os::IsDir(m_extension_path_), "The apple extension path is not a directory, please check.");
}
void Launch::SetCudaDeviceId(int32_t device_id) {
m_cuda_device_id_ = device_id;
}
int32_t Launch::GetCudaDeviceId() const {
return m_cuda_device_id_;
}
} // namespace inspire

View File

@@ -7,14 +7,17 @@
#include "intypedef.h"
#include "inspireface_internal.h"
#include "information.h"
#include "feature_hub/feature_hub_db.h"
#include "initialization_module/launch.h"
#include "initialization_module/resource_manage.h"
#include "recognition_module/similarity_converter.h"
#include "feature_hub_db.h"
#include <launch.h>
#include "runtime_module/resource_manage.h"
#include "similarity_converter.h"
#include "middleware/inference_wrapper/inference_wrapper.h"
#if defined(ISF_ENABLE_TENSORRT)
#include "middleware/cuda_toolkit.h"
#include "cuda_toolkit.h"
#endif
#include <cstdarg>
#define FACE_FEATURE_SIZE 512 ///< Temporary setup
using namespace inspire;
@@ -345,6 +348,31 @@ HResult HFReleaseInspireFaceSession(HFSession handle) {
return HSUCCEED;
}
HResult HFSwitchLandmarkEngine(HFSessionLandmarkEngine engine) {
inspire::Launch::LandmarkEngine type;
if (engine == HF_LANDMARK_HYPLMV2_0_25) {
type = inspire::Launch::LANDMARK_HYPLMV2_0_25;
} else if (engine == HF_LANDMARK_HYPLMV2_0_50) {
type = inspire::Launch::LANDMARK_HYPLMV2_0_50;
} else if (engine == HF_LANDMARK_INSIGHTFACE_2D106_TRACK) {
type = inspire::Launch::LANDMARK_INSIGHTFACE_2D106_TRACK;
} else {
INSPIRE_LOGE("Unsupported Landmark engine.");
return HERR_INVALID_PARAM;
}
INSPIREFACE_CONTEXT->SwitchLandmarkEngine(type);
return HSUCCEED;
}
HResult HFQuerySupportedPixelLevelsForFaceDetection(PHFFaceDetectPixelList pixel_levels) {
auto ret = INSPIREFACE_CONTEXT->GetFaceDetectPixelList();
pixel_levels->size = ret.size();
for (int i = 0; i < ret.size(); i++) {
pixel_levels->pixel_level[i] = ret[i];
}
return HSUCCEED;
}
HResult HFCreateInspireFaceSession(HFSessionCustomParameter parameter, HFDetectMode detectMode, HInt32 maxDetectFaceNum, HInt32 detectPixelLevel,
HInt32 trackByDetectModeFPS, HFSession *handle) {
inspire::ContextCustomParameter param;
@@ -355,7 +383,7 @@ HResult HFCreateInspireFaceSession(HFSessionCustomParameter parameter, HFDetectM
param.enable_ir_liveness = parameter.enable_ir_liveness;
param.enable_recognition = parameter.enable_recognition;
param.enable_face_attribute = parameter.enable_face_attribute;
param.enable_detect_mode_landmark = parameter.enable_detect_mode_landmark;
param.enable_face_pose = parameter.enable_face_pose;
inspire::DetectModuleMode detMode = inspire::DETECT_MODE_ALWAYS_DETECT;
if (detectMode == HF_DETECT_MODE_LIGHT_TRACK) {
detMode = inspire::DETECT_MODE_LIGHT_TRACK;
@@ -401,8 +429,8 @@ HResult HFCreateInspireFaceSessionOptional(HOption customOption, HFDetectMode de
if (customOption & HF_ENABLE_INTERACTION) {
param.enable_interaction_liveness = true;
}
if (customOption & HF_ENABLE_DETECT_MODE_LANDMARK) {
param.enable_detect_mode_landmark = true;
if (customOption & HF_ENABLE_FACE_POSE) {
param.enable_face_pose = true;
}
inspire::DetectModuleMode detMode = inspire::DETECT_MODE_ALWAYS_DETECT;
if (detectMode == HF_DETECT_MODE_LIGHT_TRACK) {
@@ -427,56 +455,59 @@ HResult HFCreateInspireFaceSessionOptional(HOption customOption, HFDetectMode de
HResult HFLaunchInspireFace(HPath resourcePath) {
std::string path(resourcePath);
return INSPIRE_LAUNCH->Load(resourcePath);
return INSPIREFACE_CONTEXT->Load(resourcePath);
}
HResult HFReloadInspireFace(HPath resourcePath) {
std::string path(resourcePath);
return INSPIRE_LAUNCH->Reload(resourcePath);
return INSPIREFACE_CONTEXT->Reload(resourcePath);
}
HResult HFTerminateInspireFace() {
INSPIRE_LAUNCH->Unload();
INSPIREFACE_CONTEXT->Unload();
return HSUCCEED;
}
HResult HFQueryInspireFaceLaunchStatus(HInt32 *status) {
*status = INSPIRE_LAUNCH->isMLoad();
*status = INSPIREFACE_CONTEXT->isMLoad();
return HSUCCEED;
}
HResult HFFeatureHubDataDisable() {
return FEATURE_HUB_DB->DisableHub();
return INSPIREFACE_FEATURE_HUB->DisableHub();
}
HResult HFSetExpansiveHardwareRockchipDmaHeapPath(HPath path) {
INSPIRE_LAUNCH->SetRockchipDmaHeapPath(path);
INSPIREFACE_CONTEXT->SetRockchipDmaHeapPath(path);
return HSUCCEED;
}
HResult HFQueryExpansiveHardwareRockchipDmaHeapPath(HString path) {
strcpy(path, INSPIRE_LAUNCH->GetRockchipDmaHeapPath().c_str());
strcpy(path, INSPIREFACE_CONTEXT->GetRockchipDmaHeapPath().c_str());
return HSUCCEED;
}
HResult HFSetAppleCoreMLInferenceMode(HFAppleCoreMLInferenceMode mode) {
if (mode == HF_APPLE_COREML_INFERENCE_MODE_CPU) {
INSPIRE_LAUNCH->SetGlobalCoreMLInferenceMode(InferenceWrapper::COREML_CPU);
INSPIREFACE_CONTEXT->SetGlobalCoreMLInferenceMode(inspire::Launch::NN_INFERENCE_CPU);
} else if (mode == HF_APPLE_COREML_INFERENCE_MODE_GPU) {
INSPIRE_LAUNCH->SetGlobalCoreMLInferenceMode(InferenceWrapper::COREML_GPU);
INSPIREFACE_CONTEXT->SetGlobalCoreMLInferenceMode(inspire::Launch::NN_INFERENCE_COREML_GPU);
} else if (mode == HF_APPLE_COREML_INFERENCE_MODE_ANE) {
INSPIRE_LAUNCH->SetGlobalCoreMLInferenceMode(InferenceWrapper::COREML_ANE);
INSPIREFACE_CONTEXT->SetGlobalCoreMLInferenceMode(inspire::Launch::NN_INFERENCE_COREML_ANE);
} else {
INSPIRE_LOGE("Unsupported Apple CoreML inference mode.");
return HERR_INVALID_PARAM;
}
return HSUCCEED;
}
HResult HFSetCudaDeviceId(int32_t device_id) {
INSPIRE_LAUNCH->SetCudaDeviceId(device_id);
INSPIREFACE_CONTEXT->SetCudaDeviceId(device_id);
return HSUCCEED;
}
HResult HFGetCudaDeviceId(int32_t *device_id) {
*device_id = INSPIRE_LAUNCH->GetCudaDeviceId();
*device_id = INSPIREFACE_CONTEXT->GetCudaDeviceId();
return HSUCCEED;
}
@@ -530,7 +561,7 @@ HResult HFFeatureHubDataEnable(HFFeatureHubConfiguration configuration) {
param.enable_persistence = configuration.enablePersistence;
param.recognition_threshold = configuration.searchThreshold;
param.search_mode = (inspire::SearchMode)configuration.searchMode;
auto ret = FEATURE_HUB_DB->EnableHub(param);
auto ret = INSPIREFACE_FEATURE_HUB->EnableHub(param);
return ret;
}
@@ -545,6 +576,20 @@ HResult HFSessionSetTrackPreviewSize(HFSession session, HInt32 previewSize) {
return ctx->impl.SetTrackPreviewSize(previewSize);
}
HResult HFSessionGetTrackPreviewSize(HFSession session, HInt32 *previewSize) {
if (session == nullptr) {
return HERR_INVALID_CONTEXT_HANDLE;
}
HF_FaceAlgorithmSession *ctx = (HF_FaceAlgorithmSession *)session;
if (ctx == nullptr) {
return HERR_INVALID_CONTEXT_HANDLE;
}
*previewSize = ctx->impl.GetTrackPreviewSize();
return HSUCCEED;
}
HResult HFSessionSetFilterMinimumFacePixelSize(HFSession session, HInt32 minSize) {
if (session == nullptr) {
return HERR_INVALID_CONTEXT_HANDLE;
@@ -615,6 +660,8 @@ HResult HFSessionSetTrackModeDetectInterval(HFSession session, HInt32 num) {
return ctx->impl.SetTrackModeDetectInterval(num);
}
HResult HFExecuteFaceTrack(HFSession session, HFImageStream streamHandle, PHFMultipleFaceData results) {
if (session == nullptr) {
return HERR_INVALID_CONTEXT_HANDLE;
@@ -643,16 +690,28 @@ HResult HFExecuteFaceTrack(HFSession session, HFImageStream streamHandle, PHFMul
return ret;
}
HResult HFSessionLastFaceDetectionGetDebugPreviewImageSize(HFSession session, HInt32 *size) {
if (session == nullptr) {
return HERR_INVALID_CONTEXT_HANDLE;
}
HF_FaceAlgorithmSession *ctx = (HF_FaceAlgorithmSession *)session;
if (ctx == nullptr) {
return HERR_INVALID_CONTEXT_HANDLE;
}
*size = ctx->impl.GetDebugPreviewImageSize();
return HSUCCEED;
}
HResult HFCopyFaceBasicToken(HFFaceBasicToken token, HPBuffer buffer, HInt32 bufferSize) {
if (bufferSize < sizeof(inspire::HyperFaceData)) {
if (bufferSize < sizeof(inspire::FaceTrackWrap)) {
return HERR_INVALID_BUFFER_SIZE;
}
std::memcpy(buffer, token.data, sizeof(inspire::HyperFaceData));
std::memcpy(buffer, token.data, sizeof(inspire::FaceTrackWrap));
return HSUCCEED;
}
HResult HFGetFaceBasicTokenSize(HPInt32 bufferSize) {
*bufferSize = sizeof(inspire::HyperFaceData);
*bufferSize = sizeof(inspire::FaceTrackWrap);
return HSUCCEED;
}
@@ -668,7 +727,7 @@ HResult HFGetFaceDenseLandmarkFromFaceToken(HFFaceBasicToken singleFace, HPoint2
inspire::FaceBasicData data;
data.dataSize = singleFace.size;
data.data = singleFace.data;
HyperFaceData face = {0};
FaceTrackWrap face = {0};
HInt32 ret;
ret = RunDeserializeHyperFaceData((char *)data.data, data.dataSize, face);
if (ret != HSUCCEED) {
@@ -692,7 +751,7 @@ HResult HFGetFaceFiveKeyPointsFromFaceToken(HFFaceBasicToken singleFace, HPoint2
inspire::FaceBasicData data;
data.dataSize = singleFace.size;
data.data = singleFace.data;
HyperFaceData face = {0};
FaceTrackWrap face = {0};
HInt32 ret;
ret = RunDeserializeHyperFaceData((char *)data.data, data.dataSize, face);
if (ret != HSUCCEED) {
@@ -730,7 +789,7 @@ HResult HFSessionPrintTrackCostSpend(HFSession session) {
}
HResult HFFeatureHubFaceSearchThresholdSetting(float threshold) {
FEATURE_HUB_DB->SetRecognitionThreshold(threshold);
INSPIREFACE_FEATURE_HUB->SetRecognitionThreshold(threshold);
return HSUCCEED;
}
@@ -762,6 +821,35 @@ HResult HFFaceFeatureExtract(HFSession session, HFImageStream streamHandle, HFFa
return ret;
}
HResult HFFaceFeatureExtractTo(HFSession session, HFImageStream streamHandle, HFFaceBasicToken singleFace, HFFaceFeature feature) {
if (session == nullptr) {
return HERR_INVALID_CONTEXT_HANDLE;
}
if (streamHandle == nullptr) {
return HERR_INVALID_IMAGE_STREAM_HANDLE;
}
HF_FaceAlgorithmSession *ctx = (HF_FaceAlgorithmSession *)session;
if (ctx == nullptr) {
return HERR_INVALID_CONTEXT_HANDLE;
}
HF_CameraStream *stream = (HF_CameraStream *)streamHandle;
if (stream == nullptr) {
return HERR_INVALID_IMAGE_STREAM_HANDLE;
}
if (singleFace.data == nullptr || singleFace.size <= 0) {
return HERR_INVALID_FACE_TOKEN;
}
inspire::FaceBasicData data;
data.dataSize = singleFace.size;
data.data = singleFace.data;
auto ret = ctx->impl.FaceFeatureExtract(stream->impl, data);
for (int i = 0; i < ctx->impl.GetFaceFeatureCache().size(); ++i) {
feature.data[i] = ctx->impl.GetFaceFeatureCache()[i];
}
return HSUCCEED;
}
HResult HFFaceFeatureExtractCpy(HFSession session, HFImageStream streamHandle, HFFaceBasicToken singleFace, HPFloat feature) {
if (session == nullptr) {
return HERR_INVALID_CONTEXT_HANDLE;
@@ -791,6 +879,25 @@ HResult HFFaceFeatureExtractCpy(HFSession session, HFImageStream streamHandle, H
return ret;
}
HResult HFCreateFaceFeature(PHFFaceFeature feature) {
if (feature == nullptr) {
return HERR_INVALID_FACE_FEATURE;
}
feature->size = FACE_FEATURE_SIZE;
feature->data = new HFloat[FACE_FEATURE_SIZE];
RESOURCE_MANAGE->createFaceFeature((long)feature);
return HSUCCEED;
}
HResult HFReleaseFaceFeature(PHFFaceFeature feature) {
if (feature == nullptr) {
return HERR_INVALID_FACE_FEATURE;
}
delete[] feature->data;
RESOURCE_MANAGE->releaseFaceFeature((long)feature);
return HSUCCEED;
}
HResult HFFaceGetFaceAlignmentImage(HFSession session, HFImageStream streamHandle, HFFaceBasicToken singleFace, HFImageBitmap *handle) {
if (session == nullptr) {
return HERR_INVALID_CONTEXT_HANDLE;
@@ -824,6 +931,30 @@ HResult HFFaceGetFaceAlignmentImage(HFSession session, HFImageStream streamHandl
return HSUCCEED;
}
HResult HFFaceFeatureExtractWithAlignmentImage(HFSession session, HFImageStream streamHandle, HFFaceFeature feature) {
if (session == nullptr) {
return HERR_INVALID_CONTEXT_HANDLE;
}
if (streamHandle == nullptr) {
return HERR_INVALID_IMAGE_STREAM_HANDLE;
}
HF_FaceAlgorithmSession *ctx = (HF_FaceAlgorithmSession *)session;
if (ctx == nullptr) {
return HERR_INVALID_CONTEXT_HANDLE;
}
HF_CameraStream *stream = (HF_CameraStream *)streamHandle;
if (stream == nullptr) {
return HERR_INVALID_IMAGE_STREAM_HANDLE;
}
Embedded embedded;
float norm;
auto ret = ctx->impl.FaceRecognitionModule()->FaceExtractWithAlignmentImage(stream->impl, embedded, norm);
for (int i = 0; i < embedded.size(); ++i) {
feature.data[i] = embedded[i];
}
return ret;
}
HResult HFFaceComparison(HFFaceFeature feature1, HFFaceFeature feature2, HPFloat result) {
if (feature1.data == nullptr || feature2.data == nullptr) {
return HERR_INVALID_FACE_FEATURE;
@@ -834,14 +965,14 @@ HResult HFFaceComparison(HFFaceFeature feature1, HFFaceFeature feature2, HPFloat
}
*result = 0.0f;
float res = -1.0f;
auto ret = FEATURE_HUB_DB->CosineSimilarity(feature1.data, feature2.data, feature1.size, res);
auto ret = INSPIREFACE_FEATURE_HUB->CosineSimilarity(feature1.data, feature2.data, feature1.size, res);
*result = res;
return ret;
}
HResult HFGetRecommendedCosineThreshold(HPFloat threshold) {
if (!INSPIRE_LAUNCH->isMLoad()) {
if (!INSPIREFACE_CONTEXT->isMLoad()) {
INSPIRE_LOGW("Inspireface is not launched, using default threshold 0.48");
}
*threshold = SIMILARITY_CONVERTER_GET_RECOMMENDED_COSINE_THRESHOLD();
@@ -849,7 +980,7 @@ HResult HFGetRecommendedCosineThreshold(HPFloat threshold) {
}
HResult HFCosineSimilarityConvertToPercentage(HFloat similarity, HPFloat result) {
if (!INSPIRE_LAUNCH->isMLoad()) {
if (!INSPIREFACE_CONTEXT->isMLoad()) {
INSPIRE_LOGW("Inspireface is not launched.");
}
*result = SIMILARITY_CONVERTER_RUN(similarity);
@@ -857,7 +988,7 @@ HResult HFCosineSimilarityConvertToPercentage(HFloat similarity, HPFloat result)
}
HResult HFUpdateCosineSimilarityConverter(HFSimilarityConverterConfig config) {
if (!INSPIRE_LAUNCH->isMLoad()) {
if (!INSPIREFACE_CONTEXT->isMLoad()) {
INSPIRE_LOGW("Inspireface is not launched.");
}
inspire::SimilarityConverterConfig cfg;
@@ -871,7 +1002,7 @@ HResult HFUpdateCosineSimilarityConverter(HFSimilarityConverterConfig config) {
}
HResult HFGetCosineSimilarityConverter(PHFSimilarityConverterConfig config) {
if (!INSPIRE_LAUNCH->isMLoad()) {
if (!INSPIREFACE_CONTEXT->isMLoad()) {
INSPIRE_LOGW("Inspireface is not launched.");
}
inspire::SimilarityConverterConfig cfg = SIMILARITY_CONVERTER_GET_CONFIG();
@@ -884,7 +1015,7 @@ HResult HFGetCosineSimilarityConverter(PHFSimilarityConverterConfig config) {
}
HResult HFGetFeatureLength(HPInt32 num) {
*num = 512;
*num = FACE_FEATURE_SIZE;
return HSUCCEED;
}
@@ -898,7 +1029,7 @@ HResult HFFeatureHubInsertFeature(HFFaceFeatureIdentity featureIdentity, HPFaceI
for (int i = 0; i < featureIdentity.feature->size; ++i) {
feat.push_back(featureIdentity.feature->data[i]);
}
HInt32 ret = FEATURE_HUB_DB->FaceFeatureInsert(feat, featureIdentity.id, *allocId);
HInt32 ret = INSPIREFACE_FEATURE_HUB->FaceFeatureInsert(feat, featureIdentity.id, *allocId);
return ret;
}
@@ -914,10 +1045,10 @@ HResult HFFeatureHubFaceSearch(HFFaceFeature searchFeature, HPFloat confidence,
}
*confidence = -1.0f;
inspire::FaceSearchResult result;
HInt32 ret = FEATURE_HUB_DB->SearchFaceFeature(feat, result);
mostSimilar->feature = (HFFaceFeature *)FEATURE_HUB_DB->GetFaceFeaturePtrCache().get();
mostSimilar->feature->data = (HFloat *)FEATURE_HUB_DB->GetSearchFaceFeatureCache().data();
mostSimilar->feature->size = FEATURE_HUB_DB->GetSearchFaceFeatureCache().size();
HInt32 ret = INSPIREFACE_FEATURE_HUB->SearchFaceFeature(feat, result);
mostSimilar->feature = (HFFaceFeature *)INSPIREFACE_FEATURE_HUB->GetFaceFeaturePtrCache().get();
mostSimilar->feature->data = (HFloat *)INSPIREFACE_FEATURE_HUB->GetSearchFaceFeatureCache().data();
mostSimilar->feature->size = INSPIREFACE_FEATURE_HUB->GetSearchFaceFeatureCache().size();
mostSimilar->id = result.id;
if (mostSimilar->id != -1) {
*confidence = result.similarity;
@@ -935,18 +1066,18 @@ HResult HFFeatureHubFaceSearchTopK(HFFaceFeature searchFeature, HInt32 topK, PHF
for (int i = 0; i < searchFeature.size; ++i) {
feat.push_back(searchFeature.data[i]);
}
HInt32 ret = FEATURE_HUB_DB->SearchFaceFeatureTopKCache(feat, topK);
HInt32 ret = INSPIREFACE_FEATURE_HUB->SearchFaceFeatureTopKCache(feat, topK);
if (ret == HSUCCEED) {
results->size = FEATURE_HUB_DB->GetTopKConfidence().size();
results->confidence = FEATURE_HUB_DB->GetTopKConfidence().data();
results->ids = FEATURE_HUB_DB->GetTopKCustomIdsCache().data();
results->size = INSPIREFACE_FEATURE_HUB->GetTopKConfidence().size();
results->confidence = INSPIREFACE_FEATURE_HUB->GetTopKConfidence().data();
results->ids = INSPIREFACE_FEATURE_HUB->GetTopKCustomIdsCache().data();
}
return ret;
}
HResult HFFeatureHubFaceRemove(HFaceId id) {
auto ret = FEATURE_HUB_DB->FaceFeatureRemove(id);
auto ret = INSPIREFACE_FEATURE_HUB->FaceFeatureRemove(id);
return ret;
}
@@ -960,18 +1091,18 @@ HResult HFFeatureHubFaceUpdate(HFFaceFeatureIdentity featureIdentity) {
feat.push_back(featureIdentity.feature->data[i]);
}
auto ret = FEATURE_HUB_DB->FaceFeatureUpdate(feat, featureIdentity.id);
auto ret = INSPIREFACE_FEATURE_HUB->FaceFeatureUpdate(feat, featureIdentity.id);
return ret;
}
HResult HFFeatureHubGetFaceIdentity(HFaceId id, PHFFaceFeatureIdentity identity) {
auto ret = FEATURE_HUB_DB->GetFaceFeature(id);
auto ret = INSPIREFACE_FEATURE_HUB->GetFaceFeature(id);
if (ret == HSUCCEED) {
identity->id = id;
identity->feature = (HFFaceFeature *)FEATURE_HUB_DB->GetFaceFeaturePtrCache().get();
identity->feature->data = (HFloat *)FEATURE_HUB_DB->GetFaceFeaturePtrCache()->data;
identity->feature->size = FEATURE_HUB_DB->GetFaceFeaturePtrCache()->dataSize;
identity->feature = (HFFaceFeature *)INSPIREFACE_FEATURE_HUB->GetFaceFeaturePtrCache().get();
identity->feature->data = (HFloat *)INSPIREFACE_FEATURE_HUB->GetFaceFeaturePtrCache()->data;
identity->feature->size = INSPIREFACE_FEATURE_HUB->GetFaceFeaturePtrCache()->dataSize;
} else {
identity->id = -1;
}
@@ -1010,10 +1141,9 @@ HResult HFMultipleFacePipelineProcess(HFSession session, HFImageStream streamHan
param.enable_ir_liveness = parameter.enable_ir_liveness;
param.enable_recognition = parameter.enable_recognition;
param.enable_face_attribute = parameter.enable_face_attribute;
param.enable_detect_mode_landmark = parameter.enable_detect_mode_landmark;
HResult ret;
std::vector<inspire::HyperFaceData> data;
std::vector<inspire::FaceTrackWrap> data;
data.resize(faces->detectedNum);
for (int i = 0; i < faces->detectedNum; ++i) {
auto &face = data[i];
@@ -1072,12 +1202,12 @@ HResult HFMultipleFacePipelineProcessOptional(HFSession session, HFImageStream s
if (customOption & HF_ENABLE_INTERACTION) {
param.enable_interaction_liveness = true;
}
if (customOption & HF_ENABLE_DETECT_MODE_LANDMARK) {
param.enable_detect_mode_landmark = true;
if (customOption & HF_ENABLE_FACE_POSE) {
param.enable_face_pose = true;
}
HResult ret;
std::vector<inspire::HyperFaceData> data;
std::vector<inspire::FaceTrackWrap> data;
data.resize(faces->detectedNum);
for (int i = 0; i < faces->detectedNum; ++i) {
auto &face = data[i];
@@ -1206,20 +1336,20 @@ HResult HFGetFaceAttributeResult(HFSession session, PHFFaceAttributeResult resul
}
HResult HFFeatureHubGetFaceCount(HInt32 *count) {
*count = FEATURE_HUB_DB->GetFaceFeatureCount();
*count = INSPIREFACE_FEATURE_HUB->GetFaceFeatureCount();
return HSUCCEED;
}
HResult HFFeatureHubViewDBTable() {
FEATURE_HUB_DB->ViewDBTable();
INSPIREFACE_FEATURE_HUB->ViewDBTable();
return HSUCCEED;
}
HResult HFFeatureHubGetExistingIds(PHFFeatureHubExistingIds ids) {
auto ret = FEATURE_HUB_DB->GetAllIds();
auto ret = INSPIREFACE_FEATURE_HUB->GetAllIds();
if (ret == HSUCCEED) {
ids->size = FEATURE_HUB_DB->GetExistingIds().size();
ids->ids = FEATURE_HUB_DB->GetExistingIds().data();
ids->size = INSPIREFACE_FEATURE_HUB->GetExistingIds().size();
ids->ids = INSPIREFACE_FEATURE_HUB->GetExistingIds().data();
}
return ret;
}

View File

@@ -24,16 +24,19 @@
extern "C" {
#endif
#define HF_ENABLE_NONE 0x00000000 ///< Flag to enable no features.
#define HF_ENABLE_FACE_RECOGNITION 0x00000002 ///< Flag to enable face recognition feature.
#define HF_ENABLE_LIVENESS 0x00000004 ///< Flag to enable RGB liveness detection feature.
#define HF_ENABLE_IR_LIVENESS 0x00000008 ///< Flag to enable IR (Infrared) liveness detection feature.
#define HF_ENABLE_MASK_DETECT 0x00000010 ///< Flag to enable mask detection feature.
#define HF_ENABLE_FACE_ATTRIBUTE 0x00000020 ///< Flag to enable face attribute prediction feature.
#define HF_ENABLE_PLACEHOLDER_ 0x00000040 ///< -
#define HF_ENABLE_QUALITY 0x00000080 ///< Flag to enable face quality assessment feature.
#define HF_ENABLE_INTERACTION 0x00000100 ///< Flag to enable interaction feature.
#define HF_ENABLE_DETECT_MODE_LANDMARK 0x00000200 ///< Flag to enable landmark detection in detection mode
#define HF_STATUS_ENABLE 1 ///< The status of the feature is enabled.
#define HF_STATUS_DISABLE 0 ///< The status of the feature is disabled.
#define HF_ENABLE_NONE 0x00000000 ///< Flag to enable no features.
#define HF_ENABLE_FACE_RECOGNITION 0x00000002 ///< Flag to enable face recognition feature.
#define HF_ENABLE_LIVENESS 0x00000004 ///< Flag to enable RGB liveness detection feature.
#define HF_ENABLE_IR_LIVENESS 0x00000008 ///< Flag to enable IR (Infrared) liveness detection feature.
#define HF_ENABLE_MASK_DETECT 0x00000010 ///< Flag to enable mask detection feature.
#define HF_ENABLE_FACE_ATTRIBUTE 0x00000020 ///< Flag to enable face attribute prediction feature.
#define HF_ENABLE_PLACEHOLDER_ 0x00000040 ///< -
#define HF_ENABLE_QUALITY 0x00000080 ///< Flag to enable face quality assessment feature.
#define HF_ENABLE_INTERACTION 0x00000100 ///< Flag to enable interaction feature.
#define HF_ENABLE_FACE_POSE 0x00000200 ///< Flag to enable face pose estimation feature.
/**
* Camera stream format.
@@ -385,6 +388,7 @@ typedef struct HFSessionCustomParameter {
HInt32 enable_face_attribute; ///< Enable face attribute prediction feature.
HInt32 enable_interaction_liveness; ///< Enable interaction for liveness detection feature.
HInt32 enable_detect_mode_landmark; ///< Enable landmark detection in detection mode
HInt32 enable_face_pose; ///< Enable face pose estimation feature.
} HFSessionCustomParameter, *PHFSessionCustomParameter;
/**
@@ -400,6 +404,38 @@ typedef enum HFDetectMode {
// to use it).
} HFDetectMode;
/**
* @brief Enum for landmark engine.
*/
typedef enum HFSessionLandmarkEngine {
HF_LANDMARK_HYPLMV2_0_25 = 0, ///< Hyplmkv2 0.25, default
HF_LANDMARK_HYPLMV2_0_50 = 1, ///< Hyplmkv2 0.50
HF_LANDMARK_INSIGHTFACE_2D106_TRACK = 2, ///< InsightFace 2d106 track
} HFSessionLandmarkEngine;
/**
* @brief Global switch the landmark engine. Set it globally before creating a session.
* If it is changed, a new session needs to be created for it to be effective.
* @param engine The landmark engine to be set.
* @return HResult indicating the success or failure of the operation.
* */
HYPER_CAPI_EXPORT extern HResult HFSwitchLandmarkEngine(HFSessionLandmarkEngine engine);
/**
* @brief Enum for supported pixel levels for face detection.
*/
typedef struct HFFaceDetectPixelList {
HInt32 pixel_level[20];
HInt32 size;
} HFFaceDetectPixelList, *PHFFaceDetectPixelList;
/**
* @brief Query the supported pixel levels for face detection. It must be used before starting.
* @param pixel_levels Pointer to the array of supported pixel levels.
* @return HResult indicating the success or failure of the operation.
* */
HYPER_CAPI_EXPORT extern HResult HFQuerySupportedPixelLevelsForFaceDetection(PHFFaceDetectPixelList pixel_levels);
/**
* @brief Create a session from a resource file.
*
@@ -488,6 +524,14 @@ typedef struct HFMultipleFaceData {
*/
HYPER_CAPI_EXPORT extern HResult HFSessionSetTrackPreviewSize(HFSession session, HInt32 previewSize);
/**
* @brief Get the track preview size in the session.
* @param session Handle to the session.
* @param previewSize The size of the preview for tracking.
* @return HResult indicating the success or failure of the operation.
*/
HYPER_CAPI_EXPORT extern HResult HFSessionGetTrackPreviewSize(HFSession session, HInt32 *previewSize);
/**
* @brief Set the minimum number of face pixels that the face detector can capture, and people below
* this number will be filtered.
@@ -534,6 +578,7 @@ HYPER_CAPI_EXPORT extern HResult HFSessionSetTrackModeNumSmoothCacheFrame(HFSess
*/
HYPER_CAPI_EXPORT extern HResult HFSessionSetTrackModeDetectInterval(HFSession session, HInt32 num);
/**
* @brief Run face tracking in the session.
*
@@ -544,6 +589,14 @@ HYPER_CAPI_EXPORT extern HResult HFSessionSetTrackModeDetectInterval(HFSession s
*/
HYPER_CAPI_EXPORT extern HResult HFExecuteFaceTrack(HFSession session, HFImageStream streamHandle, PHFMultipleFaceData results);
/**
* @brief Gets the size of the debug preview image for the last face detection in the session.
* @param session Handle to the session.
* @param size The size of the preview for tracking.
* @return HResult indicating the success or failure of the operation.
*/
HYPER_CAPI_EXPORT extern HResult HFSessionLastFaceDetectionGetDebugPreviewImageSize(HFSession session, HInt32 *size);
/**
* @brief Copies the data from a HF_FaceBasicToken to a specified buffer.
*
@@ -641,6 +694,17 @@ typedef struct HFFaceFeature {
HYPER_CAPI_EXPORT extern HResult HFFaceFeatureExtract(HFSession session, HFImageStream streamHandle, HFFaceBasicToken singleFace,
PHFFaceFeature feature);
/**
* @brief Extract face features to the HFFaceFeature that has applied for memory in advance.
* @param session Handle to the session.
* @param streamHandle Handle to the data buffer representing the camera stream component.
* @param singleFace Basic token representing a single face.
* @param feature Pointer to the buffer where the extracted feature will be copied.
* @return HResult indicating the success or failure of the operation.
*/
HYPER_CAPI_EXPORT extern HResult HFFaceFeatureExtractTo(HFSession session, HFImageStream streamHandle, HFFaceBasicToken singleFace,
HFFaceFeature feature);
/**
* @brief Extract a face feature from a given face and copy it to the provided feature buffer.
*
@@ -652,6 +716,20 @@ HYPER_CAPI_EXPORT extern HResult HFFaceFeatureExtract(HFSession session, HFImage
*/
HYPER_CAPI_EXPORT extern HResult HFFaceFeatureExtractCpy(HFSession session, HFImageStream streamHandle, HFFaceBasicToken singleFace, HPFloat feature);
/**
* @brief Create a face feature. Will allocate memory.
* @param feature Pointer to the face feature.
* @return HResult indicating the success or failure of the operation.
*/
HYPER_CAPI_EXPORT extern HResult HFCreateFaceFeature(PHFFaceFeature feature);
/**
* @brief Release a face feature. Only the features created through the HFCreateFaceFeature need to be processed.
* @param feature Pointer to the face feature.
* @return HResult indicating the success or failure of the operation.
*/
HYPER_CAPI_EXPORT extern HResult HFReleaseFaceFeature(PHFFaceFeature feature);
/**
* @brief Get the face alignment image.
* @param session Handle to the session.
@@ -663,6 +741,15 @@ HYPER_CAPI_EXPORT extern HResult HFFaceFeatureExtractCpy(HFSession session, HFIm
HYPER_CAPI_EXPORT extern HResult HFFaceGetFaceAlignmentImage(HFSession session, HFImageStream streamHandle, HFFaceBasicToken singleFace,
HFImageBitmap *handle);
/**
* @brief Use the aligned face image to extract face features to the HFFaceFeature that has applied memory in advance.
* @param session Handle to the session.
* @param streamHandle Handle to the data buffer representing the camera stream component.
* @param feature Pointer to the buffer where the extracted feature will be copied.
* @return HResult indicating the success or failure of the operation.
*/
HYPER_CAPI_EXPORT extern HResult HFFaceFeatureExtractWithAlignmentImage(HFSession session, HFImageStream streamHandle, HFFaceFeature feature);
/************************************************************************
* Feature Hub
************************************************************************/
@@ -851,7 +938,7 @@ HYPER_CAPI_EXPORT extern HResult HFFeatureHubFaceSearchTopK(HFFaceFeature search
/**
* @brief Remove a face feature from the features group based on custom ID.
*
* @param customId The custom ID of the feature to be removed.
* @param ID The custom ID of the feature to be removed.
* @return HResult indicating the success or failure of the operation.
*/
HYPER_CAPI_EXPORT extern HResult HFFeatureHubFaceRemove(HFaceId id);

View File

@@ -6,15 +6,15 @@
#ifndef INSPIREFACE_INTERNAL_H
#define INSPIREFACE_INTERNAL_H
#include "face_session.h"
#include "engine/face_session.h"
typedef struct HF_FaceAlgorithmSession {
inspire::FaceSession impl; ///< Implementation of the face context.
} HF_FaceAlgorithmSession; ///< Handle for managing face context.
typedef struct HF_CameraStream {
inspirecv::InspireImageProcess impl; ///< Implementation of the camera stream.
} HF_CameraStream; ///< Handle for managing camera stream.
inspirecv::FrameProcess impl; ///< Implementation of the camera stream.
} HF_CameraStream; ///< Handle for managing camera stream.
typedef struct HF_ImageBitmap {
inspirecv::Image impl; ///< Implementation of the image bitmap.

View File

@@ -6,7 +6,7 @@
#ifndef INSPIRE_FACE_SERIALIZE_TOOLS_H
#define INSPIRE_FACE_SERIALIZE_TOOLS_H
#include "face_data_type.h"
#include "face_warpper.h"
#include "../face_info/face_object_internal.h"
#include "herror.h"
#include "data_type.h"
@@ -27,10 +27,10 @@ inline void PrintTransformMatrix(const TransMatrix& matrix) {
}
/**
* @brief Print HyperFaceData structure.
* @param data The HyperFaceData structure to print.
* @brief Print FaceTrackWrap structure.
* @param data The FaceTrackWrap structure to print.
*/
inline void INSPIRE_API PrintHyperFaceDataDetail(const HyperFaceData& data) {
inline void INSPIRE_API PrintHyperFaceDataDetail(const FaceTrackWrap& data) {
INSPIRE_LOGI("Track State: %d", data.trackState);
INSPIRE_LOGI("In Group Index: %d", data.inGroupIndex);
INSPIRE_LOGI("Track ID: %d", data.trackId);
@@ -43,13 +43,13 @@ inline void INSPIRE_API PrintHyperFaceDataDetail(const HyperFaceData& data) {
}
/**
* @brief Convert a FaceObject to HyperFaceData.
* @brief Convert a FaceObject to FaceTrackWrap.
* @param obj The FaceObject to convert.
* @param group_index The group index.
* @return The converted HyperFaceData structure.
* @return The converted FaceTrackWrap structure.
*/
inline HyperFaceData INSPIRE_API FaceObjectInternalToHyperFaceData(const FaceObjectInternal& obj, int group_index = -1) {
HyperFaceData data;
inline FaceTrackWrap INSPIRE_API FaceObjectInternalToHyperFaceData(const FaceObjectInternal& obj, int group_index = -1) {
FaceTrackWrap data;
// Face rect
data.rect.x = obj.bbox_.GetX();
data.rect.y = obj.bbox_.GetY();
@@ -134,15 +134,15 @@ inline inspirecv::Point2f INSPIRE_API HPointToInternalPoint2f(const Point2F& poi
}
/**
* @brief Serialize HyperFaceData to a byte stream.
* @param data The HyperFaceData to serialize.
* @brief Serialize FaceTrackWrap to a byte stream.
* @param data The FaceTrackWrap to serialize.
* @param byteArray The output byte stream.
* @return The result code.
*/
inline int32_t INSPIRE_API RunSerializeHyperFaceData(const HyperFaceData& data, ByteArray& byteArray) {
inline int32_t INSPIRE_API RunSerializeHyperFaceData(const FaceTrackWrap& data, ByteArray& byteArray) {
byteArray.reserve(sizeof(data));
// Serialize the HyperFaceData structure itself
// Serialize the FaceTrackWrap structure itself
const char* dataBytes = reinterpret_cast<const char*>(&data);
byteArray.insert(byteArray.end(), dataBytes, dataBytes + sizeof(data));
@@ -150,18 +150,18 @@ inline int32_t INSPIRE_API RunSerializeHyperFaceData(const HyperFaceData& data,
}
/**
* @brief Deserialize a byte stream to HyperFaceData.
* @brief Deserialize a byte stream to FaceTrackWrap.
* @param byteArray The input byte stream.
* @param data The output HyperFaceData structure.
* @param data The output FaceTrackWrap structure.
* @return The result code.
*/
inline int32_t INSPIRE_API RunDeserializeHyperFaceData(const ByteArray& byteArray, HyperFaceData& data) {
inline int32_t INSPIRE_API RunDeserializeHyperFaceData(const ByteArray& byteArray, FaceTrackWrap& data) {
// Check if the byte stream size is sufficient
if (byteArray.size() >= sizeof(data)) {
// Copy data from the byte stream to the HyperFaceData structure
// Copy data from the byte stream to the FaceTrackWrap structure
std::memcpy(&data, byteArray.data(), sizeof(data));
} else {
INSPIRE_LOGE("The byte stream size is insufficient to restore HyperFaceData");
INSPIRE_LOGE("The byte stream size is insufficient to restore FaceTrackWrap");
return HERR_SESS_FACE_DATA_ERROR;
}
@@ -169,19 +169,19 @@ inline int32_t INSPIRE_API RunDeserializeHyperFaceData(const ByteArray& byteArra
}
/**
* @brief Deserialize a byte stream to HyperFaceData.
* @brief Deserialize a byte stream to FaceTrackWrap.
* @param byteArray The input byte stream as a character array.
* @param byteCount The size of the byte stream.
* @param data The output HyperFaceData structure.
* @param data The output FaceTrackWrap structure.
* @return The result code.
*/
inline int32_t INSPIRE_API RunDeserializeHyperFaceData(const char* byteArray, size_t byteCount, HyperFaceData& data) {
inline int32_t INSPIRE_API RunDeserializeHyperFaceData(const char* byteArray, size_t byteCount, FaceTrackWrap& data) {
// Check if the byte stream size is sufficient
if (byteCount >= sizeof(data)) {
// Copy data from the byte stream to the HyperFaceData structure
// Copy data from the byte stream to the FaceTrackWrap structure
std::memcpy(&data, byteArray, sizeof(data));
} else {
INSPIRE_LOGE("The byte stream size is insufficient to restore HyperFaceData");
INSPIRE_LOGE("The byte stream size is insufficient to restore FaceTrackWrap");
return HERR_SESS_FACE_DATA_ERROR;
}

View File

@@ -48,7 +48,7 @@ public:
index = 0;
}
FaceActionList AnalysisFaceAction() {
FaceActionList AnalysisFaceAction(const SemanticIndex& semantic_index) {
FaceActionList actionRecord;
actions.clear();
eye_state_list.clear();
@@ -64,8 +64,8 @@ public:
// count mouth aspect ratio
float mouth_widthwise_d = record_list[0][FaceLandmarkAdapt::MOUTH_LEFT_CORNER].Distance(record_list[0][FaceLandmarkAdapt::MOUTH_RIGHT_CORNER]);
float mouth_heightwise_d = record_list[0][FaceLandmarkAdapt::MOUTH_UPPER].Distance(record_list[0][FaceLandmarkAdapt::MOUTH_LOWER]);
float mouth_widthwise_d = record_list[0][semantic_index.mouth_left_corner].Distance(record_list[0][semantic_index.mouth_right_corner]);
float mouth_heightwise_d = record_list[0][semantic_index.mouth_upper].Distance(record_list[0][semantic_index.mouth_lower]);
float mouth_aspect_ratio = mouth_heightwise_d / mouth_widthwise_d;
if (mouth_aspect_ratio > 0.3) {
actions.push_back(ACT_JAW_OPEN);

View File

@@ -9,6 +9,7 @@
#include "face_process.h"
#include "face_action_data.h"
#include "track_module/quality/face_pose_quality_adapt.h"
#include "track_module/landmark/landmark_param.h"
namespace inspire {
@@ -109,11 +110,11 @@ public:
return box_square;
}
FaceActionList UpdateFaceAction() {
FaceActionList UpdateFaceAction(const SemanticIndex& semantic_index) {
inspirecv::Vec3f euler{high_result.pitch, high_result.yaw, high_result.roll};
inspirecv::Vec2f eyes{left_eye_status_.back(), right_eye_status_.back()};
face_action_->RecordActionFrame(landmark_, euler, eyes);
return face_action_->AnalysisFaceAction();
return face_action_->AnalysisFaceAction(semantic_index);
}
void DisableTracking() {

View File

@@ -4,7 +4,7 @@
*/
#include "face_session.h"
#include "initialization_module/launch.h"
#include <launch.h>
#include <utility>
#include "log.h"
#include "herror.h"
@@ -20,35 +20,30 @@ int32_t FaceSession::Configuration(DetectModuleMode detect_mode, int32_t max_det
m_detect_mode_ = detect_mode;
m_max_detect_face_ = max_detect_face;
m_parameter_ = param;
if (!INSPIRE_LAUNCH->isMLoad()) {
if (!INSPIREFACE_CONTEXT->isMLoad()) {
return HERR_ARCHIVE_NOT_LOAD;
}
if (INSPIRE_LAUNCH->getMArchive().QueryStatus() != SARC_SUCCESS) {
if (INSPIREFACE_CONTEXT->getMArchive().QueryStatus() != SARC_SUCCESS) {
return HERR_ARCHIVE_LOAD_FAILURE;
}
if (m_parameter_.enable_interaction_liveness) {
m_parameter_.enable_detect_mode_landmark = true;
}
m_face_track_ = std::make_shared<FaceTrackModule>(m_detect_mode_, m_max_detect_face_, 20, 192, detect_level_px, track_by_detect_mode_fps,
m_parameter_.enable_detect_mode_landmark);
m_face_track_->Configuration(INSPIRE_LAUNCH->getMArchive());
m_face_track_ = std::make_shared<FaceTrackModule>(m_detect_mode_, m_max_detect_face_, 20, 192, detect_level_px, track_by_detect_mode_fps, true);
m_face_track_->Configuration(INSPIREFACE_CONTEXT->getMArchive(), "", m_parameter_.enable_face_pose || m_parameter_.enable_face_quality);
// SetDetectMode(m_detect_mode_);
m_face_recognition_ = std::make_shared<FeatureExtractionModule>(INSPIRE_LAUNCH->getMArchive(), m_parameter_.enable_recognition);
m_face_recognition_ = std::make_shared<FeatureExtractionModule>(INSPIREFACE_CONTEXT->getMArchive(), m_parameter_.enable_recognition);
if (m_face_recognition_->QueryStatus() != HSUCCEED) {
return m_face_recognition_->QueryStatus();
}
m_face_pipeline_ = std::make_shared<FacePipelineModule>(INSPIRE_LAUNCH->getMArchive(), param.enable_liveness, param.enable_mask_detect,
m_face_pipeline_ = std::make_shared<FacePipelineModule>(INSPIREFACE_CONTEXT->getMArchive(), param.enable_liveness, param.enable_mask_detect,
param.enable_face_attribute, param.enable_interaction_liveness);
m_face_track_cost_ = std::make_shared<inspirecv::TimeSpend>("FaceTrack");
m_face_track_cost_ = std::make_shared<inspire::SpendTimer>("FaceTrack");
return HSUCCEED;
}
int32_t FaceSession::FaceDetectAndTrack(inspirecv::InspireImageProcess& process) {
int32_t FaceSession::FaceDetectAndTrack(inspirecv::FrameProcess& process) {
std::lock_guard<std::mutex> lock(m_mtx_);
if (m_enable_track_cost_spend_) {
m_face_track_cost_->Start();
@@ -81,7 +76,7 @@ int32_t FaceSession::FaceDetectAndTrack(inspirecv::InspireImageProcess& process)
m_face_track_->UpdateStream(process);
for (int i = 0; i < m_face_track_->trackingFace.size(); ++i) {
auto& face = m_face_track_->trackingFace[i];
HyperFaceData data = FaceObjectInternalToHyperFaceData(face, i);
FaceTrackWrap data = FaceObjectInternalToHyperFaceData(face, i);
ByteArray byteArray;
auto ret = RunSerializeHyperFaceData(data, byteArray);
if (ret != HSUCCEED) {
@@ -118,6 +113,11 @@ int32_t FaceSession::FaceDetectAndTrack(inspirecv::InspireImageProcess& process)
return HSUCCEED;
}
int32_t FaceSession::SetLandmarkLoop(int32_t value) {
// TODO: implement this function
return HSUCCEED;
}
int32_t FaceSession::SetFaceDetectThreshold(float value) {
m_face_track_->SetDetectThreshold(value);
return HSUCCEED;
@@ -139,8 +139,7 @@ const int32_t FaceSession::GetNumberOfFacesCurrentlyDetected() const {
return m_face_track_->trackingFace.size();
}
int32_t FaceSession::FacesProcess(inspirecv::InspireImageProcess& process, const std::vector<HyperFaceData>& faces,
const CustomPipelineParameter& param) {
int32_t FaceSession::FacesProcess(inspirecv::FrameProcess& process, const std::vector<FaceTrackWrap>& faces, const CustomPipelineParameter& param) {
std::lock_guard<std::mutex> lock(m_mtx_);
m_mask_results_cache_.resize(faces.size(), -1.0f);
m_rgb_liveness_results_cache_.resize(faces.size(), -1.0f);
@@ -205,7 +204,7 @@ int32_t FaceSession::FacesProcess(inspirecv::InspireImageProcess& process, const
m_react_left_eye_results_cache_[i] = new_eye_left;
m_react_right_eye_results_cache_[i] = new_eye_right;
}
const auto actions = target.UpdateFaceAction();
const auto actions = target.UpdateFaceAction(INSPIREFACE_CONTEXT->getMArchive().GetLandmarkParam()->semantic_index);
m_action_normal_results_cache_[i] = actions.normal;
m_action_jaw_open_results_cache_[i] = actions.jawOpen;
m_action_blink_results_cache_[i] = actions.blink;
@@ -324,24 +323,52 @@ const std::vector<int>& FaceSession::GetFaceRaiseHeadAactionsResultCache() const
return m_action_raise_head_results_cache_;
}
int32_t FaceSession::FaceFeatureExtract(inspirecv::InspireImageProcess& process, FaceBasicData& data) {
int32_t FaceSession::FaceFeatureExtract(inspirecv::FrameProcess& process, FaceBasicData& data, bool normalize) {
std::lock_guard<std::mutex> lock(m_mtx_);
int32_t ret;
HyperFaceData face = {0};
FaceTrackWrap face = {0};
ret = RunDeserializeHyperFaceData((char*)data.data, data.dataSize, face);
if (ret != HSUCCEED) {
return ret;
}
m_face_feature_cache_.clear();
ret = m_face_recognition_->FaceExtract(process, face, m_face_feature_cache_, m_face_feature_norm_);
ret = m_face_recognition_->FaceExtract(process, face, m_face_feature_cache_, m_face_feature_norm_, normalize);
return ret;
}
int32_t FaceSession::FaceGetFaceAlignmentImage(inspirecv::InspireImageProcess& process, FaceBasicData& data, inspirecv::Image& image) {
int32_t FaceSession::FaceFeatureExtract(inspirecv::FrameProcess& process, FaceTrackWrap& data, bool normalize) {
std::lock_guard<std::mutex> lock(m_mtx_);
int32_t ret;
HyperFaceData face = {0};
m_face_feature_cache_.clear();
ret = m_face_recognition_->FaceExtract(process, data, m_face_feature_cache_, m_face_feature_norm_, normalize);
if (ret != HSUCCEED) {
return ret;
}
return ret;
}
int32_t FaceSession::FaceFeatureExtractWithAlignmentImage(inspirecv::FrameProcess& process, Embedded& embedding, float& norm, bool normalize) {
std::lock_guard<std::mutex> lock(m_mtx_);
int32_t ret;
m_face_feature_cache_.clear();
ret = m_face_recognition_->FaceExtractWithAlignmentImage(process, embedding, norm, normalize);
return ret;
}
int32_t FaceSession::FaceFeatureExtractWithAlignmentImage(const inspirecv::Image& wrapped, FaceEmbedding& embedding, float& norm, bool normalize) {
std::lock_guard<std::mutex> lock(m_mtx_);
int32_t ret;
ret = m_face_recognition_->FaceExtractWithAlignmentImage(wrapped, embedding.embedding, norm, normalize);
return ret;
}
int32_t FaceSession::FaceGetFaceAlignmentImage(inspirecv::FrameProcess& process, FaceBasicData& data, inspirecv::Image& image) {
std::lock_guard<std::mutex> lock(m_mtx_);
int32_t ret;
FaceTrackWrap face = {0};
ret = RunDeserializeHyperFaceData((char*)data.data, data.dataSize, face);
if (ret != HSUCCEED) {
return ret;
@@ -361,7 +388,7 @@ const CustomPipelineParameter& FaceSession::getMParameter() const {
int32_t FaceSession::FaceQualityDetect(FaceBasicData& data, float& result) {
int32_t ret;
HyperFaceData face = {0};
FaceTrackWrap face = {0};
ret = RunDeserializeHyperFaceData((char*)data.data, data.dataSize, face);
// PrintHyperFaceData(face);
if (ret != HSUCCEED) {
@@ -396,6 +423,10 @@ int32_t FaceSession::SetTrackPreviewSize(const int32_t preview_size) {
return HSUCCEED;
}
int32_t FaceSession::GetTrackPreviewSize() const {
return m_face_track_->GetTrackPreviewSize();
}
int32_t FaceSession::SetTrackFaceMinimumSize(int32_t minSize) {
m_face_track_->SetMinimumFacePxSize(minSize);
return HSUCCEED;
@@ -428,4 +459,8 @@ void FaceSession::PrintTrackCostSpend() {
}
}
int32_t FaceSession::GetDebugPreviewImageSize() const {
return m_face_track_->GetDebugPreviewImageSize();
}
} // namespace inspire

View File

@@ -14,29 +14,12 @@
#include "pipeline_module/face_pipeline_module.h"
#include "middleware/model_archive/inspire_archive.h"
#include "recognition_module/face_feature_extraction_module.h"
#include "middleware/inspirecv_image_process.h"
#include "frame_process.h"
#include "common/face_data/face_serialize_tools.h"
#include "spend_timer.h"
namespace inspire {
/**
* @struct CustomPipelineParameter
* @brief Structure to hold custom parameters for the face detection and processing pipeline.
*
* Includes options for enabling various features such as recognition, liveness detection, and quality assessment.
*/
typedef struct CustomPipelineParameter {
bool enable_recognition = false; ///< Enable face recognition feature
bool enable_liveness = false; ///< Enable RGB liveness detection feature
bool enable_ir_liveness = false; ///< Enable IR (Infrared) liveness detection feature
bool enable_mask_detect = false; ///< Enable mask detection feature
bool enable_face_attribute = false; ///< Enable face attribute prediction feature
bool enable_face_quality = false; ///< Enable face quality assessment feature
bool enable_interaction_liveness = false; ///< Enable interactive liveness detection feature
bool enable_detect_mode_landmark = false; ///< Enable landmark detection in detection mode
} ContextCustomParameter;
/**
* @class FaceContext
* @brief Manages the context for face detection, tracking, and feature extraction in the HyperFaceRepo project.
@@ -68,7 +51,14 @@ public:
* @param image The camera stream to process for face detection and tracking.
* @return int32_t Returns the number of faces detected and tracked.
*/// Method for face detection and tracking
int32_t FaceDetectAndTrack(inspirecv::InspireImageProcess& process);
int32_t FaceDetectAndTrack(inspirecv::FrameProcess& process);
/**
* @brief Set the face landmark loop
* @param value The landmark loop value
* @return int32_t Status code of the operation.
* */
int32_t SetLandmarkLoop(int32_t value);
/**
* @brief Set the threshold of face detection function, which only acts on the detection model
@@ -86,11 +76,11 @@ public:
/**
* @brief Processes faces using the provided pipeline parameters.
* @param image Camera stream containing faces.
* @param faces Vector of HyperFaceData for detected faces.
* @param faces Vector of FaceTrackWrap for detected faces.
* @param param Custom pipeline parameters.
* @return int32_t Status code of the processing.
*/
int32_t FacesProcess(inspirecv::InspireImageProcess& process, const std::vector<HyperFaceData>& faces, const CustomPipelineParameter& param);
int32_t FacesProcess(inspirecv::FrameProcess& process, const std::vector<FaceTrackWrap>& faces, const CustomPipelineParameter& param);
/**
* @brief Retrieves the face recognition module.
@@ -116,7 +106,31 @@ public:
* @param data FaceBasicData to store extracted features.
* @return int32_t Status code of the feature extraction.
*/
int32_t FaceFeatureExtract(inspirecv::InspireImageProcess& process, FaceBasicData& data);
int32_t FaceFeatureExtract(inspirecv::FrameProcess& process, FaceBasicData& data, bool normalize = true);
/**
* @brief Extracts features of a face from an image.
* @param image Camera stream containing the face.
* @param data FaceTrackWrap to store extracted features.
* @return int32_t Status code of the feature extraction.
*/
int32_t FaceFeatureExtract(inspirecv::FrameProcess& process, FaceTrackWrap& data, bool normalize = true);
/**
* @brief Extracts features of a face from an image.
* @param image Camera stream containing the face.
* @param data FaceTrackWrap to store extracted features.
* @return int32_t Status code of the feature extraction.
*/
int32_t FaceFeatureExtractWithAlignmentImage(inspirecv::FrameProcess& process, Embedded& embedding, float& norm, bool normalize = true);
/**
* @brief Extracts features of a face from an image.
* @param image Camera stream containing the face.
* @param data FaceTrackWrap to store extracted features.
* @return int32_t Status code of the feature extraction.
*/
int32_t FaceFeatureExtractWithAlignmentImage(const inspirecv::Image& wrapped, FaceEmbedding& embedding, float& norm, bool normalize = true);
/**
* @brief Gets the face alignment image.
@@ -125,7 +139,7 @@ public:
* @param image The output image.
* @return int32_t The status code of the operation.
*/
int32_t FaceGetFaceAlignmentImage(inspirecv::InspireImageProcess& process, FaceBasicData& data, inspirecv::Image& image);
int32_t FaceGetFaceAlignmentImage(inspirecv::FrameProcess& process, FaceBasicData& data, inspirecv::Image& image);
/**
* @brief Retrieves the custom pipeline parameters.
@@ -148,6 +162,12 @@ public:
*/
int32_t SetTrackPreviewSize(int32_t preview_size);
/**
* @brief Gets the preview size for face tracking.
* @return int32_t The preview size.
*/
int32_t GetTrackPreviewSize() const;
/**
* @brief Filter the minimum face pixel size.
* @param minSize The minimum pixel value.
@@ -347,6 +367,12 @@ public:
* */
void PrintTrackCostSpend();
/**
* @brief Get the debug preview image size
* @return int32_t The debug preview image size
* */
int32_t GetDebugPreviewImageSize() const;
private:
// Private member variables
CustomPipelineParameter m_parameter_; ///< Stores custom parameters for the pipeline
@@ -390,7 +416,7 @@ private:
std::mutex m_mtx_; ///< Mutex for thread safety.
// cost spend
std::shared_ptr<inspirecv::TimeSpend> m_face_track_cost_;
std::shared_ptr<inspire::SpendTimer> m_face_track_cost_;
int m_enable_track_cost_spend_ = 0;
};

View File

@@ -2,6 +2,9 @@
#include "sqlite-vec.h"
#include "isf_check.h"
#include <algorithm>
#if defined(__ANDROID__)
#include <android/log.h>
#endif
namespace inspire {
@@ -118,20 +121,13 @@ std::vector<int64_t> EmbeddingDB::BatchInsertVectors(const std::vector<VectorDat
std::vector<int64_t> insertedIds;
insertedIds.reserve(vectors.size());
try {
for (const auto &data : vectors) {
int64_t id = 0;
bool ret = InsertVector(data.id, data.vector, id);
if (!ret) {
throw std::runtime_error("Failed to insert vector");
}
insertedIds.push_back(id);
}
ExecuteSQL("COMMIT");
} catch (...) {
ExecuteSQL("ROLLBACK");
throw;
for (const auto &data : vectors) {
int64_t id = 0;
bool ret = InsertVector(data.id, data.vector, id);
INSPIREFACE_CHECK_MSG(ret, "Failed to insert vector");
insertedIds.push_back(id);
}
ExecuteSQL("COMMIT");
return insertedIds;
}
@@ -141,20 +137,13 @@ std::vector<int64_t> EmbeddingDB::BatchInsertVectors(const std::vector<std::vect
std::vector<int64_t> insertedIds;
insertedIds.reserve(vectors.size());
try {
for (const auto &vector : vectors) {
int64_t id = 0;
bool ret = InsertVector(0, vector, id);
if (!ret) {
throw std::runtime_error("Failed to insert vector");
}
insertedIds.push_back(id);
}
ExecuteSQL("COMMIT");
} catch (...) {
ExecuteSQL("ROLLBACK");
throw;
for (const auto &vector : vectors) {
int64_t id = 0;
bool ret = InsertVector(0, vector, id);
INSPIREFACE_CHECK_MSG(ret, "Failed to insert vector");
insertedIds.push_back(id);
}
ExecuteSQL("COMMIT");
return insertedIds;
}

View File

@@ -19,18 +19,12 @@
#include <memory>
#include <stdexcept>
#include <mutex>
#include "data_type.h"
#define EMBEDDING_DB inspire::EmbeddingDB
namespace inspire {
// Search for most similar vectors
struct FaceSearchResult {
int64_t id;
double similarity;
std::vector<float> feature;
};
// Vector data structure
struct VectorData {
int64_t id; // This field is ignored in auto-increment mode

View File

@@ -9,13 +9,42 @@
#include <thread>
#include "middleware/utils.h"
#include "middleware/system.h"
#include "log.h"
#include "feature_hub/embedding_db/embedding_db.h"
#define DB_FILE_NAME ".feature_hub_db_v0"
namespace inspire {
class FeatureHubDB::Impl {
public:
Impl() : m_enable_(false), m_recognition_threshold_(0.48f), m_search_mode_(SEARCH_MODE_EAGER) {}
Embedded m_search_face_feature_cache_;
Embedded m_getter_face_feature_cache_;
std::shared_ptr<FaceFeaturePtr> m_face_feature_ptr_cache_;
std::vector<FaceSearchResult> m_search_top_k_cache_;
std::vector<float> m_top_k_confidence_;
std::vector<int64_t> m_top_k_custom_ids_cache_;
std::vector<int64_t> m_all_ids_;
DatabaseConfiguration m_db_configuration_;
float m_recognition_threshold_;
SearchMode m_search_mode_;
bool m_enable_;
std::mutex m_res_mtx_;
};
std::mutex FeatureHubDB::mutex_;
std::shared_ptr<FeatureHubDB> FeatureHubDB::instance_ = nullptr;
FeatureHubDB::FeatureHubDB() {}
FeatureHubDB::FeatureHubDB() : pImpl(new Impl()) {}
FeatureHubDB::~FeatureHubDB() = default;
std::shared_ptr<FeatureHubDB> FeatureHubDB::GetInstance() {
std::lock_guard<std::mutex> lock(mutex_);
@@ -26,79 +55,76 @@ std::shared_ptr<FeatureHubDB> FeatureHubDB::GetInstance() {
}
int32_t FeatureHubDB::DisableHub() {
if (!m_enable_) {
if (!pImpl->m_enable_) {
INSPIRE_LOGW("FeatureHub is already disabled.");
return HSUCCEED;
}
// Close the database if it starts
if (EMBEDDING_DB::GetInstance().IsInitialized()) {
EMBEDDING_DB::Deinit();
// if (ret != HSUCCEED) {
// INSPIRE_LOGE("Failed to close the database: %d", ret);
// return ret;
// }
// m_db_.reset();
}
m_search_face_feature_cache_.clear();
pImpl->m_search_face_feature_cache_.clear();
m_db_configuration_ = DatabaseConfiguration(); // Reset using the default constructor
m_recognition_threshold_ = 0.0f;
m_search_mode_ = SEARCH_MODE_EAGER;
pImpl->m_db_configuration_ = DatabaseConfiguration();
pImpl->m_recognition_threshold_ = 0.0f;
pImpl->m_search_mode_ = SEARCH_MODE_EAGER;
m_face_feature_ptr_cache_.reset();
m_enable_ = false;
pImpl->m_face_feature_ptr_cache_.reset();
pImpl->m_enable_ = false;
return HSUCCEED;
}
int32_t FeatureHubDB::GetAllIds() {
if (!m_enable_) {
if (!pImpl->m_enable_) {
INSPIRE_LOGE("FeatureHub is disabled, please enable it before it can be served");
return HERR_FT_HUB_DISABLE;
}
m_all_ids_ = EMBEDDING_DB::GetInstance().GetAllIds();
pImpl->m_all_ids_ = EMBEDDING_DB::GetInstance().GetAllIds();
return HSUCCEED;
}
int32_t FeatureHubDB::EnableHub(const DatabaseConfiguration &configuration) {
int32_t ret;
if (m_enable_) {
if (pImpl->m_enable_) {
INSPIRE_LOGW("You have enabled the FeatureHub feature. It is not valid to do so again");
return HSUCCEED;
}
// Config
m_db_configuration_ = configuration;
m_recognition_threshold_ = m_db_configuration_.recognition_threshold;
if (m_recognition_threshold_ < -1.0f || m_recognition_threshold_ > 1.0f) {
pImpl->m_db_configuration_ = configuration;
pImpl->m_recognition_threshold_ = pImpl->m_db_configuration_.recognition_threshold;
if (pImpl->m_recognition_threshold_ < -1.0f || pImpl->m_recognition_threshold_ > 1.0f) {
INSPIRE_LOGW("The search threshold entered does not fit the required range (-1.0f, 1.0f) and has been set to 0.5 by default");
m_recognition_threshold_ = 0.5f;
pImpl->m_recognition_threshold_ = 0.5f;
}
std::string dbFile = ":memory:";
if (m_db_configuration_.enable_persistence) {
if (IsDirectory(m_db_configuration_.persistence_db_path)) {
dbFile = os::PathJoin(m_db_configuration_.persistence_db_path, DB_FILE_NAME);
if (pImpl->m_db_configuration_.enable_persistence) {
if (IsDirectory(pImpl->m_db_configuration_.persistence_db_path)) {
dbFile = os::PathJoin(pImpl->m_db_configuration_.persistence_db_path, DB_FILE_NAME);
} else {
dbFile = m_db_configuration_.persistence_db_path;
dbFile = pImpl->m_db_configuration_.persistence_db_path;
}
}
EMBEDDING_DB::Init(dbFile, 512, IdMode(configuration.primary_key_mode));
m_enable_ = true;
m_face_feature_ptr_cache_ = std::make_shared<FaceFeatureEntity>();
pImpl->m_enable_ = true;
pImpl->m_face_feature_ptr_cache_ = std::make_shared<FaceFeatureEntity>();
return HSUCCEED;
}
int32_t FeatureHubDB::CosineSimilarity(const std::vector<float> &v1, const std::vector<float> &v2, float &res, bool normalize) {
if (v1.size() != v2.size() || v1.empty()) {
return HERR_SESS_REC_CONTRAST_FEAT_ERR; // The similarity cannot be calculated if the vector lengths are not equal
return HERR_SESS_REC_CONTRAST_FEAT_ERR;
}
if (normalize) {
std::vector<float> v1_norm = v1;
std::vector<float> v2_norm = v2;
float mse1 = 0.0f;
float mse2 = 0.0f;
for (const auto &one : v1_norm) {
mse1 += one * one;
}
@@ -114,9 +140,9 @@ int32_t FeatureHubDB::CosineSimilarity(const std::vector<float> &v1, const std::
for (float &one : v2_norm) {
one /= mse2;
}
res = simd_dot(v1_norm.data(), v2_norm.data(), v1_norm.size());
} else {
// Calculate the cosine similarity
res = simd_dot(v1.data(), v2.data(), v1.size());
}
@@ -129,6 +155,7 @@ int32_t FeatureHubDB::CosineSimilarity(const float *v1, const float *v2, int32_t
std::vector<float> v2_norm(v2, v2 + size);
float mse1 = 0.0f;
float mse2 = 0.0f;
for (const auto &one : v1_norm) {
mse1 += one * one;
}
@@ -136,6 +163,7 @@ int32_t FeatureHubDB::CosineSimilarity(const float *v1, const float *v2, int32_t
for (float &one : v1_norm) {
one /= mse1;
}
for (const auto &one : v2_norm) {
mse2 += one * one;
}
@@ -143,6 +171,7 @@ int32_t FeatureHubDB::CosineSimilarity(const float *v1, const float *v2, int32_t
for (float &one : v2_norm) {
one /= mse2;
}
res = simd_dot(v1_norm.data(), v2_norm.data(), v1_norm.size());
} else {
res = simd_dot(v1, v2, size);
@@ -152,37 +181,34 @@ int32_t FeatureHubDB::CosineSimilarity(const float *v1, const float *v2, int32_t
}
int32_t FeatureHubDB::GetFaceFeatureCount() {
if (!m_enable_) {
if (!pImpl->m_enable_) {
INSPIRE_LOGW("FeatureHub is disabled, please enable it before it can be served");
return 0;
}
int totalFeatureCount = 0;
// Iterate over all FeatureBlocks and add up the number of feature vectors used
totalFeatureCount = EMBEDDING_DB::GetInstance().GetVectorCount();
return totalFeatureCount;
return EMBEDDING_DB::GetInstance().GetVectorCount();
}
int32_t FeatureHubDB::SearchFaceFeature(const Embedded &queryFeature, FaceSearchResult &searchResult, bool returnFeature) {
std::lock_guard<std::mutex> lock(mutex_);
if (!m_enable_) {
if (!pImpl->m_enable_) {
INSPIRE_LOGE("FeatureHub is disabled, please enable it before it can be served");
return HSUCCEED;
}
m_search_face_feature_cache_.clear();
auto results = EMBEDDING_DB::GetInstance().SearchSimilarVectors(queryFeature, 1, m_recognition_threshold_, returnFeature);
pImpl->m_search_face_feature_cache_.clear();
auto results = EMBEDDING_DB::GetInstance().SearchSimilarVectors(queryFeature, 1, pImpl->m_recognition_threshold_, returnFeature);
searchResult.id = -1;
if (!results.empty()) {
auto &searched = results[0];
searchResult.similarity = searched.similarity;
searchResult.id = searched.id;
if (returnFeature) {
searchResult.feature = searched.feature;
// copy feature to cache
m_search_face_feature_cache_ = searched.feature;
m_face_feature_ptr_cache_->data = m_search_face_feature_cache_.data();
m_face_feature_ptr_cache_->dataSize = m_search_face_feature_cache_.size();
pImpl->m_search_face_feature_cache_ = searched.feature;
pImpl->m_face_feature_ptr_cache_->data = pImpl->m_search_face_feature_cache_.data();
pImpl->m_face_feature_ptr_cache_->dataSize = pImpl->m_search_face_feature_cache_.size();
}
}
@@ -191,16 +217,18 @@ int32_t FeatureHubDB::SearchFaceFeature(const Embedded &queryFeature, FaceSearch
int32_t FeatureHubDB::SearchFaceFeatureTopKCache(const Embedded &queryFeature, size_t topK) {
std::lock_guard<std::mutex> lock(mutex_);
if (!m_enable_) {
if (!pImpl->m_enable_) {
INSPIRE_LOGE("FeatureHub is disabled, please enable it before it can be served");
return HERR_FT_HUB_DISABLE;
}
m_top_k_confidence_.clear();
m_top_k_custom_ids_cache_.clear();
auto results = EMBEDDING_DB::GetInstance().SearchSimilarVectors(queryFeature, topK, m_recognition_threshold_, false);
pImpl->m_top_k_confidence_.clear();
pImpl->m_top_k_custom_ids_cache_.clear();
auto results = EMBEDDING_DB::GetInstance().SearchSimilarVectors(queryFeature, topK, pImpl->m_recognition_threshold_, false);
for (size_t i = 0; i < results.size(); i++) {
m_top_k_custom_ids_cache_.push_back(results[i].id);
m_top_k_confidence_.push_back(results[i].similarity);
pImpl->m_top_k_custom_ids_cache_.push_back(results[i].id);
pImpl->m_top_k_confidence_.push_back(results[i].similarity);
}
return HSUCCEED;
@@ -209,17 +237,18 @@ int32_t FeatureHubDB::SearchFaceFeatureTopKCache(const Embedded &queryFeature, s
int32_t FeatureHubDB::SearchFaceFeatureTopK(const Embedded &queryFeature, std::vector<FaceSearchResult> &searchResult, size_t topK,
bool returnFeature) {
std::lock_guard<std::mutex> lock(mutex_);
if (!m_enable_) {
if (!pImpl->m_enable_) {
INSPIRE_LOGW("FeatureHub is disabled, please enable it before it can be served");
return HERR_FT_HUB_DISABLE;
}
searchResult = EMBEDDING_DB::GetInstance().SearchSimilarVectors(queryFeature, topK, m_recognition_threshold_, returnFeature);
searchResult = EMBEDDING_DB::GetInstance().SearchSimilarVectors(queryFeature, topK, pImpl->m_recognition_threshold_, returnFeature);
return HSUCCEED;
}
int32_t FeatureHubDB::FaceFeatureInsert(const std::vector<float> &feature, int32_t id, int64_t &result_id) {
std::lock_guard<std::mutex> lock(mutex_);
if (!m_enable_) {
if (!pImpl->m_enable_) {
INSPIRE_LOGE("FeatureHub is disabled, please enable it before it can be served");
return HERR_FT_HUB_DISABLE;
}
@@ -235,65 +264,62 @@ int32_t FeatureHubDB::FaceFeatureInsert(const std::vector<float> &feature, int32
int32_t FeatureHubDB::FaceFeatureRemove(int32_t id) {
std::lock_guard<std::mutex> lock(mutex_);
if (!m_enable_) {
if (!pImpl->m_enable_) {
INSPIRE_LOGE("FeatureHub is disabled, please enable it before it can be served");
return HERR_FT_HUB_DISABLE;
}
EMBEDDING_DB::GetInstance().DeleteVector(id);
EMBEDDING_DB::GetInstance().DeleteVector(id);
return HSUCCEED;
}
int32_t FeatureHubDB::FaceFeatureUpdate(const std::vector<float> &feature, int32_t customId) {
std::lock_guard<std::mutex> lock(mutex_);
if (!m_enable_) {
if (!pImpl->m_enable_) {
INSPIRE_LOGE("FeatureHub is disabled, please enable it before it can be served");
return HERR_FT_HUB_DISABLE;
}
try {
EMBEDDING_DB::GetInstance().UpdateVector(customId, feature);
} catch (const std::exception &e) {
INSPIRE_LOGW("Failed to update face feature, id: %d", customId);
return HERR_FT_HUB_NOT_FOUND_FEATURE;
}
EMBEDDING_DB::GetInstance().UpdateVector(customId, feature);
return HSUCCEED;
}
int32_t FeatureHubDB::GetFaceFeature(int32_t id) {
std::lock_guard<std::mutex> lock(mutex_);
if (!m_enable_) {
if (!pImpl->m_enable_) {
INSPIRE_LOGE("FeatureHub is disabled, please enable it before it can be served");
return HERR_FT_HUB_DISABLE;
}
auto vec = EMBEDDING_DB::GetInstance().GetVector(id);
if (vec.empty()) {
return HERR_FT_HUB_NOT_FOUND_FEATURE;
}
m_getter_face_feature_cache_ = vec;
m_face_feature_ptr_cache_->data = m_getter_face_feature_cache_.data();
m_face_feature_ptr_cache_->dataSize = m_getter_face_feature_cache_.size();
pImpl->m_getter_face_feature_cache_ = vec;
pImpl->m_face_feature_ptr_cache_->data = pImpl->m_getter_face_feature_cache_.data();
pImpl->m_face_feature_ptr_cache_->dataSize = pImpl->m_getter_face_feature_cache_.size();
return HSUCCEED;
}
int32_t FeatureHubDB::GetFaceFeature(int32_t id, std::vector<float> &feature) {
std::lock_guard<std::mutex> lock(mutex_);
if (!m_enable_) {
if (!pImpl->m_enable_) {
INSPIRE_LOGW("FeatureHub is disabled, please enable it before it can be served");
return HERR_FT_HUB_DISABLE;
}
try {
feature = EMBEDDING_DB::GetInstance().GetVector(id);
} catch (const std::exception &e) {
INSPIRE_LOGW("Failed to get face feature, id: %d", id);
feature = EMBEDDING_DB::GetInstance().GetVector(id);
if (feature.empty()) {
return HERR_FT_HUB_NOT_FOUND_FEATURE;
}
return HSUCCEED;
}
int32_t FeatureHubDB::ViewDBTable() {
if (!m_enable_) {
if (!pImpl->m_enable_) {
INSPIRE_LOGE("FeatureHub is disabled, please enable it before it can be served");
return HERR_FT_HUB_DISABLE;
}
@@ -302,33 +328,33 @@ int32_t FeatureHubDB::ViewDBTable() {
}
void FeatureHubDB::SetRecognitionThreshold(float threshold) {
m_recognition_threshold_ = threshold;
pImpl->m_recognition_threshold_ = threshold;
}
void FeatureHubDB::SetRecognitionSearchMode(SearchMode mode) {
m_search_mode_ = mode;
pImpl->m_search_mode_ = mode;
}
// =========== Getter ===========
const Embedded &FeatureHubDB::GetSearchFaceFeatureCache() const {
return m_search_face_feature_cache_;
return pImpl->m_search_face_feature_cache_;
}
const std::shared_ptr<FaceFeaturePtr> &FeatureHubDB::GetFaceFeaturePtrCache() const {
return m_face_feature_ptr_cache_;
return pImpl->m_face_feature_ptr_cache_;
}
std::vector<float> &FeatureHubDB::GetTopKConfidence() {
return m_top_k_confidence_;
return pImpl->m_top_k_confidence_;
}
std::vector<int64_t> &FeatureHubDB::GetTopKCustomIdsCache() {
return m_top_k_custom_ids_cache_;
return pImpl->m_top_k_custom_ids_cache_;
}
std::vector<int64_t> &FeatureHubDB::GetExistingIds() {
return m_all_ids_;
return pImpl->m_all_ids_;
}
} // namespace inspire

View File

@@ -0,0 +1,395 @@
#include "frame_process.h"
#include <vector>
#include <MNN/ImageProcess.hpp>
#include "isf_check.h"
namespace inspirecv {
class FrameProcess::Impl {
public:
Impl() : buffer_(nullptr), height_(0), width_(0), preview_scale_(0), preview_size_(192), rotation_mode_(ROTATION_0) {
SetDataFormat(NV21);
SetDestFormat(BGR);
config_.filterType = MNN::CV::BILINEAR;
config_.wrap = MNN::CV::ZERO;
}
void SetDataFormat(DATA_FORMAT data_format) {
if (data_format == NV21) {
config_.sourceFormat = MNN::CV::YUV_NV21;
}
if (data_format == NV12) {
config_.sourceFormat = MNN::CV::YUV_NV12;
}
if (data_format == RGBA) {
config_.sourceFormat = MNN::CV::RGBA;
}
if (data_format == RGB) {
config_.sourceFormat = MNN::CV::RGB;
}
if (data_format == BGR) {
config_.sourceFormat = MNN::CV::BGR;
}
if (data_format == BGRA) {
config_.sourceFormat = MNN::CV::BGRA;
}
}
void SetDestFormat(DATA_FORMAT data_format) {
if (data_format == NV21) {
config_.destFormat = MNN::CV::YUV_NV21;
}
if (data_format == NV12) {
config_.destFormat = MNN::CV::YUV_NV12;
}
if (data_format == RGBA) {
config_.destFormat = MNN::CV::RGBA;
}
if (data_format == RGB) {
config_.destFormat = MNN::CV::RGB;
}
if (data_format == BGR) {
config_.destFormat = MNN::CV::BGR;
}
if (data_format == BGRA) {
config_.destFormat = MNN::CV::BGRA;
}
}
void UpdateTransformMatrix() {
float srcPoints[] = {0.0f, 0.0f, 0.0f, (float)(height_ - 1), (float)(width_ - 1), 0.0f, (float)(width_ - 1), (float)(height_ - 1)};
float dstPoints[8];
if (rotation_mode_ == ROTATION_270) {
float points[] = {(float)(height_ * preview_scale_ - 1),
0.0f,
0.0f,
0.0f,
(float)(height_ * preview_scale_ - 1),
(float)(width_ * preview_scale_ - 1),
0.0f,
(float)(width_ * preview_scale_ - 1)};
memcpy(dstPoints, points, sizeof(points));
} else if (rotation_mode_ == ROTATION_90) {
float points[] = {0.0f,
(float)(width_ * preview_scale_ - 1),
(float)(height_ * preview_scale_ - 1),
(float)(width_ * preview_scale_ - 1),
0.0f,
0.0f,
(float)(height_ * preview_scale_ - 1),
0.0f};
memcpy(dstPoints, points, sizeof(points));
} else if (rotation_mode_ == ROTATION_180) {
float points[] = {(float)(width_ * preview_scale_ - 1),
(float)(height_ * preview_scale_ - 1),
(float)(width_ * preview_scale_ - 1),
0.0f,
0.0f,
(float)(height_ * preview_scale_ - 1),
0.0f,
0.0f};
memcpy(dstPoints, points, sizeof(points));
} else { // ROTATION_0
float points[] = {0.0f,
0.0f,
0.0f,
(float)(height_ * preview_scale_ - 1),
(float)(width_ * preview_scale_ - 1),
0.0f,
(float)(width_ * preview_scale_ - 1),
(float)(height_ * preview_scale_ - 1)};
memcpy(dstPoints, points, sizeof(points));
}
tr_.setPolyToPoly((MNN::CV::Point *)dstPoints, (MNN::CV::Point *)srcPoints, 4);
}
const uint8_t *buffer_; // Pointer to the data buffer.
int height_; // Height of the camera stream image.
int width_; // Width of the camera stream image.
float preview_scale_; // Scaling factor for the preview image.
int preview_size_; // Size of the preview image.
MNN::CV::Matrix tr_; // Affine transformation matrix.
ROTATION_MODE rotation_mode_; // Current rotation mode.
MNN::CV::ImageProcess::Config config_; // Image processing configuration.
};
FrameProcess FrameProcess::Create(const uint8_t *data_buffer, int height, int width, DATA_FORMAT data_format, ROTATION_MODE rotation_mode) {
FrameProcess process;
process.SetDataBuffer(data_buffer, height, width);
process.SetDataFormat(data_format);
process.SetRotationMode(rotation_mode);
return process;
}
FrameProcess FrameProcess::Create(const inspirecv::Image &image, DATA_FORMAT data_format, ROTATION_MODE rotation_mode) {
return Create(image.Data(), image.Height(), image.Width(), data_format, rotation_mode);
}
FrameProcess::FrameProcess() : pImpl(std::make_unique<Impl>()) {
pImpl->UpdateTransformMatrix();
}
FrameProcess::~FrameProcess() = default;
FrameProcess::FrameProcess(const FrameProcess &other) : pImpl(std::make_unique<Impl>(*other.pImpl)) {}
FrameProcess::FrameProcess(FrameProcess &&other) noexcept = default;
FrameProcess &FrameProcess::operator=(const FrameProcess &other) {
if (this != &other) {
*pImpl = *other.pImpl;
}
return *this;
}
FrameProcess &FrameProcess::operator=(FrameProcess &&other) noexcept = default;
void FrameProcess::SetDataBuffer(const uint8_t *data_buffer, int height, int width) {
pImpl->buffer_ = data_buffer;
pImpl->height_ = height;
pImpl->width_ = width;
pImpl->preview_scale_ = pImpl->preview_size_ / static_cast<float>(std::max(height, width));
pImpl->UpdateTransformMatrix();
}
void FrameProcess::SetPreviewSize(const int size) {
pImpl->preview_size_ = size;
pImpl->preview_scale_ = pImpl->preview_size_ / static_cast<float>(std::max(pImpl->height_, pImpl->width_));
pImpl->UpdateTransformMatrix();
}
void FrameProcess::SetPreviewScale(const float scale) {
pImpl->preview_scale_ = scale;
pImpl->preview_size_ = static_cast<int>(pImpl->preview_scale_ * std::max(pImpl->height_, pImpl->width_));
pImpl->UpdateTransformMatrix();
}
void FrameProcess::SetRotationMode(ROTATION_MODE mode) {
pImpl->rotation_mode_ = mode;
pImpl->UpdateTransformMatrix();
}
void FrameProcess::SetDataFormat(DATA_FORMAT data_format) {
pImpl->SetDataFormat(data_format);
}
void FrameProcess::SetDestFormat(DATA_FORMAT data_format) {
pImpl->SetDestFormat(data_format);
}
float FrameProcess::GetPreviewScale() {
return pImpl->preview_scale_;
}
inspirecv::TransformMatrix FrameProcess::GetAffineMatrix() const {
auto affine_matrix = inspirecv::TransformMatrix::Create();
affine_matrix[0] = pImpl->tr_[0];
affine_matrix[1] = pImpl->tr_[1];
affine_matrix[2] = pImpl->tr_[2];
affine_matrix[3] = pImpl->tr_[3];
affine_matrix[4] = pImpl->tr_[4];
affine_matrix[5] = pImpl->tr_[5];
return affine_matrix;
}
int FrameProcess::GetHeight() const {
return pImpl->height_;
}
int FrameProcess::GetWidth() const {
return pImpl->width_;
}
ROTATION_MODE FrameProcess::getRotationMode() const {
return pImpl->rotation_mode_;
}
inspirecv::Image FrameProcess::ExecuteImageAffineProcessing(inspirecv::TransformMatrix &affine_matrix, const int width_out,
const int height_out) const {
int sw = pImpl->width_;
int sh = pImpl->height_;
int rot_sw = sw;
int rot_sh = sh;
MNN::CV::Matrix tr;
std::vector<float> tr_cv({1, 0, 0, 0, 1, 0, 0, 0, 1});
memcpy(tr_cv.data(), affine_matrix.Squeeze().data(), sizeof(float) * 6);
tr.set9(tr_cv.data());
MNN::CV::Matrix tr_inv;
tr.invert(&tr_inv);
std::shared_ptr<MNN::CV::ImageProcess> process(MNN::CV::ImageProcess::create(pImpl->config_));
process->setMatrix(tr_inv);
auto img_out = inspirecv::Image::Create(width_out, height_out, 3);
std::shared_ptr<MNN::Tensor> tensor(MNN::Tensor::create<uint8_t>(std::vector<int>{1, height_out, width_out, 3}, (uint8_t *)img_out.Data()));
auto ret = process->convert(pImpl->buffer_, sw, sh, 0, tensor.get());
INSPIREFACE_CHECK_MSG(ret == MNN::ErrorCode::NO_ERROR, "ImageProcess::convert failed");
return img_out;
}
inspirecv::Image FrameProcess::ExecutePreviewImageProcessing(bool with_rotation) {
return ExecuteImageScaleProcessing(pImpl->preview_scale_, with_rotation);
}
inspirecv::Image FrameProcess::ExecuteImageScaleProcessing(const float scale, bool with_rotation) {
int sw = pImpl->width_;
int sh = pImpl->height_;
int rot_sw = sw;
int rot_sh = sh;
// MNN::CV::Matrix tr;
std::shared_ptr<MNN::CV::ImageProcess> process(MNN::CV::ImageProcess::create(pImpl->config_));
if (pImpl->rotation_mode_ == ROTATION_270 && with_rotation) {
float srcPoints[] = {
0.0f, 0.0f, 0.0f, (float)(pImpl->height_ - 1), (float)(pImpl->width_ - 1), 0.0f, (float)(pImpl->width_ - 1), (float)(pImpl->height_ - 1),
};
float dstPoints[] = {
(float)(pImpl->height_ * scale - 1), 0.0f, 0.0f, 0.0f, (float)(pImpl->height_ * scale - 1), (float)(pImpl->width_ * scale - 1), 0.0f,
(float)(pImpl->width_ * scale - 1)};
pImpl->tr_.setPolyToPoly((MNN::CV::Point *)dstPoints, (MNN::CV::Point *)srcPoints, 4);
process->setMatrix(pImpl->tr_);
int scaled_height = static_cast<int>(pImpl->width_ * scale);
int scaled_width = static_cast<int>(pImpl->height_ * scale);
inspirecv::Image img_out(scaled_width, scaled_height, 3);
std::shared_ptr<MNN::Tensor> tensor(
MNN::Tensor::create<uint8_t>(std::vector<int>{1, scaled_height, scaled_width, 3}, (uint8_t *)img_out.Data()));
auto ret = process->convert(pImpl->buffer_, sw, sh, 0, tensor.get());
INSPIREFACE_CHECK_MSG(ret == MNN::ErrorCode::NO_ERROR, "ImageProcess::convert failed");
return img_out;
} else if (pImpl->rotation_mode_ == ROTATION_90 && with_rotation) {
float srcPoints[] = {
0.0f, 0.0f, 0.0f, (float)(pImpl->height_ - 1), (float)(pImpl->width_ - 1), 0.0f, (float)(pImpl->width_ - 1), (float)(pImpl->height_ - 1),
};
float dstPoints[] = {
0.0f,
(float)(pImpl->width_ * scale - 1),
(float)(pImpl->height_ * scale - 1),
(float)(pImpl->width_ * scale - 1),
0.0f,
0.0f,
(float)(pImpl->height_ * scale - 1),
0.0f,
};
pImpl->tr_.setPolyToPoly((MNN::CV::Point *)dstPoints, (MNN::CV::Point *)srcPoints, 4);
process->setMatrix(pImpl->tr_);
int scaled_height = static_cast<int>(pImpl->width_ * scale);
int scaled_width = static_cast<int>(pImpl->height_ * scale);
inspirecv::Image img_out(scaled_width, scaled_height, 3);
std::shared_ptr<MNN::Tensor> tensor(
MNN::Tensor::create<uint8_t>(std::vector<int>{1, scaled_height, scaled_width, 3}, (uint8_t *)img_out.Data()));
auto ret = process->convert(pImpl->buffer_, sw, sh, 0, tensor.get());
INSPIREFACE_CHECK_MSG(ret == MNN::ErrorCode::NO_ERROR, "ImageProcess::convert failed");
return img_out;
} else if (pImpl->rotation_mode_ == ROTATION_180 && with_rotation) {
float srcPoints[] = {
0.0f, 0.0f, 0.0f, (float)(pImpl->height_ - 1), (float)(pImpl->width_ - 1), 0.0f, (float)(pImpl->width_ - 1), (float)(pImpl->height_ - 1),
};
float dstPoints[] = {
(float)(pImpl->width_ * scale - 1),
(float)(pImpl->height_ * scale - 1),
(float)(pImpl->width_ * scale - 1),
0.0f,
0.0f,
(float)(pImpl->height_ * scale - 1),
0.0f,
0.0f,
};
pImpl->tr_.setPolyToPoly((MNN::CV::Point *)dstPoints, (MNN::CV::Point *)srcPoints, 4);
process->setMatrix(pImpl->tr_);
int scaled_height = static_cast<int>(pImpl->height_ * scale);
int scaled_width = static_cast<int>(pImpl->width_ * scale);
inspirecv::Image img_out(scaled_width, scaled_height, 3);
std::shared_ptr<MNN::Tensor> tensor(
MNN::Tensor::create<uint8_t>(std::vector<int>{1, scaled_height, scaled_width, 3}, (uint8_t *)img_out.Data()));
auto ret = process->convert(pImpl->buffer_, sw, sh, 0, tensor.get());
INSPIREFACE_CHECK_MSG(ret == MNN::ErrorCode::NO_ERROR, "ImageProcess::convert failed");
return img_out;
} else {
float srcPoints[] = {
0.0f, 0.0f, 0.0f, (float)(pImpl->height_ - 1), (float)(pImpl->width_ - 1), 0.0f, (float)(pImpl->width_ - 1), (float)(pImpl->height_ - 1),
};
float dstPoints[] = {
0.0f,
0.0f,
0.0f,
(float)(pImpl->height_ * scale - 1),
(float)(pImpl->width_ * scale - 1),
0.0f,
(float)(pImpl->width_ * scale - 1),
(float)(pImpl->height_ * scale - 1),
};
pImpl->tr_.setPolyToPoly((MNN::CV::Point *)dstPoints, (MNN::CV::Point *)srcPoints, 4);
process->setMatrix(pImpl->tr_);
int scaled_height = static_cast<int>(pImpl->height_ * scale);
int scaled_width = static_cast<int>(pImpl->width_ * scale);
inspirecv::Image img_out(scaled_width, scaled_height, 3);
std::shared_ptr<MNN::Tensor> tensor(
MNN::Tensor::create<uint8_t>(std::vector<int>{1, scaled_height, scaled_width, 3}, (uint8_t *)img_out.Data()));
auto ret = process->convert(pImpl->buffer_, sw, sh, 0, tensor.get());
INSPIREFACE_CHECK_MSG(ret == MNN::ErrorCode::NO_ERROR, "ImageProcess::convert failed");
return img_out;
}
}
inspirecv::TransformMatrix FrameProcess::GetRotationModeAffineMatrix() const {
float srcPoints[] = {0.0f, 0.0f, 0.0f, (float)(pImpl->height_ - 1), (float)(pImpl->width_ - 1), 0.0f, (float)(pImpl->width_ - 1), (float)(pImpl->height_ - 1)};
float dstPoints[8];
if (pImpl->rotation_mode_ == ROTATION_270) {
float points[] = {(float)(pImpl->height_ - 1),
0.0f,
0.0f,
0.0f,
(float)(pImpl->height_ - 1),
(float)(pImpl->width_ - 1),
0.0f,
(float)(pImpl->width_ - 1)};
memcpy(dstPoints, points, sizeof(points));
} else if (pImpl->rotation_mode_ == ROTATION_90) {
float points[] = {0.0f,
(float)(pImpl->width_ - 1),
(float)(pImpl->height_ - 1),
(float)(pImpl->width_ - 1),
0.0f,
0.0f,
(float)(pImpl->height_ - 1),
0.0f};
memcpy(dstPoints, points, sizeof(points));
} else if (pImpl->rotation_mode_ == ROTATION_180) {
float points[] = {(float)(pImpl->width_ - 1),
(float)(pImpl->height_ - 1),
(float)(pImpl->width_ - 1),
0.0f,
0.0f,
(float)(pImpl->height_ - 1),
0.0f,
0.0f};
memcpy(dstPoints, points, sizeof(points));
} else { // ROTATION_0
float points[] = {0.0f,
0.0f,
0.0f,
(float)(pImpl->height_ - 1),
(float)(pImpl->width_ - 1),
0.0f,
(float)(pImpl->width_ - 1),
(float)(pImpl->height_ - 1)};
memcpy(dstPoints, points, sizeof(points));
}
MNN::CV::Matrix tr;
tr.setPolyToPoly((MNN::CV::Point *)dstPoints, (MNN::CV::Point *)srcPoints, 4);
auto affine_matrix = inspirecv::TransformMatrix::Create();
affine_matrix[0] = tr[0];
affine_matrix[1] = tr[1];
affine_matrix[2] = tr[2];
affine_matrix[3] = tr[3];
affine_matrix[4] = tr[4];
affine_matrix[5] = tr[5];
return affine_matrix;
}
} // namespace inspirecv

View File

@@ -25,7 +25,7 @@
#include "RgaUtils.h"
#include "rga/utils.h"
#include "rga/dma_alloc.h"
#include "initialization_module/launch.h"
#include <launch.h>
namespace inspire {
@@ -109,7 +109,7 @@ private:
channels = c;
buffer_size = width * height * channels;
int ret = dma_buf_alloc(INSPIRE_LAUNCH->GetRockchipDmaHeapPath().c_str(), buffer_size, &dma_fd, &virtual_addr);
int ret = dma_buf_alloc(INSPIREFACE_CONTEXT->GetRockchipDmaHeapPath().c_str(), buffer_size, &dma_fd, &virtual_addr);
if (ret < 0) {
INSPIRECV_LOG(ERROR) << "Failed to allocate DMA buffer: " << ret;
return false;

View File

@@ -0,0 +1,22 @@
#ifndef INSPIRE_CUDA_TOOLKIT_H
#define INSPIRE_CUDA_TOOLKIT_H
#include "data_type.h"
namespace inspire {
// Get the number of CUDA devices
int32_t INSPIRE_API_EXPORT GetCudaDeviceCount(int32_t *device_count);
// Check the availability of CUDA
int32_t INSPIRE_API_EXPORT CheckCudaUsability(int32_t *is_support);
// Internal function, print detailed information of CUDA devices
int32_t INSPIRE_API_EXPORT _PrintCudaDeviceInfo();
// Wrapper function to print CUDA device information
int32_t INSPIRE_API_EXPORT PrintCudaDeviceInfo();
} // namespace inspire
#endif // INSPIRE_CUDA_TOOLKIT_H

View File

@@ -6,6 +6,8 @@
#ifndef INSPIRE_FACE_DATATYPE_H
#define INSPIRE_FACE_DATATYPE_H
#include <inspirecv/inspirecv.h>
#include <cstdint>
#if defined(_WIN32) && (defined(_DEBUG) || defined(DEBUG))
#define _CRTDBG_MAP_ALLOC
@@ -16,7 +18,15 @@
#define INSPIRE_API
#endif
#include <inspirecv/inspirecv.h>
#if defined(_WIN32)
#ifdef ISF_BUILD_SHARED_LIBS
#define INSPIRE_API_EXPORT __declspec(dllexport)
#else
#define INSPIRE_API_EXPORT
#endif
#else
#define INSPIRE_API_EXPORT __attribute__((visibility("default")))
#endif // _WIN32
#ifndef M_PI
#define M_PI 3.14159265358979323846264338327950288
@@ -152,6 +162,33 @@ typedef std::string String;
*/
typedef std::vector<int> IndexList;
/**
* @enum DetectMode
* @brief Enumeration for different detection modes.
*/
enum DetectModuleMode {
DETECT_MODE_ALWAYS_DETECT = 0, ///< Detection mode: Always detect
DETECT_MODE_LIGHT_TRACK, ///< Detection mode: Light face track
DETECT_MODE_TRACK_BY_DETECT, ///< Detection mode: Tracking by detection
};
/**
* @struct CustomPipelineParameter
* @brief Structure to hold custom parameters for the face detection and processing pipeline.
*
* Includes options for enabling various features such as recognition, liveness detection, and quality assessment.
*/
typedef struct CustomPipelineParameter {
bool enable_recognition = false; ///< Enable face recognition feature
bool enable_liveness = false; ///< Enable RGB liveness detection feature
bool enable_ir_liveness = false; ///< Enable IR (Infrared) liveness detection feature
bool enable_mask_detect = false; ///< Enable mask detection feature
bool enable_face_attribute = false; ///< Enable face attribute prediction feature
bool enable_face_quality = false; ///< Enable face quality assessment feature
bool enable_interaction_liveness = false; ///< Enable interactive liveness detection feature
bool enable_face_pose = false; ///< Enable face pose estimation feature
} ContextCustomParameter;
/** @struct FaceLoc
* @brief Struct representing standardized face landmarks for detection.
*
@@ -191,6 +228,74 @@ typedef struct FaceFeatureEntity {
float* data;
} FaceFeaturePtr;
// Search for most similar vectors
struct FaceSearchResult {
int64_t id;
double similarity;
std::vector<float> feature;
};
/** @struct FaceEmbedding
* @brief Struct for face embedding data.
*
* Contains the isNormal flag and the embedding vector.
*/
struct FaceEmbedding {
int32_t isNormal;
float norm;
Embedded embedding;
};
/** @struct FaceInteractionState
* @brief Struct for face interaction state data.
*
* Contains the confidence scores for face interaction.
*/
struct FaceInteractionState {
float left_eye_status_confidence;
float right_eye_status_confidence;
};
/** @struct FaceInteractionAction
* @brief Struct for face interaction action data.
*
* Contains the actions for face interaction.
*/
struct FaceInteractionAction {
int32_t normal; ///< Normal action.
int32_t shake; ///< Shake action.
int32_t jawOpen; ///< Jaw open action.
int32_t headRaise; ///< Head raise action.
int32_t blink; ///< Blink action.
};
/** @struct FaceAttributeResult
* @brief Struct for face attribute result data.
*
* Contains the results for face attribute.
*/
struct FaceAttributeResult {
int32_t race; ///< Race of the detected face.
///< 0: Black;
///< 1: Asian;
///< 2: Latino/Hispanic;
///< 3: Middle Eastern;
///< 4: White;
int32_t gender; ///< Gender of the detected face.
///< 0: Female;
///< 1: Male;
int32_t ageBracket; ///< Age bracket of the detected face.
///< 0: 0-2 years old;
///< 1: 3-9 years old;
///< 2: 10-19 years old;
///< 3: 20-29 years old;
///< 4: 30-39 years old;
///< 5: 40-49 years old;
///< 6: 50-59 years old;
///< 7: 60-69 years old;
///< 8: more than 70 years old;
};
/** @} */
} // namespace inspire

View File

@@ -8,11 +8,8 @@
#ifndef INSPIRE_FACE_FACEDATATYPE_H
#define INSPIRE_FACE_FACEDATATYPE_H
// Include the necessary header files
#include "../../data_type.h"
#include "../face_info/face_object_internal.h"
#include "data_type.h"
// Define the namespace "inspire" for encapsulation
namespace inspire {
/**
@@ -55,9 +52,9 @@ typedef struct TransMatrix {
} TransMatrix;
/**
* Struct to represent hyper face data.
* Struct to represent basic face data.
*/
typedef struct HyperFaceData {
typedef struct FaceTrackWrap {
int trackState; ///< Track state
int inGroupIndex; ///< Index within a group
int trackId; ///< Track ID
@@ -69,7 +66,7 @@ typedef struct HyperFaceData {
float quality[5]; ///< Quality values for key points
Point2F densityLandmark[106]; ///< Face density landmark
int densityLandmarkEnable; ///< Density landmark enable
} HyperFaceData;
} FaceTrackWrap;
} // namespace inspire

View File

@@ -6,28 +6,17 @@
#ifndef INSPIRE_FEATURE_HUB_DB_H
#define INSPIRE_FEATURE_HUB_DB_H
#include <mutex>
#include <memory>
#include <vector>
#include <string>
#include <memory>
#include "data_type.h"
#include "feature_hub/embedding_db/embedding_db.h"
#include "log.h"
#include <mutex>
// Default database file name used in the FaceContext.
#define DB_FILE_NAME ".feature_hub_db_v0"
#define FEATURE_HUB_DB FeatureHubDB::GetInstance()
#define INSPIREFACE_FEATURE_HUB inspire::FeatureHubDB::GetInstance()
#define INSPIRE_INVALID_ID -1
namespace inspire {
// Comparator function object to sort SearchResult by score (descending order)
struct CompareByScore {
bool operator()(const FaceSearchResult& a, const FaceSearchResult& b) const {
return a.similarity > b.similarity;
}
};
typedef enum SearchMode {
SEARCH_MODE_EAGER = 0, // Eager mode: Stops when a vector meets the threshold.
SEARCH_MODE_EXHAUSTIVE, // Exhaustive mode: Searches until the best match is found.
@@ -43,7 +32,7 @@ typedef enum PrimaryKeyMode {
* @brief Structure to configure database settings for FaceRecognition.
*/
using DatabaseConfiguration = struct DatabaseConfiguration {
PrimaryKeyMode primary_key_mode = PrimaryKeyMode::AUTO_INCREMENT; ///<
PrimaryKeyMode primary_key_mode = PrimaryKeyMode::AUTO_INCREMENT; ///< Primary key mode
bool enable_persistence = false; ///< Whether to enable data persistence.
std::string persistence_db_path; ///< Path to the database file.
float recognition_threshold = 0.48f; ///< Face search threshold
@@ -51,72 +40,75 @@ using DatabaseConfiguration = struct DatabaseConfiguration {
};
/**
* @class FeatureHub
* @class FeatureHubDB
* @brief Service for internal feature vector storage.
*
* This class provides methods for face feature extraction, registration, update, search, and more.
* It uses the PIMPL (Pointer to Implementation) pattern to hide implementation details.
*/
class INSPIRE_API FeatureHubDB {
private:
static std::mutex mutex_; ///< Mutex lock
static std::shared_ptr<FeatureHubDB> instance_; ///< FeatureHub Instance
class INSPIRE_API_EXPORT FeatureHubDB {
public:
/**
* @brief Constructor for FeatureHubDB class.
*/
FeatureHubDB();
/**
* @brief Destructor for FeatureHubDB class.
*/
~FeatureHubDB();
FeatureHubDB(const FeatureHubDB&) = delete;
FeatureHubDB& operator=(const FeatureHubDB&) = delete;
public:
/**
* @brief Enables the feature hub with the specified configuration and matrix core.
*
* This function initializes and configures the feature hub based on the provided database
* configuration and the specified matrix processing core. It prepares the hub for operation,
* setting up necessary resources such as database connections and data processing pipelines.
*
* @param configuration The database configuration settings used to configure the hub.
* @param core The matrix core used for processing, defaulting to OpenCV if not specified.
* @brief Gets the singleton instance of FeatureHubDB.
* @return Shared pointer to the FeatureHubDB instance.
*/
static std::shared_ptr<FeatureHubDB> GetInstance();
/**
* @brief Enables the feature hub with the specified configuration.
* @param configuration The database configuration settings.
* @return int32_t Returns a status code indicating success (0) or failure (non-zero).
*/
int32_t EnableHub(const DatabaseConfiguration& configuration);
/**
* @brief Disables the feature hub, freeing all associated resources.
*
* This function stops all operations within the hub, releases all occupied resources,
* such as database connections and internal data structures. It is used to safely
* shutdown the hub when it is no longer needed or before the application exits, ensuring
* that all resources are properly cleaned up.
*
* @return int32_t Returns a status code indicating success (0) or failure (non-zero).
*/
int32_t DisableHub();
/**
* @brief Get all ids in the database.
* @param ids Output parameter to store the ids.
* @return int32_t Status code of the operation.
*/
int32_t GetAllIds();
static std::shared_ptr<FeatureHubDB> GetInstance();
/**
* @brief Searches for a face feature within stored data.
* @param queryFeature Embedded feature to search for.
* @param searchResult SearchResult object to store search results.
* @param returnFeature Whether to return the feature data.
* @return int32_t Status code of the search operation.
*/
int32_t SearchFaceFeature(const Embedded& queryFeature, FaceSearchResult& searchResult, bool returnFeature = true);
/**
* @brief Search the stored data for the top k facial features that are most similar.
* @param topK Maximum search
* @param queryFeature Embedded feature to search for.
* @param topK Maximum number of results to return.
* @return int32_t Status code of the search operation.
*/
int32_t SearchFaceFeatureTopKCache(const Embedded& queryFeature, size_t topK);
/**
* @brief Search the stored data for the top k facial features that are most similar.
* @param topK Maximum search
* @param queryFeature Embedded feature to search for.
* @param searchResult Vector to store search results.
* @param topK Maximum number of results to return.
* @param returnFeature Whether to return the feature data.
* @return int32_t Status code of the search operation.
*/
int32_t SearchFaceFeatureTopK(const Embedded& queryFeature, std::vector<FaceSearchResult>& searchResult, size_t topK, bool returnFeature = false);
@@ -124,39 +116,38 @@ public:
/**
* @brief Inserts a face feature with a custom ID.
* @param feature Vector of floats representing the face feature.
* @param tag String tag associated with the feature.
* @param customId Custom ID for the feature.
* @param id ID for the feature.
* @param result_id Output parameter to store the resulting ID.
* @return int32_t Status code of the insertion operation.
*/
int32_t FaceFeatureInsert(const std::vector<float>& feature, int32_t id, int64_t& result_id);
/**
* @brief Removes a face feature by its custom ID.
* @param customId Custom ID of the feature to remove.
* @brief Removes a face feature by its ID.
* @param id ID of the feature to remove.
* @return int32_t Status code of the removal operation.
*/
int32_t FaceFeatureRemove(int32_t id);
/**
* @brief Updates a face feature by its custom ID.
* @brief Updates a face feature by its ID.
* @param feature Vector of floats representing the new face feature.
* @param tag String tag associated with the feature.
* @param customId Custom ID of the feature to update.
* @param customId ID of the feature to update.
* @return int32_t Status code of the update operation.
*/
int32_t FaceFeatureUpdate(const std::vector<float>& feature, int32_t customId);
/**
* @brief Retrieves a face feature by its custom ID.
* @param customId Custom ID of the feature to retrieve.
* @brief Retrieves a face feature by its ID.
* @param id ID of the feature to retrieve.
* @return int32_t Status code of the retrieval operation.
*/
int32_t GetFaceFeature(int32_t id);
/**
* @brief Retrieves a face feature by its custom ID.
* @param customId Custom ID of the feature to retrieve.
* @param feature Vector of floats representing the face feature.
* @brief Retrieves a face feature by its ID.
* @param id ID of the feature to retrieve.
* @param feature Vector to store the retrieved feature.
* @return int32_t Status code of the retrieval operation.
*/
int32_t GetFaceFeature(int32_t id, std::vector<float>& feature);
@@ -181,27 +172,26 @@ public:
/**
* @brief Computes the cosine similarity between two feature vectors.
*
* @param v1 First feature vector.
* @param v2 Second feature vector.
* @param res Output parameter to store the cosine similarity result.
* @param normalize Whether to normalize the vectors before computing similarity.
* @return int32_t Status code indicating success (0) or failure.
*/
static int32_t CosineSimilarity(const std::vector<float>& v1, const std::vector<float>& v2, float& res, bool normalize = false);
/**
* @brief Computes the cosine similarity between two feature vectors.
*
* @param v1 Pointer to the first feature vector.
* @param v2 Pointer to the second feature vector.
* @param size Size of the feature vectors.
* @param res Output parameter to store the cosine similarity result.
* @param normalize Whether to normalize the vectors before computing similarity.
* @return int32_t Status code indicating success (0) or failure.
*/
static int32_t CosineSimilarity(const float* v1, const float* v2, int32_t size, float& res, bool normalize = true);
public:
// Getter Function
// Getter methods
/**
* @brief Gets the cache used for search operations in face feature data.
@@ -216,8 +206,7 @@ public:
const std::shared_ptr<FaceFeaturePtr>& GetFaceFeaturePtrCache() const;
/**
* @brief Retrieves the total number of facial features stored in the feature block.
*
* @brief Retrieves the total number of facial features stored.
* @return int32_t Total number of facial features.
*/
int32_t GetFaceFeatureCount();
@@ -240,37 +229,15 @@ public:
*/
std::vector<int64_t>& GetExistingIds();
/**
* @brief Constructor for FeatureHub class.
*/
FeatureHubDB();
/**
* @brief Prints information about the feature matrix.
*/
void PrintFeatureMatrixInfo();
private:
Embedded m_search_face_feature_cache_; ///< Cache for face feature data used in search operations
Embedded m_getter_face_feature_cache_; ///< Cache for face feature data used in search operations
std::shared_ptr<FaceFeaturePtr> m_face_feature_ptr_cache_; ///< Shared pointer to cache of face feature pointers
class Impl;
std::vector<FaceSearchResult> m_search_top_k_cache_; ///< Cache for top k search results
std::vector<float> m_top_k_confidence_; ///< Cache for top k confidence scores
std::vector<int64_t> m_top_k_custom_ids_cache_; ///< Cache for top k custom ids
std::unique_ptr<Impl> pImpl;
std::vector<int64_t> m_all_ids_; ///< Cache for all ids
private:
DatabaseConfiguration m_db_configuration_; ///< Configuration settings for the database
float m_recognition_threshold_{0.48f}; ///< Threshold value for face recognition
SearchMode m_search_mode_{SEARCH_MODE_EAGER}; ///< Flag to determine if the search should find the most similar feature
bool m_enable_{false}; ///< Running status
std::mutex m_res_mtx_; ///< Mutex for thread safety.
static std::mutex mutex_;
static std::shared_ptr<FeatureHubDB> instance_;
};
} // namespace inspire
#endif // INSPIRE_FEATURE_HUB_DB_H
#endif // INSPIRE_FEATURE_HUB_DB_H

View File

@@ -0,0 +1,198 @@
#ifndef INSPIREFACE_FRAME_PROCESS_H
#define INSPIREFACE_FRAME_PROCESS_H
#include <memory>
#include <inspirecv/inspirecv.h>
#include "data_type.h"
namespace inspirecv {
/**
* @brief Enum to represent rotation modes.
*/
enum ROTATION_MODE { ROTATION_0 = 0, ROTATION_90 = 1, ROTATION_180 = 2, ROTATION_270 = 3 };
/**
* @brief Enum to represent data formats.
*/
enum DATA_FORMAT { NV21 = 0, NV12 = 1, RGBA = 2, RGB = 3, BGR = 4, BGRA = 5 };
/**
* @brief A class to handle camera stream and image processing.
*/
class INSPIRE_API_EXPORT FrameProcess {
public:
/**
* @brief Create a FrameProcess instance.
*
* @param data_buffer Pointer to the data buffer.
* @param height Height of the image.
* @param width Width of the image.
* @param data_format Data format (e.g., NV21, RGBA).
* @param rotation_mode Rotation mode (e.g., ROTATION_0, ROTATION_90).
* @return FrameProcess instance.
*/
static FrameProcess Create(const uint8_t* data_buffer, int height, int width, DATA_FORMAT data_format = BGR,
ROTATION_MODE rotation_mode = ROTATION_0);
/**
* @brief Create a FrameProcess instance from an inspirecv::Image.
*
* @param image The image to process.
* @param data_format Data format (e.g., NV21, RGBA).
* @param rotation_mode Rotation mode (e.g., ROTATION_0, ROTATION_90).
* @return FrameProcess instance.
*/
static FrameProcess Create(const inspirecv::Image& image, DATA_FORMAT data_format = BGR, ROTATION_MODE rotation_mode = ROTATION_0);
/**
* @brief Default constructor.
*/
FrameProcess();
/**
* @brief Destructor.
*/
~FrameProcess();
/**
* @brief Copy constructor.
*/
FrameProcess(const FrameProcess& other);
/**
* @brief Move constructor.
*/
FrameProcess(FrameProcess&& other) noexcept;
/**
* @brief Copy assignment operator.
*/
FrameProcess& operator=(const FrameProcess& other);
/**
* @brief Move assignment operator.
*/
FrameProcess& operator=(FrameProcess&& other) noexcept;
/**
* @brief Set the data buffer, height, and width of the camera stream.
*
* @param data_buffer Pointer to the data buffer.
* @param height Height of the image.
* @param width Width of the image.
*/
void SetDataBuffer(const uint8_t* data_buffer, int height, int width);
/**
* @brief Set the preview size.
*
* @param size Preview size.
*/
void SetPreviewSize(const int size);
/**
* @brief Set the preview scale.
*
* @param scale Preview scale.
*/
void SetPreviewScale(const float scale);
/**
* @brief Set the rotation mode.
*
* @param mode Rotation mode (e.g., ROTATION_0, ROTATION_90).
*/
void SetRotationMode(ROTATION_MODE mode);
/**
* @brief Set the data format.
*
* @param data_format Data format (e.g., NV21, RGBA).
*/
void SetDataFormat(DATA_FORMAT data_format);
/**
* @brief Set the destination format.
*
* @param data_format Data format (e.g., NV21, RGBA).
*/
void SetDestFormat(DATA_FORMAT data_format);
/**
* @brief Get an affine-transformed image.
*
* @param affine_matrix Affine transformation matrix.
* @param width_out Width of the output image.
* @param height_out Height of the output image.
* @return inspirecv::Image Affine-transformed image.
*/
inspirecv::Image ExecuteImageAffineProcessing(inspirecv::TransformMatrix& affine_matrix, const int width_out, const int height_out) const;
/**
* @brief Get a preview image with optional rotation.
*
* @param with_rotation True if rotation is applied, false otherwise.
* @return inspirecv::Image Preview image.
*/
inspirecv::Image ExecutePreviewImageProcessing(bool with_rotation);
/**
* @brief Get the preview scale.
*
* @return float Preview scale.
*/
float GetPreviewScale();
/**
* @brief Execute image scale processing.
*
* @param scale Scale factor.
* @param with_rotation True if rotation is applied, false otherwise.
* @return inspirecv::Image Scaled image.
*/
inspirecv::Image ExecuteImageScaleProcessing(const float scale, bool with_rotation);
/**
* @brief Get the affine transformation matrix.
*
* @return inspirecv::TransformMatrix Affine transformation matrix.
*/
inspirecv::TransformMatrix GetAffineMatrix() const;
/**
* @brief Get the rotation mode affine transformation matrix, scale coefficient is not included.
*
* @return inspirecv::TransformMatrix Rotation mode affine transformation matrix.
*/
inspirecv::TransformMatrix GetRotationModeAffineMatrix() const;
/**
* @brief Get the height of the camera stream image.
*
* @return int Height.
*/
int GetHeight() const;
/**
* @brief Get the width of the camera stream image.
*
* @return int Width.
*/
int GetWidth() const;
/**
* @brief Get the current rotation mode.
*
* @return ROTATION_MODE Current rotation mode.
*/
ROTATION_MODE getRotationMode() const;
private:
class Impl;
std::unique_ptr<Impl> pImpl;
};
} // namespace inspirecv
#endif // INSPIREFACE_FRAME_PROCESS_H

View File

@@ -0,0 +1,14 @@
#include "session.h"
#include "cuda_toolkit.h"
#include "data_type.h"
#include "log.h"
#include "herror.h"
#include "feature_hub_db.h"
#include "frame_process.h"
#include "isf_check.h"
#include "launch.h"
#include "log.h"
#include "similarity_converter.h"
#include "spend_timer.h"
#include "information.h"
#include "face_warpper.h"

View File

@@ -0,0 +1,33 @@
#ifndef INSPIRE_FACE_CHECK_H
#define INSPIRE_FACE_CHECK_H
#include "log.h"
#include "herror.h"
#define INSPIREFACE_RETURN_IF_ERROR(...) \
do { \
const int32_t _status = (__VA_ARGS__); \
if (_status != HSUCCEED) { \
INSPIRE_LOGE("Error code: %d", _status); \
return _status; \
} \
} while (0)
#define INSPIREFACE_LOG_IF(severity, condition) \
if (condition) \
INSPIRE_LOG##severity
#define INSPIREFACE_CHECK(condition) \
do { \
if (!(condition)) { \
INSPIRE_LOGF("Check failed: (%s)", #condition); \
} \
} while (0)
#define INSPIREFACE_CHECK_MSG(condition, message) \
do { \
if (!(condition)) { \
INSPIRE_LOGF("Check failed: (%s) %s", #condition, message); \
} \
} while (0)
#endif // INSPIRE_FACE_CHECK_H

View File

@@ -5,28 +5,42 @@
#pragma once
#ifndef INSPIREFACE_LAUNCH_H
#define INSPIREFACE_LAUNCH_H
#include "middleware/model_archive/inspire_archive.h"
#if defined(ISF_ENABLE_RGA)
#include "middleware/nexus_processor/rga/dma_alloc.h"
#endif
#include <mutex>
#include "middleware/inference_wrapper/inference_wrapper.h"
#include "middleware/system.h"
#ifndef INSPIRE_API
#define INSPIRE_API
#endif
#include <memory>
#include <string>
#include <cstdint>
#include "data_type.h"
#define INSPIRE_LAUNCH inspire::Launch::GetInstance()
#define INSPIREFACE_CONTEXT inspire::Launch::GetInstance()
namespace inspire {
// Forward declarations
class InspireArchive;
// The Launch class acts as the main entry point for the InspireFace system.
// It is responsible for loading static resources such as models, configurations, and parameters.
class INSPIRE_API Launch {
class INSPIRE_API_EXPORT Launch {
public:
// Special Backend enum for CoreML
enum NNInferenceBackend {
NN_INFERENCE_CPU = 0,
NN_INFERENCE_MMM_CUDA,
NN_INFERENCE_COREML_CPU,
NN_INFERENCE_COREML_GPU,
NN_INFERENCE_COREML_ANE,
NN_INFERENCE_TENSORRT_CUDA,
};
enum LandmarkEngine {
LANDMARK_HYPLMV2_0_25 = 0,
LANDMARK_HYPLMV2_0_50,
LANDMARK_INSIGHTFACE_2D106_TRACK,
};
Launch(const Launch&) = delete; // Delete the copy constructor to prevent copying.
Launch& operator=(const Launch&) = delete; // Delete the assignment operator to prevent assignment.
~Launch(); // Destructor needs to be defined where the implementation is complete
// Retrieves the singleton instance of Launch, ensuring that only one instance exists.
static std::shared_ptr<Launch> GetInstance();
@@ -61,10 +75,10 @@ public:
std::string GetExtensionPath() const;
// Set the global coreml inference mode
void SetGlobalCoreMLInferenceMode(InferenceWrapper::SpecialBackend mode);
void SetGlobalCoreMLInferenceMode(NNInferenceBackend mode);
// Get the global coreml inference mode
InferenceWrapper::SpecialBackend GetGlobalCoreMLInferenceMode() const;
NNInferenceBackend GetGlobalCoreMLInferenceMode() const;
// Build the extension path
void BuildAppleExtensionPath(const std::string& resource_path);
@@ -75,35 +89,30 @@ public:
// Get the cuda device id
int32_t GetCudaDeviceId() const;
// Set the face detect pixel list
void SetFaceDetectPixelList(const std::vector<int32_t>& pixel_list);
// Get the face detect pixel list
std::vector<int32_t> GetFaceDetectPixelList() const;
// Set the face detect model list
void SetFaceDetectModelList(const std::vector<std::string>& model_list);
// Get the face detect model list
std::vector<std::string> GetFaceDetectModelList() const;
// Switch the landmark engine
void SwitchLandmarkEngine(LandmarkEngine engine);
private:
// Parameters
std::string m_rockchip_dma_heap_path_;
// Private constructor for the singleton pattern
Launch();
// Constructor
Launch() : m_load_(false), m_archive_(nullptr) {
#if defined(ISF_ENABLE_RGA)
#if defined(ISF_RKNPU_RV1106)
m_rockchip_dma_heap_path_ = RV1106_CMA_HEAP_PATH;
#else
m_rockchip_dma_heap_path_ = DMA_HEAP_DMA32_UNCACHE_PATCH;
#endif
INSPIRE_LOGW("Rockchip dma heap configured path: %s", m_rockchip_dma_heap_path_.c_str());
#endif
} ///< Private constructor for the singleton pattern.
static std::mutex mutex_; ///< Mutex for synchronizing access to the singleton instance.
static std::shared_ptr<Launch> instance_; ///< The singleton instance of Launch.
std::string m_extension_path_;
std::unique_ptr<InspireArchive> m_archive_; ///< The archive containing all necessary resources.
bool m_load_; ///< Flag indicating whether the resources have been successfully loaded.
int32_t m_cuda_device_id_{0};
InferenceWrapper::SpecialBackend m_global_coreml_inference_mode_{InferenceWrapper::COREML_ANE}; ///< The global coreml inference mode
// Private implementation class
class Impl;
std::unique_ptr<Impl> pImpl;
};
} // namespace inspire
#endif // INSPIREFACE_LAUNCH_H
#endif // INSPIREFACE_LAUNCH_H

View File

@@ -0,0 +1,89 @@
#ifndef INSPIRE_FACE_LOG_H
#define INSPIRE_FACE_LOG_H
#include <memory>
#include <string>
#include <cstring>
#include "data_type.h"
// Macro to extract the filename from the full path
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
#ifdef ANDROID
// Android platform log macros
#define INSPIRE_ANDROID_LOG_TAG "InspireFace"
#define INSPIRE_LOGD(...) inspire::LogManager::getInstance()->logAndroid(inspire::LogLevel::ISF_LOG_DEBUG, INSPIRE_ANDROID_LOG_TAG, __VA_ARGS__)
#define INSPIRE_LOGI(...) inspire::LogManager::getInstance()->logAndroid(inspire::LogLevel::ISF_LOG_INFO, INSPIRE_ANDROID_LOG_TAG, __VA_ARGS__)
#define INSPIRE_LOGW(...) inspire::LogManager::getInstance()->logAndroid(inspire::LogLevel::ISF_LOG_WARN, INSPIRE_ANDROID_LOG_TAG, __VA_ARGS__)
#define INSPIRE_LOGE(...) inspire::LogManager::getInstance()->logAndroid(inspire::LogLevel::ISF_LOG_ERROR, INSPIRE_ANDROID_LOG_TAG, __VA_ARGS__)
#define INSPIRE_LOGF(...) inspire::LogManager::getInstance()->logAndroid(inspire::LogLevel::ISF_LOG_FATAL, INSPIRE_ANDROID_LOG_TAG, __VA_ARGS__)
#else
// Standard platform log macros
#define INSPIRE_LOGD(...) \
inspire::LogManager::getInstance()->logStandard(inspire::LogLevel::ISF_LOG_DEBUG, __FILENAME__, __FUNCTION__, __LINE__, __VA_ARGS__)
#define INSPIRE_LOGI(...) inspire::LogManager::getInstance()->logStandard(inspire::LogLevel::ISF_LOG_INFO, "", "", -1, __VA_ARGS__)
#define INSPIRE_LOGW(...) inspire::LogManager::getInstance()->logStandard(inspire::LogLevel::ISF_LOG_WARN, "", "", -1, __VA_ARGS__)
#define INSPIRE_LOGE(...) inspire::LogManager::getInstance()->logStandard(inspire::LogLevel::ISF_LOG_ERROR, "", "", -1, __VA_ARGS__)
#define INSPIRE_LOGF(...) inspire::LogManager::getInstance()->logStandard(inspire::LogLevel::ISF_LOG_FATAL, "", "", -1, __VA_ARGS__)
#endif
// Macro to set the global log level
#define INSPIRE_SET_LOG_LEVEL(level) inspire::LogManager::getInstance()->setLogLevel(level)
namespace inspire {
// Log levels
enum LogLevel { ISF_LOG_NONE = 0, ISF_LOG_DEBUG, ISF_LOG_INFO, ISF_LOG_WARN, ISF_LOG_ERROR, ISF_LOG_FATAL };
/**
* @class LogManager
* @brief A singleton class for logging messages to the console or Android logcat.
*
* This class provides methods to log messages of different severity levels (DEBUG, INFO, WARN, ERROR, FATAL)
* to the console or Android logcat based on the current log level setting.
*
* Implementation details are hidden using the PIMPL (Pointer to Implementation) pattern.
*/
class INSPIRE_API_EXPORT LogManager {
public:
// Get the singleton instance
static LogManager* getInstance();
// Destructor
~LogManager();
// Set the log level
void setLogLevel(LogLevel level);
// Get the current log level
LogLevel getLogLevel() const;
#ifdef ANDROID
// Method for logging on the Android platform
void logAndroid(LogLevel level, const char* tag, const char* format, ...) const;
#else
// Method for standard platform logging
void logStandard(LogLevel level, const char* filename, const char* function, int line, const char* format, ...) const;
#endif
private:
// Private constructor for singleton pattern
LogManager();
// Disable copy construction and assignment
LogManager(const LogManager&) = delete;
LogManager& operator=(const LogManager&) = delete;
// Forward declaration of the implementation class
class Impl;
// Pointer to implementation
std::unique_ptr<Impl> pImpl;
// Static instance for singleton pattern
static LogManager* instance;
};
} // namespace inspire
#endif // INSPIRE_FACE_LOG_H

View File

@@ -0,0 +1,199 @@
#ifndef INSPIRE_FACE_SESSION_H
#define INSPIRE_FACE_SESSION_H
#include <memory>
#include "data_type.h"
#include "frame_process.h"
#include "face_warpper.h"
namespace inspire {
/**
* @brief The face algorithm session class.
*/
class INSPIRE_API_EXPORT Session {
public:
Session();
~Session();
Session(Session&&) noexcept;
Session& operator=(Session&&) noexcept;
Session(const Session&) = delete;
Session& operator=(const Session&) = delete;
/**
* @brief Create a new session with the given parameters.
* @param detect_mode The mode of face detection.
* @param max_detect_face The maximum number of faces to detect.
* @param param The custom pipeline parameter.
* @param detect_level_px The detection level in pixels.
* @param track_by_detect_mode_fps The tracking frame rate.
* @return A new session.
*/
static Session Create(DetectModuleMode detect_mode, int32_t max_detect_face, const CustomPipelineParameter& param, int32_t detect_level_px = -1,
int32_t track_by_detect_mode_fps = -1);
/**
* @brief Create a new session pointer with the given parameters.
* @param detect_mode The mode of face detection.
* @param max_detect_face The maximum number of faces to detect.
* @param param The custom pipeline parameter.
* @param detect_level_px The detection level in pixels.
* @param track_by_detect_mode_fps The tracking frame rate.
* @return A raw pointer to new session. The caller is responsible for memory management.
*/
static Session* CreatePtr(DetectModuleMode detect_mode, int32_t max_detect_face, const CustomPipelineParameter& param,
int32_t detect_level_px = -1, int32_t track_by_detect_mode_fps = -1) {
return new Session(Create(detect_mode, max_detect_face, param, detect_level_px, track_by_detect_mode_fps));
}
/**
* @brief Set the track preview size.
* @param preview_size The preview size.
*/
void SetTrackPreviewSize(int32_t preview_size);
/**
* @brief Set the minimum face pixel size.
* @param min_face_pixel_size The minimum face pixel size.
*/
void SetFilterMinimumFacePixelSize(int32_t min_face_pixel_size);
/**
* @brief Set the face detect threshold.
* @param threshold The face detect threshold.
*/
void SetFaceDetectThreshold(float threshold);
/**
* @brief Set the track mode smooth ratio.
* @param smooth_ratio The track mode smooth ratio.
*/
void SetTrackModeSmoothRatio(int32_t smooth_ratio);
/**
* @brief Set the track mode num smooth cache frame.
* @param num_smooth_cache_frame The track mode num smooth cache frame.
*/
void SetTrackModeNumSmoothCacheFrame(int32_t num_smooth_cache_frame);
/**
* @brief Set the track mode detect interval.
* @param detect_interval The track mode detect interval.
*/
void SetTrackModeDetectInterval(int32_t detect_interval);
/**
* @brief Detect and track the faces in the frame.
* @param process The frame process.
* @param results The detected faces.
*/
int32_t FaceDetectAndTrack(inspirecv::FrameProcess& process, std::vector<FaceTrackWrap>& results);
/**
* @brief Get the face bounding box.
* @param face_data The face data.
* @return The face bounding box.
*/
inspirecv::Rect2i GetFaceBoundingBox(const FaceTrackWrap& face_data);
/**
* @brief Get the face dense landmark.
* @param face_data The face data.
* @return The face dense landmark.
*/
std::vector<inspirecv::Point2f> GetFaceDenseLandmark(const FaceTrackWrap& face_data);
/**
* @brief Get the face five key points.
* @param face_data The face data.
* @return The face five key points.
*/
std::vector<inspirecv::Point2f> GetFaceFiveKeyPoints(const FaceTrackWrap& face_data);
/**
* @brief Extract the face feature.
* @param process The frame process.
* @param data The face data.
* @param embedding The face embedding.
* @param normalize The normalize flag.
*/
int32_t FaceFeatureExtract(inspirecv::FrameProcess& process, FaceTrackWrap& data, FaceEmbedding& embedding, bool normalize = true);
/**
* @brief Get the face alignment image.
* @param process The frame process.
* @param data The face data.
* @param wrapped The wrapped image.
*/
void GetFaceAlignmentImage(inspirecv::FrameProcess& process, FaceTrackWrap& data, inspirecv::Image& wrapped);
/**
* @brief Extract the face feature with alignment image.
* @param process The frame process.
* @param embedding The face embedding.
* @param normalize The normalize flag.
*/
int32_t FaceFeatureExtractWithAlignmentImage(inspirecv::FrameProcess& process, FaceEmbedding& embedding, bool normalize = true);
/**
* @brief Extract the face feature with alignment image.
* @param wrapped The wrapped image.
* @param embedding The face embedding.
* @param normalize The normalize flag.
*/
int32_t FaceFeatureExtractWithAlignmentImage(const inspirecv::Image& wrapped, FaceEmbedding& embedding, bool normalize = true);
/**
* @brief Multiple face pipeline process.
* @param process The frame process.
* @param param The custom pipeline parameter.
* @param face_data_list The face data list.
*/
int32_t MultipleFacePipelineProcess(inspirecv::FrameProcess& process, const CustomPipelineParameter& param,
const std::vector<FaceTrackWrap>& face_data_list);
/**
* @brief Get the RGB liveness confidence.
* @return The RGB liveness confidence.
*/
std::vector<float> GetRGBLivenessConfidence();
/**
* @brief Get the face mask confidence.
* @return The face mask confidence.
*/
std::vector<float> GetFaceMaskConfidence();
/**
* @brief Get the face quality confidence.
* @return The face quality confidence.
*/
std::vector<float> GetFaceQualityConfidence();
/**
* @brief Get the face interaction state.
* @return The face interaction state.
*/
std::vector<FaceInteractionState> GetFaceInteractionState();
/**
* @brief Get the face interaction action.
* @return The face interaction action.
*/
std::vector<FaceInteractionAction> GetFaceInteractionAction();
/**
* @brief Get the face attribute result.
* @return The face attribute result.
*/
std::vector<FaceAttributeResult> GetFaceAttributeResult();
private:
class Impl;
std::unique_ptr<Impl> pImpl;
};
} // namespace inspire
#endif // INSPIRE_FACE_SESSION_H

View File

@@ -4,6 +4,7 @@
#include <iostream>
#include <cmath>
#include <mutex>
#include "data_type.h"
#define SIMILARITY_CONVERTER_UPDATE_CONFIG(config) inspire::SimilarityConverter::getInstance().updateConfig(config)
#define SIMILARITY_CONVERTER_RUN(cosine) inspire::SimilarityConverter::getInstance().convert(cosine)
@@ -22,7 +23,7 @@ struct SimilarityConverterConfig {
double outputMax = 1.0; // Maximum value of output range
};
class SimilarityConverter {
class INSPIRE_API_EXPORT SimilarityConverter {
private:
SimilarityConverterConfig config;
double outputScale; // Scale of output range

View File

@@ -0,0 +1,51 @@
#ifndef INSPIRE_FACE_TIMER_H
#define INSPIRE_FACE_TIMER_H
#include "data_type.h"
namespace inspire {
// Get the current time in microseconds.
uint64_t INSPIRE_API_EXPORT _now();
/**
* @brief A class to measure the cost of a block of code.
*/
class INSPIRE_API_EXPORT SpendTimer {
public:
SpendTimer();
explicit SpendTimer(const std::string &name);
void Start();
void Stop();
void Reset();
uint64_t Get() const;
uint64_t Average() const;
uint64_t Total() const;
uint64_t Count() const;
uint64_t Min() const;
uint64_t Max() const;
const std::string &name() const;
std::string Report() const;
static void Disable();
protected:
uint64_t start_;
uint64_t stop_;
uint64_t total_;
uint64_t count_;
uint64_t min_;
uint64_t max_;
std::string name_;
static int is_enable;
};
INSPIRE_API_EXPORT std::ostream &operator<<(std::ostream &os, const SpendTimer &timer);
#define TIME_NOW inspirecv::_now()
} // namespace inspire
#endif // INSPIRE_FACE_TIMER_H

View File

@@ -1,15 +0,0 @@
/**
* Created by Jingyu Yan
* @date 2024-10-01
*/
#ifndef INSPIRE_FACE_INFORMATION_H
#define INSPIRE_FACE_INFORMATION_H
#define INSPIRE_FACE_VERSION_MAJOR_STR "1"
#define INSPIRE_FACE_VERSION_MINOR_STR "2"
#define INSPIRE_FACE_VERSION_PATCH_STR "0"
#define INSPIRE_FACE_EXTENDED_INFORMATION "InspireFace[Community Edition]@General - Build Time: 2025-03-25"
#endif // INSPIRE_FACE_INFORMATION_H

View File

@@ -1,42 +0,0 @@
#ifndef INSPIRE_FACE_CHECK_H
#define INSPIRE_FACE_CHECK_H
#include "log.h"
#include "herror.h"
#define INSPIREFACE_RETURN_IF_ERROR(...) \
do { \
const int32_t _status = (__VA_ARGS__); \
if (_status != HSUCCEED) { \
INSPIRE_LOGE("Error code: %d", _status); \
return _status; \
} \
} while (0)
#define INSPIREFACE_LOG_IF(severity, condition) \
if (condition) \
INSPIRE_LOG##severity
#define INSPIREFACE_CHECK(condition) \
do { \
if (!(condition)) { \
INSPIRE_LOGF("Check failed: (%s)", #condition); \
} \
} while (0)
#define INSPIREFACE_CHECK_MSG(condition, message) \
do { \
if (!(condition)) { \
INSPIRE_LOGF("Check failed: (%s) %s", #condition, message); \
} \
} while (0)
#define INSPIREFACE_CHECK_EQ(a, b) INSPIREFACE_CHECK((a) == (b)) << "Expected equality of these values: " << #a << " vs " << #b
#define INSPIREFACE_CHECK_NE(a, b) INSPIREFACE_CHECK((a) != (b)) << "Expected inequality of these values: " << #a << " vs " << #b
#define INSPIREFACE_CHECK_LE(a, b) INSPIREFACE_CHECK((a) <= (b)) << "Expected " << #a << " <= " << #b
#define INSPIREFAFECE_CHECK_LT(a, b) INSPIREFACE_CHECK((a) < (b)) << "Expected " << #a << " < " << #b
#define INSPIREFAFECE_CHECK_GE(a, b) INSPIREFACE_CHECK((a) >= (b)) << "Expected " << #a << " >= " << #b
#define INSPIREFAFECE_CHECK_GT(a, b) INSPIREFACE_CHECK((a) > (b)) << "Expected " << #a << " > " << #b
#endif // INSPIRE_FACE_CHECK_H

View File

@@ -3,11 +3,150 @@
* @date 2024-10-01
*/
#include "log.h"
#include <mutex>
#include <cstdarg>
#include <cstring>
#include <iostream>
#ifdef ANDROID
#include <android/log.h>
#endif
namespace inspire {
// Static Logger initialization
// Implementation class for LogManager
class LogManager::Impl {
public:
Impl() : currentLevel(LogLevel::ISF_LOG_INFO) {}
LogLevel currentLevel;
static std::mutex mutex;
};
// Static initialization
std::mutex LogManager::Impl::mutex;
LogManager* LogManager::instance = nullptr;
std::mutex LogManager::mutex;
// Constructor
LogManager::LogManager() : pImpl(std::make_unique<Impl>()) {}
// Destructor
LogManager::~LogManager() = default;
// Get singleton instance
LogManager* LogManager::getInstance() {
std::lock_guard<std::mutex> lock(Impl::mutex);
if (instance == nullptr) {
instance = new LogManager();
}
return instance;
}
// Set log level
void LogManager::setLogLevel(LogLevel level) {
pImpl->currentLevel = level;
}
// Get log level
LogLevel LogManager::getLogLevel() const {
return pImpl->currentLevel;
}
#ifdef ANDROID
// Android logging implementation
void LogManager::logAndroid(LogLevel level, const char* tag, const char* format, ...) const {
if (pImpl->currentLevel == LogLevel::ISF_LOG_NONE || level < pImpl->currentLevel)
return;
int androidLevel;
switch (level) {
case LogLevel::ISF_LOG_DEBUG:
androidLevel = ANDROID_LOG_DEBUG;
break;
case LogLevel::ISF_LOG_INFO:
androidLevel = ANDROID_LOG_INFO;
break;
case LogLevel::ISF_LOG_WARN:
androidLevel = ANDROID_LOG_WARN;
break;
case LogLevel::ISF_LOG_ERROR:
androidLevel = ANDROID_LOG_ERROR;
break;
case LogLevel::ISF_LOG_FATAL:
androidLevel = ANDROID_LOG_FATAL;
break;
default:
androidLevel = ANDROID_LOG_DEFAULT;
}
va_list args;
va_start(args, format);
__android_log_vprint(androidLevel, tag, format, args);
va_end(args);
// If the log level is fatal, flush the error stream and abort the program
if (level == LogLevel::ISF_LOG_FATAL) {
std::flush(std::cerr);
abort();
}
}
#else
// Standard logging implementation
void LogManager::logStandard(LogLevel level, const char* filename, const char* function, int line, const char* format, ...) const {
// Check whether the current level is LOG NONE or the log level is not enough to log
if (pImpl->currentLevel == LogLevel::ISF_LOG_NONE || level < pImpl->currentLevel)
return;
// Build log prefix dynamically based on available data
bool hasPrintedPrefix = false;
if (filename && strlen(filename) > 0) {
printf("[%s]", filename);
hasPrintedPrefix = true;
}
if (function && strlen(function) > 0) {
printf("[%s]", function);
hasPrintedPrefix = true;
}
if (line != -1) {
printf("[%d]", line);
hasPrintedPrefix = true;
}
// Only add colon and space if any prefix was printed
if (hasPrintedPrefix) {
printf(": ");
}
// Set text color for different log levels, but only if not on iOS
#ifndef TARGET_OS_IOS
if (level == LogLevel::ISF_LOG_ERROR || level == LogLevel::ISF_LOG_FATAL) {
printf("\033[1;31m"); // Red color for errors and fatal issues
} else if (level == LogLevel::ISF_LOG_WARN) {
printf("\033[1;33m"); // Yellow color for warnings
}
#endif
// Print the actual log message
va_list args;
va_start(args, format);
vprintf(format, args);
va_end(args);
// Reset text color if needed, but only if not on iOS
#ifndef TARGET_OS_IOS
if (level == LogLevel::ISF_LOG_ERROR || level == LogLevel::ISF_LOG_WARN || level == LogLevel::ISF_LOG_FATAL) {
printf("\033[0m"); // Reset color
}
#endif
printf("\n"); // New line after log message
// If the log level is fatal, flush the error stream and abort the program
if (level == LogLevel::ISF_LOG_FATAL) {
std::flush(std::cerr);
abort();
}
}
#endif
} // namespace inspire

View File

@@ -1,184 +0,0 @@
#ifndef INSPIRE_FACE_LOG_H
#define INSPIRE_FACE_LOG_H
#include <mutex>
#include <string>
#include <cstdarg>
#include <cstring>
#include <iostream>
#ifndef INSPIRE_API
#define INSPIRE_API
#endif
// Macro to extract the filename from the full path
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
#ifdef ANDROID
// Android platform log macros
#include <android/log.h>
#define INSPIRE_ANDROID_LOG_TAG "InspireFace"
#define INSPIRE_LOGD(...) inspire::LogManager::getInstance()->logAndroid(inspire::ISF_LOG_DEBUG, INSPIRE_ANDROID_LOG_TAG, __VA_ARGS__)
#define INSPIRE_LOGI(...) inspire::LogManager::getInstance()->logAndroid(inspire::ISF_LOG_INFO, INSPIRE_ANDROID_LOG_TAG, __VA_ARGS__)
#define INSPIRE_LOGW(...) inspire::LogManager::getInstance()->logAndroid(inspire::ISF_LOG_WARN, INSPIRE_ANDROID_LOG_TAG, __VA_ARGS__)
#define INSPIRE_LOGE(...) inspire::LogManager::getInstance()->logAndroid(inspire::ISF_LOG_ERROR, INSPIRE_ANDROID_LOG_TAG, __VA_ARGS__)
#define INSPIRE_LOGF(...) inspire::LogManager::getInstance()->logAndroid(inspire::ISF_LOG_FATAL, INSPIRE_ANDROID_LOG_TAG, __VA_ARGS__)
#else
// Standard platform log macros
#define INSPIRE_LOGD(...) inspire::LogManager::getInstance()->logStandard(inspire::ISF_LOG_DEBUG, __FILENAME__, __FUNCTION__, __LINE__, __VA_ARGS__)
#define INSPIRE_LOGI(...) inspire::LogManager::getInstance()->logStandard(inspire::ISF_LOG_INFO, "", "", -1, __VA_ARGS__)
#define INSPIRE_LOGW(...) inspire::LogManager::getInstance()->logStandard(inspire::ISF_LOG_WARN, "", "", -1, __VA_ARGS__)
#define INSPIRE_LOGE(...) inspire::LogManager::getInstance()->logStandard(inspire::ISF_LOG_ERROR, "", "", -1, __VA_ARGS__)
#define INSPIRE_LOGF(...) inspire::LogManager::getInstance()->logStandard(inspire::ISF_LOG_FATAL, "", "", -1, __VA_ARGS__)
#endif
// Macro to set the global log level
#define INSPIRE_SET_LOG_LEVEL(level) inspire::LogManager::getInstance()->setLogLevel(level)
namespace inspire {
// Log levels
enum LogLevel { ISF_LOG_NONE = 0, ISF_LOG_DEBUG, ISF_LOG_INFO, ISF_LOG_WARN, ISF_LOG_ERROR, ISF_LOG_FATAL };
/**
* @class LogManager
* @brief A singleton class for logging messages to the console or Android logcat.
*
* This class provides methods to log messages of different severity levels (DEBUG, INFO, WARN, ERROR, FATAL)
* to the console or Android logcat based on the current log level setting.
*/
class INSPIRE_API LogManager {
private:
LogLevel currentLevel;
static LogManager* instance;
static std::mutex mutex;
// Private constructor
LogManager() : currentLevel(ISF_LOG_INFO) {} // Default log level is INFO
public:
// Disable copy construction and assignment
LogManager(const LogManager&) = delete;
LogManager& operator=(const LogManager&) = delete;
// Get the singleton instance
static LogManager* getInstance() {
std::lock_guard<std::mutex> lock(mutex);
if (instance == nullptr) {
instance = new LogManager();
}
return instance;
}
// Set the log level
void setLogLevel(LogLevel level) {
currentLevel = level;
}
// Get the current log level
LogLevel getLogLevel() const {
return currentLevel;
}
#ifdef ANDROID
// Method for logging on the Android platform
void logAndroid(LogLevel level, const char* tag, const char* format, ...) const {
if (currentLevel == ISF_LOG_NONE || level < currentLevel)
return;
int androidLevel;
switch (level) {
case ISF_LOG_DEBUG:
androidLevel = ANDROID_LOG_DEBUG;
break;
case ISF_LOG_INFO:
androidLevel = ANDROID_LOG_INFO;
break;
case ISF_LOG_WARN:
androidLevel = ANDROID_LOG_WARN;
break;
case ISF_LOG_ERROR:
androidLevel = ANDROID_LOG_ERROR;
break;
case ISF_LOG_FATAL:
androidLevel = ANDROID_LOG_FATAL;
break;
default:
androidLevel = ANDROID_LOG_DEFAULT;
}
va_list args;
va_start(args, format);
__android_log_vprint(androidLevel, tag, format, args);
va_end(args);
// If the log level is fatal, flush the error stream and abort the program
if (level == ISF_LOG_FATAL) {
std::flush(std::cerr);
abort();
}
}
#else
// Method for standard platform logging
void logStandard(LogLevel level, const char* filename, const char* function, int line, const char* format, ...) const {
// Check whether the current level is LOG NONE or the log level is not enough to log
if (currentLevel == ISF_LOG_NONE || level < currentLevel)
return;
// Build log prefix dynamically based on available data
bool hasPrintedPrefix = false;
if (filename && strlen(filename) > 0) {
printf("[%s]", filename);
hasPrintedPrefix = true;
}
if (function && strlen(function) > 0) {
printf("[%s]", function);
hasPrintedPrefix = true;
}
if (line != -1) {
printf("[%d]", line);
hasPrintedPrefix = true;
}
// Only add colon and space if any prefix was printed
if (hasPrintedPrefix) {
printf(": ");
}
// Set text color for different log levels, but only if not on iOS
#ifndef TARGET_OS_IOS
if (level == ISF_LOG_ERROR || level == ISF_LOG_FATAL) {
printf("\033[1;31m"); // Red color for errors and fatal issues
} else if (level == ISF_LOG_WARN) {
printf("\033[1;33m"); // Yellow color for warnings
}
#endif
// Print the actual log message
va_list args;
va_start(args, format);
vprintf(format, args);
va_end(args);
// Reset text color if needed, but only if not on iOS
#ifndef TARGET_OS_IOS
if (level == ISF_LOG_ERROR || level == ISF_LOG_WARN || level == ISF_LOG_FATAL) {
printf("\033[0m"); // Reset color
}
#endif
printf("\n"); // New line after log message
// If the log level is fatal, flush the error stream and abort the program
if (level == ISF_LOG_FATAL) {
std::flush(std::cerr);
abort();
}
}
#endif
};
} // namespace inspire
#endif // INSPIRE_FACE_LOG_H

View File

@@ -13,8 +13,8 @@
#include "configurable.h"
#include "log.h"
#include "model_archive/inspire_archive.h"
#include "nexus_processor/image_processor.h"
#include "initialization_module/launch.h"
#include "image_process/nexus_processor/image_processor.h"
#include <launch.h>
#include "system.h"
namespace inspire {
@@ -51,7 +51,7 @@ public:
* @param type Type of the inference helper (default: INFER_MNN).
* @return int32_t Status of the loading and initialization process.
*/
int32_t loadData(InspireModel &model, InferenceWrapper::EngineType type = InferenceWrapper::INFER_MNN, bool dynamic = false) {
int32_t LoadData(InspireModel &model, InferenceWrapper::EngineType type = InferenceWrapper::INFER_MNN, bool dynamic = false) {
m_infer_type_ = type;
// must
pushData<int>(model.Config(), "model_index", 0);
@@ -78,7 +78,7 @@ public:
m_nn_inference_->SetNumThreads(getData<int>("threads"));
if (m_infer_type_ == InferenceWrapper::INFER_TENSORRT) {
m_nn_inference_->SetDevice(INSPIRE_LAUNCH->GetCudaDeviceId());
m_nn_inference_->SetDevice(INSPIREFACE_CONTEXT->GetCudaDeviceId());
}
#if defined(ISF_GLOBAL_INFERENCE_BACKEND_USE_MNN_CUDA) && !defined(ISF_ENABLE_RKNN)
@@ -87,7 +87,13 @@ public:
#endif
#if defined(ISF_ENABLE_APPLE_EXTENSION)
m_nn_inference_->SetSpecialBackend(INSPIRE_LAUNCH->GetGlobalCoreMLInferenceMode());
if (INSPIREFACE_CONTEXT->GetGlobalCoreMLInferenceMode() == InferenceWrapper::COREML_CPU) {
m_nn_inference_->SetSpecialBackend(InferenceWrapper::COREML_CPU);
} else if (INSPIREFACE_CONTEXT->GetGlobalCoreMLInferenceMode() == InferenceWrapper::COREML_GPU) {
m_nn_inference_->SetSpecialBackend(InferenceWrapper::COREML_GPU);
} else if (INSPIREFACE_CONTEXT->GetGlobalCoreMLInferenceMode() == InferenceWrapper::COREML_ANE) {
m_nn_inference_->SetSpecialBackend(InferenceWrapper::COREML_ANE);
}
#endif
m_output_tensor_info_list_.clear();
@@ -99,7 +105,7 @@ public:
}
int32_t ret;
if (model.loadFilePath) {
auto extensionPath = INSPIRE_LAUNCH->GetExtensionPath();
auto extensionPath = INSPIREFACE_CONTEXT->GetExtensionPath();
if (extensionPath.empty()) {
INSPIRE_LOGE("Extension path is empty");
return InferenceWrapper::WrapperError;

View File

@@ -1,23 +1,29 @@
#ifdef ISF_ENABLE_TENSORRT
#ifndef INSPIRE_CUDA_TOOLKIT_H
#define INSPIRE_CUDA_TOOLKIT_H
#include <cuda_toolkit.h>
#include <cuda_runtime_api.h>
#include <NvInfer.h>
#endif // ISF_ENABLE_TENSORRT
#include <log.h>
#include "herror.h"
#include <herror.h>
namespace inspire {
inline static int32_t GetCudaDeviceCount(int32_t *device_count) {
int32_t INSPIRE_API_EXPORT GetCudaDeviceCount(int32_t *device_count) {
#ifdef ISF_ENABLE_TENSORRT
cudaError_t error = cudaGetDeviceCount(device_count);
if (error != cudaSuccess) {
INSPIRE_LOGE("CUDA error: %s", cudaGetErrorString(error));
return HERR_DEVICE_CUDA_UNKNOWN_ERROR;
}
return HSUCCEED;
#else
*device_count = 0;
return HERR_DEVICE_CUDA_NOT_SUPPORT;
#endif
}
inline static int32_t CheckCudaUsability(int32_t *is_support) {
int32_t INSPIRE_API_EXPORT CheckCudaUsability(int32_t *is_support) {
#ifdef ISF_ENABLE_TENSORRT
int device_count;
auto ret = GetCudaDeviceCount(&device_count);
if (ret != HSUCCEED) {
@@ -30,9 +36,14 @@ inline static int32_t CheckCudaUsability(int32_t *is_support) {
}
*is_support = device_count > 0;
return HSUCCEED;
#else
*is_support = 0;
return HERR_DEVICE_CUDA_NOT_SUPPORT;
#endif
}
inline static int32_t _PrintCudaDeviceInfo() {
int32_t INSPIRE_API_EXPORT _PrintCudaDeviceInfo() {
#ifdef ISF_ENABLE_TENSORRT
try {
INSPIRE_LOGW("TensorRT version: %d.%d.%d", NV_TENSORRT_MAJOR, NV_TENSORRT_MINOR, NV_TENSORRT_PATCH);
@@ -98,16 +109,22 @@ inline static int32_t _PrintCudaDeviceInfo() {
INSPIRE_LOGE("error when printing CUDA device info: %s", e.what());
return HERR_DEVICE_CUDA_UNKNOWN_ERROR;
}
#else
INSPIRE_LOGE("CUDA/TensorRT support is not enabled");
return HERR_DEVICE_CUDA_NOT_SUPPORT;
#endif
}
inline static int32_t PrintCudaDeviceInfo() {
int32_t INSPIRE_API_EXPORT PrintCudaDeviceInfo() {
#ifdef ISF_ENABLE_TENSORRT
INSPIRE_LOGW("================================================");
auto ret = _PrintCudaDeviceInfo();
INSPIRE_LOGW("================================================");
return ret;
#else
INSPIRE_LOGE("CUDA/TensorRT support is not enabled");
return HERR_DEVICE_CUDA_NOT_SUPPORT;
#endif
}
} // namespace inspire
#endif // INSPIRE_CUDA_TOOLKIT_H
#endif // ISF_ENABLE_TENSORRT
} // namespace inspire

View File

@@ -223,6 +223,12 @@ public:
virtual int32_t Process(std::vector<OutputTensorInfo>& output_tensor_info_list) = 0;
virtual int32_t ParameterInitialization(std::vector<InputTensorInfo>& input_tensor_info_list,
std::vector<OutputTensorInfo>& output_tensor_info_list) = 0;
#ifdef BATCH_FORWARD_IMPLEMENTED
virtual int32_t PreProcessBatch(const std::vector<std::vector<InputTensorInfo>>& input_tensor_info_list) = 0;
virtual int32_t ProcessBatch(std::vector<std::vector<OutputTensorInfo>>& output_tensor_info_list) = 0;
virtual int32_t PostProcessBatch(std::vector<std::vector<OutputTensorInfo>>& output_tensor_info_list) = 0;
#endif
virtual int32_t SetSpecialBackend(SpecialBackend backend) {
special_backend_ = backend;

View File

@@ -1,4 +1,5 @@
#if INFERENCE_WRAPPER_ENABLE_TENSORRT
#include <iostream>
#include <cstdint>
#include <cstdlib>
#include <cmath>
@@ -40,7 +41,6 @@ int32_t InferenceWrapperTensorRT::Initialize(char* model_buffer, int model_size,
net_->setDevice(device_id_);
auto ret = net_->readFromBin(model_buffer, model_size);
if (ret != WrapperOk) {
std::cout << "model_size: " << model_size << std::endl;
PRINT_E("Failed to load TensorRT model\n");
return WrapperError;
}

View File

@@ -1,392 +0,0 @@
#ifndef INSPIRECV_IMAGE_PROCESS_H
#define INSPIRECV_IMAGE_PROCESS_H
#include <memory>
#include <inspirecv/inspirecv.h>
#include <MNN/ImageProcess.hpp>
#include "isf_check.h"
// using namespace inspire;
namespace inspirecv {
/**
* @brief Enum to represent rotation modes.
*/
enum ROTATION_MODE { ROTATION_0 = 0, ROTATION_90 = 1, ROTATION_180 = 2, ROTATION_270 = 3 };
/**
* @brief Enum to represent data formats.
*/
enum DATA_FORMAT { NV21 = 0, NV12 = 1, RGBA = 2, RGB = 3, BGR = 4, BGRA = 5 };
/**
* @brief A class to handle camera stream and image processing.
*/
class InspireImageProcess {
public:
static InspireImageProcess Create(const uint8_t *data_buffer, int height, int width, DATA_FORMAT data_format = BGR,
ROTATION_MODE rotation_mode = ROTATION_0) {
InspireImageProcess process;
process.SetDataBuffer(data_buffer, height, width);
process.SetDataFormat(data_format);
process.SetRotationMode(rotation_mode);
return process;
}
InspireImageProcess() {
SetDataFormat(NV21);
SetDestFormat(BGR);
config_.filterType = MNN::CV::BILINEAR;
config_.wrap = MNN::CV::ZERO;
rotation_mode_ = ROTATION_0;
preview_size_ = 192;
UpdateTransformMatrix();
}
/**
* @brief Set the data buffer, height, and width of the camera stream.
*
* @param data_buffer Pointer to the data buffer.
* @param height Height of the image.
* @param width Width of the image.
*/
void SetDataBuffer(const uint8_t *data_buffer, int height, int width) {
this->buffer_ = data_buffer;
this->height_ = height;
this->width_ = width;
preview_scale_ = preview_size_ / static_cast<float>(std::max(height, width));
UpdateTransformMatrix();
}
/**
* @brief Set the preview size.
*
* @param size Preview size.
*/
void SetPreviewSize(const int size) {
preview_size_ = size;
preview_scale_ = preview_size_ / static_cast<float>(std::max(this->height_, this->width_));
UpdateTransformMatrix();
}
void SetPreviewScale(const float scale) {
preview_scale_ = scale;
preview_size_ = static_cast<int>(preview_scale_ * std::max(this->height_, this->width_));
UpdateTransformMatrix();
}
/**
* @brief Set the rotation mode.
*
* @param mode Rotation mode (e.g., ROTATION_0, ROTATION_90).
*/
void SetRotationMode(ROTATION_MODE mode) {
rotation_mode_ = mode;
UpdateTransformMatrix();
}
/**
* @brief Set the data format.
*
* @param data_format Data format (e.g., NV21, RGBA).
*/
void SetDataFormat(DATA_FORMAT data_format) {
if (data_format == NV21) {
config_.sourceFormat = MNN::CV::YUV_NV21;
}
if (data_format == NV12) {
config_.sourceFormat = MNN::CV::YUV_NV12;
}
if (data_format == RGBA) {
config_.sourceFormat = MNN::CV::RGBA;
}
if (data_format == RGB) {
config_.sourceFormat = MNN::CV::RGB;
}
if (data_format == BGR) {
config_.sourceFormat = MNN::CV::BGR;
}
if (data_format == BGRA) {
config_.sourceFormat = MNN::CV::BGRA;
}
}
/**
* @brief Set the destination format.
*
* @param data_format Data format (e.g., NV21, RGBA).
*/
void SetDestFormat(DATA_FORMAT data_format) {
if (data_format == NV21) {
config_.destFormat = MNN::CV::YUV_NV21;
}
if (data_format == NV12) {
config_.destFormat = MNN::CV::YUV_NV12;
}
if (data_format == RGBA) {
config_.destFormat = MNN::CV::RGBA;
}
if (data_format == RGB) {
config_.destFormat = MNN::CV::RGB;
}
if (data_format == BGR) {
config_.destFormat = MNN::CV::BGR;
}
if (data_format == BGRA) {
config_.destFormat = MNN::CV::BGRA;
}
}
/**
* @brief Get an affine-transformed image.
*
* @param affine_matrix Affine transformation matrix.
* @param width_out Width of the output image.
* @param height_out Height of the output image.
* @return cv::Mat Affine-transformed image.
*/
inspirecv::Image ExecuteImageAffineProcessing(inspirecv::TransformMatrix &affine_matrix, const int width_out, const int height_out) const {
int sw = width_;
int sh = height_;
int rot_sw = sw;
int rot_sh = sh;
MNN::CV::Matrix tr;
std::vector<float> tr_cv({1, 0, 0, 0, 1, 0, 0, 0, 1});
memcpy(tr_cv.data(), affine_matrix.Squeeze().data(), sizeof(float) * 6);
tr.set9(tr_cv.data());
MNN::CV::Matrix tr_inv;
tr.invert(&tr_inv);
std::shared_ptr<MNN::CV::ImageProcess> process(MNN::CV::ImageProcess::create(config_));
process->setMatrix(tr_inv);
auto img_out = inspirecv::Image::Create(width_out, height_out, 3);
std::shared_ptr<MNN::Tensor> tensor(MNN::Tensor::create<uint8_t>(std::vector<int>{1, height_out, width_out, 3}, (uint8_t *)img_out.Data()));
auto ret = process->convert(buffer_, sw, sh, 0, tensor.get());
INSPIREFACE_CHECK_MSG(ret == MNN::ErrorCode::NO_ERROR, "ImageProcess::convert failed");
return img_out;
}
/**
* @brief Get a preview image with optional rotation.
*
* @param with_rotation True if rotation is applied, false otherwise.
* @return cv::Mat Preview image.
*/
inspirecv::Image ExecutePreviewImageProcessing(bool with_rotation) {
return ExecuteImageScaleProcessing(preview_scale_, with_rotation);
}
/**
* @brief Get the preview scale.
*
* @return float Preview scale.
*/
float GetPreviewScale() {
return preview_scale_;
}
/**
* @brief Execute image scale processing.
*
* @param scale Scale factor.
* @param with_rotation True if rotation is applied, false otherwise.
* @return inspirecv::Image Scaled image.
*/
inspirecv::Image ExecuteImageScaleProcessing(const float scale, bool with_rotation) {
int sw = width_;
int sh = height_;
int rot_sw = sw;
int rot_sh = sh;
// MNN::CV::Matrix tr;
std::shared_ptr<MNN::CV::ImageProcess> process(MNN::CV::ImageProcess::create(config_));
if (rotation_mode_ == ROTATION_270 && with_rotation) {
float srcPoints[] = {
0.0f, 0.0f, 0.0f, (float)(height_ - 1), (float)(width_ - 1), 0.0f, (float)(width_ - 1), (float)(height_ - 1),
};
float dstPoints[] = {(float)(height_ * scale - 1), 0.0f, 0.0f, 0.0f, (float)(height_ * scale - 1), (float)(width_ * scale - 1), 0.0f,
(float)(width_ * scale - 1)};
tr_.setPolyToPoly((MNN::CV::Point *)dstPoints, (MNN::CV::Point *)srcPoints, 4);
process->setMatrix(tr_);
int scaled_height = static_cast<int>(width_ * scale);
int scaled_width = static_cast<int>(height_ * scale);
inspirecv::Image img_out(scaled_width, scaled_height, 3);
std::shared_ptr<MNN::Tensor> tensor(
MNN::Tensor::create<uint8_t>(std::vector<int>{1, scaled_height, scaled_width, 3}, (uint8_t *)img_out.Data()));
auto ret = process->convert(buffer_, sw, sh, 0, tensor.get());
INSPIREFACE_CHECK_MSG(ret == MNN::ErrorCode::NO_ERROR, "ImageProcess::convert failed");
return img_out;
} else if (rotation_mode_ == ROTATION_90 && with_rotation) {
float srcPoints[] = {
0.0f, 0.0f, 0.0f, (float)(height_ - 1), (float)(width_ - 1), 0.0f, (float)(width_ - 1), (float)(height_ - 1),
};
float dstPoints[] = {
0.0f, (float)(width_ * scale - 1), (float)(height_ * scale - 1), (float)(width_ * scale - 1), 0.0f, 0.0f, (float)(height_ * scale - 1),
0.0f,
};
tr_.setPolyToPoly((MNN::CV::Point *)dstPoints, (MNN::CV::Point *)srcPoints, 4);
process->setMatrix(tr_);
int scaled_height = static_cast<int>(width_ * scale);
int scaled_width = static_cast<int>(height_ * scale);
inspirecv::Image img_out(scaled_width, scaled_height, 3);
std::shared_ptr<MNN::Tensor> tensor(
MNN::Tensor::create<uint8_t>(std::vector<int>{1, scaled_height, scaled_width, 3}, (uint8_t *)img_out.Data()));
auto ret = process->convert(buffer_, sw, sh, 0, tensor.get());
INSPIREFACE_CHECK_MSG(ret == MNN::ErrorCode::NO_ERROR, "ImageProcess::convert failed");
return img_out;
} else if (rotation_mode_ == ROTATION_180 && with_rotation) {
float srcPoints[] = {
0.0f, 0.0f, 0.0f, (float)(height_ - 1), (float)(width_ - 1), 0.0f, (float)(width_ - 1), (float)(height_ - 1),
};
float dstPoints[] = {
(float)(width_ * scale - 1),
(float)(height_ * scale - 1),
(float)(width_ * scale - 1),
0.0f,
0.0f,
(float)(height_ * scale - 1),
0.0f,
0.0f,
};
tr_.setPolyToPoly((MNN::CV::Point *)dstPoints, (MNN::CV::Point *)srcPoints, 4);
process->setMatrix(tr_);
int scaled_height = static_cast<int>(height_ * scale);
int scaled_width = static_cast<int>(width_ * scale);
inspirecv::Image img_out(scaled_width, scaled_height, 3);
std::shared_ptr<MNN::Tensor> tensor(
MNN::Tensor::create<uint8_t>(std::vector<int>{1, scaled_height, scaled_width, 3}, (uint8_t *)img_out.Data()));
auto ret = process->convert(buffer_, sw, sh, 0, tensor.get());
INSPIREFACE_CHECK_MSG(ret == MNN::ErrorCode::NO_ERROR, "ImageProcess::convert failed");
return img_out;
} else {
float srcPoints[] = {
0.0f, 0.0f, 0.0f, (float)(height_ - 1), (float)(width_ - 1), 0.0f, (float)(width_ - 1), (float)(height_ - 1),
};
float dstPoints[] = {
0.0f,
0.0f,
0.0f,
(float)(height_ * scale - 1),
(float)(width_ * scale - 1),
0.0f,
(float)(width_ * scale - 1),
(float)(height_ * scale - 1),
};
tr_.setPolyToPoly((MNN::CV::Point *)dstPoints, (MNN::CV::Point *)srcPoints, 4);
process->setMatrix(tr_);
int scaled_height = static_cast<int>(height_ * scale);
int scaled_width = static_cast<int>(width_ * scale);
inspirecv::Image img_out(scaled_width, scaled_height, 3);
std::shared_ptr<MNN::Tensor> tensor(
MNN::Tensor::create<uint8_t>(std::vector<int>{1, scaled_height, scaled_width, 3}, (uint8_t *)img_out.Data()));
auto ret = process->convert(buffer_, sw, sh, 0, tensor.get());
INSPIREFACE_CHECK_MSG(ret == MNN::ErrorCode::NO_ERROR, "ImageProcess::convert failed");
return img_out;
}
}
inspirecv::TransformMatrix GetAffineMatrix() const {
auto affine_matrix = inspirecv::TransformMatrix::Create();
affine_matrix[0] = tr_[0];
affine_matrix[1] = tr_[1];
affine_matrix[2] = tr_[2];
affine_matrix[3] = tr_[3];
affine_matrix[4] = tr_[4];
affine_matrix[5] = tr_[5];
return affine_matrix;
}
/**
* @brief Get the height of the camera stream image.
*
* @return int Height.
*/
int GetHeight() const {
return height_;
}
/**
* @brief Get the width of the camera stream image.
*
* @return int Width.
*/
int GetWidth() const {
return width_;
}
/**
* @brief Get the current rotation mode.
*
* @return ROTATION_MODE Current rotation mode.
*/
ROTATION_MODE getRotationMode() const {
return rotation_mode_;
}
private:
void UpdateTransformMatrix() {
float srcPoints[] = {0.0f, 0.0f, 0.0f, (float)(height_ - 1), (float)(width_ - 1), 0.0f, (float)(width_ - 1), (float)(height_ - 1)};
float dstPoints[8];
if (rotation_mode_ == ROTATION_270) {
float points[] = {(float)(height_ * preview_scale_ - 1),
0.0f,
0.0f,
0.0f,
(float)(height_ * preview_scale_ - 1),
(float)(width_ * preview_scale_ - 1),
0.0f,
(float)(width_ * preview_scale_ - 1)};
memcpy(dstPoints, points, sizeof(points));
} else if (rotation_mode_ == ROTATION_90) {
float points[] = {0.0f,
(float)(width_ * preview_scale_ - 1),
(float)(height_ * preview_scale_ - 1),
(float)(width_ * preview_scale_ - 1),
0.0f,
0.0f,
(float)(height_ * preview_scale_ - 1),
0.0f};
memcpy(dstPoints, points, sizeof(points));
} else if (rotation_mode_ == ROTATION_180) {
float points[] = {(float)(width_ * preview_scale_ - 1),
(float)(height_ * preview_scale_ - 1),
(float)(width_ * preview_scale_ - 1),
0.0f,
0.0f,
(float)(height_ * preview_scale_ - 1),
0.0f,
0.0f};
memcpy(dstPoints, points, sizeof(points));
} else { // ROTATION_0
float points[] = {0.0f,
0.0f,
0.0f,
(float)(height_ * preview_scale_ - 1),
(float)(width_ * preview_scale_ - 1),
0.0f,
(float)(width_ * preview_scale_ - 1),
(float)(height_ * preview_scale_ - 1)};
memcpy(dstPoints, points, sizeof(points));
}
tr_.setPolyToPoly((MNN::CV::Point *)dstPoints, (MNN::CV::Point *)srcPoints, 4);
}
private:
const uint8_t *buffer_; ///< Pointer to the data buffer.
int buffer_size_; ///< Size of the data buffer.
std::vector<float> rotation_matrix; ///< Rotation matrix.
int height_; ///< Height of the camera stream image.
int width_; ///< Width of the camera stream image.
float preview_scale_; ///< Scaling factor for the preview image.
int preview_size_; ///< Size of the preview image.
MNN::CV::Matrix tr_; ///< Affine transformation matrix.
ROTATION_MODE rotation_mode_; ///< Current rotation mode.
MNN::CV::ImageProcess::Config config_; ///< Configuration for image processing.
std::shared_ptr<MNN::CV::ImageProcess> process_; ///< Image processing instance.
};
} // namespace inspirecv
#endif // INSPIRECV_IMAGE_PROCESS_H

View File

@@ -10,7 +10,9 @@
#include "inspire_model/inspire_model.h"
#include "yaml-cpp/yaml.h"
#include "fstream"
#include "recognition_module/similarity_converter.h"
#include "similarity_converter.h"
#include "launch.h"
#include "track_module/landmark/landmark_param.h"
namespace inspire {
@@ -110,6 +112,17 @@ public:
return m_archive_->GetFileContent(filename);
}
const std::vector<int>& GetFaceDetectPixelList() const {
return m_face_detect_pixel_list_;
}
const std::vector<std::string>& GetFaceDetectModelList() const {
return m_face_detect_model_list_;
}
const std::shared_ptr<LandmarkParam>& GetLandmarkParam() const {
return m_landmark_param_;
}
private:
int32_t loadManifestFile() {
if (m_archive_->QueryLoadStatus() == SARC_SUCCESS) {
@@ -156,10 +169,29 @@ private:
config.middleScore, config.steepness, config.outputMin, config.outputMax);
SIMILARITY_CONVERTER_SET_RECOMMENDED_COSINE_THRESHOLD(config.threshold);
}
// Load face detect model
if (m_config_["face_detect_pixel_list"] && m_config_["face_detect_model_list"]) {
auto node_face_detect_pixel_list = m_config_["face_detect_pixel_list"];
for (std::size_t i = 0; i < node_face_detect_pixel_list.size(); ++i) {
m_face_detect_pixel_list_.push_back(node_face_detect_pixel_list[i].as<int>());
}
auto node_face_detect_model_list = m_config_["face_detect_model_list"];
for (std::size_t i = 0; i < node_face_detect_model_list.size(); ++i) {
m_face_detect_model_list_.push_back(node_face_detect_model_list[i].as<std::string>());
}
if (m_face_detect_pixel_list_.size() != m_face_detect_model_list_.size()) {
return FORMAT_ERROR;
}
} else {
m_face_detect_pixel_list_ = {160, 320, 640};
m_face_detect_model_list_ = {"face_detect_160", "face_detect_320", "face_detect_640"};
}
m_landmark_param_ = std::make_shared<LandmarkParam>(m_config_["landmark_table"]);
}
return 0;
}
private:
std::shared_ptr<CoreArchive> m_archive_;
YAML::Node m_config_;
@@ -172,6 +204,11 @@ private:
std::string m_version_;
std::string m_major_;
std::string m_release_time_;
std::vector<int> m_face_detect_pixel_list_;
std::vector<std::string> m_face_detect_model_list_;
std::shared_ptr<LandmarkParam> m_landmark_param_;
};
} // namespace inspire

View File

@@ -0,0 +1,111 @@
#include "spend_timer.h"
#include <ostream>
#include <sstream>
#include "log.h"
#if defined(_MSC_VER)
#include <chrono> // NOLINT
#else
#include <sys/time.h>
#endif
namespace inspire {
#if defined(_MSC_VER)
uint64_t INSPIRE_API_EXPORT _now() {
return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}
#else
uint64_t INSPIRE_API_EXPORT _now() {
struct timeval tv;
gettimeofday(&tv, nullptr);
return static_cast<uint64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
}
#endif // defined(_MSC_VER)
int SpendTimer::is_enable = true;
SpendTimer::SpendTimer() {
Reset();
}
SpendTimer::SpendTimer(const std::string &name) {
name_ = name;
Reset();
}
void SpendTimer::Start() {
start_ = _now();
}
void SpendTimer::Stop() {
stop_ = _now();
uint64_t d = stop_ - start_;
total_ += d;
++count_;
min_ = std::min(min_, d);
max_ = std::max(max_, d);
}
void SpendTimer::Reset() {
start_ = 0;
stop_ = 0;
total_ = 0;
count_ = 0;
min_ = UINT64_MAX;
max_ = 0;
}
uint64_t SpendTimer::Get() const {
return stop_ - start_;
}
uint64_t SpendTimer::Average() const {
return count_ == 0 ? 0 : total_ / count_;
}
uint64_t SpendTimer::Total() const {
return total_;
}
uint64_t SpendTimer::Count() const {
return count_;
}
uint64_t SpendTimer::Min() const {
return count_ == 0 ? 0 : min_;
}
uint64_t SpendTimer::Max() const {
return max_;
}
const std::string &SpendTimer::name() const {
return name_;
}
std::string SpendTimer::Report() const {
std::stringstream ss;
if (is_enable) {
ss << "[Time(us) Total:" << Total() << " Ave:" << Average() << " Min:" << Min() << " Max:" << Max() << " Count:" << Count() << " " << name_
<< "]";
} else {
ss << "Timer Disabled.";
}
return ss.str();
}
void SpendTimer::Disable() {
is_enable = false;
}
std::ostream &operator<<(std::ostream &os, const SpendTimer &timer) {
os << timer.Report();
return os;
}
} // namespace inspire

View File

@@ -7,9 +7,9 @@
#include "log.h"
#include "track_module/landmark/face_landmark_adapt.h"
#include "track_module/landmark/landmark_param.h"
#include "recognition_module/dest_const.h"
#include "herror.h"
#include "liveness/order_of_hyper_landmark.h"
namespace inspire {
@@ -31,7 +31,7 @@ FacePipelineModule::FacePipelineModule(InspireArchive &archive, bool enableLiven
INSPIRE_LOGE("InitAgePredict error.");
}
}
m_landmark_param_ = archive.GetLandmarkParam();
// Initialize the mask detection model
if (m_enable_mask_detect_) {
InspireModel maskModel;
@@ -73,9 +73,11 @@ FacePipelineModule::FacePipelineModule(InspireArchive &archive, bool enableLiven
}
}
int32_t FacePipelineModule::Process(inspirecv::InspireImageProcess &processor, const HyperFaceData &face, FaceProcessFunctionOption proc) {
int32_t FacePipelineModule::Process(inspirecv::FrameProcess &processor, const FaceTrackWrap &face, FaceProcessFunctionOption proc) {
// Original image
inspirecv::Image originImage;
inspirecv::Image scaleImage;
std::vector<inspirecv::Point2f> stand_lmk;
switch (proc) {
case PROCESS_MASK: {
if (m_mask_predict_ == nullptr) {
@@ -97,8 +99,29 @@ int32_t FacePipelineModule::Process(inspirecv::InspireImageProcess &processor, c
if (m_rgb_anti_spoofing_ == nullptr) {
return HERR_SESS_PIPELINE_FAILURE; // uninitialized
}
// New scheme: padding differences cause errors in inference results
// inspirecv::TransformMatrix rotation_mode_affine = processor.GetAffineMatrix();
// if (stand_lmk.empty()) {
// std::vector<inspirecv::Point2f> lmk;
// for (const auto &p : face.densityLandmark) {
// lmk.emplace_back(p.x, p.y);
// }
// stand_lmk = inspirecv::ApplyTransformToPoints(lmk, rotation_mode_affine.GetInverse());
// }
// auto rect_face = inspirecv::MinBoundingRect(stand_lmk);
// auto rect_pts = rect_face.Square(2.7f).As<float>().ToFourVertices();
// std::vector<inspirecv::Point2f> dst_pts = {{0, 0}, {112, 0}, {112, 112}, {0, 112}};
// std::vector<inspirecv::Point2f> camera_pts = inspirecv::ApplyTransformToPoints(rect_pts, rotation_mode_affine);
// auto affine = inspirecv::SimilarityTransformEstimate(camera_pts, dst_pts);
// auto image_affine = processor.ExecuteImageAffineProcessing(affine, 112, 112);
// image_affine.Write("liveness_affine.jpg");
if (originImage.Empty()) {
// This is a poor approach that impacts performance,
// but in order to capture clearer images and improve liveness detection accuracy,
// we have to keep it.
originImage = processor.ExecuteImageScaleProcessing(1.0, true);
}
inspirecv::Rect2i oriRect(face.rect.x, face.rect.y, face.rect.width, face.rect.height);
@@ -106,6 +129,7 @@ int32_t FacePipelineModule::Process(inspirecv::InspireImageProcess &processor, c
auto crop = originImage.Crop(rect);
auto score = (*m_rgb_anti_spoofing_)(crop);
// crop.Show();
// crop.Resize(112, 112).Write("liveness.jpg");
faceLivenessCache = score;
break;
}
@@ -113,61 +137,37 @@ int32_t FacePipelineModule::Process(inspirecv::InspireImageProcess &processor, c
if (m_blink_predict_ == nullptr) {
return HERR_SESS_PIPELINE_FAILURE; // uninitialized
}
if (originImage.Empty()) {
originImage = processor.ExecuteImageScaleProcessing(1.0, true);
}
std::vector<std::vector<int>> order_list = {HLMK_LEFT_EYE_POINTS_INDEX, HLMK_RIGHT_EYE_POINTS_INDEX};
std::vector<std::vector<int>> order_list = {m_landmark_param_->semantic_index.left_eye_region, m_landmark_param_->semantic_index.right_eye_region};
eyesStatusCache = {0, 0};
inspirecv::Point2f left_eye = inspirecv::Point2f(face.keyPoints[0].x, face.keyPoints[0].y);
inspirecv::Point2f right_eye = inspirecv::Point2f(face.keyPoints[1].x, face.keyPoints[1].y);
std::vector<inspirecv::Point2f> eyes = {left_eye, right_eye};
auto new_eyes_points = inspirecv::ApplyTransformToPoints(eyes, processor.GetAffineMatrix().GetInverse());
// Get affine matrix
inspirecv::TransformMatrix rotation_mode_affine = processor.GetAffineMatrix();
// Get stand landmark
if (stand_lmk.empty()) {
std::vector<inspirecv::Point2f> lmk;
for (const auto &p : face.densityLandmark) {
lmk.emplace_back(p.x, p.y);
}
stand_lmk = inspirecv::ApplyTransformToPoints(lmk, rotation_mode_affine.GetInverse());
}
for (size_t i = 0; i < order_list.size(); i++) {
const auto &index = order_list[i];
std::vector<inspirecv::Point2i> points;
for (const auto &idx : index) {
points.emplace_back(face.densityLandmark[idx].x, face.densityLandmark[idx].y);
points.emplace_back(stand_lmk[idx].GetX(), stand_lmk[idx].GetY());
}
auto rect = inspirecv::MinBoundingRect(points);
auto mat = processor.GetAffineMatrix();
auto new_rect = inspirecv::ApplyTransformToRect(rect, mat.GetInverse()).Square(1.3f);
// Use more accurate 5 key point calibration
auto cx = new_eyes_points[i].GetX();
auto cy = new_eyes_points[i].GetY();
new_rect.SetX(cx - new_rect.GetWidth() / 2);
new_rect.SetY(cy - new_rect.GetHeight() / 2);
auto rect_eye = inspirecv::MinBoundingRect(points).Square(1.5f);
auto rect_pts_eye = rect_eye.As<float>().ToFourVertices();
std::vector<inspirecv::Point2f> dst_pts_eye = {{0, 0}, {64, 0}, {64, 64}, {0, 64}};
std::vector<inspirecv::Point2f> camera_pts_eye = inspirecv::ApplyTransformToPoints(rect_pts_eye, rotation_mode_affine);
// Ensure rect stays within image bounds while maintaining aspect ratio
float originalAspectRatio = new_rect.GetWidth() / new_rect.GetHeight();
// Adjust position and size to fit within image bounds
if (new_rect.GetX() < 0) {
new_rect.SetWidth(new_rect.GetWidth() + new_rect.GetX()); // Reduce width by overflow amount
new_rect.SetX(0);
}
if (new_rect.GetY() < 0) {
new_rect.SetHeight(new_rect.GetHeight() + new_rect.GetY()); // Reduce height by overflow amount
new_rect.SetY(0);
}
float rightOverflow = (new_rect.GetX() + new_rect.GetWidth()) - originImage.Width();
if (rightOverflow > 0) {
new_rect.SetWidth(new_rect.GetWidth() - rightOverflow);
}
float bottomOverflow = (new_rect.GetY() + new_rect.GetHeight()) - originImage.Height();
if (bottomOverflow > 0) {
new_rect.SetHeight(new_rect.GetHeight() - bottomOverflow);
}
// Maintain minimum size (e.g., 20x20 ixels)
const float minSize = 20.0f;
if (new_rect.GetWidth() < minSize || new_rect.GetHeight() < minSize) {
continue; // Skip this eye if the crop region is too small
}
auto crop = originImage.Crop(new_rect);
auto score = (*m_blink_predict_)(crop);
auto affine_eye = inspirecv::SimilarityTransformEstimate(camera_pts_eye, dst_pts_eye);
auto eye_affine = processor.ExecuteImageAffineProcessing(affine_eye, 64, 64);
// eye_affine.Write("eye_"+std::to_string(i)+".jpg");
// auto crop = originImage.Crop(new_rect);
auto score = (*m_blink_predict_)(eye_affine);
eyesStatusCache[i] = score;
}
break;
@@ -190,12 +190,12 @@ int32_t FacePipelineModule::Process(inspirecv::InspireImageProcess &processor, c
return HSUCCEED;
}
int32_t FacePipelineModule::Process(inspirecv::InspireImageProcess &processor, FaceObjectInternal &face) {
int32_t FacePipelineModule::Process(inspirecv::FrameProcess &processor, FaceObjectInternal &face) {
// In the tracking state, the count meets the requirements or the pipeline is executed in the detection state
auto lmk = face.keyPointFive;
std::vector<inspirecv::Point2f> lmk_5 = {lmk[FaceLandmarkAdapt::LEFT_EYE_CENTER], lmk[FaceLandmarkAdapt::RIGHT_EYE_CENTER],
lmk[FaceLandmarkAdapt::NOSE_CORNER], lmk[FaceLandmarkAdapt::MOUTH_LEFT_CORNER],
lmk[FaceLandmarkAdapt::MOUTH_RIGHT_CORNER]};
std::vector<inspirecv::Point2f> lmk_5 = {lmk[m_landmark_param_->semantic_index.left_eye_center], lmk[m_landmark_param_->semantic_index.right_eye_center],
lmk[m_landmark_param_->semantic_index.nose_corner], lmk[m_landmark_param_->semantic_index.mouth_left_corner],
lmk[m_landmark_param_->semantic_index.mouth_right_corner]};
auto trans = inspirecv::SimilarityTransformEstimateUmeyama(SIMILARITY_TRANSFORM_DEST, lmk_5);
auto crop = processor.ExecuteImageAffineProcessing(trans, FACE_CROP_SIZE, FACE_CROP_SIZE);
if (m_mask_predict_ != nullptr) {
@@ -225,7 +225,7 @@ int32_t FacePipelineModule::Process(inspirecv::InspireImageProcess &processor, F
int32_t FacePipelineModule::InitFaceAttributePredict(InspireModel &model) {
m_attribute_predict_ = std::make_shared<FaceAttributePredictAdapt>();
auto ret = m_attribute_predict_->loadData(model, model.modelType);
auto ret = m_attribute_predict_->LoadData(model, model.modelType);
if (ret != InferenceWrapper::WrapperOk) {
return HERR_ARCHIVE_LOAD_FAILURE;
}
@@ -234,7 +234,7 @@ int32_t FacePipelineModule::InitFaceAttributePredict(InspireModel &model) {
int32_t FacePipelineModule::InitMaskPredict(InspireModel &model) {
m_mask_predict_ = std::make_shared<MaskPredictAdapt>();
auto ret = m_mask_predict_->loadData(model, model.modelType);
auto ret = m_mask_predict_->LoadData(model, model.modelType);
if (ret != InferenceWrapper::WrapperOk) {
return HERR_ARCHIVE_LOAD_FAILURE;
}
@@ -248,7 +248,7 @@ int32_t FacePipelineModule::InitRBGAntiSpoofing(InspireModel &model) {
#else
m_rgb_anti_spoofing_ = std::make_shared<RBGAntiSpoofingAdapt>(input_size[0]);
#endif
auto ret = m_rgb_anti_spoofing_->loadData(model, model.modelType);
auto ret = m_rgb_anti_spoofing_->LoadData(model, model.modelType);
if (ret != InferenceWrapper::WrapperOk) {
return HERR_ARCHIVE_LOAD_FAILURE;
}
@@ -257,7 +257,7 @@ int32_t FacePipelineModule::InitRBGAntiSpoofing(InspireModel &model) {
int32_t FacePipelineModule::InitBlinkFromLivenessInteraction(InspireModel &model) {
m_blink_predict_ = std::make_shared<BlinkPredictAdapt>();
auto ret = m_blink_predict_->loadData(model, model.modelType);
auto ret = m_blink_predict_->LoadData(model, model.modelType);
if (ret != InferenceWrapper::WrapperOk) {
return HERR_ARCHIVE_LOAD_FAILURE;
}

View File

@@ -6,15 +6,15 @@
#ifndef INSPIRE_FACE_PIPELINE_MODULE_H
#define INSPIRE_FACE_PIPELINE_MODULE_H
#include "middleware/inspirecv_image_process.h"
#include "frame_process.h"
#include "common/face_info/face_object_internal.h"
#include "attribute/face_attribute_adapt.h"
#include "attribute/mask_predict_adapt.h"
#include "liveness/rgb_anti_spoofing_adapt.h"
#include "liveness/blink_predict_adapt.h"
#include "middleware/model_archive/inspire_archive.h"
#include "common/face_data/face_data_type.h"
#include "face_warpper.h"
#include "track_module/landmark/landmark_param.h"
namespace inspire {
/**
@@ -56,17 +56,17 @@ public:
* @param face FaceObject representing the detected face.
* @return int32_t Status code indicating success (0) or failure.
*/
int32_t Process(inspirecv::InspireImageProcess &processor, FaceObjectInternal &face);
int32_t Process(inspirecv::FrameProcess &processor, FaceObjectInternal &face);
/**
* @brief Processes a face using the specified FaceProcessFunction.
*
* @param image CameraStream instance containing the image.
* @param face HyperFaceData representing the detected face.
* @param face FaceTrackWrap representing the detected face.
* @param proc The FaceProcessFunction to apply to the face.
* @return int32_t Status code indicating success (0) or failure.
*/
int32_t Process(inspirecv::InspireImageProcess &processor, const HyperFaceData &face, FaceProcessFunctionOption proc);
int32_t Process(inspirecv::FrameProcess &processor, const FaceTrackWrap &face, FaceProcessFunctionOption proc);
/**
* @brief Get Rgb AntiSpoofing module
@@ -125,6 +125,7 @@ private:
std::shared_ptr<MaskPredictAdapt> m_mask_predict_; ///< Pointer to MaskPredict instance.
std::shared_ptr<RBGAntiSpoofingAdapt> m_rgb_anti_spoofing_; ///< Pointer to RBGAntiSpoofing instance.
std::shared_ptr<BlinkPredictAdapt> m_blink_predict_; ///< Pointer to Blink predict instance.
std::shared_ptr<LandmarkParam> m_landmark_param_; ///< Pointer to LandmarkParam instance.
public:
float faceMaskCache; ///< Cache for face mask detection result.

View File

@@ -96,6 +96,7 @@ JNIEXPORT jobject INSPIRE_FACE_JNI(InspireFace_CreateSession)(JNIEnv *env, jobje
jfieldID enableFaceQualityField = env->GetFieldID(customParamClass, "enableFaceQuality", "I");
jfieldID enableFaceAttributeField = env->GetFieldID(customParamClass, "enableFaceAttribute", "I");
jfieldID enableInteractionLivenessField = env->GetFieldID(customParamClass, "enableInteractionLiveness", "I");
jfieldID enableFacePoseField = env->GetFieldID(customParamClass, "enableFacePose", "I");
// Create HFSessionCustomParameter struct
HFSessionCustomParameter parameter;
@@ -106,6 +107,7 @@ JNIEXPORT jobject INSPIRE_FACE_JNI(InspireFace_CreateSession)(JNIEnv *env, jobje
parameter.enable_face_quality = env->GetIntField(customParameter, enableFaceQualityField);
parameter.enable_face_attribute = env->GetIntField(customParameter, enableFaceAttributeField);
parameter.enable_interaction_liveness = env->GetIntField(customParameter, enableInteractionLivenessField);
parameter.enable_face_pose = env->GetIntField(customParameter, enableFacePoseField);
// Create session
HFSession handle;
@@ -164,6 +166,7 @@ JNIEXPORT jobject INSPIRE_FACE_JNI(InspireFace_CreateImageStreamFromBitmap)(JNIE
return nullptr;
}
if (AndroidBitmap_lockPixels(env, bitmap, &pixels) < 0) {
AndroidBitmap_unlockPixels(env, bitmap);
INSPIRE_LOGE("Failed to lock pixels");
return nullptr;
}
@@ -236,6 +239,7 @@ JNIEXPORT jobject INSPIRE_FACE_JNI(InspireFace_CreateImageStreamFromByteBuffer)(
jfieldID streamHandleField = env->GetFieldID(streamClass, "handle", "J");
jobject imageStreamObj = env->NewObject(streamClass, constructor);
env->SetLongField(imageStreamObj, streamHandleField, (jlong)streamHandle);
env->ReleaseByteArrayElements(data, (jbyte *)buf, JNI_ABORT);
return imageStreamObj;
}
@@ -365,6 +369,9 @@ JNIEXPORT jobject INSPIRE_FACE_JNI(InspireFace_ExecuteFaceTrack)(JNIEnv *env, jo
env->SetLongField(token, tokenHandleField, (jlong)results.tokens[i].data);
env->SetIntField(token, sizeField, results.tokens[i].size);
env->SetObjectArrayElement(tokenArray, i, token);
env->DeleteLocalRef(rect);
env->DeleteLocalRef(angle);
env->DeleteLocalRef(token);
}
// Set arrays to MultipleFaceData
@@ -1311,7 +1318,7 @@ JNIEXPORT jboolean INSPIRE_FACE_JNI(InspireFace_MultipleFacePipelineProcess)(JNI
jfieldID enableFaceQualityField = env->GetFieldID(paramClass, "enableFaceQuality", "I");
jfieldID enableFaceAttributeField = env->GetFieldID(paramClass, "enableFaceAttribute", "I");
jfieldID enableInteractionLivenessField = env->GetFieldID(paramClass, "enableInteractionLiveness", "I");
jfieldID enableDetectModeLandmarkField = env->GetFieldID(paramClass, "enableDetectModeLandmark", "I");
jfieldID enableFacePoseField = env->GetFieldID(paramClass, "enableFacePose", "I");
// Get parameter values
HFSessionCustomParameter customParam;
customParam.enable_recognition = env->GetIntField(parameter, enableRecognitionField);
@@ -1321,7 +1328,7 @@ JNIEXPORT jboolean INSPIRE_FACE_JNI(InspireFace_MultipleFacePipelineProcess)(JNI
customParam.enable_face_quality = env->GetIntField(parameter, enableFaceQualityField);
customParam.enable_face_attribute = env->GetIntField(parameter, enableFaceAttributeField);
customParam.enable_interaction_liveness = env->GetIntField(parameter, enableInteractionLivenessField);
customParam.enable_detect_mode_landmark = env->GetIntField(parameter, enableDetectModeLandmarkField);
customParam.enable_face_pose = env->GetIntField(parameter, enableFacePoseField);
// Call native function
HResult ret = HFMultipleFacePipelineProcess((HFSession)sessionHandle, (HFImageStream)streamHandleValue, &faceData, customParam);
@@ -1645,6 +1652,12 @@ JNIEXPORT jobject INSPIRE_FACE_JNI(InspireFace_GetFaceAttributeResult)(JNIEnv *e
if (!raceArray || !genderArray || !ageBracketArray) {
INSPIRE_LOGE("Failed to create arrays");
if (raceArray)
env->DeleteLocalRef(raceArray);
if (genderArray)
env->DeleteLocalRef(genderArray);
if (ageBracketArray)
env->DeleteLocalRef(ageBracketArray);
return nullptr;
}
@@ -1656,6 +1669,10 @@ JNIEXPORT jobject INSPIRE_FACE_JNI(InspireFace_GetFaceAttributeResult)(JNIEnv *e
env->SetObjectField(attributeObj, genderField, genderArray);
env->SetObjectField(attributeObj, ageBracketField, ageBracketArray);
env->DeleteLocalRef(raceArray);
env->DeleteLocalRef(genderArray);
env->DeleteLocalRef(ageBracketArray);
return attributeObj;
}
@@ -1689,9 +1706,8 @@ JNIEXPORT jobject INSPIRE_FACE_JNI(InspireFace_QueryInspireFaceVersion)(JNIEnv *
jfieldID majorField = env->GetFieldID(versionClass, "major", "I");
jfieldID minorField = env->GetFieldID(versionClass, "minor", "I");
jfieldID patchField = env->GetFieldID(versionClass, "patch", "I");
jfieldID infoField = env->GetFieldID(versionClass, "information", "Ljava/lang/String;");
if (!majorField || !minorField || !patchField || !infoField) {
if (!majorField || !minorField || !patchField) {
INSPIRE_LOGE("Failed to get InspireFaceVersion field IDs");
return nullptr;
}
@@ -1701,32 +1717,6 @@ JNIEXPORT jobject INSPIRE_FACE_JNI(InspireFace_QueryInspireFaceVersion)(JNIEnv *
env->SetIntField(version, minorField, versionInfo.minor);
env->SetIntField(version, patchField, versionInfo.patch);
// Get extended information
HFInspireFaceExtendedInformation extendedInfo;
HFQueryInspireFaceExtendedInformation(&extendedInfo);
// Sanitize the information string to ensure valid UTF-8
std::string sanitizedInfo;
const char *rawInfo = extendedInfo.information;
while (*rawInfo) {
unsigned char c = static_cast<unsigned char>(*rawInfo);
if (c < 0x80 || (c >= 0xC0 && c <= 0xF4)) {
// Valid UTF-8 start byte
sanitizedInfo += *rawInfo;
}
rawInfo++;
}
// Convert sanitized string to Java string
jstring infoString = env->NewStringUTF(sanitizedInfo.c_str());
if (infoString) {
env->SetObjectField(version, infoField, infoString);
} else {
// Fallback to a safe string if conversion fails
jstring fallbackString = env->NewStringUTF("Version information unavailable");
env->SetObjectField(version, infoField, fallbackString);
}
return version;
}

View File

@@ -23,13 +23,14 @@ FeatureExtractionModule::FeatureExtractionModule(InspireArchive &archive, bool e
INSPIRE_LOGE("FaceRecognition error.");
}
}
m_landmark_param_ = archive.GetLandmarkParam();
}
int32_t FeatureExtractionModule::InitExtractInteraction(InspireModel &model) {
try {
auto input_size = model.Config().get<std::vector<int>>("input_size");
m_extract_ = std::make_shared<ExtractAdapt>();
auto ret = m_extract_->loadData(model, model.modelType);
auto ret = m_extract_->LoadData(model, model.modelType);
if (ret != InferenceWrapper::WrapperOk) {
return HERR_ARCHIVE_LOAD_FAILURE;
}
@@ -45,7 +46,7 @@ int32_t FeatureExtractionModule::QueryStatus() const {
return m_status_code_;
}
int32_t FeatureExtractionModule::FaceExtract(inspirecv::InspireImageProcess &processor, const HyperFaceData &face, Embedded &embedded, float &norm,
int32_t FeatureExtractionModule::FaceExtract(inspirecv::FrameProcess &processor, const FaceTrackWrap &face, Embedded &embedded, float &norm,
bool normalize) {
if (m_extract_ == nullptr) {
return HERR_SESS_REC_EXTRACT_FAILURE;
@@ -64,16 +65,37 @@ int32_t FeatureExtractionModule::FaceExtract(inspirecv::InspireImageProcess &pro
return 0;
}
int32_t FeatureExtractionModule::FaceExtract(inspirecv::InspireImageProcess &processor, const FaceObjectInternal &face, Embedded &embedded,
float &norm, bool normalize) {
int32_t FeatureExtractionModule::FaceExtractWithAlignmentImage(inspirecv::FrameProcess &processor, Embedded &embedded, float &norm,
bool normalize) {
if (m_extract_ == nullptr) {
return HERR_SESS_REC_EXTRACT_FAILURE;
}
auto crop = processor.ExecuteImageScaleProcessing(1.0f, false);
embedded = (*m_extract_)(crop, norm, normalize);
return 0;
}
int32_t FeatureExtractionModule::FaceExtractWithAlignmentImage(const inspirecv::Image& wrapped, Embedded &embedded, float &norm,
bool normalize) {
if (m_extract_ == nullptr) {
return HERR_SESS_REC_EXTRACT_FAILURE;
}
embedded = (*m_extract_)(wrapped, norm, normalize);
return 0;
}
int32_t FeatureExtractionModule::FaceExtract(inspirecv::FrameProcess &processor, const FaceObjectInternal &face, Embedded &embedded, float &norm,
bool normalize) {
if (m_extract_ == nullptr) {
return HERR_SESS_REC_EXTRACT_FAILURE;
}
auto lmk = face.landmark_;
std::vector<inspirecv::Point2f> lmk_5 = {lmk[FaceLandmarkAdapt::LEFT_EYE_CENTER], lmk[FaceLandmarkAdapt::RIGHT_EYE_CENTER],
lmk[FaceLandmarkAdapt::NOSE_CORNER], lmk[FaceLandmarkAdapt::MOUTH_LEFT_CORNER],
lmk[FaceLandmarkAdapt::MOUTH_RIGHT_CORNER]};
std::vector<inspirecv::Point2f> lmk_5 = {lmk[m_landmark_param_->semantic_index.left_eye_center], lmk[m_landmark_param_->semantic_index.right_eye_center],
lmk[m_landmark_param_->semantic_index.nose_corner], lmk[m_landmark_param_->semantic_index.mouth_left_corner],
lmk[m_landmark_param_->semantic_index.mouth_right_corner]};
auto trans = inspirecv::SimilarityTransformEstimateUmeyama(SIMILARITY_TRANSFORM_DEST, lmk_5);
auto crop = processor.ExecuteImageAffineProcessing(trans, FACE_CROP_SIZE, FACE_CROP_SIZE);

View File

@@ -8,9 +8,9 @@
#include <mutex>
#include "extract/extract_adapt.h"
#include "common/face_info/face_object_internal.h"
#include "common/face_data/face_data_type.h"
#include "face_warpper.h"
#include "middleware/model_archive/inspire_archive.h"
#include "middleware/inspirecv_image_process.h"
#include "frame_process.h"
namespace inspire {
@@ -35,23 +35,40 @@ public:
/**
* @brief Extracts a facial feature from an image and stores it in the provided 'embedded'.
*
* @param processor inspirecv::InspireImageProcess instance containing the image.
* @param processor inspirecv::FrameProcess instance containing the image.
* @param face FaceObject representing the detected face.
* @param embedded Output parameter to store the extracted facial feature.
* @return int32_t Status code indicating success (0) or failure.
*/
int32_t FaceExtract(inspirecv::InspireImageProcess &processor, const FaceObjectInternal &face, Embedded &embedded, float &norm,
bool normalize = false);
int32_t FaceExtract(inspirecv::FrameProcess &processor, const FaceObjectInternal &face, Embedded &embedded, float &norm, bool normalize = false);
/**
* @brief Extracts a facial feature from an image and stores it in the provided 'embedded'.
*
* @param processor inspirecv::InspireImageProcess instance containing the image.
* @param face HyperFaceData representing the detected face.
* @param processor inspirecv::FrameProcess instance containing the image.
* @param face FaceTrackWrap representing the detected face.
* @param embedded Output parameter to store the extracted facial feature.
* @return int32_t Status code indicating success (0) or failure.
*/
int32_t FaceExtract(inspirecv::InspireImageProcess &processor, const HyperFaceData &face, Embedded &embedded, float &norm, bool normalize = true);
int32_t FaceExtract(inspirecv::FrameProcess &processor, const FaceTrackWrap &face, Embedded &embedded, float &norm, bool normalize = true);
/**
* @brief Extracts a facial feature from an image and stores it in the provided 'embedded'.
*
* @param processor inspirecv::FrameProcess instance containing the image.
* @param embedded Output parameter to store the extracted facial feature.
* @return int32_t Status code indicating success (0) or failure.
*/
int32_t FaceExtractWithAlignmentImage(inspirecv::FrameProcess &processor, Embedded &embedding, float &norm, bool normalize = true);
/**
* @brief Extracts a facial feature from an image and stores it in the provided 'embedding'.
*
* @param wrapped inspirecv::Image instance containing the image.
* @param embedding Output parameter to store the extracted facial feature.
* @return int32_t Status code indicating success (0) or failure.
*/
int32_t FaceExtractWithAlignmentImage(const inspirecv::Image& wrapped, Embedded &embedding, float &norm, bool normalize = true);
/**
* @brief Gets the Extract instance associated with this FaceRecognition.
@@ -71,6 +88,7 @@ private:
private:
std::shared_ptr<ExtractAdapt> m_extract_; ///< Pointer to the Extract instance.
std::shared_ptr<LandmarkParam> m_landmark_param_; ///< Pointer to the LandmarkParam instance.
int32_t m_status_code_; ///< Status code
};

View File

@@ -0,0 +1,294 @@
/**
* Created by Jingyu Yan
* @date 2024-10-01
*/
#include "launch.h"
#include "log.h"
#include "herror.h"
#include "isf_check.h"
// Include the implementation details here, hidden from public header
#include "middleware/model_archive/inspire_archive.h"
#if defined(ISF_ENABLE_RGA)
#include "image_process/nexus_processor/rga/dma_alloc.h"
#endif
#include <mutex>
#include "middleware/inference_wrapper/inference_wrapper.h"
#include "middleware/system.h"
#if defined(ISF_ENABLE_TENSORRT)
#include "cuda_toolkit.h"
#endif
#define APPLE_EXTENSION_SUFFIX ".bundle"
namespace inspire {
// Implementation class definition
class Launch::Impl {
public:
Impl() : m_load_(false), m_archive_(nullptr), m_cuda_device_id_(0), m_global_coreml_inference_mode_(InferenceWrapper::COREML_ANE) {
#if defined(ISF_ENABLE_RGA)
#if defined(ISF_RKNPU_RV1106)
m_rockchip_dma_heap_path_ = RV1106_CMA_HEAP_PATH;
#else
m_rockchip_dma_heap_path_ = DMA_HEAP_DMA32_UNCACHE_PATCH;
#endif
INSPIRE_LOGW("Rockchip dma heap configured path: %s", m_rockchip_dma_heap_path_.c_str());
#endif
m_face_detect_pixel_list_ = {160, 320, 640};
m_face_detect_model_list_ = {"face_detect_160", "face_detect_320", "face_detect_640"};
}
// Face Detection pixel size
std::vector<int32_t> m_face_detect_pixel_list_;
// Face Detection model list
std::vector<std::string> m_face_detect_model_list_;
// Static members
static std::mutex mutex_;
static std::shared_ptr<Launch> instance_;
// Data members
std::string m_rockchip_dma_heap_path_;
std::string m_extension_path_;
std::unique_ptr<InspireArchive> m_archive_;
bool m_load_;
int32_t m_cuda_device_id_;
InferenceWrapper::SpecialBackend m_global_coreml_inference_mode_;
};
// Initialize static members
std::mutex Launch::Impl::mutex_;
std::shared_ptr<Launch> Launch::Impl::instance_ = nullptr;
// Constructor implementation
Launch::Launch() : pImpl(std::make_unique<Impl>()) {}
// Destructor implementation
Launch::~Launch() = default;
std::shared_ptr<Launch> Launch::GetInstance() {
std::lock_guard<std::mutex> lock(Impl::mutex_);
if (!Impl::instance_) {
Impl::instance_ = std::shared_ptr<Launch>(new Launch());
}
return Impl::instance_;
}
InspireArchive& Launch::getMArchive() {
std::lock_guard<std::mutex> lock(pImpl->mutex_);
if (!pImpl->m_archive_) {
throw std::runtime_error("Archive not initialized");
}
return *(pImpl->m_archive_);
}
int32_t Launch::Load(const std::string& path) {
std::lock_guard<std::mutex> lock(pImpl->mutex_);
#if defined(ISF_ENABLE_TENSORRT)
int32_t support_cuda;
auto ret = CheckCudaUsability(&support_cuda);
if (ret != HSUCCEED) {
INSPIRE_LOGE("An error occurred while checking CUDA device support. Please ensure that your environment supports CUDA!");
return ret;
}
if (!support_cuda) {
INSPIRE_LOGE("Your environment does not support CUDA! Please ensure that your environment supports CUDA!");
return HERR_DEVICE_CUDA_NOT_SUPPORT;
}
#endif
INSPIREFACE_CHECK_MSG(os::IsExists(path), "The package path does not exist because the launch failed.");
#if defined(ISF_ENABLE_APPLE_EXTENSION)
BuildAppleExtensionPath(path);
#endif
if (!pImpl->m_load_) {
try {
pImpl->m_archive_ = std::make_unique<InspireArchive>();
pImpl->m_archive_->ReLoad(path);
// Update face detect pixel list and model list
pImpl->m_face_detect_pixel_list_ = pImpl->m_archive_->GetFaceDetectPixelList();
pImpl->m_face_detect_model_list_ = pImpl->m_archive_->GetFaceDetectModelList();
if (pImpl->m_archive_->QueryStatus() == SARC_SUCCESS) {
pImpl->m_load_ = true;
INSPIRE_LOGI("Successfully loaded resources");
return HSUCCEED;
} else {
pImpl->m_archive_.reset();
INSPIRE_LOGE("Failed to load resources");
return HERR_ARCHIVE_LOAD_MODEL_FAILURE;
}
} catch (const std::exception& e) {
pImpl->m_archive_.reset();
INSPIRE_LOGE("Exception during resource loading: %s", e.what());
return HERR_ARCHIVE_LOAD_MODEL_FAILURE;
}
} else {
INSPIRE_LOGW("There is no need to call launch more than once, as subsequent calls will not affect the initialization.");
return HSUCCEED;
}
}
int32_t Launch::Reload(const std::string& path) {
std::lock_guard<std::mutex> lock(pImpl->mutex_);
INSPIREFACE_CHECK_MSG(os::IsExists(path), "The package path does not exist because the launch failed.");
#if defined(ISF_ENABLE_APPLE_EXTENSION)
BuildAppleExtensionPath(path);
#endif
try {
// Clean up existing archive if it exists
if (pImpl->m_archive_) {
pImpl->m_archive_.reset();
pImpl->m_load_ = false;
}
// Create and load new archive
pImpl->m_archive_ = std::make_unique<InspireArchive>();
pImpl->m_archive_->ReLoad(path);
if (pImpl->m_archive_->QueryStatus() == SARC_SUCCESS) {
pImpl->m_load_ = true;
INSPIRE_LOGI("Successfully reloaded resources");
return HSUCCEED;
} else {
pImpl->m_archive_.reset();
INSPIRE_LOGE("Failed to reload resources");
return HERR_ARCHIVE_LOAD_MODEL_FAILURE;
}
} catch (const std::exception& e) {
pImpl->m_archive_.reset();
INSPIRE_LOGE("Exception during resource reloading: %s", e.what());
return HERR_ARCHIVE_LOAD_MODEL_FAILURE;
}
}
bool Launch::isMLoad() const {
return pImpl->m_load_;
}
void Launch::Unload() {
std::lock_guard<std::mutex> lock(pImpl->mutex_);
if (pImpl->m_load_) {
pImpl->m_archive_.reset();
pImpl->m_load_ = false;
INSPIRE_LOGI("All resources have been successfully unloaded and system is reset.");
} else {
INSPIRE_LOGW("Unload called but system was not loaded.");
}
}
void Launch::SetRockchipDmaHeapPath(const std::string& path) {
std::lock_guard<std::mutex> lock(pImpl->mutex_);
pImpl->m_rockchip_dma_heap_path_ = path;
}
std::string Launch::GetRockchipDmaHeapPath() const {
std::lock_guard<std::mutex> lock(pImpl->mutex_);
return pImpl->m_rockchip_dma_heap_path_;
}
void Launch::ConfigurationExtensionPath(const std::string& path) {
#if defined(ISF_ENABLE_APPLE_EXTENSION)
INSPIREFACE_CHECK_MSG(os::IsDir(path), "The apple extension path is not a directory, please check.");
#endif
INSPIREFACE_CHECK_MSG(os::IsExists(path), "The extension path is not exists, please check.");
pImpl->m_extension_path_ = path;
}
std::string Launch::GetExtensionPath() const {
std::lock_guard<std::mutex> lock(pImpl->mutex_);
return pImpl->m_extension_path_;
}
void Launch::SetGlobalCoreMLInferenceMode(NNInferenceBackend mode) {
std::lock_guard<std::mutex> lock(pImpl->mutex_);
if (mode == NN_INFERENCE_CPU) {
pImpl->m_global_coreml_inference_mode_ = InferenceWrapper::COREML_CPU;
} else if (mode == NN_INFERENCE_COREML_GPU) {
pImpl->m_global_coreml_inference_mode_ = InferenceWrapper::COREML_GPU;
} else if (mode == NN_INFERENCE_COREML_ANE) {
pImpl->m_global_coreml_inference_mode_ = InferenceWrapper::COREML_ANE;
} else {
INSPIRE_LOGE("Invalid CoreML inference mode");
}
if (pImpl->m_global_coreml_inference_mode_ == InferenceWrapper::COREML_CPU) {
INSPIRE_LOGW("Global CoreML Compute Units set to CPU Only.");
} else if (pImpl->m_global_coreml_inference_mode_ == InferenceWrapper::COREML_GPU) {
INSPIRE_LOGW("Global CoreML Compute Units set to CPU and GPU.");
} else if (pImpl->m_global_coreml_inference_mode_ == InferenceWrapper::COREML_ANE) {
INSPIRE_LOGW("Global CoreML Compute Units set to Auto Switch (ANE, GPU, CPU).");
}
}
Launch::NNInferenceBackend Launch::GetGlobalCoreMLInferenceMode() const {
std::lock_guard<std::mutex> lock(pImpl->mutex_);
if (pImpl->m_global_coreml_inference_mode_ == InferenceWrapper::COREML_CPU) {
return NN_INFERENCE_CPU;
} else if (pImpl->m_global_coreml_inference_mode_ == InferenceWrapper::COREML_GPU) {
return NN_INFERENCE_COREML_GPU;
} else if (pImpl->m_global_coreml_inference_mode_ == InferenceWrapper::COREML_ANE) {
return NN_INFERENCE_COREML_ANE;
} else {
INSPIRE_LOGE("Invalid CoreML inference mode");
return NN_INFERENCE_CPU;
}
}
void Launch::BuildAppleExtensionPath(const std::string& resource_path) {
std::string basename = os::Basename(resource_path);
pImpl->m_extension_path_ = os::PathJoin(os::Dirname(resource_path), basename + APPLE_EXTENSION_SUFFIX);
INSPIREFACE_CHECK_MSG(os::IsExists(pImpl->m_extension_path_), "The apple extension path is not exists, please check.");
INSPIREFACE_CHECK_MSG(os::IsDir(pImpl->m_extension_path_), "The apple extension path is not a directory, please check.");
}
void Launch::SetCudaDeviceId(int32_t device_id) {
std::lock_guard<std::mutex> lock(pImpl->mutex_);
pImpl->m_cuda_device_id_ = device_id;
}
int32_t Launch::GetCudaDeviceId() const {
std::lock_guard<std::mutex> lock(pImpl->mutex_);
return pImpl->m_cuda_device_id_;
}
void Launch::SetFaceDetectPixelList(const std::vector<int32_t>& pixel_list) {
std::lock_guard<std::mutex> lock(pImpl->mutex_);
pImpl->m_face_detect_pixel_list_ = pixel_list;
}
std::vector<int32_t> Launch::GetFaceDetectPixelList() const {
std::lock_guard<std::mutex> lock(pImpl->mutex_);
return pImpl->m_face_detect_pixel_list_;
}
void Launch::SetFaceDetectModelList(const std::vector<std::string>& model_list) {
std::lock_guard<std::mutex> lock(pImpl->mutex_);
pImpl->m_face_detect_model_list_ = model_list;
}
std::vector<std::string> Launch::GetFaceDetectModelList() const {
std::lock_guard<std::mutex> lock(pImpl->mutex_);
return pImpl->m_face_detect_model_list_;
}
void Launch::SwitchLandmarkEngine(LandmarkEngine engine) {
std::lock_guard<std::mutex> lock(pImpl->mutex_);
if (pImpl->m_archive_->QueryStatus() != SARC_SUCCESS) {
INSPIRE_LOGE("The InspireFace is not initialized, please call launch first.");
return;
}
auto landmark_param = pImpl->m_archive_->GetLandmarkParam();
bool ret = false;
if (engine == LANDMARK_HYPLMV2_0_25) {
ret = landmark_param->ReLoad("landmark");
} else if (engine == LANDMARK_HYPLMV2_0_50) {
ret = landmark_param->ReLoad("landmark_0_50");
} else if (engine == LANDMARK_INSIGHTFACE_2D106_TRACK) {
ret = landmark_param->ReLoad("landmark_insightface_2d106");
}
INSPIREFACE_CHECK_MSG(ret, "Failed to switch landmark engine");
}
} // namespace inspire

View File

@@ -35,6 +35,7 @@ private:
std::unordered_map<long, bool> sessionMap;
std::unordered_map<long, bool> streamMap;
std::unordered_map<long, bool> imageBitmapMap;
std::unordered_map<long, bool> faceFeatureMap;
// The private constructor guarantees singletons
ResourceManager() {}
@@ -106,6 +107,23 @@ public:
return false; // Release failed, possibly because the handle could not be found or was released
}
// Create and record face features
void createFaceFeature(long handle) {
std::lock_guard<std::mutex> lock(mutex);
faceFeatureMap[handle] = false; // false indicates that it is not released
}
// Release face feature
bool releaseFaceFeature(long handle) {
std::lock_guard<std::mutex> lock(mutex);
auto it = faceFeatureMap.find(handle);
if (it != faceFeatureMap.end() && !it->second) {
it->second = true; // Mark as released
return true;
}
return false; // Release failed, possibly because the handle could not be found or was released
}
// Gets a list of unreleased session handles
std::vector<long> getUnreleasedSessions() {
std::lock_guard<std::mutex> lock(mutex);
@@ -142,10 +160,24 @@ public:
return unreleasedImageBitmaps;
}
// Gets a list of unreleased face feature handles
std::vector<long> getUnreleasedFaceFeatures() {
std::lock_guard<std::mutex> lock(mutex);
std::vector<long> unreleasedFaceFeatures;
for (const auto& entry : faceFeatureMap) {
if (!entry.second) {
unreleasedFaceFeatures.push_back(entry.first);
}
}
return unreleasedFaceFeatures;
}
// Method to print resource management statistics
void printResourceStatistics() {
std::lock_guard<std::mutex> lock(mutex);
INSPIRE_LOGI("================================================================");
INSPIRE_LOGI("%-15s%-15s%-15s%-15s", "Resource Name", "Total Created", "Total Released", "Not Released");
INSPIRE_LOGI("----------------------------------------------------------------");
// Print session statistics
int totalSessionsCreated = sessionMap.size();
@@ -182,6 +214,19 @@ public:
++bitmapsNotReleased;
}
INSPIRE_LOGI("%-15s%-15d%-15d%-15d", "Bitmap", totalBitmapsCreated, totalBitmapsReleased, bitmapsNotReleased);
// Print face feature statistics
int totalFeaturesCreated = faceFeatureMap.size();
int totalFeaturesReleased = 0;
int featuresNotReleased = 0;
for (const auto& entry : faceFeatureMap) {
if (entry.second)
++totalFeaturesReleased;
if (!entry.second)
++featuresNotReleased;
}
INSPIRE_LOGI("%-15s%-15d%-15d%-15d", "FaceFeature", totalFeaturesCreated, totalFeaturesReleased, featuresNotReleased);
INSPIRE_LOGI("================================================================");
}
};

View File

@@ -0,0 +1,277 @@
#include <memory>
#include "session.h"
#include "engine/face_session.h"
#include "recognition_module/dest_const.h"
namespace inspire {
class Session::Impl {
public:
Impl() : m_face_session_(std::make_unique<FaceSession>()) {}
int32_t Configure(DetectModuleMode detect_mode, int32_t max_detect_face, CustomPipelineParameter param, int32_t detect_level_px,
int32_t track_by_detect_mode_fps) {
return m_face_session_->Configuration(detect_mode, max_detect_face, param, detect_level_px, track_by_detect_mode_fps);
}
~Impl() = default;
void SetTrackPreviewSize(int32_t preview_size) {
m_face_session_->SetTrackPreviewSize(preview_size);
}
void SetFilterMinimumFacePixelSize(int32_t min_face_pixel_size) {
m_face_session_->SetTrackFaceMinimumSize(min_face_pixel_size);
}
void SetFaceDetectThreshold(float threshold) {
m_face_session_->SetFaceDetectThreshold(threshold);
}
void SetTrackModeSmoothRatio(int32_t smooth_ratio) {
m_face_session_->SetTrackModeSmoothRatio(smooth_ratio);
}
void SetTrackModeNumSmoothCacheFrame(int32_t num_smooth_cache_frame) {
m_face_session_->SetTrackModeNumSmoothCacheFrame(num_smooth_cache_frame);
}
void SetTrackModeDetectInterval(int32_t detect_interval) {
m_face_session_->SetTrackModeDetectInterval(detect_interval);
}
int32_t FaceDetectAndTrack(inspirecv::FrameProcess& process, std::vector<FaceTrackWrap>& results) {
int32_t ret = m_face_session_->FaceDetectAndTrack(process);
if (ret < 0) {
return ret;
}
results.clear();
const auto& face_data = m_face_session_->GetDetectCache();
for (const auto& data : face_data) {
FaceTrackWrap hyper_face_data;
RunDeserializeHyperFaceData(data, hyper_face_data);
results.emplace_back(hyper_face_data);
}
return ret;
}
inspirecv::Rect2i GetFaceBoundingBox(const FaceTrackWrap& face_data) {
return inspirecv::Rect2i{face_data.rect.x, face_data.rect.y, face_data.rect.width, face_data.rect.height};
}
std::vector<inspirecv::Point2f> GetFaceDenseLandmark(const FaceTrackWrap& face_data) {
std::vector<inspirecv::Point2f> points;
for (const auto& p : face_data.densityLandmark) {
points.emplace_back(inspirecv::Point2f(p.x, p.y));
}
return points;
}
std::vector<inspirecv::Point2f> GetFaceFiveKeyPoints(const FaceTrackWrap& face_data) {
std::vector<inspirecv::Point2f> points;
for (const auto& p : face_data.keyPoints) {
points.emplace_back(inspirecv::Point2f(p.x, p.y));
}
return points;
}
int32_t FaceFeatureExtract(inspirecv::FrameProcess& process, FaceTrackWrap& data, FaceEmbedding& embedding, bool normalize) {
int32_t ret = m_face_session_->FaceFeatureExtract(process, data, normalize);
if (ret < 0) {
return ret;
}
embedding.isNormal = normalize;
embedding.embedding = m_face_session_->GetFaceFeatureCache();
embedding.norm = m_face_session_->GetFaceFeatureNormCache();
return ret;
}
int32_t FaceFeatureExtractWithAlignmentImage(inspirecv::FrameProcess& process, FaceEmbedding& embedding, bool normalize) {
int32_t ret = m_face_session_->FaceFeatureExtractWithAlignmentImage(process, embedding.embedding, embedding.norm, normalize);
if (ret < 0) {
return ret;
}
embedding.isNormal = normalize;
embedding.norm = embedding.norm;
return ret;
}
int32_t FaceFeatureExtractWithAlignmentImage(const inspirecv::Image& wrapped, FaceEmbedding& embedding, bool normalize) {
int32_t ret = m_face_session_->FaceFeatureExtractWithAlignmentImage(wrapped, embedding, embedding.norm, normalize);
if (ret < 0) {
return ret;
}
embedding.isNormal = normalize;
embedding.norm = embedding.norm;
return ret;
}
void GetFaceAlignmentImage(inspirecv::FrameProcess& process, FaceTrackWrap& data, inspirecv::Image& wrapped) {
std::vector<inspirecv::Point2f> pointsFive;
for (const auto& p : data.keyPoints) {
pointsFive.push_back(inspirecv::Point2f(p.x, p.y));
}
auto trans = inspirecv::SimilarityTransformEstimateUmeyama(SIMILARITY_TRANSFORM_DEST, pointsFive);
wrapped = process.ExecuteImageAffineProcessing(trans, FACE_CROP_SIZE, FACE_CROP_SIZE);
}
int32_t MultipleFacePipelineProcess(inspirecv::FrameProcess& process, const CustomPipelineParameter& param,
const std::vector<FaceTrackWrap>& face_data_list) {
int32_t ret = m_face_session_->FacesProcess(process, face_data_list, param);
return ret;
}
std::vector<float> GetRGBLivenessConfidence() {
return m_face_session_->GetDetConfidenceCache();
}
std::vector<float> GetFaceMaskConfidence() {
return m_face_session_->GetMaskResultsCache();
}
std::vector<float> GetFaceQualityConfidence() {
return m_face_session_->GetFaceQualityScoresResultsCache();
}
std::vector<FaceInteractionState> GetFaceInteractionState() {
auto left_eyes_confidence = m_face_session_->GetFaceInteractionLeftEyeStatusCache();
auto right_eyes_confidence = m_face_session_->GetFaceInteractionRightEyeStatusCache();
std::vector<FaceInteractionState> face_interaction_state;
for (size_t i = 0; i < left_eyes_confidence.size(); ++i) {
face_interaction_state.emplace_back(FaceInteractionState{left_eyes_confidence[i], right_eyes_confidence[i]});
}
return face_interaction_state;
}
std::vector<FaceInteractionAction> GetFaceInteractionAction() {
auto num = m_face_session_->GetFaceNormalAactionsResultCache().size();
std::vector<FaceInteractionAction> face_interaction_action;
face_interaction_action.resize(num);
for (size_t i = 0; i < num; ++i) {
face_interaction_action[i].normal = m_face_session_->GetFaceNormalAactionsResultCache()[i];
face_interaction_action[i].shake = m_face_session_->GetFaceShakeAactionsResultCache()[i];
face_interaction_action[i].jawOpen = m_face_session_->GetFaceJawOpenAactionsResultCache()[i];
face_interaction_action[i].headRaise = m_face_session_->GetFaceRaiseHeadAactionsResultCache()[i];
face_interaction_action[i].blink = m_face_session_->GetFaceBlinkAactionsResultCache()[i];
}
return face_interaction_action;
}
std::vector<FaceAttributeResult> GetFaceAttributeResult() {
auto num = m_face_session_->GetFaceNormalAactionsResultCache().size();
std::vector<FaceAttributeResult> face_attribute_result;
face_attribute_result.resize(num);
for (size_t i = 0; i < num; ++i) {
face_attribute_result[i].race = m_face_session_->GetFaceRaceResultsCache()[i];
face_attribute_result[i].gender = m_face_session_->GetFaceGenderResultsCache()[i];
face_attribute_result[i].ageBracket = m_face_session_->GetFaceAgeBracketResultsCache()[i];
}
return face_attribute_result;
}
std::unique_ptr<FaceSession> m_face_session_;
};
Session::Session() : pImpl(std::make_unique<Impl>()) {}
Session::~Session() = default;
Session::Session(Session&&) noexcept = default;
Session& Session::operator=(Session&&) noexcept = default;
Session Session::Create(DetectModuleMode detect_mode, int32_t max_detect_face, const CustomPipelineParameter& param, int32_t detect_level_px,
int32_t track_by_detect_mode_fps) {
Session session;
session.pImpl->Configure(detect_mode, max_detect_face, param, detect_level_px, track_by_detect_mode_fps);
return session;
}
void Session::SetTrackPreviewSize(int32_t preview_size) {
pImpl->SetTrackPreviewSize(preview_size);
}
void Session::SetFilterMinimumFacePixelSize(int32_t min_face_pixel_size) {
pImpl->SetFilterMinimumFacePixelSize(min_face_pixel_size);
}
void Session::SetFaceDetectThreshold(float threshold) {
pImpl->SetFaceDetectThreshold(threshold);
}
void Session::SetTrackModeSmoothRatio(int32_t smooth_ratio) {
pImpl->SetTrackModeSmoothRatio(smooth_ratio);
}
void Session::SetTrackModeNumSmoothCacheFrame(int32_t num_smooth_cache_frame) {
pImpl->SetTrackModeNumSmoothCacheFrame(num_smooth_cache_frame);
}
void Session::SetTrackModeDetectInterval(int32_t detect_interval) {
pImpl->SetTrackModeDetectInterval(detect_interval);
}
int32_t Session::FaceDetectAndTrack(inspirecv::FrameProcess& process, std::vector<FaceTrackWrap>& results) {
return pImpl->FaceDetectAndTrack(process, results);
}
inspirecv::Rect2i Session::GetFaceBoundingBox(const FaceTrackWrap& face_data) {
return pImpl->GetFaceBoundingBox(face_data);
}
std::vector<inspirecv::Point2f> Session::GetFaceDenseLandmark(const FaceTrackWrap& face_data) {
return pImpl->GetFaceDenseLandmark(face_data);
}
std::vector<inspirecv::Point2f> Session::GetFaceFiveKeyPoints(const FaceTrackWrap& face_data) {
return pImpl->GetFaceFiveKeyPoints(face_data);
}
int32_t Session::FaceFeatureExtract(inspirecv::FrameProcess& process, FaceTrackWrap& data, FaceEmbedding& embedding, bool normalize) {
return pImpl->FaceFeatureExtract(process, data, embedding, normalize);
}
int32_t Session::FaceFeatureExtractWithAlignmentImage(inspirecv::FrameProcess& process, FaceEmbedding& embedding, bool normalize) {
return pImpl->FaceFeatureExtractWithAlignmentImage(process, embedding, normalize);
}
int32_t Session::FaceFeatureExtractWithAlignmentImage(const inspirecv::Image& wrapped, FaceEmbedding& embedding, bool normalize) {
return pImpl->FaceFeatureExtractWithAlignmentImage(wrapped, embedding, normalize);
}
void Session::GetFaceAlignmentImage(inspirecv::FrameProcess& process, FaceTrackWrap& data, inspirecv::Image& wrapped) {
pImpl->GetFaceAlignmentImage(process, data, wrapped);
}
int32_t Session::MultipleFacePipelineProcess(inspirecv::FrameProcess& process, const CustomPipelineParameter& param,
const std::vector<FaceTrackWrap>& face_data_list) {
return pImpl->MultipleFacePipelineProcess(process, param, face_data_list);
}
std::vector<float> Session::GetRGBLivenessConfidence() {
return pImpl->GetRGBLivenessConfidence();
}
std::vector<float> Session::GetFaceMaskConfidence() {
return pImpl->GetFaceMaskConfidence();
}
std::vector<float> Session::GetFaceQualityConfidence() {
return pImpl->GetFaceQualityConfidence();
}
std::vector<FaceInteractionState> Session::GetFaceInteractionState() {
return pImpl->GetFaceInteractionState();
}
std::vector<FaceInteractionAction> Session::GetFaceInteractionAction() {
return pImpl->GetFaceInteractionAction();
}
std::vector<FaceAttributeResult> Session::GetFaceAttributeResult() {
return pImpl->GetFaceAttributeResult();
}
} // namespace inspire

View File

@@ -5,7 +5,7 @@
#include "face_detect_adapt.h"
#include "cost_time.h"
#include <inspirecv/time_spend.h>
#include "spend_timer.h"
namespace inspire {
@@ -13,7 +13,7 @@ FaceDetectAdapt::FaceDetectAdapt(int input_size, float nms_threshold, float cls_
: AnyNetAdapter("FaceDetectAdapt"), m_nms_threshold_(nms_threshold), m_cls_threshold_(cls_threshold), m_input_size_(input_size) {}
FaceLocList FaceDetectAdapt::operator()(const inspirecv::Image &bgr) {
inspirecv::TimeSpend time_image_process("Image process");
inspire::SpendTimer time_image_process("Image process");
time_image_process.Start();
int ori_w = bgr.Width();
int ori_h = bgr.Height();
@@ -31,14 +31,14 @@ FaceLocList FaceDetectAdapt::operator()(const inspirecv::Image &bgr) {
// pad.Write("pad.jpg");
// LOGD("Prepare");
AnyTensorOutputs outputs;
inspirecv::TimeSpend time_forward("Forward");
inspire::SpendTimer time_forward("Forward");
time_forward.Start();
Forward(pad, outputs);
time_forward.Stop();
// std::cout << time_forward << std::endl;
// LOGD("Forward");
inspirecv::TimeSpend time_decode("Decode");
inspire::SpendTimer time_decode("Decode");
time_decode.Start();
std::vector<FaceLoc> results;
std::vector<int> strides = {8, 16, 32};
@@ -154,4 +154,8 @@ bool SortBoxSizeAdapt(const FaceLoc &a, const FaceLoc &b) {
return sq_a > sq_b;
}
int FaceDetectAdapt::GetInputSize() const {
return m_input_size_;
}
} // namespace inspire

View File

@@ -6,9 +6,9 @@
#pragma once
#ifndef INSPIRE_FACE_TRACK_MODULE_FACE_DETECT_FACE_DETECT_ADAPT_H
#define INSPIRE_FACE_TRACK_MODULE_FACE_DETECT_FACE_DETECT_ADAPT_H
#include "../../data_type.h"
#include "data_type.h"
#include "middleware/any_net_adapter.h"
#include "middleware/nexus_processor/image_processor.h"
#include "image_process/nexus_processor/image_processor.h"
namespace inspire {
@@ -41,6 +41,12 @@ public:
/** @brief Set face classification threshold */
void SetClsThreshold(float mClsThreshold);
/**
* @brief Get the input size
* @return int The input size
*/
int GetInputSize() const;
private:
/**
* @brief Applies non-maximum suppression to reduce overlapping detected faces.

View File

@@ -5,7 +5,7 @@
#pragma once
#ifndef INSPIRE_FACE_TRACK_MODULE_FACE_DETECT_RNET_ADAPT_H
#define INSPIRE_FACE_TRACK_MODULE_FACE_DETECT_RNET_ADAPT_H
#include "../../data_type.h"
#include "data_type.h"
#include "middleware/any_net_adapter.h"
namespace inspire {

View File

@@ -5,7 +5,6 @@
#include "face_track_module.h"
#include "log.h"
#include "landmark/mean_shape.h"
#include <algorithm>
#include <cstddef>
#include "middleware/costman.h"
@@ -14,7 +13,8 @@
#include "herror.h"
#include "middleware/costman.h"
#include "cost_time.h"
#include <inspirecv/time_spend.h>
#include "spend_timer.h"
#include "launch.h"
namespace inspire {
@@ -35,7 +35,8 @@ FaceTrackModule::FaceTrackModule(DetectModuleMode mode, int max_detected_faces,
// In lightweight tracking mode, landmark detection is always required
m_detect_mode_landmark_ = true;
} else {
m_detect_mode_landmark_ = detect_mode_landmark;
// This version uses lmk106 to replace five key points, so lmk must be forcibly enabled!
m_detect_mode_landmark_ = true;
}
if (m_mode_ == DETECT_MODE_TRACK_BY_DETECT) {
m_TbD_tracker_ = std::make_shared<BYTETracker>(TbD_mode_fps, 30);
@@ -45,17 +46,20 @@ FaceTrackModule::FaceTrackModule(DetectModuleMode mode, int max_detected_faces,
void FaceTrackModule::SparseLandmarkPredict(const inspirecv::Image &raw_face_crop, std::vector<inspirecv::Point2f> &landmarks_output, float &score,
float size) {
COST_TIME_SIMPLE(SparseLandmarkPredict);
landmarks_output.resize(FaceLandmarkAdapt::NUM_OF_LANDMARK);
landmarks_output.resize(m_landmark_param_->num_of_landmark);
std::vector<float> lmk_out = (*m_landmark_predictor_)(raw_face_crop);
for (int i = 0; i < FaceLandmarkAdapt::NUM_OF_LANDMARK; ++i) {
for (int i = 0; i < m_landmark_param_->num_of_landmark; ++i) {
float x = lmk_out[i * 2 + 0] * size;
float y = lmk_out[i * 2 + 1] * size;
landmarks_output[i] = inspirecv::Point<float>(x, y);
}
score = (*m_refine_net_)(raw_face_crop);
}
bool FaceTrackModule::TrackFace(inspirecv::InspireImageProcess &image, FaceObjectInternal &face) {
float FaceTrackModule::PredictTrackScore(const inspirecv::Image &raw_face_crop) {
return (*m_refine_net_)(raw_face_crop);
}
bool FaceTrackModule::TrackFace(inspirecv::FrameProcess &image, FaceObjectInternal &face) {
COST_TIME_SIMPLE(TrackFace);
// If the face confidence level is below 0.1, disable tracking
if (face.GetConfidence() < 0.1) {
@@ -79,21 +83,13 @@ bool FaceTrackModule::TrackFace(inspirecv::InspireImageProcess &image, FaceObjec
inspirecv::TransformMatrix rotation_mode_affine = image.GetAffineMatrix();
std::vector<inspirecv::Point2f> camera_pts = ApplyTransformToPoints(rect_pts, rotation_mode_affine);
// camera_pts.erase(camera_pts.end() - 1);
std::vector<inspirecv::Point2f> dst_pts = {{0, 0}, {112, 0}, {112, 112}, {0, 112}};
std::vector<inspirecv::Point2f> dst_pts = {{0, 0},
{(float)m_landmark_param_->input_size, 0},
{(float)m_landmark_param_->input_size, (float)m_landmark_param_->input_size},
{0, (float)m_landmark_param_->input_size}};
affine = inspirecv::SimilarityTransformEstimate(camera_pts, dst_pts);
face.setTransMatrix(affine);
std::vector<inspirecv::Point2f> dst_pts_extensive = {{0, 0},
{(float)m_crop_extensive_size_, 0},
{(float)m_crop_extensive_size_, (float)m_crop_extensive_size_},
{0, (float)m_crop_extensive_size_}};
// Add extensive rect
inspirecv::Rect2i extensive_rect = rect_square.Square(m_crop_extensive_ratio_);
auto extensive_rect_pts = extensive_rect.As<float>().ToFourVertices();
std::vector<inspirecv::Point2f> camera_pts_extensive = ApplyTransformToPoints(extensive_rect_pts, rotation_mode_affine);
inspirecv::TransformMatrix extensive_affine = inspirecv::SimilarityTransformEstimate(camera_pts_extensive, dst_pts);
face.setTransMatrixExtensive(extensive_affine);
if (!m_detect_mode_landmark_) {
/*If landmark is not extracted, the detection frame of the preview image needs to be changed
back to the coordinate system of the original image */
@@ -105,41 +101,72 @@ bool FaceTrackModule::TrackFace(inspirecv::InspireImageProcess &image, FaceObjec
if (m_face_quality_ != nullptr) {
COST_TIME_SIMPLE(FaceQuality);
auto affine_extensive = face.getTransMatrixExtensive();
auto pre_crop = image.ExecuteImageAffineProcessing(affine_extensive, m_crop_extensive_size_, m_crop_extensive_size_);
auto affine_extensive = face.getTransMatrix();
auto trans_e = ScaleAffineMatrixPreserveCenter(affine_extensive, m_crop_extensive_ratio_, m_landmark_param_->input_size);
auto pre_crop = image.ExecuteImageAffineProcessing(trans_e, m_landmark_param_->input_size, m_landmark_param_->input_size);
auto res = (*m_face_quality_)(pre_crop);
// pre_crop.Show("pre_crop");
auto affine_extensive_inv = affine_extensive.GetInverse();
std::vector<inspirecv::Point2f> lmk_extensive = ApplyTransformToPoints(res.lmk, affine_extensive_inv);
res.lmk = lmk_extensive;
face.high_result = res;
} else {
// If face pose and quality model is not initialized, set the default value
FacePoseQualityAdaptResult empty_result;
empty_result.lmk = std::vector<inspirecv::Point2f>(5, inspirecv::Point2f(0, 0));
empty_result.lmk_quality = std::vector<float>(5, 2.0f);
empty_result.pitch = 0.0f;
empty_result.yaw = 0.0f;
empty_result.roll = 0.0f;
face.high_result = empty_result;
}
if (m_detect_mode_landmark_) {
// If Landmark need to be extracted in detection mode,
// Landmark must be detected when fast tracing is enabled
affine = face.getTransMatrix();
inspirecv::Image crop;
// Get the RGB image after affine transformation
crop = image.ExecuteImageAffineProcessing(affine, 112, 112);
inspirecv::TransformMatrix affine_inv = affine.GetInverse();
std::vector<inspirecv::Point2f> landmark_rawout;
std::vector<float> bbox;
std::vector<std::vector<inspirecv::Point2f>> multiscale_landmark_back;
auto track_crop = image.ExecuteImageAffineProcessing(affine, m_landmark_param_->input_size, m_landmark_param_->input_size);
score = PredictTrackScore(track_crop);
// track_crop.Show("track_crop");
for (int i = 0; i < m_multiscale_landmark_scales_.size(); i++) {
inspirecv::Image crop;
// Get the RGB image after affine transformation
auto affine_scale = ScaleAffineMatrixPreserveCenter(affine, m_multiscale_landmark_scales_[i], m_landmark_param_->input_size);
crop = image.ExecuteImageAffineProcessing(affine_scale, m_landmark_param_->input_size, m_landmark_param_->input_size);
std::vector<inspirecv::Point2f> lmk_predict;
// Predicted sparse key point
SparseLandmarkPredict(crop, lmk_predict, score, m_landmark_param_->input_size);
// Save the first scale landmark
if (i == 0) {
landmark_rawout = lmk_predict;
}
std::vector<inspirecv::Point2f> lmk_back;
// Convert key points back to the original coordinate system
lmk_back.resize(lmk_predict.size());
lmk_back = inspirecv::ApplyTransformToPoints(lmk_predict, affine_scale.GetInverse());
multiscale_landmark_back.push_back(lmk_back);
}
landmark_back = MultiFrameLandmarkMean(multiscale_landmark_back);
Timer lmk_cost_time;
// Predicted sparse key point
SparseLandmarkPredict(crop, landmark_rawout, score, 112);
// Extract 5 key points
std::vector<inspirecv::Point2f> lmk_5 = {
landmark_rawout[FaceLandmarkAdapt::LEFT_EYE_CENTER], landmark_rawout[FaceLandmarkAdapt::RIGHT_EYE_CENTER],
landmark_rawout[FaceLandmarkAdapt::NOSE_CORNER], landmark_rawout[FaceLandmarkAdapt::MOUTH_LEFT_CORNER],
landmark_rawout[FaceLandmarkAdapt::MOUTH_RIGHT_CORNER]};
landmark_rawout[m_landmark_param_->semantic_index.left_eye_center], landmark_rawout[m_landmark_param_->semantic_index.right_eye_center],
landmark_rawout[m_landmark_param_->semantic_index.nose_corner], landmark_rawout[m_landmark_param_->semantic_index.mouth_left_corner],
landmark_rawout[m_landmark_param_->semantic_index.mouth_right_corner]};
face.setAlignMeanSquareError(lmk_5);
// Convert key points back to the original coordinate system
landmark_back.resize(landmark_rawout.size());
landmark_back = inspirecv::ApplyTransformToPoints(landmark_rawout, affine_inv);
int MODE = 1;
if (MODE > 0) {
@@ -148,15 +175,16 @@ bool FaceTrackModule::TrackFace(inspirecv::InspireImageProcess &image, FaceObjec
} else if (face.TrackingState() == ISF_READY || face.TrackingState() == ISF_TRACKING) {
COST_TIME_SIMPLE(LandmarkBack);
inspirecv::TransformMatrix trans_m;
inspirecv::TransformMatrix tmp = face.getTransMatrix();
std::vector<inspirecv::Point2f> inside_points = landmark_rawout;
std::vector<inspirecv::Point2f> mean_shape_(FaceLandmarkAdapt::NUM_OF_LANDMARK);
for (int k = 0; k < FaceLandmarkAdapt::NUM_OF_LANDMARK; k++) {
mean_shape_[k].SetX(mean_shape[k * 2]);
mean_shape_[k].SetY(mean_shape[k * 2 + 1]);
// inspirecv::TransformMatrix tmp = face.getTransMatrix();
std::vector<inspirecv::Point2f> inside_points;
if (m_landmark_param_->input_size == 112) {
inside_points = landmark_rawout;
} else {
inside_points = LandmarkCropped(landmark_rawout);
}
auto &mean_shape_ = m_landmark_param_->mean_shape_points;
auto _affine = inspirecv::SimilarityTransformEstimate(inside_points, mean_shape_);
auto mid_inside_points = ApplyTransformToPoints(inside_points, _affine);
inside_points = FixPointsMeanshape(mid_inside_points, mean_shape_);
@@ -164,44 +192,19 @@ bool FaceTrackModule::TrackFace(inspirecv::InspireImageProcess &image, FaceObjec
trans_m = inspirecv::SimilarityTransformEstimate(landmark_back, inside_points);
face.setTransMatrix(trans_m);
face.EnableTracking();
Timer extensive_cost_time;
// Add extensive rect
// Calculate center point of landmarks
inspirecv::Point2f center(0.0f, 0.0f);
for (const auto &pt : landmark_back) {
center.SetX(center.GetX() + pt.GetX());
center.SetY(center.GetY() + pt.GetY());
}
center.SetX(center.GetX() / landmark_back.size());
center.SetY(center.GetY() / landmark_back.size());
// Create expanded points by scaling from center by 1.3
std::vector<inspirecv::Point2f> lmk_back_rect = landmark_back;
for (auto &pt : lmk_back_rect) {
pt.SetX(center.GetX() + (pt.GetX() - center.GetX()) * m_crop_extensive_ratio_);
pt.SetY(center.GetY() + (pt.GetY() - center.GetY()) * m_crop_extensive_ratio_);
}
inspirecv::TransformMatrix extensive_affine = inspirecv::SimilarityTransformEstimate(lmk_back_rect, mid_inside_points);
face.setTransMatrixExtensive(extensive_affine);
// INSPIRE_LOGD("Extensive Affine Cost %f", extensive_cost_time.GetCostTimeUpdate());
}
}
// Add five key points to landmark_back
for (int i = 0; i < 5; i++) {
landmark_back.push_back(face.high_result.lmk[i]);
}
// Update face key points
face.SetLandmark(landmark_back, true, true, m_track_mode_smooth_ratio_, m_track_mode_num_smooth_cache_frame_,
(FaceLandmarkAdapt::NUM_OF_LANDMARK + 10) * 2);
m_landmark_param_->num_of_landmark * 2);
// Get the smoothed landmark
auto &landmark_smooth = face.landmark_smooth_aux_.back();
// Update the face key points
face.high_result.lmk[0] = landmark_smooth[FaceLandmarkAdapt::NUM_OF_LANDMARK + 0];
face.high_result.lmk[1] = landmark_smooth[FaceLandmarkAdapt::NUM_OF_LANDMARK + 1];
face.high_result.lmk[2] = landmark_smooth[FaceLandmarkAdapt::NUM_OF_LANDMARK + 2];
face.high_result.lmk[3] = landmark_smooth[FaceLandmarkAdapt::NUM_OF_LANDMARK + 3];
face.high_result.lmk[4] = landmark_smooth[FaceLandmarkAdapt::NUM_OF_LANDMARK + 4];
face.high_result.lmk[0] = landmark_smooth[m_landmark_param_->semantic_index.left_eye_center];
face.high_result.lmk[1] = landmark_smooth[m_landmark_param_->semantic_index.right_eye_center];
face.high_result.lmk[2] = landmark_smooth[m_landmark_param_->semantic_index.nose_corner];
face.high_result.lmk[3] = landmark_smooth[m_landmark_param_->semantic_index.mouth_left_corner];
face.high_result.lmk[4] = landmark_smooth[m_landmark_param_->semantic_index.mouth_right_corner];
}
// If tracking status, update the confidence level
@@ -212,17 +215,18 @@ bool FaceTrackModule::TrackFace(inspirecv::InspireImageProcess &image, FaceObjec
return true;
}
void FaceTrackModule::UpdateStream(inspirecv::InspireImageProcess &image) {
inspirecv::TimeSpend total("UpdateStream");
void FaceTrackModule::UpdateStream(inspirecv::FrameProcess &image) {
inspire::SpendTimer total("UpdateStream");
total.Start();
COST_TIME_SIMPLE(FaceTrackUpdateStream);
detection_index_ += 1;
if (m_mode_ == DETECT_MODE_ALWAYS_DETECT || m_mode_ == DETECT_MODE_TRACK_BY_DETECT)
trackingFace.clear();
if (trackingFace.empty() || detection_index_ % detection_interval_ == 0 || m_mode_ == DETECT_MODE_ALWAYS_DETECT ||
if (trackingFace.empty() || (detection_interval_ > 0 && detection_index_ % detection_interval_ == 0) || m_mode_ == DETECT_MODE_ALWAYS_DETECT ||
m_mode_ == DETECT_MODE_TRACK_BY_DETECT) {
image.SetPreviewSize(track_preview_size_);
inspirecv::Image image_detect = image.ExecutePreviewImageProcessing(true);
m_debug_preview_image_size_ = image_detect.Width();
nms();
for (auto const &face : trackingFace) {
@@ -336,7 +340,7 @@ void FaceTrackModule::DetectFace(const inspirecv::Image &input, float scale) {
tracking_idx_ = tracking_idx_ + 1;
}
FaceObjectInternal faceinfo(tracking_idx_, bbox[i], FaceLandmarkAdapt::NUM_OF_LANDMARK + 10);
FaceObjectInternal faceinfo(tracking_idx_, bbox[i], m_landmark_param_->num_of_landmark + 10);
faceinfo.detect_bbox_ = bbox[i];
faceinfo.SetConfidence(boxes[i].score);
@@ -350,11 +354,12 @@ void FaceTrackModule::DetectFace(const inspirecv::Image &input, float scale) {
}
}
int FaceTrackModule::Configuration(inspire::InspireArchive &archive, const std::string &expansion_path) {
int FaceTrackModule::Configuration(inspire::InspireArchive &archive, const std::string &expansion_path, bool enable_face_pose_and_quality) {
// Initialize the detection model
m_landmark_param_ = archive.GetLandmarkParam();
m_expansion_path_ = std::move(expansion_path);
InspireModel detModel;
auto scheme = ChoiceMultiLevelDetectModel(m_dynamic_detection_input_level_);
auto scheme = ChoiceMultiLevelDetectModel(m_dynamic_detection_input_level_, track_preview_size_);
auto ret = archive.LoadModel(scheme, detModel);
if (ret != SARC_SUCCESS) {
INSPIRE_LOGE("Load %s error: %d", scheme.c_str(), ret);
@@ -364,9 +369,9 @@ int FaceTrackModule::Configuration(inspire::InspireArchive &archive, const std::
// Initialize the landmark model
InspireModel lmkModel;
ret = archive.LoadModel("landmark", lmkModel);
ret = archive.LoadModel(m_landmark_param_->landmark_engine_name, lmkModel);
if (ret != SARC_SUCCESS) {
INSPIRE_LOGE("Load %s error: %d", "landmark", ret);
INSPIRE_LOGE("Load %s error: %d", m_landmark_param_->landmark_engine_name.c_str(), ret);
return HERR_ARCHIVE_LOAD_MODEL_FAILURE;
}
InitLandmarkModel(lmkModel);
@@ -379,22 +384,25 @@ int FaceTrackModule::Configuration(inspire::InspireArchive &archive, const std::
return HERR_ARCHIVE_LOAD_MODEL_FAILURE;
}
InitRNetModel(rnetModel);
// Initialize the pose quality model
InspireModel pquModel;
ret = archive.LoadModel("pose_quality", pquModel);
if (ret != SARC_SUCCESS) {
INSPIRE_LOGE("Load %s error: %d", "pose_quality", ret);
return HERR_ARCHIVE_LOAD_MODEL_FAILURE;
if (enable_face_pose_and_quality) {
// Initialize the pose quality model
InspireModel pquModel;
ret = archive.LoadModel("pose_quality", pquModel);
if (ret != SARC_SUCCESS) {
INSPIRE_LOGE("Load %s error: %d", "pose_quality", ret);
return HERR_ARCHIVE_LOAD_MODEL_FAILURE;
}
InitFacePoseAndQualityModel(pquModel);
}
InitFacePoseModel(pquModel);
m_landmark_crop_ratio_ = m_landmark_param_->expansion_scale;
m_multiscale_landmark_scales_ = GenerateCropScales(m_landmark_crop_ratio_, m_multiscale_landmark_loop_num_);
return 0;
}
int FaceTrackModule::InitLandmarkModel(InspireModel &model) {
m_landmark_predictor_ = std::make_shared<FaceLandmarkAdapt>(112);
auto ret = m_landmark_predictor_->loadData(model, model.modelType);
m_landmark_predictor_ =
std::make_shared<FaceLandmarkAdapt>(m_landmark_param_->input_size, m_landmark_param_->normalization_mode == "CenterScaling");
auto ret = m_landmark_predictor_->LoadData(model, model.modelType);
if (ret != InferenceWrapper::WrapperOk) {
return HERR_ARCHIVE_LOAD_FAILURE;
}
@@ -406,7 +414,7 @@ int FaceTrackModule::InitDetectModel(InspireModel &model) {
input_size = model.Config().get<std::vector<int>>("input_size");
m_face_detector_ = std::make_shared<FaceDetectAdapt>(input_size[0]);
auto ret = m_face_detector_->loadData(model, model.modelType, false);
auto ret = m_face_detector_->LoadData(model, model.modelType, false);
if (ret != InferenceWrapper::WrapperOk) {
return HERR_ARCHIVE_LOAD_FAILURE;
}
@@ -415,16 +423,16 @@ int FaceTrackModule::InitDetectModel(InspireModel &model) {
int FaceTrackModule::InitRNetModel(InspireModel &model) {
m_refine_net_ = std::make_shared<RNetAdapt>();
auto ret = m_refine_net_->loadData(model, model.modelType);
auto ret = m_refine_net_->LoadData(model, model.modelType);
if (ret != InferenceWrapper::WrapperOk) {
return HERR_ARCHIVE_LOAD_FAILURE;
}
return HSUCCEED;
}
int FaceTrackModule::InitFacePoseModel(InspireModel &model) {
int FaceTrackModule::InitFacePoseAndQualityModel(InspireModel &model) {
m_face_quality_ = std::make_shared<FacePoseQualityAdapt>();
auto ret = m_face_quality_->loadData(model, model.modelType);
auto ret = m_face_quality_->LoadData(model, model.modelType);
if (ret != InferenceWrapper::WrapperOk) {
return HERR_ARCHIVE_LOAD_FAILURE;
}
@@ -441,35 +449,46 @@ void FaceTrackModule::SetMinimumFacePxSize(float value) {
void FaceTrackModule::SetTrackPreviewSize(int preview_size) {
track_preview_size_ = preview_size;
if (track_preview_size_ == -1) {
track_preview_size_ = m_face_detector_->GetInputSize();
} else if (track_preview_size_ < 192) {
INSPIRE_LOGW("Track preview size %d is less than the minimum input size %d", track_preview_size_, 192);
track_preview_size_ = 192;
}
}
std::string FaceTrackModule::ChoiceMultiLevelDetectModel(const int32_t pixel_size) {
const int32_t supported_sizes[] = {160, 320, 640};
const std::string scheme_names[] = {"face_detect_160", "face_detect_320", "face_detect_640"};
const int32_t num_sizes = sizeof(supported_sizes) / sizeof(supported_sizes[0]);
int32_t FaceTrackModule::GetTrackPreviewSize() const {
return track_preview_size_;
}
std::string FaceTrackModule::ChoiceMultiLevelDetectModel(const int32_t pixel_size, int32_t &final_size) {
const auto face_detect_pixel_list = Launch::GetInstance()->GetFaceDetectPixelList();
const auto face_detect_model_list = Launch::GetInstance()->GetFaceDetectModelList();
const int32_t num_sizes = face_detect_pixel_list.size();
if (pixel_size == -1) {
return scheme_names[1];
final_size = face_detect_pixel_list[1];
return face_detect_model_list[1];
}
// Check for exact match
for (int i = 0; i < num_sizes; ++i) {
if (pixel_size == supported_sizes[i]) {
return scheme_names[i];
if (pixel_size == face_detect_pixel_list[i]) {
final_size = face_detect_pixel_list[i];
return face_detect_model_list[i];
}
}
// Find the closest match
int32_t closest_size = supported_sizes[0];
std::string closest_scheme = scheme_names[0];
int32_t min_diff = std::abs(pixel_size - supported_sizes[0]);
int32_t closest_size = face_detect_pixel_list[0];
std::string closest_scheme = face_detect_model_list[0];
int32_t min_diff = std::abs(pixel_size - face_detect_pixel_list[0]);
for (int i = 1; i < num_sizes; ++i) {
int32_t diff = std::abs(pixel_size - supported_sizes[i]);
int32_t diff = std::abs(pixel_size - face_detect_pixel_list[i]);
if (diff < min_diff) {
min_diff = diff;
closest_size = supported_sizes[i];
closest_scheme = scheme_names[i];
closest_size = face_detect_pixel_list[i];
closest_scheme = face_detect_model_list[i];
}
}
@@ -477,6 +496,7 @@ std::string FaceTrackModule::ChoiceMultiLevelDetectModel(const int32_t pixel_siz
"Input pixel size %d is not supported. Choosing the closest scheme: %s closest_scheme for "
"size %d.",
pixel_size, closest_scheme.c_str(), closest_size);
final_size = closest_size;
return closest_scheme;
}
@@ -497,4 +517,13 @@ void FaceTrackModule::SetTrackModeDetectInterval(int value) {
detection_interval_ = value;
}
void FaceTrackModule::SetMultiscaleLandmarkLoop(int value) {
m_multiscale_landmark_loop_num_ = value;
m_multiscale_landmark_scales_ = GenerateCropScales(m_landmark_crop_ratio_, m_multiscale_landmark_loop_num_);
}
int32_t FaceTrackModule::GetDebugPreviewImageSize() const {
return m_debug_preview_image_size_;
}
} // namespace inspire

View File

@@ -8,26 +8,17 @@
#include <iostream>
#include "face_detect/face_detect_adapt.h"
#include "face_detect/rnet_adapt.h"
#include "landmark/face_landmark_adapt.h"
#include "landmark/all.h"
#include "common/face_info/face_object_internal.h"
#include "middleware/inspirecv_image_process.h"
#include "frame_process.h"
#include "quality/face_pose_quality_adapt.h"
#include "middleware/model_archive/inspire_archive.h"
#include "tracker_optional/bytetrack/BYTETracker.h"
#include <data_type.h>
#include "landmark/landmark_param.h"
namespace inspire {
/**
* @enum DetectMode
* @brief Enumeration for different detection modes.
*/
enum DetectModuleMode {
DETECT_MODE_ALWAYS_DETECT = 0, ///< Detection mode: Always detect
DETECT_MODE_LIGHT_TRACK, ///< Detection mode: Light face track
DETECT_MODE_TRACK_BY_DETECT, ///< Detection mode: Tracking by detection
};
/**
* @class FaceTrack
* @brief Class for tracking faces in video streams.
@@ -44,7 +35,7 @@ public:
* @param track_preview_size Size of the preview for tracking.
* @param dynamic_detection_input_level Change the detector input size.
*/
explicit FaceTrackModule(DetectModuleMode mode, int max_detected_faces = 1, int detection_interval = 20, int track_preview_size = 192,
explicit FaceTrackModule(DetectModuleMode mode, int max_detected_faces = 1, int detection_interval = 20, int track_preview_size = -1,
int dynamic_detection_input_level = -1, int TbD_mode_fps = 30, bool detect_mode_landmark = true);
/**
@@ -53,20 +44,26 @@ public:
* @param expansion_path Expand the path if you need it.
* @return int Status of the configuration.
*/
int Configuration(InspireArchive &archive, const std::string &expansion_path = "");
int Configuration(InspireArchive &archive, const std::string &expansion_path = "", bool enable_face_pose_and_quality = false);
/**
* @brief Updates the video stream for face tracking.
* @param image Camera stream to process.
* @param is_detect Flag to enable/disable face detection.
*/
void UpdateStream(inspirecv::InspireImageProcess &image);
void UpdateStream(inspirecv::FrameProcess &image);
/**
* @brief Sets the preview size for tracking.
* @param preview_size Size of the preview for tracking.
*/
void SetTrackPreviewSize(int preview_size = 192);
void SetTrackPreviewSize(int preview_size = -1);
/**
* @brief Gets the preview size for tracking.
* @return Size of the preview for tracking.
*/
int32_t GetTrackPreviewSize() const;
private:
/**
@@ -79,13 +76,20 @@ private:
void SparseLandmarkPredict(const inspirecv::Image &raw_face_crop, std::vector<inspirecv::Point2f> &landmarks_output, float &score,
float size = 112.0);
/**
* @brief Predicts the tracking score for a cropped face image.
* @param raw_face_crop Cropped face image.
* @return float Tracking score.
*/
float PredictTrackScore(const inspirecv::Image &raw_face_crop);
/**
* @brief Tracks a face in the given image stream.
* @param image Camera stream containing the face.
* @param face FaceObject to be tracked.
* @return bool Status of face tracking.
*/
bool TrackFace(inspirecv::InspireImageProcess &image, FaceObjectInternal &face);
bool TrackFace(inspirecv::FrameProcess &image, FaceObjectInternal &face);
/**
* @brief Blacks out the region specified in the image for tracking.
@@ -129,18 +133,18 @@ private:
int InitRNetModel(InspireModel &model);
/**
* @brief Initializes the face pose estimation model.
* @param model Pointer to the face pose model to be initialized.
* @brief Initializes the face pose and quality estimation model.
* @param model Pointer to the face pose and quality model to be initialized.
* @return int Status of the initialization process. Returns 0 for success.
*/
int InitFacePoseModel(InspireModel &model);
int InitFacePoseAndQualityModel(InspireModel &model);
/**
* @brief Select the detection model scheme to be used according to the input pixel level.
* @param pixel_size Currently, only 160, 320, and 640 pixel sizes are supported.
* @return Return the corresponding scheme name, only ”face_detect_160”, ”face_detect_320”, ”face_detect_640” are supported.
*/
std::string ChoiceMultiLevelDetectModel(const int32_t pixel_size);
std::string ChoiceMultiLevelDetectModel(const int32_t pixel_size, int32_t &final_size);
public:
/**
@@ -177,9 +181,18 @@ public:
*/
void SetTrackModeDetectInterval(int value);
/**
* @brief Set the multiscale landmark loop num
* @param value Multiscale landmark loop num
*/
void SetMultiscaleLandmarkLoop(int value);
public:
std::vector<FaceObjectInternal> trackingFace; ///< Vector of FaceObjects currently being tracked.
public:
int32_t GetDebugPreviewImageSize() const;
private:
const int max_detected_faces_; ///< Maximum number of faces to detect.
std::vector<FaceObjectInternal> candidate_faces_; ///< Vector of candidate FaceObjects for tracking.
@@ -189,6 +202,10 @@ private:
int track_preview_size_; ///< Size of the tracking preview.
int filter_minimum_face_px_size = 0; ///< Minimum face pixel allowed to be retained (take the edge with the smallest Rect).
private:
// Debug cache
int32_t m_debug_preview_image_size_{0}; ///< Debug preview image size
private:
std::shared_ptr<FaceDetectAdapt> m_face_detector_; ///< Shared pointer to the face detector.
std::shared_ptr<FaceLandmarkAdapt> m_landmark_predictor_; ///< Shared pointer to the landmark predictor.
@@ -212,6 +229,14 @@ private:
int m_track_mode_num_smooth_cache_frame_ = 5; ///< Track mode number of smooth cache frame
float m_track_mode_smooth_ratio_ = 0.05; ///< Track mode smooth ratio
int m_multiscale_landmark_loop_num_ = 1; ///< Multiscale landmark loop num
float m_landmark_crop_ratio_ = 1.1f;
std::vector<float> m_multiscale_landmark_scales_;
std::shared_ptr<LandmarkParam> m_landmark_param_;
};
} // namespace inspire

View File

@@ -8,5 +8,8 @@
#include "face_landmark_adapt.h"
#include "mean_shape.h"
#include "order_of_hyper_landmark.h"
#include "face_landmark_adapt.h"
#include "landmark_tools.h"
#endif // INSPIREFACE_LMK_ALL_H

View File

@@ -12,12 +12,18 @@ std::vector<float> FaceLandmarkAdapt::operator()(const inspirecv::Image& bgr_aff
COST_TIME_SIMPLE(FaceLandmarkAdapt);
AnyTensorOutputs outputs;
Forward(bgr_affine, outputs);
const auto& out = outputs[0].second;
auto& out = outputs[0].second;
if (m_is_center_scaling_) {
for (int i = 0; i < out.size(); ++i) {
out[i] = (out[i] + 1) / 2;
}
}
return out;
}
FaceLandmarkAdapt::FaceLandmarkAdapt(int input_size) : AnyNetAdapter("FaceLandmarkAdapt"), m_input_size_(input_size) {}
FaceLandmarkAdapt::FaceLandmarkAdapt(int input_size, bool is_center_scaling)
: AnyNetAdapter("FaceLandmarkAdapt"), m_input_size_(input_size), m_is_center_scaling_(is_center_scaling) {}
int FaceLandmarkAdapt::getInputSize() const {
return m_input_size_;

View File

@@ -5,7 +5,7 @@
#pragma once
#ifndef INSPIRE_FACE_TRACK_MODULE_LANDMARK_FACE_LANDMARK_ADAPT_H
#define INSPIRE_FACE_TRACK_MODULE_LANDMARK_FACE_LANDMARK_ADAPT_H
#include "../../data_type.h"
#include "data_type.h"
#include "middleware/any_net_adapter.h"
namespace inspire {
@@ -29,7 +29,7 @@ public:
* @brief Constructor for the FaceLandmark class.
* @param input_size The size of the input image for the neural network.
*/
explicit FaceLandmarkAdapt(int input_size = 112);
explicit FaceLandmarkAdapt(int input_size = 112, bool is_center_scaling = false);
/**
* @brief Gets the input size for the neural network model.
@@ -38,18 +38,12 @@ public:
int getInputSize() const;
public:
const static int LEFT_EYE_CENTER = 67; ///< Landmark index for the center of the left eye.
const static int RIGHT_EYE_CENTER = 68; ///< Landmark index for the center of the right eye.
const static int NOSE_CORNER = 100; ///< Landmark index for the tip of the nose.
const static int MOUTH_LEFT_CORNER = 104; ///< Landmark index for the left corner of the mouth.
const static int MOUTH_RIGHT_CORNER = 105; ///< Landmark index for the right corner of the mouth.
const static int MOUTH_LOWER = 84; ///< Landmark index for the lower corner of the mouth.
const static int MOUTH_UPPER = 87; ///< Landmark index for the upper corner of the mouth.
const static int NUM_OF_LANDMARK = 106; ///< Total number of landmarks detected.
private:
const int m_input_size_; ///< The input size for the neural network model.
bool m_is_center_scaling_; ///< Whether to use center scaling.
};
} // namespace inspire

View File

@@ -0,0 +1,127 @@
#ifndef INSPIRE_LANDMARK_PARAM_H
#define INSPIRE_LANDMARK_PARAM_H
#include "data_type.h"
#include "yaml-cpp/yaml.h"
#include "mean_shape.h"
#include "log.h"
#include "landmark_tools.h"
#include "order_of_hyper_landmark.h"
namespace inspire {
typedef struct {
int32_t left_eye_center = 67;
int32_t right_eye_center = 68;
int32_t nose_corner = 100;
int32_t mouth_left_corner = 104;
int32_t mouth_right_corner = 105;
int32_t mouth_lower = 84;
int32_t mouth_upper = 87;
std::vector<int32_t> left_eye_region = HLMK_LEFT_EYE_POINTS_INDEX;
std::vector<int32_t> right_eye_region = HLMK_RIGHT_EYE_POINTS_INDEX;
} SemanticIndex;
class INSPIRE_API LandmarkParam {
public:
LandmarkParam(const YAML::Node &config) {
LoadDefaultMeshShape();
if (!config) {
} else {
// TODO: parse config
m_is_available_ = true;
}
m_table_ = config;
}
void LoadDefaultMeshShape() {
mean_shape_points.clear();
mean_shape_points.resize(num_of_landmark);
for (int k = 0; k < num_of_landmark; k++) {
mean_shape_points[k].SetX(HYPLMK_MESH_SHAPE[k * 2]);
mean_shape_points[k].SetY(HYPLMK_MESH_SHAPE[k * 2 + 1]);
}
}
bool ReLoad(const std::string &name) {
if (!m_is_available_) {
landmark_engine_name = name;
return true;
}
// parse config
auto landmark_table = m_table_[name];
if (!landmark_table) {
INSPIRE_LOGE("landmark config not found: %s", name.c_str());
return false;
}
num_of_landmark = landmark_table["num_of_landmark"].as<int>();
expansion_scale = landmark_table["expansion_scale"].as<float>();
input_size = landmark_table["input_size"].as<int>();
auto semanic_index = landmark_table["semantic_index"];
if (!semanic_index) {
INSPIRE_LOGE("semantic_index not found: %s", name.c_str());
return false;
}
semantic_index.left_eye_center = semanic_index["left_eye_center"].as<int>();
semantic_index.right_eye_center = semanic_index["right_eye_center"].as<int>();
semantic_index.nose_corner = semanic_index["nose_corner"].as<int>();
semantic_index.mouth_left_corner = semanic_index["mouth_left_corner"].as<int>();
semantic_index.mouth_right_corner = semanic_index["mouth_right_corner"].as<int>();
semantic_index.mouth_lower = semanic_index["mouth_lower"].as<int>();
semantic_index.mouth_upper = semanic_index["mouth_upper"].as<int>();
auto left_eye_region = semanic_index["left_eye_region"];
if (left_eye_region) {
semantic_index.left_eye_region = left_eye_region.as<std::vector<int>>();
}
auto right_eye_region = semanic_index["right_eye_region"];
if (right_eye_region) {
semantic_index.right_eye_region = right_eye_region.as<std::vector<int>>();
}
auto mesh_shape = landmark_table["mesh_shape"];
if (mesh_shape.size() > 0) {
std::vector<float> mesh_shape_data = mesh_shape.as<std::vector<float>>();
mean_shape_points.clear();
mean_shape_points.resize(num_of_landmark);
if (mesh_shape_data.size() == num_of_landmark * 2) {
for (int i = 0; i < num_of_landmark; i++) {
mean_shape_points[i].SetX(mesh_shape_data[i * 2]);
mean_shape_points[i].SetY(mesh_shape_data[i * 2 + 1]);
}
mean_shape_points = LandmarkCropped(mean_shape_points);
// auto img = inspirecv::Image::Create(192, 192, 3);
// img.Fill(0);
// for (int i = 0; i < num_of_landmark; i++) {
// auto point = mean_shape_points[i];
// img.DrawCircle(inspirecv::Point2i(point.GetX(), point.GetY()), 2, inspirecv::Color::Red);
// }
// img.Show("mean_shape");
} else {
INSPIRE_LOGE("norm_track_index_from_112x size is not equal to num_of_landmark: %s", name.c_str());
return false;
}
} else {
LoadDefaultMeshShape();
}
normalization_mode = landmark_table["normalization_mode"].as<std::string>();
landmark_engine_name = name;
return true;
}
public:
int num_of_landmark{106};
float expansion_scale{1.1f};
int input_size{112};
std::vector<inspirecv::Point2f> mean_shape_points;
SemanticIndex semantic_index;
std::string landmark_engine_name{"landmark"};
std::string normalization_mode{"MinMax"};
private:
YAML::Node m_table_;
bool m_is_available_{false};
};
} // namespace inspire
#endif // INSPIRE_LANDMARK_PARAM_H

View File

@@ -0,0 +1,126 @@
/**
* Created by Jingyu Yan
* @date 2025-04-26
*/
#pragma once
#ifndef INSPIRE_FACE_TRACK_MODULE_LANDMARK_TOOLS_H
#define INSPIRE_FACE_TRACK_MODULE_LANDMARK_TOOLS_H
#include <vector>
#include <cmath>
#include <algorithm>
#include <inspirecv/inspirecv.h>
namespace inspire {
// Generate crop scales
inline std::vector<float> GenerateCropScales(float start_scale, int N) {
std::vector<float> result;
if (N <= 0)
return result;
result.push_back(start_scale);
float delta_step = 0.02f; // Initial step size
int direction = -1; // First decrease
int expand_count = 1; // Current expansion count
for (int i = 1; i < N; ++i) {
// The step size increases slightly every two expansions, e.g. 0.02 -> 0.04 -> 0.06
float current_delta = delta_step * (static_cast<float>((expand_count + 1) / 2));
float new_scale = start_scale + direction * current_delta;
// Keep 5 decimal places
new_scale = std::round(new_scale * 100000.0f) / 100000.0f;
result.push_back(new_scale);
direction *= -1; // Alternating positive and negative
expand_count++;
}
return result;
}
inline inspirecv::TransformMatrix ScaleAffineMatrixPreserveCenter(const inspirecv::TransformMatrix& affine, float scale, int output_size = 112) {
// The center point in the output image is (cx, cy).
float cx = output_size / 2.0f;
float cy = output_size / 2.0f;
// 1. Obtain the position of the current center point in the original image (inverse affine transformation)
inspirecv::TransformMatrix inv_affine = affine.GetInverse();
float center_x = inv_affine.Get(0, 0) * cx + inv_affine.Get(0, 1) * cy + inv_affine.Get(0, 2);
float center_y = inv_affine.Get(1, 0) * cx + inv_affine.Get(1, 1) * cy + inv_affine.Get(1, 2);
// 2. Create a new scaling matrix (note that the scale value should be reduced → to "expand the cropping area")
float inv_scale = 1.0f / scale;
float new_a11 = affine.Get(0, 0) * inv_scale;
float new_a12 = affine.Get(0, 1) * inv_scale;
float new_a21 = affine.Get(1, 0) * inv_scale;
float new_a22 = affine.Get(1, 1) * inv_scale;
// 3. Calculate the new offset to ensure that the center of the scaled image is still mapped to (cx, cy)
float new_b1 = cx - (new_a11 * center_x + new_a12 * center_y);
float new_b2 = cy - (new_a21 * center_x + new_a22 * center_y);
return inspirecv::TransformMatrix::Create(new_a11, new_a12, new_b1, new_a21, new_a22, new_b2);
}
inline std::vector<inspirecv::Point2f> MultiFrameLandmarkMean(const std::vector<std::vector<inspirecv::Point2f>>& points) {
std::vector<inspirecv::Point2f> mean_points;
if (points.empty()) {
return mean_points;
}
if (points.size() == 1) {
return points[0];
}
size_t num_frames = points.size();
size_t num_points = points[0].size();
// Initialize to 0
mean_points.resize(num_points, inspirecv::Point2f(0.0f, 0.0f));
// Accumulate the coordinates of each point
for (const auto& frame : points) {
if (frame.size() != num_points)
continue; // skip invalid frame size
for (size_t i = 0; i < num_points; ++i) {
mean_points[i].SetX(mean_points[i].GetX() + frame[i].GetX());
mean_points[i].SetY(mean_points[i].GetY() + frame[i].GetY());
}
}
// Calculate the average
for (auto& pt : mean_points) {
pt.SetX(pt.GetX() / static_cast<float>(num_frames));
pt.SetY(pt.GetY() / static_cast<float>(num_frames));
}
return mean_points;
}
inline std::vector<inspirecv::Point2f> LandmarkCropped(const std::vector<inspirecv::Point2f>& points, int output_size = 192) {
inspirecv::Rect2f rect = inspirecv::MinBoundingRect(points);
// Calculate the scale
float scale = output_size / std::max(rect.GetWidth(), rect.GetHeight());
// Create the result vector and scale each point
std::vector<inspirecv::Point2f> result;
result.reserve(points.size());
for (const auto& pt : points) {
// Translate to the origin (subtract the top-left corner coordinates), then scale
result.push_back(inspirecv::Point2f((pt.GetX() - rect.GetX()) * scale, (pt.GetY() - rect.GetY()) * scale));
}
return result;
}
} // namespace inspire
#endif // INSPIRE_FACE_TRACK_MODULE_LANDMARK_TOOLS_H

View File

@@ -6,7 +6,8 @@
#ifndef INSPIREFACE_MEAN_SHAPE_H
#define INSPIREFACE_MEAN_SHAPE_H
static const float mean_shape[212] = {
// The mean shape of the hyperlandmarkv2 model
static const float HYPLMK_MESH_SHAPE[212] = {
108.38947968990857, 25.872507850440652, 107.83061691599185, 32.740667888401575, 107.39614893771035, 39.61981246234094, 106.91122964874452,
46.49403692042678, 106.2010288700007, 53.34741525135486, 105.09197874965457, 60.15582154225913, 103.42464830472807, 66.83258235371402,
101.06766895276914, 73.25011372147983, 97.9868709520959, 79.30544663628336, 94.26571910204586, 84.93503057741032, 90.00562918536812,

View File

@@ -10,10 +10,10 @@
namespace inspire {
// HyperLandmark left eye contour points sequence of dense facial landmarks.
// HyperLandmarkV2 left eye contour points sequence of dense facial landmarks.
const std::vector<int> HLMK_LEFT_EYE_POINTS_INDEX = {51, 52, 53, 54, 55, 56, 57, 58};
// HyperLandmark right eye contour points sequence of dense facial landmarks.
// HyperLandmarkV2 right eye contour points sequence of dense facial landmarks.
const std::vector<int> HLMK_RIGHT_EYE_POINTS_INDEX = {59, 60, 61, 62, 63, 64, 65, 66};
} // namespace inspire

View File

@@ -13,7 +13,14 @@ FacePoseQualityAdapt::FacePoseQualityAdapt() : AnyNetAdapter("FacePoseQuality")
FacePoseQualityAdaptResult FacePoseQualityAdapt::operator()(const inspirecv::Image &img) {
FacePoseQualityAdaptResult res;
AnyTensorOutputs outputs;
Forward(img, outputs);
if (img.Width() != INPUT_WIDTH || img.Height() != INPUT_HEIGHT) {
uint8_t* resized_data = nullptr;
m_processor_->Resize(img.Data(), img.Width(), img.Height(), img.Channels(), &resized_data, INPUT_WIDTH, INPUT_HEIGHT);
auto resized = inspirecv::Image::Create(INPUT_WIDTH, INPUT_HEIGHT, img.Channels(), resized_data, false);
Forward(resized, outputs);
} else {
Forward(img, outputs);
}
const auto &output = outputs[0].second;
res.pitch = output[0] * 90;
res.yaw = output[1] * 90;

View File

@@ -6,7 +6,7 @@
#ifndef INSPIRE_FACE_TRACK_MODULE_QUALITY_FACE_POSE_QUALITY_ADAPT_H
#define INSPIRE_FACE_TRACK_MODULE_QUALITY_FACE_POSE_QUALITY_ADAPT_H
#include "../../data_type.h"
#include "data_type.h"
#include "middleware/any_net_adapter.h"
namespace inspire {

Some files were not shown because too many files have changed in this diff Show More