/// <summary> /// preprocess input data and transform to 1d feature vector for DTW /// according to specific gesture model /// </summary> /// <param name="input"></param> /// <returns></returns> private ArrayList PreprocessGesture(Gesture input, int gid) { ArrayList dtw_data = new ArrayList(); // each frame for(int i=0; i<input.data.Count; i++) { // Extract the coordinates of the points. List<Point> pts = new List<Point>(); Point shoulderRight = new Point( input.data[i].Joints[JointType.ShoulderRight].Position.X, input.data[i].Joints[JointType.ShoulderRight].Position.Y); Point shoulderLeft = new Point( input.data[i].Joints[JointType.ShoulderLeft].Position.X, input.data[i].Joints[JointType.ShoulderLeft].Position.Y); foreach (Joint j in input.data[i].Joints) { if (GESTURE_CONFIG[gid].jointWeights[j.JointType] > 0) { Point p = new Point(j.Position.X, j.Position.Y); pts.Add(p); } } // Center the data var center = new Point( input.data[i].Joints[JointType.ShoulderCenter].Position.X, input.data[i].Joints[JointType.ShoulderCenter].Position.Y); for (int k = 0; k < pts.Count; k++) pts[k] = new Point(pts[k].X - center.X, pts[k].Y - center.Y); // Normalization of the coordinates double shoulderDist = Math.Sqrt(Math.Pow((shoulderLeft.X - shoulderRight.X), 2) + Math.Pow((shoulderLeft.Y - shoulderRight.Y), 2)); for (int k = 0; k < pts.Count; k++) pts[k] = new Point(pts[k].X / shoulderDist, pts[k].Y / shoulderDist); // save in 1d double array double[] feat_vec = new double[pts.Count * 2]; for (int k = 0; k < pts.Count; k++) { feat_vec[k * 2] = pts[k].X; feat_vec[k * 2 + 1] = pts[k].Y; } dtw_data.Add(feat_vec); } return dtw_data; }
/// <summary> /// read database data from files /// </summary> public bool LoadGestureDatabase(string database_dir) { // enumerate gesture directories if (!Directory.Exists(database_dir)) return false; // clear GESTURE_LIST.Clear(); GESTURE_CONFIG.Clear(); GESTURE_DATABASE.Clear(); // load all gesture config if (!LoadAllGestureConfig()) { Debug.WriteLine("Fail to load gesture config."); return false; } // load actual gesture data for each type foreach (int gid in GESTURE_LIST.Keys) { string gdir = GESTURE_DATABASE_DIR + GESTURE_LIST[gid] + "\\"; List<Gesture> cur_gestures = new List<Gesture>(); IEnumerable<string> gesture_files = Directory.EnumerateFiles(gdir, "*.xml"); foreach (string filename in gesture_files) { Gesture gtemp = new Gesture(); gtemp.data = KinectRecorder.ReadFromSkeletonFile(filename); gtemp.name = GESTURE_LIST[gid]; if (gtemp.data.Count > gesture_max_len) gesture_max_len = gtemp.data.Count; if (gtemp.data.Count < gesture_min_len) gesture_min_len = gtemp.data.Count; cur_gestures.Add(gtemp); } // add to database if (cur_gestures.Count > 0) GESTURE_DATABASE.Add(gid, cur_gestures); } // no actual model data if (GESTURE_DATABASE.Count == 0) return false; return true; }
/// <summary> /// match to each database template /// </summary> public double MatchToDatabase(Gesture input, out string res) { // find the most similar gesture in database to test gesture double mindist = double.PositiveInfinity; res = "Unknown"; foreach (int gid in GESTURE_DATABASE.Keys) { foreach (Gesture temp in GESTURE_DATABASE[gid]) { double dist = GestureSimilarity(input, temp, gid); if(dist < mindist) { mindist = dist; res = GESTURE_LIST[gid]; } } } return mindist; }
/// <summary> /// measure similarity between input gesture and a gesture template /// </summary> public double GestureSimilarity(Gesture input, Gesture template, int gid) { ArrayList input_data = PreprocessGesture(input, gid); ArrayList temp_data = PreprocessGesture(template, gid); double dist = DynamicTimeWarping(input_data, temp_data, GESTURE_CONFIG[gid].jointWeights); return dist; }