Example #1
0
        /// <summary>
        /// Split the given face on the given plane. Remove the original face
        /// and add as many new faces as required for the split.
        /// </summary>
        /// <param name="faceIndex">The index of the face to split.</param>
        /// <param name="plane">The plane to split the face on. The face will not be split
        /// if it is not intersected by this plane.</param>
        /// <param name="onPlaneDistance">If a given edge of the face has a vertex that is within
        /// this distance of the plane, the edge will not be split.</param>
        /// <returns>Returns if the edge was actually split.</returns>
        public bool SplitFace(int faceIndex, Plane plane, double onPlaneDistance = .001)
        {
            var newVertices = new List <Vector3Float>();
            var newFaces    = new List <Face>();

            if (Faces[faceIndex].Split(this.Vertices, plane, newFaces, newVertices, onPlaneDistance))
            {
                var vertexCount = Vertices.Count;
                // remove the face index
                Faces.RemoveAt(faceIndex);
                // add the new vertices
                Vertices.AddRange(newVertices);
                // add the new faces (have to make the vertex indices to the new vertices
                foreach (var newFace in newFaces)
                {
                    Face faceNewIndices = newFace;
                    faceNewIndices.v0 += vertexCount;
                    faceNewIndices.v1 += vertexCount;
                    faceNewIndices.v2 += vertexCount;
                    Faces.Add(faceNewIndices);
                }

                CleanAndMerge();

                return(true);
            }

            return(false);
        }
        /// <inheritdoc />
        public override List <Chain <TNode> > GetChains(IGraph <TNode> graph)
        {
            Initialize(graph);

            if (Faces.Count != 0)
            {
                // Get faces and remove the largest one
                Faces.RemoveAt(Faces.MaxBy(x => x.Count));
            }

            var decomposition = new PartialDecomposition(Faces);

            decomposition = GetFirstComponent(decomposition);

            while (decomposition.GetAllCoveredVertices().Count != Graph.VerticesCount)
            {
                decomposition = ExtendDecomposition(decomposition);
            }

            var chains = decomposition.GetFinalDecomposition();

            logger.WriteLine("Final decomposition:");
            foreach (var chain in chains)
            {
                logger.WriteLine($"[{string.Join(",", chain.Nodes)}]");
            }

            return(chains);
        }
Example #3
0
        /// <inheritdoc />
        public override List <Chain <TNode> > GetChains(IGraph <TNode> graph)
        {
            Initialize(graph);

            if (Faces.Count != 0)
            {
                // Get faces and remove the largest one
                Faces.RemoveAt(Faces.MaxBy(x => x.Count));
            }

            var chains = new List <Chain <TNode> >();

            if (Faces.Count != 0)
            {
                chains.Add(new Chain <TNode>(GetFirstCycle(Faces), chains.Count));
            }

            // Process cycles
            while (Faces.Count != 0)
            {
                var chain       = GetNextCycle();
                var isFromCycle = chain != null;

                if (chain == null)
                {
                    chain = GetNextPath();
                }

                // Must not happen. There must always be a cycle or a path while we have at least one face available.
                if (chain == null)
                {
                    throw new InvalidOperationException();
                }

                chains.Add(new Chain <TNode>(chain, chains.Count)
                {
                    IsFromFace = isFromCycle,
                });
            }

            // Add remaining nodes
            while (Graph.Vertices.Any(x => !IsCovered(x)))
            {
                var chain = GetNextPath();

                // Must not happen. There must always be a path while there are vertices that are not covered. (The graph must be connected)
                if (chain == null)
                {
                    throw new InvalidOperationException();
                }

                chains.Add(new Chain <TNode>(chain, chains.Count)
                {
                    IsFromFace = false,
                });
            }

            return(chains);
        }
Example #4
0
        private void _removeFace(int id)
        {
            try
            {
                int index = -1;

                for (int i = 0; i < Faces.Count; i++)
                {
                    if (Faces[i].Id == id)
                    {
                        index = i;
                        break;
                    }
                }

                // Index found
                if (index != -1)
                {
                    Face v = Faces[index];

                    // Raise event first
                    RaiseFaceLost(v.Id, v.Gender, v.Age, v.DwellTime);
                    m_startTimeMap.Remove(v.Id);

                    // Remove index from map and Face from list
                    Faces.RemoveAt(index);
                    FaceCount = Faces.Count;

                    Console.WriteLine("Remove faces " + id + " at index " + index);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("REMOVE VIEWER ERROR: " + ex.Message);
                ActivityLog += "REMOVE VIEWER ERROR:" + ex.Message + "\n";
            }
        }
Example #5
0
        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;
            }
        }
Example #6
0
        /// <summary>
        /// Tries to find a face that neighbours with covered vertices in the smallest depth.
        /// Chooses the smallest one if there are multiple in the same depth.
        /// </summary>
        /// <returns></returns>
        private List <TNode> GetNextCycle()
        {
            var bestFaceIndex = -1;
            var bestFaceSize  = int.MaxValue;
            var bestFaceDepth = int.MaxValue;

            for (var i = 0; i < Faces.Count; i++)
            {
                var face = Faces[i];

                // Check nodes in the face and also all neighbouring nodes
                var nodesToCheck = face.Concat(face.SelectMany(x => Graph.GetNeighbours(x))).Where(IsCovered).ToList();

                // If nodesToCheck is empty, it does not neighbour with any covered node and therefore we skip it
                if (nodesToCheck.Count == 0)
                {
                    continue;
                }

                var minDepth = nodesToCheck.Min(GetDepth);
                var size     = face.Count(x => !IsCovered(x));

                if (minDepth < bestFaceDepth || (minDepth == bestFaceDepth && size < bestFaceSize))
                {
                    bestFaceIndex = i;
                    bestFaceSize  = size;
                    bestFaceDepth = minDepth;
                }
            }

            // Return null if no face was found. That means that we must now consider paths and then come back to cycles.
            if (bestFaceIndex == -1)
            {
                return(null);
            }

            var nextFace = Faces[bestFaceIndex].Where(x => !IsCovered(x)).ToList();

            Faces.RemoveAt(bestFaceIndex);

            // This must not happen as all faces with all nodes covered must be already removed
            if (nextFace.Count == 0)
            {
                throw new InvalidOperationException();
            }

            var chain   = new List <TNode>();
            var counter = ChainsCounter;

            // Find a vertex that neighbours with a covered node
            var firstVertexIndex = -1;

            for (var i = 0; i < nextFace.Count; i++)
            {
                var vertex = nextFace[i];

                if (Graph.GetNeighbours(vertex).Any(IsCovered))
                {
                    firstVertexIndex = i;
                    break;
                }
            }

            // This must not happen as we considered only faces that neighbour with already covered vertices
            if (firstVertexIndex == -1)
            {
                throw new InvalidOperationException();
            }

            // Use the first vertex
            SetDepth(nextFace[firstVertexIndex], counter++);
            chain.Add(nextFace[firstVertexIndex]);
            nextFace.RemoveAt(firstVertexIndex);

            // Add vertices starting with the ones that neighbour with nodes on the highest depths
            while (nextFace.Count != 0)
            {
                var nextVertexIndex = nextFace.MinBy(SmallestCoveredNeighbourDepth);
                var nextVertex      = nextFace[nextVertexIndex];

                SetDepth(nextVertex, counter++);
                chain.Add(nextVertex);
                nextFace.Remove(nextVertex);
            }

            // Fix depths
            foreach (var node in chain)
            {
                SetDepth(node, ChainsCounter);
            }

            ChainsCounter++;
            RemoveCoveredNodes(Faces);

            return(chain);
        }