Skip to content

Commit 3ec23bb

Browse files
authored
Merge pull request #76 from HyperInspire/dev/action
Dev/action Former-commit-id: bc9fe17
2 parents 33299ce + 4818ecd commit 3ec23bb

File tree

17 files changed

+424
-87
lines changed

17 files changed

+424
-87
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
99
# Current version
1010
set(INSPIRE_FACE_VERSION_MAJOR 1)
1111
set(INSPIRE_FACE_VERSION_MINOR 1)
12-
set(INSPIRE_FACE_VERSION_PATCH 4)
12+
set(INSPIRE_FACE_VERSION_PATCH 5)
1313

1414
# Converts the version number to a string
1515
string(CONCAT INSPIRE_FACE_VERSION_MAJOR_STR ${INSPIRE_FACE_VERSION_MAJOR})

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ Please contact [contact@insightface.ai](mailto:contact@insightface.ai?subject=In
1313

1414
## Change Logs
1515

16+
**`2024-07-07`** Add some face action detection to the face interaction module.
17+
1618
**`2024-07-05`** Fixed some bugs in the python ctypes interface.
1719

1820
**`2024-07-03`** Add the blink detection algorithm of face interaction module.

cpp/inspireface/c_api/inspireface.cc

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -500,9 +500,14 @@ HResult HFMultipleFacePipelineProcess(HFSession session, HFImageStream streamHan
500500
if (stream == nullptr) {
501501
return HERR_INVALID_IMAGE_STREAM_HANDLE;
502502
}
503-
if (faces->detectedNum <= 0 || faces->tokens->data == nullptr) {
503+
if (faces->tokens->data == nullptr) {
504504
return HERR_INVALID_FACE_LIST;
505505
}
506+
507+
if (faces->detectedNum == 0) {
508+
return HSUCCEED;
509+
}
510+
506511
inspire::ContextCustomParameter param;
507512
param.enable_mask_detect = parameter.enable_mask_detect;
508513
param.enable_face_attribute = parameter.enable_face_quality;
@@ -545,9 +550,12 @@ HResult HFMultipleFacePipelineProcessOptional(HFSession session, HFImageStream s
545550
if (stream == nullptr) {
546551
return HERR_INVALID_IMAGE_STREAM_HANDLE;
547552
}
548-
if (faces->detectedNum <= 0 || faces->tokens->data == nullptr) {
553+
if (faces->tokens->data == nullptr) {
549554
return HERR_INVALID_FACE_LIST;
550555
}
556+
if (faces->detectedNum == 0) {
557+
return HSUCCEED;
558+
}
551559

552560
inspire::ContextCustomParameter param;
553561
if (customOption & HF_ENABLE_FACE_RECOGNITION) {
@@ -572,7 +580,6 @@ HResult HFMultipleFacePipelineProcessOptional(HFSession session, HFImageStream s
572580
param.enable_interaction_liveness = true;
573581
}
574582

575-
576583
HResult ret;
577584
std::vector<inspire::HyperFaceData> data;
578585
data.resize(faces->detectedNum);
@@ -654,8 +661,8 @@ HResult HFFaceQualityDetect(HFSession session, HFFaceBasicToken singleFace, HFlo
654661

655662
}
656663

657-
HResult HFGetFaceIntereactionResult(HFSession session, PHFFaceIntereactionResult result) {
658-
if (session == nullptr) {
664+
HResult HFGetFaceIntereactionStateResult(HFSession session, PHFFaceIntereactionState result) {
665+
if (session == nullptr) {
659666
return HERR_INVALID_CONTEXT_HANDLE;
660667
}
661668
HF_FaceAlgorithmSession *ctx = (HF_FaceAlgorithmSession* ) session;
@@ -669,6 +676,24 @@ HResult HFGetFaceIntereactionResult(HFSession session, PHFFaceIntereactionResult
669676
return HSUCCEED;
670677
}
671678

679+
HResult HFGetFaceIntereactionActionsResult(HFSession session, PHFFaceIntereactionsActions actions) {
680+
if (session == nullptr) {
681+
return HERR_INVALID_CONTEXT_HANDLE;
682+
}
683+
HF_FaceAlgorithmSession *ctx = (HF_FaceAlgorithmSession* ) session;
684+
if (ctx == nullptr) {
685+
return HERR_INVALID_CONTEXT_HANDLE;
686+
}
687+
actions->num = ctx->impl.GetFaceNormalAactionsResultCache().size();
688+
actions->normal = (HInt32* )ctx->impl.GetFaceNormalAactionsResultCache().data();
689+
actions->blink = (HInt32* )ctx->impl.GetFaceBlinkAactionsResultCache().data();
690+
actions->shake = (HInt32* )ctx->impl.GetFaceShakeAactionsResultCache().data();
691+
actions->headRiase = (HInt32* )ctx->impl.GetFaceRaiseHeadAactionsResultCache().data();
692+
actions->jawOpen = (HInt32* )ctx->impl.GetFaceJawOpenAactionsResultCache().data();
693+
694+
return HSUCCEED;
695+
}
696+
672697
HResult HFGetFaceAttributeResult(HFSession session, PHFFaceAttributeResult results) {
673698
if (session == nullptr) {
674699
return HERR_INVALID_CONTEXT_HANDLE;

cpp/inspireface/c_api/inspireface.h

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -638,19 +638,38 @@ HYPER_CAPI_EXPORT extern HResult HFFaceQualityDetect(HFSession session, HFFaceBa
638638
/**
639639
* @brief Facial states in the face interaction module.
640640
*/
641-
typedef struct HFFaceIntereactionResult {
641+
typedef struct HFFaceIntereactionState {
642642
HInt32 num; ///< Number of faces detected.
643643
HPFloat leftEyeStatusConfidence; ///< Left eye state: confidence close to 1 means open, close to 0 means closed.
644644
HPFloat rightEyeStatusConfidence; ///< Right eye state: confidence close to 1 means open, close to 0 means closed.
645-
} HFFaceIntereactionResult, *PHFFaceIntereactionResult;
645+
} HFFaceIntereactionState, *PHFFaceIntereactionState;
646646

647647
/**
648648
* @brief Get the prediction results of face interaction.
649649
* @param session Handle to the session.
650650
* @param result Facial state prediction results in the face interaction module.
651651
*/
652-
HYPER_CAPI_EXPORT extern HResult HFGetFaceIntereactionResult(HFSession session, PHFFaceIntereactionResult result);
652+
HYPER_CAPI_EXPORT extern HResult HFGetFaceIntereactionStateResult(HFSession session, PHFFaceIntereactionState result);
653653

654+
/**
655+
* @brief Actions detected in the face interaction module.
656+
*/
657+
typedef struct HFFaceIntereactionsActions {
658+
HInt32 num; ///< Number of actions detected.
659+
HPInt32 normal; ///< Normal actions.
660+
HPInt32 shake; ///< Shake actions.
661+
HPInt32 jawOpen; ///< Jaw open actions.
662+
HPInt32 headRiase; ///< Head raise actions.
663+
HPInt32 blink; ///< Blink actions.
664+
} HFFaceIntereactionsActions, *PHFFaceIntereactionsActions;
665+
666+
/**
667+
* @brief Get the prediction results of face interaction actions.
668+
* @param session Handle to the session.
669+
* @param actions Facial action prediction results in the face interaction module.
670+
* @return HResult indicating success or failure of the function call.
671+
*/
672+
HYPER_CAPI_EXPORT extern HResult HFGetFaceIntereactionActionsResult(HFSession session, PHFFaceIntereactionsActions actions);
654673
/**
655674
* @brief Struct representing face attribute results.
656675
*
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
#ifndef INSPIRSE_FACE_FACE_ACTION_H
2+
#define INSPIRSE_FACE_FACE_ACTION_H
3+
4+
#include <iostream>
5+
#include "opencv2/opencv.hpp"
6+
#include "middleware/utils.h"
7+
#include "data_type.h"
8+
#include "track_module/landmark/face_landmark.h"
9+
10+
namespace inspire {
11+
12+
enum FACE_ACTION {
13+
NORMAL = 0,
14+
SHAKE = 0,
15+
BLINK = 1,
16+
JAW_OPEN = 2,
17+
RAISE_HEAD = 3
18+
};
19+
20+
typedef struct FaceActions{
21+
int normal = 0;
22+
int shake = 0;
23+
int blink = 0;
24+
int jawOpen = 0;
25+
int raiseHead = 0;
26+
} FaceActions;
27+
28+
class INSPIRE_API FaceActionAnalyse {
29+
public:
30+
FaceActionAnalyse(int record_list_length) {
31+
record_list.resize(record_list_length);
32+
record_list_euler.resize(record_list_length);
33+
record_list_eyes.resize(record_list_length);
34+
record_size = record_list_length;
35+
index = 0;
36+
}
37+
38+
void RecordActionFrame(const std::vector<cv::Point2f> &landmark,
39+
const cv::Vec3f &euler_angle,
40+
const cv::Vec2f &eyes_status) {
41+
MoveRecordList();
42+
record_list[0] = landmark;
43+
record_list_euler[0] = euler_angle;
44+
record_list_eyes[0] = eyes_status;
45+
index += 1;
46+
}
47+
48+
void Reset() {
49+
record_list.clear();
50+
record_list.resize(record_size);
51+
record_list_euler.clear();
52+
record_list_euler.resize(record_size);
53+
record_list_eyes.clear();
54+
record_list_eyes.resize(record_size);
55+
index = 0;
56+
}
57+
58+
FaceActions AnalysisFaceAction() {
59+
FaceActions actionRecord;
60+
actions.clear();
61+
eye_state_list.clear();
62+
if (index < record_list.size()) {
63+
actions.push_back(NORMAL);
64+
actionRecord.normal = 1;
65+
} else {
66+
for (int i = 0; i < record_list_eyes.size(); i++) {
67+
const auto &eye = record_list_eyes[i];
68+
std::pair<float, float> eye_state(eye[0], eye[1]);
69+
eye_state_list.push_back(eye_state);
70+
}
71+
72+
// count mouth aspect ratio
73+
float mouth_widthwise_d =
74+
PointDistance(record_list[0][FaceLandmark::MOUTH_LEFT_CORNER],
75+
record_list[0][FaceLandmark::MOUTH_RIGHT_CORNER]);
76+
float mouth_heightwise_d =
77+
PointDistance(record_list[0][FaceLandmark::MOUTH_UPPER],
78+
record_list[0][FaceLandmark::MOUTH_LOWER]);
79+
float mouth_aspect_ratio = mouth_heightwise_d / mouth_widthwise_d;
80+
if (mouth_aspect_ratio > 0.3) {
81+
actions.push_back(JAW_OPEN);
82+
actionRecord.jawOpen = 1;
83+
}
84+
85+
int counter_eye_open = 0;
86+
int counter_eye_close = 0;
87+
for (auto &e : eye_state_list) {
88+
if (e.first < 0.5 || e.second < 0.5) {
89+
counter_eye_close += 1;
90+
}
91+
if (e.first > 0.5 || e.second > 0.5) {
92+
counter_eye_open += 1;
93+
}
94+
}
95+
if (counter_eye_close > 0 && counter_eye_open > 2 &&
96+
record_list_euler[0][1] > -6 && record_list_euler[0][0] < 6) {
97+
actions.push_back(BLINK);
98+
actionRecord.blink = 1;
99+
Reset();
100+
}
101+
102+
bool counter_head_shake_left = false;
103+
bool counter_head_shake_right = false;
104+
for (auto &e : record_list_euler) {
105+
if (e[1] < -6) {
106+
counter_head_shake_left = true;
107+
}
108+
if (e[1] > 6) {
109+
counter_head_shake_right = true;
110+
}
111+
}
112+
if (counter_head_shake_left && counter_head_shake_right) {
113+
actions.push_back(SHAKE);
114+
actionRecord.shake = 1;
115+
}
116+
117+
if (record_list_euler[0][0] > 10) {
118+
actions.push_back(RAISE_HEAD);
119+
actionRecord.raiseHead = 1;
120+
}
121+
122+
}
123+
return actionRecord;
124+
}
125+
126+
std::vector<FACE_ACTION> GetActions() const {
127+
return actions;
128+
}
129+
130+
private:
131+
void MoveRecordList() {
132+
// for(int i = 0 ; i < record_list.size() - 1 ; i++){
133+
// record_list[i+1] = record_list[i];
134+
// record_list_euler[i+1] = record_list_euler[i];
135+
//}
136+
for (int i = record_list.size() - 1; i > 0; i--) {
137+
record_list[i] = record_list[i - 1];
138+
record_list_euler[i] = record_list_euler[i - 1];
139+
record_list_eyes[i] = record_list_eyes[i - 1];
140+
}
141+
}
142+
143+
144+
std::vector<std::vector<cv::Point2f>> record_list;
145+
std::vector<cv::Vec3f> record_list_euler;
146+
std::vector<cv::Vec2f> record_list_eyes;
147+
std::vector<std::pair<float, float>> eye_state_list; // pair left right
148+
std::vector<float> mouth_state_list;
149+
std::vector<FACE_ACTION> actions;
150+
int record_size;
151+
int index;
152+
};
153+
154+
} // namespace inspire
155+
156+
#endif

cpp/inspireface/common/face_info/face_object.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "data_type.h"
1111
#include "face_process.h"
1212
#include "track_module/quality/face_pose_quality.h"
13+
#include "face_action.h"
1314

1415
namespace inspire {
1516

@@ -28,7 +29,7 @@ class INSPIRE_API FaceObject {
2829
tracking_count_ = 0;
2930
pose_euler_angle_.resize(3);
3031
keyPointFive.resize(5);
31-
// face_action_ = std::make_shared<FaceAction>(10);
32+
face_action_ = std::make_shared<FaceActionAnalyse>(10);
3233
}
3334

3435
void UpdateMatrix(const cv::Mat &matrix) {
@@ -137,9 +138,11 @@ class INSPIRE_API FaceObject {
137138
return box_square;
138139
}
139140

140-
void UpdateFaceAction() {
141-
// face_action_->RecordActionFrame(landmark_, euler_angle_);
142-
// face_action_->AnalysisFaceAction();
141+
FaceActions UpdateFaceAction() {
142+
cv::Vec3f euler(high_result.pitch, high_result.yaw, high_result.roll);
143+
cv::Vec2f eyes(left_eye_status_.back(), right_eye_status_.back());
144+
face_action_->RecordActionFrame(landmark_, euler, eyes);
145+
return face_action_->AnalysisFaceAction();
143146
}
144147

145148
void DisableTracking() { tracking_state_ = UNTRACKING; }
@@ -318,7 +321,7 @@ class INSPIRE_API FaceObject {
318321

319322
private:
320323
TRACK_STATE tracking_state_;
321-
// std::shared_ptr<FaceAction> face_action_;
324+
std::shared_ptr<FaceActionAnalyse> face_action_;
322325
int face_id_;
323326
};
324327

cpp/inspireface/face_context.cpp

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,13 @@ int32_t FaceContext::FaceDetectAndTrack(CameraStream &image) {
6363
m_quality_score_results_cache_.clear();
6464
m_react_left_eye_results_cache_.clear();
6565
m_react_right_eye_results_cache_.clear();
66+
67+
m_action_normal_results_cache_.clear();
68+
m_action_shake_results_cache_.clear();
69+
m_action_blink_results_cache_.clear();
70+
m_action_jaw_open_results_cache_.clear();
71+
m_action_raise_head_results_cache_.clear();
72+
6673
m_quality_score_results_cache_.clear();
6774
m_attribute_race_results_cache_.clear();
6875
m_attribute_gender_results_cache_.clear();
@@ -138,6 +145,11 @@ int32_t FaceContext::FacesProcess(CameraStream &image, const std::vector<HyperFa
138145
m_attribute_race_results_cache_.resize(faces.size(), -1);
139146
m_attribute_gender_results_cache_.resize(faces.size(), -1);
140147
m_attribute_age_results_cache_.resize(faces.size(), -1);
148+
m_action_normal_results_cache_.resize(faces.size(), -1);
149+
m_action_jaw_open_results_cache_.resize(faces.size(), -1);
150+
m_action_blink_results_cache_.resize(faces.size(), -1);
151+
m_action_raise_head_results_cache_.resize(faces.size(), -1);
152+
m_action_shake_results_cache_.resize(faces.size(), -1);
141153
for (int i = 0; i < faces.size(); ++i) {
142154
const auto &face = faces[i];
143155
// RGB Liveness Detect
@@ -190,7 +202,12 @@ int32_t FaceContext::FacesProcess(CameraStream &image, const std::vector<HyperFa
190202
m_react_left_eye_results_cache_[i] = new_eye_left;
191203
m_react_right_eye_results_cache_[i] = new_eye_right;
192204
}
193-
205+
const auto actions = target.UpdateFaceAction();
206+
m_action_normal_results_cache_[i] = actions.normal;
207+
m_action_jaw_open_results_cache_[i] = actions.jawOpen;
208+
m_action_blink_results_cache_[i] = actions.blink;
209+
m_action_raise_head_results_cache_[i] = actions.raiseHead;
210+
m_action_shake_results_cache_[i] = actions.shake;
194211
} else {
195212
INSPIRE_LOGD("Serialized objects cannot connect to trace objects in memory, and there may be some problems");
196213
}
@@ -274,6 +291,26 @@ const std::vector<int>& FaceContext::GetFaceAgeBracketResultsCache() const {
274291
return m_attribute_age_results_cache_;
275292
}
276293

294+
const std::vector<int>& FaceContext::GetFaceNormalAactionsResultCache() const {
295+
return m_action_normal_results_cache_;
296+
}
297+
298+
const std::vector<int>& FaceContext::GetFaceJawOpenAactionsResultCache() const {
299+
return m_action_jaw_open_results_cache_;
300+
}
301+
302+
const std::vector<int>& FaceContext::GetFaceBlinkAactionsResultCache() const {
303+
return m_action_blink_results_cache_;
304+
}
305+
306+
const std::vector<int>& FaceContext::GetFaceShakeAactionsResultCache() const {
307+
return m_action_shake_results_cache_;
308+
}
309+
310+
const std::vector<int>& FaceContext::GetFaceRaiseHeadAactionsResultCache() const {
311+
return m_action_raise_head_results_cache_;
312+
}
313+
277314
int32_t FaceContext::FaceFeatureExtract(CameraStream &image, FaceBasicData& data) {
278315
std::lock_guard<std::mutex> lock(m_mtx_);
279316
int32_t ret;

0 commit comments

Comments
 (0)