private void _updateFacesList(JArray ja) { ObservableCollection <Face> newColl = new ObservableCollection <Face>(); // Main face computation int mainFaceID = -1; float maxHeadSize = -1; // Copy new faces in existing list if (ja != null) { foreach (var v in ja) { // Parse basic info int id = int.Parse(v["id"].ToString()); string gender = v["gender"].ToString(); string age = v["age"].ToString(); var ageInteger = Int16.Parse(age); string ageRange = ageInteger <= 16 ? "child" : ageInteger <= 30 ? "young adult" : ageInteger <= 45 ? "middle-aged adult" : "old-aged adult"; var x = float.Parse(v["location"]["x"].ToString(), CultureInfo.InvariantCulture); // TODO: normalize? (or on C++ side?) var y = float.Parse(v["location"]["y"].ToString(), CultureInfo.InvariantCulture); var width = float.Parse(v["location"]["width"].ToString(), CultureInfo.InvariantCulture); var height = float.Parse(v["location"]["height"].ToString(), CultureInfo.InvariantCulture); // Intuiface coordinates system: move X&Y coords to represent the center of the head, not the top left corner x += width / 2; y += height / 2; // TODO: estimate distance in cm based on head size and camera focal / calibration step ?? var faceSize = (int)(width * height * 100 * 100); // Current value: % of head area over total image area var mainEmotion = v["mainEmotion"]["emotion"].ToString(); var mainEmotionConfidence = float.Parse(v["mainEmotion"]["confidence"].ToString(), CultureInfo.InvariantCulture); // Parse additional emotions EmotionConfidence emotionConfidence = new EmotionConfidence(); emotionConfidence.Angry = float.Parse(v["emotions"]["anger"].ToString(), CultureInfo.InvariantCulture); emotionConfidence.Happy = float.Parse(v["emotions"]["happy"].ToString(), CultureInfo.InvariantCulture); emotionConfidence.Neutral = float.Parse(v["emotions"]["neutral"].ToString(), CultureInfo.InvariantCulture); emotionConfidence.Sad = float.Parse(v["emotions"]["sad"].ToString(), CultureInfo.InvariantCulture); emotionConfidence.Surprised = float.Parse(v["emotions"]["surprise"].ToString(), CultureInfo.InvariantCulture); // Parse head pose estimation HeadPoseEstimation headPoseEstimation = new HeadPoseEstimation(); headPoseEstimation.Pitch = float.Parse(v["headpose"]["pitch"].ToString(), CultureInfo.InvariantCulture); headPoseEstimation.Yaw = float.Parse(v["headpose"]["yaw"].ToString(), CultureInfo.InvariantCulture); headPoseEstimation.Roll = float.Parse(v["headpose"]["roll"].ToString(), CultureInfo.InvariantCulture); // If face size filtering is active, check is head is bigger than threshold if (faceSize < m_dMinimumFaceSize) { // Don't add face to list continue; } // TODO: use real "distance" and take the min one. if (faceSize > maxHeadSize) { maxHeadSize = faceSize; mainFaceID = id; } newColl.Add(new Face() { Id = id, X = x, Y = y, Width = width, Height = height, FaceSize = faceSize, Gender = gender, Age = age, AgeRange = ageRange, MainEmotion = mainEmotion, MainEmotionConfidence = mainEmotionConfidence, EmotionConfidence = emotionConfidence, HeadPoseEstimation = headPoseEstimation }); } } // Order by ID newColl = new ObservableCollection <Face>(newColl.OrderBy(i => i.Id)); // Compare old (current) list and new list var removed = Faces.Except(newColl); var added = newColl.Except(Faces); if (removed.Count() > 0) { foreach (var face in removed) { // Raise face lost event Console.WriteLine("Face lost: " + face); RaiseFaceLost(face.Id, face.Gender, face.Age, face.DwellTime); m_startTimeMap.Remove(face.Id); } } if (added.Count() > 0) { foreach (var face in added) { // Raise face added event Console.WriteLine("Face added: " + face); RaiseFaceDetected(face.Id, face.Gender, face.Age); m_startTimeMap.Add(face.Id, DateTime.Now); } } // Update elements in current list with new list int newCount = newColl.Count; int oldCount = Faces.Count; if (newCount != oldCount) { // Raise face count changed event Console.WriteLine("Face count changed: " + newCount); RaiseFaceCountChanged(newCount); } // Adjust number of faces in Faces // New users to add if (newCount > oldCount) { for (int i = 0; i < newCount - oldCount; i++) { Faces.Add(new Face()); } } // Users to remove else if (newCount < oldCount) { for (int i = newCount; i < oldCount; i++) { Faces.RemoveAt(newCount); } } // Copy new coll properties in existing faces list. int index = 0; foreach (var item in newColl) { try { Faces[index].Id = item.Id; Faces[index].X = item.X; Faces[index].Y = item.Y; Faces[index].Width = item.Width; Faces[index].Height = item.Height; Faces[index].Gender = item.Gender; Faces[index].Age = item.Age; Faces[index].AgeRange = item.AgeRange; Faces[index].DwellTime = (DateTime.Now - m_startTimeMap[item.Id]).TotalSeconds; Faces[index].FaceSize = item.FaceSize; Faces[index].MainEmotion = item.MainEmotion; Faces[index].MainEmotionConfidence = item.MainEmotionConfidence; // Additional emotions Faces[index].EmotionConfidence = item.EmotionConfidence; // Head pose estimation Faces[index].HeadPoseEstimation = item.HeadPoseEstimation; } catch (Exception ex) { ActivityLog += ex.Message + "\n"; throw; } index++; } // Update face count FaceCount = Faces.Count(); // Copy main face info if (FaceCount > 0) { var item = Faces.FirstOrDefault(i => i.Id == mainFaceID); if (item != null) { MainFace.Id = item.Id; MainFace.X = item.X; MainFace.Y = item.Y; MainFace.Width = item.Width; MainFace.Height = item.Height; MainFace.FaceSize = item.FaceSize; MainFace.Gender = item.Gender; MainFace.Age = item.Age; MainFace.AgeRange = item.AgeRange; MainFace.DwellTime = (DateTime.Now - m_startTimeMap[item.Id]).TotalSeconds; MainFace.MainEmotion = item.MainEmotion; MainFace.MainEmotionConfidence = item.MainEmotionConfidence; // Additional emotions MainFace.EmotionConfidence = item.EmotionConfidence; // Head pose estimation MainFace.HeadPoseEstimation = item.HeadPoseEstimation; // Mark the face detected IsMainFaceDetected = true; } else { IsMainFaceDetected = false; } } else { IsMainFaceDetected = false; } }