first phrase
This commit is contained in:
109
reproject/new_emotion_test.py
Normal file
109
reproject/new_emotion_test.py
Normal file
@@ -0,0 +1,109 @@
|
||||
import cv2
|
||||
import numpy as np
|
||||
import math
|
||||
from hsemotion_onnx.facial_emotions import HSEmotionRecognizer
|
||||
|
||||
EMOTION_VA_MAP = {
|
||||
'happy': (0.85, 0.60),
|
||||
'sad': (-0.75, -0.60),
|
||||
'angry': (-0.70, 0.80),
|
||||
'fear': (-0.65, 0.75),
|
||||
'surprise': (0.20, 0.85),
|
||||
'disgust': (-0.80, 0.40),
|
||||
'neutral': (0.00, 0.00),
|
||||
'contempt': (-0.60, 0.50),
|
||||
}
|
||||
|
||||
EMOTION_HANDLE = {
|
||||
'happiness': 'happy',
|
||||
'sadness': 'sad',
|
||||
'anger': 'angry',
|
||||
'fear': 'fear',
|
||||
'surprise': 'surprise',
|
||||
'disgust': 'disgust',
|
||||
'neutral': 'neutral',
|
||||
'contempt': 'contempt',
|
||||
}
|
||||
|
||||
def get_fine_grained_emotion(valence, arousal):
|
||||
radius = math.sqrt(valence**2 + arousal**2)
|
||||
angle = math.degrees(math.atan2(arousal, valence))
|
||||
|
||||
if radius < 0.25:
|
||||
return "neutral"
|
||||
|
||||
if 0 <= angle < 90:
|
||||
if angle > 60: return "excited"
|
||||
elif angle > 30: return "happy"
|
||||
else: return "pleased"
|
||||
elif 90 <= angle <= 180:
|
||||
if angle > 150: return "nervous"
|
||||
elif angle > 120: return "angry"
|
||||
else: return "annoying"
|
||||
elif -180 <= angle < -90:
|
||||
if angle < -150: return "sad"
|
||||
elif angle < -120: return "bored"
|
||||
else: return "sleepy"
|
||||
elif -90 <= angle < 0:
|
||||
if angle < -60: return "calm"
|
||||
elif angle < -30: return "peaceful"
|
||||
else: return "relaxed"
|
||||
|
||||
return "neutral"
|
||||
|
||||
class EmotionAnalyzer:
|
||||
def __init__(self):
|
||||
print("正在加载 HSEmotion-ONNX 模型...")
|
||||
self.fer = HSEmotionRecognizer(model_name='enet_b0_8_best_vgaf')
|
||||
print("HSEmotion-ONNX 模型加载完成")
|
||||
|
||||
def calculate_va_score(self, emotion_prob):
|
||||
valence_sum = 0.0
|
||||
arousal_sum = 0.0
|
||||
total_prob = 0.0
|
||||
|
||||
for emotion, prob in emotion_prob.items():
|
||||
key = EMOTION_HANDLE.get(emotion.lower(), emotion.lower())
|
||||
if key in EMOTION_VA_MAP:
|
||||
v, a = EMOTION_VA_MAP[key]
|
||||
valence_sum += v * prob
|
||||
arousal_sum += a * prob
|
||||
total_prob += prob
|
||||
|
||||
if total_prob == 0:
|
||||
return 0.0, 0.0
|
||||
return valence_sum, arousal_sum
|
||||
|
||||
def analyze(self, face_img_bgr):
|
||||
if face_img_bgr is None or face_img_bgr.size == 0:
|
||||
return []
|
||||
|
||||
face_img_rgb = cv2.cvtColor(face_img_bgr, cv2.COLOR_BGR2RGB)
|
||||
|
||||
# predict_emotions 返回主要情绪标签和概率数组
|
||||
emotion_raw, scores = self.fer.predict_emotions(face_img_rgb, logits=False)
|
||||
|
||||
probabilities = {}
|
||||
for idx, score in enumerate(scores):
|
||||
raw_label = self.fer.idx_to_class[idx]
|
||||
key = EMOTION_HANDLE.get(raw_label.lower(), raw_label.lower())
|
||||
|
||||
probabilities[key] = float(score)
|
||||
|
||||
valence, arousal = self.calculate_va_score(probabilities)
|
||||
fine_grained_label = get_fine_grained_emotion(valence, arousal)
|
||||
|
||||
result = {
|
||||
"box": {},
|
||||
"vaVal": (round(valence, 4), round(arousal, 4)),
|
||||
"probabilities": probabilities,
|
||||
"dominant_emotion": EMOTION_HANDLE.get(emotion_raw.lower(), emotion_raw.lower()),
|
||||
"emotion": fine_grained_label
|
||||
}
|
||||
|
||||
return [result]
|
||||
|
||||
analyzer_instance = EmotionAnalyzer()
|
||||
|
||||
def analyze_emotion_with_hsemotion(face_crop_bgr):
|
||||
return analyzer_instance.analyze(face_crop_bgr)
|
||||
Reference in New Issue
Block a user