Add SquatDetector class
authorKwang Son <k.son@samsung.com>
Fri, 16 Apr 2021 06:07:38 +0000 (15:07 +0900)
committerPiotr Czaja <p.czaja@samsung.com>
Tue, 14 Sep 2021 11:01:34 +0000 (13:01 +0200)
 - get squat similarity score

Signed-off-by: Kwang Son <k.son@samsung.com>
Fitness/Services/SquatDetector.cs [new file with mode: 0644]

diff --git a/Fitness/Services/SquatDetector.cs b/Fitness/Services/SquatDetector.cs
new file mode 100644 (file)
index 0000000..7f0efdb
--- /dev/null
@@ -0,0 +1,65 @@
+using System.Collections.Generic;
+using System.Numerics;
+using Tizen.Multimedia;
+
+namespace Fitness.Services
+{
+    /// <summary>
+    /// SquatDetector
+    /// </summary>
+    public static class SquatDetector
+    {
+        // SquatLowerBody is calculation result of squat picture from
+        // https://review.tizen.org/gerrit/gitweb?p=test/tct/native/api.git;a=blob;f=src/utc/capi-media-vision/res/inference/images/poseLandmark.jpg;h=4bead08b8dc3687f228b5ab885e7136b26331cfb;hb=refs/heads/tizen
+        // Each below vector is derived from two points respectively,
+        // 0. RightUpLeg => RightLowLeg,
+        // 1. RightLowLeg => RightFoot,
+        // 2. LeftUpLeg => LeftLowLeg,
+        // 4. LeftLowLeg => LeftFoot
+        private static readonly List<Vector2> SquatLowerBody = new List<Vector2>(new[]
+        {
+                new Vector2(0.6666667F, -0.33333334F),
+                new Vector2(0F, -1F),
+                new Vector2(-0.6666667F, -0.33333334F),
+                new Vector2(0F, -1F),
+        });
+
+        /// <summary>
+        /// Return Average of lower body <see href="https://en.wikipedia.org/wiki/Cosine_similarity">Cosine Similarity</see> between input user pose and ground truth pose
+        /// </summary>
+        /// <param name="userBody">user body detection result</param>
+        /// <returns>Float score value. (-1.0 ~ 1.0)</returns>
+        public static float Similarity(List<Point> userBody)
+        {
+            if (userBody.Count != System.Enum.GetNames(typeof(PoseDetector.BodyPart)).Length)
+            {
+                throw new System.ArgumentException("Must have all points which represent user body", "userBody");
+            }
+
+            List<Vector2> userSquat = new List<Vector2>(new[]
+            {
+                L1UnitVectorize(userBody[(int)PoseDetector.BodyPart.RightUpLeg], userBody[(int)PoseDetector.BodyPart.RightLowLeg]),
+                L1UnitVectorize(userBody[(int)PoseDetector.BodyPart.RightLowLeg], userBody[(int)PoseDetector.BodyPart.RightFoot]),
+                L1UnitVectorize(userBody[(int)PoseDetector.BodyPart.LeftUpLeg], userBody[(int)PoseDetector.BodyPart.LeftLowLeg]),
+                L1UnitVectorize(userBody[(int)PoseDetector.BodyPart.LeftLowLeg], userBody[(int)PoseDetector.BodyPart.LeftFoot]),
+            });
+
+            float score = 0F;
+
+            for (int i = 0; i < SquatLowerBody.Count; i++)
+            {
+                score += Vector2.Dot(SquatLowerBody[i], userSquat[i]) / SquatLowerBody[i].Length() / userSquat[i].Length();
+            }
+
+            return score / SquatLowerBody.Count;
+        }
+
+        private static Vector2 L1UnitVectorize(Point from, Point to)
+        {
+            int vx = from.X - to.X;
+            int vy = from.Y - to.Y;
+            float parent = System.Math.Abs(vx) + System.Math.Abs(vy);
+            return new Vector2(vx / parent, vy / parent);
+        }
+    }
+}