From bc0947fac18de7220dd3a738c8e8f1db8a8fdb2e Mon Sep 17 00:00:00 2001
From: Pim Hutting
Date: Mon, 19 Jan 2026 18:26:15 +0100
Subject: [PATCH] chore: added a dot
---
.../visual_emotion_recognition_agent.py | 53 ++++++++++---------
1 file changed, 29 insertions(+), 24 deletions(-)
diff --git a/src/control_backend/agents/perception/visual_emotion_recognition_agent/visual_emotion_recognition_agent.py b/src/control_backend/agents/perception/visual_emotion_recognition_agent/visual_emotion_recognition_agent.py
index 647ddac..465903b 100644
--- a/src/control_backend/agents/perception/visual_emotion_recognition_agent/visual_emotion_recognition_agent.py
+++ b/src/control_backend/agents/perception/visual_emotion_recognition_agent/visual_emotion_recognition_agent.py
@@ -6,23 +6,27 @@ import cv2
import numpy as np
import zmq
import zmq.asyncio as azmq
-from control_backend.agents.perception.visual_emotion_recognition_agentvisual_emotion_recognizer import ( # noqa
- DeepFaceEmotionRecognizer,
-)
from pydantic_core import ValidationError
from control_backend.agents import BaseAgent
+from control_backend.agents.perception.visual_emotion_recognition_agent.visual_emotion_recognizer import ( # noqa
+ DeepFaceEmotionRecognizer,
+)
from control_backend.core.agent_system import InternalMessage
from control_backend.core.config import settings
from control_backend.schemas.belief_message import Belief
class VisualEmotionRecognitionAgent(BaseAgent):
- def __init__(self, name: str, socket_address: str, bind: bool = False, timeout_ms: int = 1000,
- window_duration:
- int = settings.behaviour_settings.visual_emotion_recognition_window_duration_s
- , min_frames_required: int =
- settings.behaviour_settings.visual_emotion_recognition_min_frames_per_face):
+ def __init__(
+ self,
+ name: str,
+ socket_address: str,
+ bind: bool = False,
+ timeout_ms: int = 1000,
+ window_duration: int = settings.behaviour_settings.visual_emotion_recognition_window_duration_s, # noqa
+ min_frames_required: int = settings.behaviour_settings.visual_emotion_recognition_min_frames_per_face, # noqa
+ ):
"""
Initialize the Visual Emotion Recognition Agent.
@@ -31,7 +35,7 @@ class VisualEmotionRecognitionAgent(BaseAgent):
:param bind: Whether to bind to the socket address (True) or connect (False)
:param timeout_ms: Timeout for socket receive operations in milliseconds
:param window_duration: Duration in seconds over which to aggregate emotions
- :param min_frames_required: Minimum number of frames per face required to consider a face
+ :param min_frames_required: Minimum number of frames per face required to consider a face
valid
"""
super().__init__(name)
@@ -78,29 +82,29 @@ class VisualEmotionRecognitionAgent(BaseAgent):
# Tracks counts of detected emotions per face index
face_stats = defaultdict(Counter)
-
+
prev_dominant_emotions = set()
while self._running:
try:
frame_bytes = await self.video_in_socket.recv()
-
+
# Convert bytes to a numpy buffer
nparr = np.frombuffer(frame_bytes, np.uint8)
-
+
# Decode image into the generic Numpy Array DeepFace expects
frame_image = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
if frame_image is None:
# Could not decode image, skip this frame
continue
-
+
# Get the dominant emotion from each face
current_emotions = self.emotion_recognizer.sorted_dominant_emotions(frame_image)
# Update emotion counts for each detected face
for i, emotion in enumerate(current_emotions):
face_stats[i][emotion] += 1
-
+
# If window duration has passed, process the collected stats
if time.time() >= next_window_time:
window_dominant_emotions = set()
@@ -111,35 +115,36 @@ class VisualEmotionRecognitionAgent(BaseAgent):
if total_detections >= self.min_frames_required:
dominant_emotion = counter.most_common(1)[0][0]
window_dominant_emotions.add(dominant_emotion)
-
+
await self.update_emotions(prev_dominant_emotions, window_dominant_emotions)
prev_dominant_emotions = window_dominant_emotions
face_stats.clear()
next_window_time = time.time() + self.window_duration
-
+
except zmq.Again:
self.logger.warning("No video frame received within timeout.")
async def update_emotions(self, prev_emotions: set[str], emotions: set[str]):
"""
- Compare emotions from previous window and current emotions,
+ Compare emotions from previous window and current emotions,
send updates to BDI Core Agent.
"""
emotions_to_remove = prev_emotions - emotions
emotions_to_add = emotions - prev_emotions
if not emotions_to_add and not emotions_to_remove:
- return
-
+ return
+
emotion_beliefs_remove = []
for emotion in emotions_to_remove:
self.logger.info(f"Emotion '{emotion}' has disappeared.")
try:
- emotion_beliefs_remove.append(Belief(name="emotion_detected", arguments=[emotion],
- remove=True))
+ emotion_beliefs_remove.append(
+ Belief(name="emotion_detected", arguments=[emotion], remove=True)
+ )
except ValidationError:
self.logger.warning("Invalid belief for emotion removal: %s", emotion)
-
+
emotion_beliefs_add = []
for emotion in emotions_to_add:
self.logger.info(f"New emotion detected: '{emotion}'")
@@ -147,7 +152,7 @@ class VisualEmotionRecognitionAgent(BaseAgent):
emotion_beliefs_add.append(Belief(name="emotion_detected", arguments=[emotion]))
except ValidationError:
self.logger.warning("Invalid belief for new emotion: %s", emotion)
-
+
beliefs_list_add = [b.model_dump() for b in emotion_beliefs_add]
beliefs_list_remove = [b.model_dump() for b in emotion_beliefs_remove]
payload = {"create": beliefs_list_add, "delete": beliefs_list_remove}
@@ -158,4 +163,4 @@ class VisualEmotionRecognitionAgent(BaseAgent):
body=json.dumps(payload),
thread="beliefs",
)
- await self.send(message)
\ No newline at end of file
+ await self.send(message)