using OpenCVForUnity.CoreModule; using System; using System.Collections.Generic; using System.Linq; using UnityEngine; public abstract class PoseBase { public Dictionary<string, Point> GetVaildPoints(List<Point> points, List<string> pointName) { var retVal = new Dictionary<string, Point>(); foreach (var name in pointName) { if (points[YogaConfig.BODY_PARTS[name]] != new Point(-1, -1)) { retVal.Add(name, points[YogaConfig.BODY_PARTS[name]]); } } return retVal; } public virtual Vector2 GetBasicVectorDirection(List<Point> startPoint, List<Point> endPoint) { return Vector2.zero; } //public abstract void SpeedCheck(double v); public abstract bool? MovementValidation(Vector2 distance, TimeSpan totalTimeSpan, int level); public abstract void ExcellenceEstimate(List<(TimeSpan, Vector2)> frameData, Vector2 distance, TimeSpan totalTimeSpan); /// <summary> /// 侦测两帧之间的平均矢量 /// </summary> /// <param name="firstPoints">起始帧</param> /// <param name="lastPoints">结束帧</param> /// <param name="startPoint">必须为必要点</param> /// <param name="endPoint">必须为必要点</param> /// <returns></returns> protected Vector2 GetTwoPointAverageVector(List<Point> firstPoints, List<Point> lastPoints, string startPoint, string endPoint) { Vector2 startEyeV = new Vector2((float)startPoint.p(firstPoints).x - (float)endPoint.p(firstPoints).x, (float)startPoint.p(firstPoints).y - (float)endPoint.p(firstPoints).y); Vector2 endEyeV = new Vector2((float)startPoint.p(lastPoints).x - (float)endPoint.p(lastPoints).x, (float)startPoint.p(lastPoints).y - (float)endPoint.p(lastPoints).y); return endEyeV - startEyeV; } /// <summary> /// 对全身多点进行平均矢量计算 /// </summary> /// <param name="startPoint"></param> /// <param name="endPoint"></param> /// <param name="movePoint"></param> /// <returns></returns> protected Vector2 GetAverageVector(List<Point> startPoint, List<Point> endPoint, string movePoint) { List<Vector2> vectors = new List<Vector2>(); for (int i = 0; i < endPoint.Count; i++) { string tagName = i.tagName(); //自身不计算 if (movePoint.Equals(tagName) || movePoint.Equals(tagName)) continue; //动点位不计算 if (IsMovePoint(tagName)) continue; if (!tagName.IsValid(startPoint) || !tagName.IsValid(endPoint)) continue; //动点-静点 Vector2 startEyeV = new Vector2((float)movePoint.p(startPoint).x - (float)tagName.p(startPoint).x, (float)movePoint.p(startPoint).y - (float)tagName.p(startPoint).y); Vector2 endEyeV = new Vector2((float)movePoint.p(endPoint).x - (float)tagName.p(endPoint).x, (float)movePoint.p(endPoint).y - (float)tagName.p(endPoint).y); vectors.Add(endEyeV - startEyeV); } //计算平均矢量 Vector2 averageV = new Vector2(0, 0); foreach (var v in vectors) { averageV += v; } averageV /= vectors.Count; return averageV; } /// <summary> /// 确定该点位是否为运动时会跟着动的点位 /// 内部重写,写明该动作的所有动点 /// </summary> /// <param name="tagName">点位名称</param> /// <returns>是否为动点</returns> protected abstract bool IsMovePoint(string tagName); /// <summary> /// 动作检测 /// </summary> /// <param name="points"></param> /// <param name="isDataReGather"></param> public virtual bool? AnalyzingAction(List<(DateTime, List<Point>)> points, bool isDataReGather = false) { if (points.Count < 2) // 0级实现逻辑 不做操作 return null; var distance = Vector2.zero; TimeSpan totalTimeSpan = TimeSpan.MinValue; for (int i = 0; i < points.Count - 1; i++) { var p1 = points[i].Item2; var p2 = points[i + 1].Item2; var vector = GetBasicVectorDirection(p1, p2); distance += vector; //if (vector.magnitude > 0.1f) //如果矢量长度大于0.1f,说明有运动 //{ // TimeSpan timeSpan = points[i + 1].Item1 - points[i].Item1; // if (totalTimeSpan == TimeSpan.MinValue) // totalTimeSpan = timeSpan; // else // totalTimeSpan += timeSpan; //} TimeSpan timeSpan = points[i + 1].Item1 - points[i].Item1; totalTimeSpan = timeSpan; } LogPrint.Log($"distance: {distance}, totalTimeSpan: {totalTimeSpan}", PrintLevel.Normal); int level = 0; if (points.Count > 3 && !isDataReGather) //1级实现逻辑 { level = 1; } if (points.Count > 10 && !isDataReGather) //2级实现逻辑 { level = 2; } //动作方位是否正确 return MovementValidation(distance, totalTimeSpan, level); } /// <summary> /// 帮助方法,用于提供基础的检测,动作方位是否正确 /// </summary> /// <param name="distance"></param> /// <param name="totalTimeSpan"></param> /// <param name="direction"></param> /// <param name="level"></param> protected bool? BasicMovementValidation(Vector2 distance, TimeSpan totalTimeSpan, Vector2 direction, int level) { if (totalTimeSpan.TotalMilliseconds / 1000 < 0.05f) { LogPrint.Error("TimeSpan too short"); return null; } var angle = Vector2.SignedAngle(direction, distance); LogPrint.Log($"Angle:{angle}", PrintLevel.Normal); if (MathF.Abs(angle) < 50 && distance.magnitude > 10f) { //EventManager.Instance.Dispatch(YogaEventType.Action_Success); //方向不能偏移超过10° return true; } else { LogPrint.Warning($"Angle:{angle}, magnitude:{distance.magnitude}", PrintLevel.Important); //EventManager.Instance.Dispatch(YogaEventType.Action_Fail); return false; } //if (level < 1) // return; //暂时关闭距离检测,因为距离检测不准确 //LogPrint.Log($"distance x:{distance.x}, y:{distance.y}, magnitude:{distance.magnitude}", PrintLevel.Normal); //if (distance.magnitude > 80f || distance.magnitude < 20f) // EventManager.Instance.Dispatch(YogaEventType.Action_MoveDistanceNotAccurate); //else // EventManager.Instance.Dispatch(YogaEventType.Action_MoveDistanceExactly); //if (level < 2) // return; //暂时关闭速度检测,因为速度检测不准确 //var speed = distance.magnitude / totalTimeSpan.TotalSeconds; //LogPrint.Log($"speed:{speed}", PrintLevel.Normal); //if (speed > 20f) // EventManager.Instance.Dispatch(YogaEventType.Action_SpeedTooFast); //速度语音提示 //else if (speed < 8f) // EventManager.Instance.Dispatch(YogaEventType.Action_SpeedTooSlow); //速度语音提示 } }