using System.Collections; using System.Collections.Generic; using UnityEngine; using System; using System.IO; public class GestureRecognizer : MonoBehaviour { public bool recording = true; public bool anomaliesTesting = false; public string templateSaveName; public int pointsPerGesture = 30; public float samplingRate = 0.01f; public bool limitSamples = false; public int maxPointsAllowed = 100; public float standardRatio = 100f; public float devTightness = 1f; public float anomaliesFactor = 5f; private bool gestureStarted; private bool gestureComplete; private bool inputReady; private string gestureFileName = "gestures.json"; private TwoDPoint startPoint; private TwoDPoint currentPoint; private DrawnGesture currentGesture; private List<TwoDPoint> currentPointList; private TwoDPoint[] reducedPoints; private GestureTemplates templates; private float tempTime = 0f; private void Awake() { } void Start() { LoadTemplates(); varInitialization(); } #region variable initialization and reset private void varInitialization() { currentPoint = new TwoDPoint(0, 0); startPoint = new TwoDPoint(0, 0); currentPointList = new List<TwoDPoint>(); currentPointList.Add(new TwoDPoint(0, 0)); reducedPoints = new TwoDPoint[pointsPerGesture]; for (int i = 0; i < pointsPerGesture; i++) { reducedPoints[i] = new TwoDPoint(0, 0); } gestureStarted = false; gestureComplete = false; inputReady = false; currentGesture = new DrawnGesture("currentGesture", pointsPerGesture); } private void varReset() { for (int i = 0; i < pointsPerGesture; i++) { reducedPoints[i].SetX(0); reducedPoints[i].SetY(0); } currentPointList.Clear(); currentPointList.Add(new TwoDPoint(0, 0)); gestureStarted = false; gestureComplete = false; } #endregion void Update() { tempTime += Time.deltaTime; if (Input.GetMouseButton(0)) { if (inputReady) { if (!gestureStarted) { gestureStarted = true; StartGesture(); } if ((!gestureComplete) && (tempTime > samplingRate)) { tempTime = 0f; ContinueGesture(); } if (gestureComplete) { EndGesture(); } } } else { if (gestureStarted) { EndGesture(); } inputReady = true; } if(Input.GetKeyDown(KeyCode.S)) { SaveTemplates(); } } private void SaveTemplates() { if (!Directory.Exists(Application.dataPath + "/StreamingAssets/")) Directory.CreateDirectory(Application.dataPath + "/StreamingAssets/"); string filePath = Application.dataPath + "/StreamingAssets/" + gestureFileName; string saveData = JsonUtility.ToJson(templates); File.WriteAllText(filePath, saveData); } private void LoadTemplates() { templates = new GestureTemplates(); string filePath = Path.Combine(Application.streamingAssetsPath, gestureFileName); if (File.Exists(filePath)) { string data = File.ReadAllText(filePath); templates = JsonUtility.FromJson<GestureTemplates>(data); } } private void StartGesture() { Debug.Log("gesture started"); startPoint.SetX(Input.mousePosition.x); startPoint.SetY(Input.mousePosition.y); gestureComplete = false; } private void ContinueGesture() { currentPoint.SetX(Input.mousePosition.x - startPoint.GetX()); currentPoint.SetY(Input.mousePosition.y - startPoint.GetY()); currentPointList.Add(new TwoDPoint(currentPoint.GetX(), currentPoint.GetY())); if (currentPoint.GetX() > currentGesture.GetMaxX()) { currentGesture.SetMaxX(currentPoint.GetX()); } if (currentPoint.GetX() < currentGesture.GetMinX()) { currentGesture.SetMinX(currentPoint.GetX()); } if (currentPoint.GetY() > currentGesture.GetMaxY()) { currentGesture.SetMaxY(currentPoint.GetY()); } if (currentPoint.GetY() < currentGesture.GetMinY()) { currentGesture.SetMinY(currentPoint.GetY()); } if (limitSamples && currentPointList.Count >= maxPointsAllowed) { gestureComplete = true; Debug.Log(message: "Gesture Complete!"); } } private void EndGesture() { if (inputReady) inputReady = false; gestureStarted = false; gestureComplete = true; Rescale(currentGesture); MapPoints(currentGesture); if (recording) { currentGesture.SetName(templateSaveName); templates.templates.Add(new DrawnGesture(currentGesture.GetName(), pointsPerGesture, currentGesture.GetMaxX(), currentGesture.GetMaxY(), currentGesture.GetMinX(), currentGesture.GetMinY(), currentGesture.GetPoints())); } else { DrawnGesture m = FindMatch(currentGesture, templates); Debug.Log(m.GetName()); } varReset(); } private void Rescale(DrawnGesture gesture) { float scale = 1f; float xrange = gesture.GetMaxX() - gesture.GetMinX(); float yrange = gesture.GetMaxY() - gesture.GetMinY(); if (xrange >= yrange) { scale = standardRatio / (gesture.GetMaxX() - gesture.GetMinX()); } else { scale = standardRatio / (gesture.GetMaxY() - gesture.GetMinY()); } if (scale != 1) { foreach (TwoDPoint point in currentPointList) { point.SetX(point.GetX() * scale); point.SetY(point.GetY() * scale); } } } private void MapPoints(DrawnGesture gesture) { reducedPoints[0].SetX(currentPointList[0].GetX()); reducedPoints[0].SetY(currentPointList[0].GetY()); int newIndex = 1; float totalDistance = TotalDistance(); float coveredDistance = 0; float thisDistance = 0; float idealInterval = totalDistance / pointsPerGesture; for (int i = 0; i < currentPointList.Count - 1; i++) { thisDistance = PointDistance(currentPointList[i], currentPointList[i + 1]); bool passedIdeal = (coveredDistance + thisDistance) >= idealInterval; if (passedIdeal) { TwoDPoint reference = currentPointList[i]; while (passedIdeal && newIndex < reducedPoints.Length) { float percentNeeded = (idealInterval - coveredDistance) / thisDistance; if (percentNeeded > 1f) percentNeeded = 1f; if (percentNeeded < 0f) percentNeeded = 0f; float new_x = (((1f - percentNeeded) * reference.GetX()) + (percentNeeded * currentPointList[i + 1].GetX())); float new_y = (((1f - percentNeeded) * reference.GetY()) + (percentNeeded * currentPointList[i + 1].GetY())); reducedPoints[newIndex] = new TwoDPoint(new_x, new_y); reference = reducedPoints[newIndex]; newIndex++; thisDistance = (coveredDistance + thisDistance) - idealInterval; coveredDistance = 0; passedIdeal = (coveredDistance + thisDistance) >= idealInterval; } coveredDistance = thisDistance; } else { coveredDistance += thisDistance; } gesture.SetPoints(reducedPoints); } } private DrawnGesture FindMatch(DrawnGesture playerGesture, GestureTemplates templates) { float minAvgDifference = float.MaxValue; DrawnGesture match = new DrawnGesture("no match", pointsPerGesture); foreach (DrawnGesture template in templates.templates) { Debug.Log(template.GetName()); float d = AverageDifference(playerGesture, template); Debug.Log(d.ToString()); if (d < minAvgDifference) { minAvgDifference = d; match = template; } } return match; } private float AverageDifference(DrawnGesture playerGesture, DrawnGesture template) { int numPoints = playerGesture.GetNumPoints(); if (numPoints != template.GetNumPoints()) { Debug.Log("Number of points differs from templates"); return -1f; } float totalDifference = 0; for (int i = 0; i < numPoints; i++) { totalDifference += PointDistance(playerGesture.GetPoints()[i], template.GetPoints()[i]); } return (totalDifference / numPoints); } private float AverageDifferenceWithAnomalies(DrawnGesture playerGesture, DrawnGesture template) { int numPoints = playerGesture.GetNumPoints(); if (numPoints != template.GetNumPoints()) { Debug.Log("Number of points differs from templates"); return -1f; } float totalDifference = 0; float[] sampleDifferences = new float[numPoints]; float[] sampleDeviations = new float[numPoints]; float standardDev = 0; for (int i = 0; i < numPoints; i++) { float thisDistance = PointDistance(playerGesture.GetPoints()[i], template.GetPoints()[i]); sampleDifferences[i] = thisDistance; totalDifference += thisDistance; } float average = totalDifference / numPoints; for (int i = 0; i < numPoints; i++) { sampleDeviations[i] = Math.Abs(sampleDifferences[i] - average); standardDev += sampleDifferences[i]; } standardDev = standardDev / numPoints; for (int i = 0; i < numPoints; i++) { if (Math.Abs(sampleDeviations[i]) > devTightness * standardDev) { totalDifference -= sampleDifferences[i]; totalDifference += anomaliesFactor * sampleDifferences[i]; } } average = totalDifference / numPoints; return (average); } private float TotalDistance() { float totalDistance = 0; for (int i = 0; i < currentPointList.Count - 1; i++) { totalDistance += PointDistance(currentPointList[i], currentPointList[i + 1]); } Debug.Log("total distance: " + totalDistance); return totalDistance; } private float PointDistance(TwoDPoint a, TwoDPoint b) { float xDif = a.GetX() - b.GetX(); float yDif = a.GetY() - b.GetY(); return Mathf.Sqrt((xDif * xDif) + (yDif * yDif)); } } [Serializable] public class GestureTemplates { public List<DrawnGesture> templates; public GestureTemplates() { templates = new List<DrawnGesture>(); } } [Serializable] public class DrawnGesture { private TwoDPoint[] points; private string name; private float maxX; private float minX; private float maxY; private float minY; private int numPoints; public DrawnGesture(string newName, int pointsPerGesture) { numPoints = pointsPerGesture; points = new TwoDPoint[numPoints]; name = newName; maxX = 0; maxY = 0; } public DrawnGesture(string newName, int pointsPerGesture, float max_x, float max_y, float min_x, float min_y, TwoDPoint[] newPoints) { numPoints = pointsPerGesture; points = new TwoDPoint[numPoints]; SetPoints(newPoints); name = newName; maxX = max_x; minX = min_x; maxY = max_y; minY = min_y; } public void Reset() { maxX = 0; minX = 0; maxY = 0; minY = 0; name = ""; Array.Clear(points, 0, numPoints); } public TwoDPoint[] GetPoints() { return points; } public void SetPoints(TwoDPoint[] new_points) { for (int i = 0; i < numPoints; i++) { points[i] = new TwoDPoint(new_points[i].GetX(), new_points[i].GetY()); } } public string GetName() { return name; } public void SetName(string n) { name = n; } public float GetMaxX() { return maxX; } public void SetMaxX(float x) { maxX = x; } public float GetMaxY() { return maxY; } public void SetMaxY(float y) { maxY = y; } public float GetMinY() { return minY; } public void SetMinY(float y) { minY = y; } public float GetMinX() { return minX; } public void SetMinX(float x) { minX = x; } public int GetNumPoints() { return numPoints; } public void SetNumPoints(int n) { numPoints = n; } } [Serializable] public class TwoDPoint { private float x; private float y; public TwoDPoint(float startx, float starty) { x = startx; y = starty; } public float GetX() { return x; } public void SetX(float new_x) { x = new_x; } public float GetY() { return y; } public void SetY(float new_y) { y = new_y; } }


============================


๊นƒํ—ˆ๋ธŒ์—์„œ ๋‹ค์šด๋ฐ›์€ ํ’€์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. ์ด๊ฑฐ ์™œ json ํŒŒ์ผ์— ์ €์žฅํ•ด๋„ ๋นˆ๋ฐฐ์—ด๋งŒ ๋ณด์ผ๊นŒ์š”?


์ €์žฅ๊ณผ ๊ด€๋ จ๋œ ํ•จ์ˆ˜๋Š” SaveTemplate, LoadTemplate์ž…๋‹ˆ๋‹ค. ์ €์žฅ๋˜๋Š” ๋‚ด์šฉ์€ ๋งจ ์•„๋ž˜ serailize๋œ ํด๋ž˜์Šค๋“ค์ด๊ตฌ์š”.


์•Œ๋ ค์ฃผ์‹œ๋ฉด ใ„ณ์š”.