2025-03-25 00:51:26 +08:00
|
|
|
/**
|
|
|
|
|
* Created by Jingyu Yan
|
|
|
|
|
* @date 2024-10-01
|
|
|
|
|
*/
|
2025-05-22 16:07:26 +08:00
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
2025-03-25 00:51:26 +08:00
|
|
|
#include <inspireface.h>
|
|
|
|
|
|
|
|
|
|
int main(int argc, char* argv[]) {
|
2025-05-22 16:07:26 +08:00
|
|
|
HResult ret;
|
|
|
|
|
const char* packPath;
|
|
|
|
|
const char* sourcePath;
|
|
|
|
|
int rotation;
|
|
|
|
|
HFRotation rotation_enum;
|
|
|
|
|
HOption option;
|
|
|
|
|
HFDetectMode detMode;
|
|
|
|
|
HInt32 maxDetectNum;
|
|
|
|
|
HInt32 detectPixelLevel;
|
|
|
|
|
HFSession session;
|
|
|
|
|
HFImageBitmap image;
|
|
|
|
|
HFImageStream imageHandle;
|
|
|
|
|
HFMultipleFaceData multipleFaceData;
|
|
|
|
|
int faceNum;
|
|
|
|
|
HFImageBitmap drawImage;
|
|
|
|
|
HFImageBitmapData data;
|
|
|
|
|
int index;
|
|
|
|
|
HFFaceMaskConfidence maskConfidence;
|
|
|
|
|
HFFaceQualityConfidence qualityConfidence;
|
|
|
|
|
HOption pipelineOption;
|
|
|
|
|
HFFaceDetectPixelList pixelLevels;
|
2025-06-15 01:41:04 +08:00
|
|
|
HFFaceEmotionResult faceEmotionResult;
|
2025-05-22 16:07:26 +08:00
|
|
|
|
|
|
|
|
/* Check whether the number of parameters is correct */
|
2025-03-25 00:51:26 +08:00
|
|
|
if (argc < 3 || argc > 4) {
|
|
|
|
|
HFLogPrint(HF_LOG_ERROR, "Usage: %s <pack_path> <source_path> [rotation]", argv[0]);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-22 16:07:26 +08:00
|
|
|
packPath = argv[1];
|
|
|
|
|
sourcePath = argv[2];
|
|
|
|
|
rotation = 0;
|
2025-03-25 00:51:26 +08:00
|
|
|
|
2025-05-22 16:07:26 +08:00
|
|
|
/* If rotation is provided, check and set the value */
|
2025-03-25 00:51:26 +08:00
|
|
|
if (argc == 4) {
|
2025-05-22 16:07:26 +08:00
|
|
|
rotation = atoi(argv[3]);
|
2025-03-25 00:51:26 +08:00
|
|
|
if (rotation != 0 && rotation != 90 && rotation != 180 && rotation != 270) {
|
|
|
|
|
HFLogPrint(HF_LOG_ERROR, "Invalid rotation value. Allowed values are 0, 90, 180, 270.");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-05-22 16:07:26 +08:00
|
|
|
|
|
|
|
|
/* Set rotation based on input parameter */
|
2025-03-25 00:51:26 +08:00
|
|
|
switch (rotation) {
|
|
|
|
|
case 90:
|
|
|
|
|
rotation_enum = HF_CAMERA_ROTATION_90;
|
|
|
|
|
break;
|
|
|
|
|
case 180:
|
|
|
|
|
rotation_enum = HF_CAMERA_ROTATION_180;
|
|
|
|
|
break;
|
|
|
|
|
case 270:
|
|
|
|
|
rotation_enum = HF_CAMERA_ROTATION_270;
|
|
|
|
|
break;
|
|
|
|
|
case 0:
|
|
|
|
|
default:
|
|
|
|
|
rotation_enum = HF_CAMERA_ROTATION_0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HFLogPrint(HF_LOG_INFO, "Pack file Path: %s", packPath);
|
|
|
|
|
HFLogPrint(HF_LOG_INFO, "Source file Path: %s", sourcePath);
|
|
|
|
|
HFLogPrint(HF_LOG_INFO, "Rotation: %d", rotation);
|
|
|
|
|
|
2025-05-22 16:07:26 +08:00
|
|
|
HFSetLogLevel(HF_LOG_DEBUG);
|
2025-03-25 00:51:26 +08:00
|
|
|
|
2025-05-22 16:07:26 +08:00
|
|
|
/* The resource file must be loaded before it can be used */
|
2025-03-25 00:51:26 +08:00
|
|
|
ret = HFLaunchInspireFace(packPath);
|
|
|
|
|
if (ret != HSUCCEED) {
|
|
|
|
|
HFLogPrint(HF_LOG_ERROR, "Load Resource error: %d", ret);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-22 16:07:26 +08:00
|
|
|
ret = HFQuerySupportedPixelLevelsForFaceDetection(&pixelLevels);
|
|
|
|
|
if (ret != HSUCCEED) {
|
|
|
|
|
HFLogPrint(HF_LOG_ERROR, "HFQuerySupportedPixelLevelsForFaceDetection error: %d", ret);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
HFLogPrint(HF_LOG_INFO, "Supported pixel levels for face detection: %d", pixelLevels.size);
|
|
|
|
|
for (int i = 0; i < pixelLevels.size; i++) {
|
|
|
|
|
HFLogPrint(HF_LOG_INFO, "Supported pixel level %d: %d", i + 1, pixelLevels.pixel_level[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Enable the functions in the pipeline: mask detection, live detection, and face quality
|
|
|
|
|
* detection */
|
2025-06-15 01:41:04 +08:00
|
|
|
option = HF_ENABLE_QUALITY | HF_ENABLE_MASK_DETECT | HF_ENABLE_LIVENESS | HF_ENABLE_FACE_EMOTION;
|
2025-05-22 16:07:26 +08:00
|
|
|
/* Non-video or frame sequence mode uses IMAGE-MODE, which is always face detection without
|
|
|
|
|
* tracking */
|
|
|
|
|
detMode = HF_DETECT_MODE_LIGHT_TRACK;
|
|
|
|
|
/* Maximum number of faces detected */
|
|
|
|
|
maxDetectNum = 20;
|
|
|
|
|
/* Face detection image input level */
|
2025-06-15 13:34:42 +08:00
|
|
|
detectPixelLevel = 320;
|
2025-05-22 16:07:26 +08:00
|
|
|
/* Handle of the current face SDK algorithm context */
|
|
|
|
|
session = NULL;
|
2025-03-25 00:51:26 +08:00
|
|
|
ret = HFCreateInspireFaceSessionOptional(option, detMode, maxDetectNum, detectPixelLevel, -1, &session);
|
|
|
|
|
if (ret != HSUCCEED) {
|
|
|
|
|
HFLogPrint(HF_LOG_ERROR, "Create FaceContext error: %d", ret);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HFSessionSetTrackPreviewSize(session, detectPixelLevel);
|
|
|
|
|
HFSessionSetFilterMinimumFacePixelSize(session, 4);
|
|
|
|
|
|
2025-05-22 16:07:26 +08:00
|
|
|
/* Load a image */
|
2025-03-25 00:51:26 +08:00
|
|
|
ret = HFCreateImageBitmapFromFilePath(sourcePath, 3, &image);
|
|
|
|
|
if (ret != HSUCCEED) {
|
|
|
|
|
HFLogPrint(HF_LOG_ERROR, "The source entered is not a picture or read error.");
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
2025-05-22 16:07:26 +08:00
|
|
|
/* Prepare an image parameter structure for configuration */
|
2025-03-25 00:51:26 +08:00
|
|
|
ret = HFCreateImageStreamFromImageBitmap(image, rotation_enum, &imageHandle);
|
|
|
|
|
if (ret != HSUCCEED) {
|
|
|
|
|
HFLogPrint(HF_LOG_ERROR, "Create ImageStream error: %d", ret);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-22 16:07:26 +08:00
|
|
|
/* Execute HF_FaceContextRunFaceTrack captures face information in an image */
|
2025-03-25 00:51:26 +08:00
|
|
|
ret = HFExecuteFaceTrack(session, imageHandle, &multipleFaceData);
|
|
|
|
|
if (ret != HSUCCEED) {
|
|
|
|
|
HFLogPrint(HF_LOG_ERROR, "Execute HFExecuteFaceTrack error: %d", ret);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-22 16:07:26 +08:00
|
|
|
/* Print the number of faces detected */
|
|
|
|
|
faceNum = multipleFaceData.detectedNum;
|
2025-03-25 00:51:26 +08:00
|
|
|
HFLogPrint(HF_LOG_INFO, "Num of face: %d", faceNum);
|
|
|
|
|
|
2025-05-22 16:07:26 +08:00
|
|
|
/* Copy a new image to draw */
|
2025-03-25 00:51:26 +08:00
|
|
|
ret = HFImageBitmapCopy(image, &drawImage);
|
|
|
|
|
if (ret != HSUCCEED) {
|
|
|
|
|
HFLogPrint(HF_LOG_ERROR, "Copy ImageBitmap error: %d", ret);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
ret = HFImageBitmapGetData(drawImage, &data);
|
|
|
|
|
if (ret != HSUCCEED) {
|
|
|
|
|
HFLogPrint(HF_LOG_ERROR, "Get ImageBitmap data error: %d", ret);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
2025-05-22 16:07:26 +08:00
|
|
|
for (index = 0; index < faceNum; ++index) {
|
|
|
|
|
HInt32 numOfLmk;
|
|
|
|
|
HPoint2f* denseLandmarkPoints;
|
|
|
|
|
HPoint2f fiveKeyPoints[5];
|
|
|
|
|
float area;
|
|
|
|
|
size_t i;
|
|
|
|
|
|
2025-03-25 00:51:26 +08:00
|
|
|
HFLogPrint(HF_LOG_INFO, "========================================");
|
|
|
|
|
HFLogPrint(HF_LOG_INFO, "Token size: %d", multipleFaceData.tokens[index].size);
|
|
|
|
|
HFLogPrint(HF_LOG_INFO, "Process face index: %d", index);
|
|
|
|
|
HFLogPrint(HF_LOG_INFO, "DetConfidence: %f", multipleFaceData.detConfidence[index]);
|
2025-06-16 13:19:38 +08:00
|
|
|
HFLogPrint(HF_LOG_INFO, "TrackCount: %d", multipleFaceData.trackCounts[index]);
|
2025-05-22 16:07:26 +08:00
|
|
|
|
|
|
|
|
HFImageBitmapDrawRect(drawImage, multipleFaceData.rects[index], (HColor){0, 100, 255}, 4);
|
2025-03-25 00:51:26 +08:00
|
|
|
|
2025-05-22 16:07:26 +08:00
|
|
|
/* Get the number of dense landmark points */
|
2025-03-25 00:51:26 +08:00
|
|
|
HFGetNumOfFaceDenseLandmark(&numOfLmk);
|
2025-05-22 16:07:26 +08:00
|
|
|
denseLandmarkPoints = (HPoint2f*)malloc(sizeof(HPoint2f) * numOfLmk);
|
|
|
|
|
if (denseLandmarkPoints == NULL) {
|
|
|
|
|
HFLogPrint(HF_LOG_ERROR, "Memory allocation failed!");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-25 00:51:26 +08:00
|
|
|
ret = HFGetFaceDenseLandmarkFromFaceToken(multipleFaceData.tokens[index], denseLandmarkPoints, numOfLmk);
|
|
|
|
|
if (ret != HSUCCEED) {
|
2025-05-22 16:07:26 +08:00
|
|
|
free(denseLandmarkPoints);
|
2025-03-25 00:51:26 +08:00
|
|
|
HFLogPrint(HF_LOG_ERROR, "HFGetFaceDenseLandmarkFromFaceToken error!!");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2025-05-22 16:07:26 +08:00
|
|
|
|
|
|
|
|
/* Draw dense landmark points */
|
|
|
|
|
for (i = 0; i < numOfLmk; i++) {
|
|
|
|
|
HFImageBitmapDrawCircleF(drawImage,
|
|
|
|
|
(HPoint2f){denseLandmarkPoints[i].x, denseLandmarkPoints[i].y},
|
|
|
|
|
0,
|
|
|
|
|
(HColor){100, 100, 0},
|
|
|
|
|
2);
|
2025-03-25 00:51:26 +08:00
|
|
|
}
|
2025-05-22 16:07:26 +08:00
|
|
|
free(denseLandmarkPoints);
|
|
|
|
|
|
|
|
|
|
HFaceRect rt = multipleFaceData.rects[index];
|
|
|
|
|
area = ((float)(rt.height * rt.width)) / (data.width * data.height);
|
2025-03-25 00:51:26 +08:00
|
|
|
HFLogPrint(HF_LOG_INFO, "area: %f", area);
|
|
|
|
|
|
|
|
|
|
ret = HFGetFaceFiveKeyPointsFromFaceToken(multipleFaceData.tokens[index], fiveKeyPoints, 5);
|
|
|
|
|
if (ret != HSUCCEED) {
|
|
|
|
|
HFLogPrint(HF_LOG_ERROR, "HFGetFaceFiveKeyPointsFromFaceToken error!!");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2025-05-22 16:07:26 +08:00
|
|
|
for (i = 0; i < 5; i++) {
|
|
|
|
|
HFImageBitmapDrawCircleF(drawImage, (HPoint2f){fiveKeyPoints[i].x, fiveKeyPoints[i].y}, 0, (HColor){0, 0, 232}, 2);
|
2025-03-25 00:51:26 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
HFImageBitmapWriteToFile(drawImage, "draw_detected.jpg");
|
|
|
|
|
HFLogPrint(HF_LOG_WARN, "Write to file success: %s", "draw_detected.jpg");
|
|
|
|
|
|
2025-05-22 16:07:26 +08:00
|
|
|
/* Run pipeline function */
|
|
|
|
|
/* Select the pipeline function that you want to execute, provided that it is already enabled
|
|
|
|
|
* when FaceContext is created! */
|
2025-06-15 01:41:04 +08:00
|
|
|
pipelineOption = HF_ENABLE_QUALITY | HF_ENABLE_MASK_DETECT | HF_ENABLE_LIVENESS | HF_ENABLE_FACE_EMOTION;
|
2025-05-22 16:07:26 +08:00
|
|
|
/* In this loop, all faces are processed */
|
2025-03-25 00:51:26 +08:00
|
|
|
ret = HFMultipleFacePipelineProcessOptional(session, imageHandle, &multipleFaceData, pipelineOption);
|
|
|
|
|
if (ret != HSUCCEED) {
|
|
|
|
|
HFLogPrint(HF_LOG_ERROR, "Execute Pipeline error: %d", ret);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-22 16:07:26 +08:00
|
|
|
/* Get mask detection results from the pipeline cache */
|
2025-03-25 00:51:26 +08:00
|
|
|
ret = HFGetFaceMaskConfidence(session, &maskConfidence);
|
|
|
|
|
if (ret != HSUCCEED) {
|
|
|
|
|
HFLogPrint(HF_LOG_ERROR, "Get mask detect result error: %d", ret);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-22 16:07:26 +08:00
|
|
|
/* Get face quality results from the pipeline cache */
|
2025-03-25 00:51:26 +08:00
|
|
|
ret = HFGetFaceQualityConfidence(session, &qualityConfidence);
|
|
|
|
|
if (ret != HSUCCEED) {
|
|
|
|
|
HFLogPrint(HF_LOG_ERROR, "Get face quality result error: %d", ret);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-15 01:41:04 +08:00
|
|
|
ret = HFGetFaceEmotionResult(session, &faceEmotionResult);
|
|
|
|
|
if (ret != HSUCCEED) {
|
|
|
|
|
HFLogPrint(HF_LOG_ERROR, "Get face emotion result error: %d", ret);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-22 16:07:26 +08:00
|
|
|
for (index = 0; index < faceNum; ++index) {
|
2025-03-25 00:51:26 +08:00
|
|
|
HFLogPrint(HF_LOG_INFO, "========================================");
|
|
|
|
|
HFLogPrint(HF_LOG_INFO, "Process face index from pipeline: %d", index);
|
|
|
|
|
HFLogPrint(HF_LOG_INFO, "Mask detect result: %f", maskConfidence.confidence[index]);
|
|
|
|
|
HFLogPrint(HF_LOG_INFO, "Quality predict result: %f", qualityConfidence.confidence[index]);
|
2025-06-15 01:41:04 +08:00
|
|
|
HFLogPrint(HF_LOG_INFO, "Emotion result: %d", faceEmotionResult.emotion[index]);
|
2025-05-22 16:07:26 +08:00
|
|
|
/* We set the threshold of wearing a mask as 0.85. If it exceeds the threshold, it will be
|
|
|
|
|
* judged as wearing a mask. The threshold can be adjusted according to the scene */
|
2025-03-25 00:51:26 +08:00
|
|
|
if (maskConfidence.confidence[index] > 0.85) {
|
|
|
|
|
HFLogPrint(HF_LOG_INFO, "Mask");
|
|
|
|
|
} else {
|
|
|
|
|
HFLogPrint(HF_LOG_INFO, "Non Mask");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = HFReleaseImageStream(imageHandle);
|
|
|
|
|
if (ret != HSUCCEED) {
|
|
|
|
|
HFLogPrint(HF_LOG_ERROR, "Release image stream error: %d", ret);
|
|
|
|
|
}
|
2025-05-22 16:07:26 +08:00
|
|
|
/* The memory must be freed at the end of the program */
|
2025-03-25 00:51:26 +08:00
|
|
|
ret = HFReleaseInspireFaceSession(session);
|
|
|
|
|
if (ret != HSUCCEED) {
|
|
|
|
|
HFLogPrint(HF_LOG_ERROR, "Release session error: %d", ret);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = HFReleaseImageBitmap(image);
|
|
|
|
|
if (ret != HSUCCEED) {
|
|
|
|
|
HFLogPrint(HF_LOG_ERROR, "Release image bitmap error: %d", ret);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = HFReleaseImageBitmap(drawImage);
|
|
|
|
|
if (ret != HSUCCEED) {
|
|
|
|
|
HFLogPrint(HF_LOG_ERROR, "Release draw image bitmap error: %d", ret);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-22 16:07:26 +08:00
|
|
|
HFLogPrint(HF_LOG_INFO, "");
|
|
|
|
|
HFDeBugShowResourceStatistics();
|
|
|
|
|
|
2025-03-25 00:51:26 +08:00
|
|
|
return 0;
|
|
|
|
|
}
|