/// <summary> /// Calculate distance betwee strokes. /// DP matching /// </summary> /// <param name="stroke1"></param> /// <param name="stroke2"></param> /// <returns></returns> private DPMatchingResult CalcStrokeDistance(AnalysisPenStroke stroke1, AnalysisPenStroke stroke2) { //double distance = 0.0; // parameters ---------------------------- // gap cost for DP matching double gapCost = 40.0; // --------------------------------------- Point[] points1 = stroke1.GetRamerSampledPoints_NonRecursive(AnswerSheetAnalyzer.RamerSamplingDistanceThres); Point[] points2 = stroke2.GetRamerSampledPoints_NonRecursive(AnswerSheetAnalyzer.RamerSamplingDistanceThres); if (points1.Length == 0 && points2.Length == 0) { return new DPMatchingResult(0.0, new List<int[]>()); } if (points1.Length == 0) { return new DPMatchingResult(1.0 / ((double)points2.Length * gapCost), new List<int[]>()); //return points2.Length * gapCost; } if (points2.Length == 0) { return new DPMatchingResult(1.0 / ((double)points1.Length * gapCost), new List<int[]>()); //return points1.Length * gapCost; } double[,] m = new double[points1.Length, points2.Length]; // from which direction? // 0->match, -1->fromPoints1, -2->fromPoints2 int[,] from = new int[points1.Length, points2.Length]; for (int i = 0, ilen = points1.Length; i < ilen; i++) { m[i, 0] = (double)i * gapCost; from[i, 0] = -1; } for (int i = 0, ilen = points2.Length; i < ilen; i++) { m[0, i] = (double)i * gapCost; from[0, i] = -2; } from[0, 0] = 0; for (int i = 1, ilen = points1.Length; i < ilen; i++) { for (int k = 1, klen = points2.Length; k < klen; k++) { double d = 0.0; d = CommonFunction.Distance(points1[i], points2[k]); //Console.WriteLine("Stroke distance: " + d); //double[] scores = new double[] { m[i - 1, k] + gapCost, m[i - 1, k - 1] + d, m[i, k - 1] + gapCost }; //m[i, k] = scores.Min(); double from_grp1 = m[i - 1, k] + gapCost; double from_grp2 = m[i, k - 1] + gapCost; double from_match = m[i - 1, k - 1] + d; if (from_match <= from_grp1 && from_match <= from_grp2) { m[i, k] = from_match; } else if (from_grp1 <= from_grp2 && from_grp1 <= from_match) { from[i, k] = -1; m[i, k] = from_grp1; } else if (from_grp2 <= from_grp1 && from_grp2 <= from_match) { from[i, k] = -2; m[i, k] = from_grp2; } } } // generate matching list // {group1Pos, group2Pos, distance, joinCount} List<int[]> matchingList = new List<int[]>(); int g1pos = points1.Length - 1; int g2pos = points2.Length - 1; //int matchCnt = 0; for (int i = points1.Length - 1, k = points2.Length - 1; i != 0 || k != 0; ) { int[] match = null; switch (from[i, k]) { case -1: match = new int[4] { g1pos, -1, -1, 0 }; i--; g1pos--; break; case -2: match = new int[4] { -1, g2pos, -1, 0 }; k--; g2pos--; break; default: match = new int[4] { g1pos, g2pos, -1, from[i, k] }; i--; k--; g1pos--; g2pos--; //matchCnt++; break; } matchingList.Add(match); } matchingList.Reverse(); DPMatchingResult res = new DPMatchingResult(m[points1.Length - 1, points2.Length - 1] / (Math.Min(points1.Length, points2.Length)), matchingList); //distance = m[points1.Length - 1, points2.Length - 1] / (Math.Min(points1.Length, points2.Length)); return res; }
/// <summary> /// Get samples of stroke comparison /// </summary> /// <param name="group">Answer sheet group</param> /// <param name="max">Maximum number</param> /// <returns>List of comparison result</returns> private List<StrokeComparisonResult> GetStrokeComparisons(AnswerSheetGroup group, int max) { List<StrokeComparisonResult> results = new List<StrokeComparisonResult>(); List<AnswerStep> steps1 = GroupAnswerStep(group.AnswerSheetList[0].Strokes); List<AnswerStep> steps2 = GroupAnswerStep(group.AnswerSheetList[1].Strokes); int cnt = 0; bool limit = false; foreach (AnswerStep step1 in steps1) { List<AnalysisPenStroke> strokes1 = step1.GetNormalizedStrokes(AnswerSheetAnalyzer.NormarizingHeight, false); foreach (AnswerStep step2 in steps2) { List<AnalysisPenStroke> strokes2 = step2.GetNormalizedStrokes(AnswerSheetAnalyzer.NormarizingHeight, false); for (int i = 0, ilen = strokes1.Count; i < ilen; i++) { for (int k = 0, klen = strokes2.Count; k < klen; k++) { DPMatchingResult matchRes = CalcStrokeDistance(strokes1[i], strokes2[k]); Point[] p1 = strokes1[i].GetRamerSampledPoints_NonRecursive(AnswerSheetAnalyzer.RamerSamplingDistanceThres); Point[] p2 = strokes2[k].GetRamerSampledPoints_NonRecursive(AnswerSheetAnalyzer.RamerSamplingDistanceThres); AnalysisPenStroke sampledStroke1 = new AnalysisPenStroke(); AnalysisPenStroke sampledStroke2 = new AnalysisPenStroke(); for (int m = 0, mlen = p1.Length; m < mlen; m++) { sampledStroke1.Points.Add(new AnalysisPenPoint(0, p1[m].X, p1[m].Y)); } for (int m = 0, mlen = p2.Length; m < mlen; m++) { sampledStroke2.Points.Add(new AnalysisPenPoint(0, p2[m].X, p2[m].Y)); } StrokeComparisonResult compResult = new StrokeComparisonResult(strokes1[i], strokes2[k], sampledStroke1, sampledStroke2, matchRes); results.Add(compResult); cnt++; if (max <= cnt) { limit = true; break; } } if (limit) break; } if (limit) break; } if (limit) break; } return results; }
/// <summary> /// Get normalized strokes. Call "ClearCache" function for recalculating /// </summary> /// <param name="height">Height of bounding box after the normalization</param> /// <param name="sortPos">Sort strokes in order of x coordinate of gravity point</param> /// <returns></returns> public List<AnalysisPenStroke> GetNormalizedStrokes(double height, bool sortPos) { if (this.normalizedStrokes == null) { List<AnalysisPenStroke> strokes = new List<AnalysisPenStroke>(this.Strokes); Rect gr = GetBounds(); double scale = height / gr.Height; // position sort if (sortPos) { strokes = AnalysisPenStroke.SortByCenterX(strokes); } // normalization List<AnalysisPenStroke> nstrokes = new List<AnalysisPenStroke>(); foreach (AnalysisPenStroke s in strokes) { AnalysisPenStroke ns = new AnalysisPenStroke(); Rect sr = s.BoundingBox; foreach (AnalysisPenPoint p in s.Points) { AnalysisPenPoint np = new AnalysisPenPoint(p.Time, (p.X - sr.Left) * scale, (p.Y - sr.Top) * scale); ns.Points.Add(np); //Console.Write("(" + np.X + "," + np.Y + "),"); } nstrokes.Add(ns); //Console.WriteLine(); } this.normalizedStrokes = nstrokes; } return this.normalizedStrokes; }
/// <summary> /// Load writing data file /// </summary> /// <param name="path"></param> /// <returns></returns> public List<AnalysisPenStroke> LoadStrokesFromFile(string path) { List<AnalysisPenStroke> strokes = new List<AnalysisPenStroke>(); using (StreamReader sr = new StreamReader(path)) { while (sr.Peek() >= 0) { string jsonStr = sr.ReadLine(); AnalysisPenStroke dnpStroke = new AnalysisPenStroke(jsonStr); Stroke stroke = dnpStroke.GetStrokeObject(Config.OutputCanvasWidth, Config.OutputCanvasHeight); strokes.Add(dnpStroke); } } return strokes; }
/// <summary> /// Join an answer step after the answer step /// </summary> /// <param name="step"></param> public void JoinStrokes(AnswerStep step) { // Calculate offset value for coordinate transformation Rect orgBounds = this.GetBounds(); Rect addBounds = step.GetBounds(); Point endPoint = new Point(orgBounds.Left, (orgBounds.Top + orgBounds.Bottom) / 2.0); Point startPoint = new Point(addBounds.Left, (addBounds.Top + addBounds.Bottom) / 2.0); Point offsetPoint = new Point(endPoint.X - startPoint.X, endPoint.Y - startPoint.Y); // transform coordinates to join anter the origin strokes foreach (AnalysisPenStroke addStroke in step.Strokes) { AnalysisPenStroke s = new AnalysisPenStroke(); foreach (AnalysisPenPoint addPoint in addStroke.Points) { AnalysisPenPoint p = new AnalysisPenPoint(addPoint.Time, addPoint.X + offsetPoint.X, addPoint.Y + offsetPoint.Y); s.Points.Add(p); } this.Strokes.Add(s); } }