private double computeRecognitionQuality() { int totalShapes = 0; int correctShapes = 0; foreach (Sketch.Shape correctShape in _original.Shapes) { ShapeType originalType = correctShape.Type; if (_toCompare.ShapesL.Exists(delegate(Sketch.Shape s) { return(s.Equals(correctShape)); })) { Sketch.Shape resultShape = _toCompare.ShapesL.Find(delegate(Sketch.Shape s) { return(s.Equals(correctShape)); }); ShapeType resultType = resultShape.Type; totalShapes++; if (resultType == originalType) { correctShapes++; } } } return((double)correctShapes / totalShapes); }
/// <summary> /// Determine a shape's orientation based on the angles of incoming and outgoing /// wires. This method is highly reliable (numerous user studies have shown that /// users don't draw wires coming into a gate at meaningless orientations), but /// it is sometimes off by 180 degrees (by which I mean Pi, since we are working /// in radians). /// /// NOTE: Currently, this code is not used. HOWEVER, it is extremely reliable. Often /// moreso than the orientation obtained from the image recognizer. The refiner should /// be able to use this information. /// </summary> /// <param name="shape1">The shape to check.</param> /// <param name="sketch">The sketch containing the shape.</param> public override double OrientShape(Sketch.Shape shape, Sketch.Sketch sketch) { if (shape.Classification == LogicDomain.GATE_CLASS) { // The gate's angle based on its connected wires. double connectionsAngle = 0; double numConnectedWires = 0; // Check the slope orientation of adjacent wires foreach (Sketch.Shape connectedShape in shape.ConnectedShapes) { if (connectedShape.Type.Classification == LogicDomain.WIRE_CLASS) { Sketch.EndPoint endpoint = shape.ClosestEndpointFrom(connectedShape); double slope = endpoint.Slope; // negated since our y-axis is inverted (positive-y is down) connectionsAngle -= Math.Atan(Math.Abs(slope)); numConnectedWires++; } } // Get the average angle connectionsAngle = connectionsAngle / numConnectedWires; // Connections angle is currently in the range [-Pi, Pi], so add Pi return(connectionsAngle + Math.PI); } return(0); }
/// <summary> /// Look at a particular shape's orientation angle (relative to horizontal) /// and correct it if necessary. /// /// Note: Although the image recognizer gives an orientation angle, /// we are not using it. Originally, we did use it, then calculated a new /// orientation angle using wire slopes. The new orientation angle replaced /// the original angle unless they were close to each other. However, /// now we've commented that out and are using only the wire slopes. /// /// Actually, we do use image recognizer orientation angle, but only /// for NOT gates, since both sides of NOT gates have one wire. /// </summary> /// <param name="shape1">The shape to check.</param> /// <param name="sketch">The sketch containing the shape.</param> public override void OrientShape(Sketch.Shape shape, Sketch.Sketch sketch) { if (shape.Classification == LogicDomain.GATE_CLASS && shape.Type != LogicDomain.NOT) { // The gate's angle based on it's connected wires. double connectionsAngle = 0; double numConnectedWires = 0; // Check the slope orientation of adjacent wires foreach (Sketch.Shape connectedShape in shape.ConnectedShapes) { if (connectedShape.Type.Classification == LogicDomain.WIRE_CLASS) { Sketch.EndPoint endpoint = shape.ClosestEndpointFrom(connectedShape); double slope = endpoint.Slope; // negated since our y-axis is inverted. connectionsAngle -= Math.Atan(Math.Abs(slope)); numConnectedWires++; } } // Get the average angle connectionsAngle = connectionsAngle / numConnectedWires; //// Check if the two angles are close enough //if (Math.Abs(orientationAngle - connectionsAngle) % Math.PI < CLOSE_ENOUGH || // Math.PI % Math.Abs(orientationAngle - connectionsAngle) < CLOSE_ENOUGH) // shape.Orient = shape.Orient; // Don't change anything. //else // // Use the connections rather than the template rotation angle // shape.Orient = connectionsAngle; shape.Orientation = connectionsAngle; } }
/// <summary> /// Recognize what gate a shape is using the ComboRecgonizer, /// and update the shape with the results. /// </summary> /// <param name="shape"> /// The shape to recognize (should be a gate) /// </param> public override void recognize(Sketch.Shape shape, Featurefy.FeatureSketch featureSketch) { // Initialize variables to store results in ShapeType bestType = new ShapeType(); float bestProbability = 0F; double bestOrientation; // Recognize the shape and pick out the best option Dictionary <ShapeType, double> alternativeTypes = Recognize(shape, out bestOrientation); foreach (KeyValuePair <ShapeType, double> pair in alternativeTypes) { ShapeType type = pair.Key; double prob = pair.Value; if (prob > bestProbability) { bestType = type; bestProbability = (float)prob; } } // Update the shape to reflect this recognition shape.setRecognitionResults( bestType, bestProbability, alternativeTypes, bestOrientation); }
private double computeRecognitionQuality() { int totalShapes = 0; int correctShapes = 0; foreach (Sketch.Shape correctShape in _original.Shapes) { ShapeType originalType = correctShape.Type; if (originalType.Equals(new ShapeType())) // skip unidentified shapes { continue; } if (_toCompare.ShapesL.Exists(delegate(Sketch.Shape s) { return(s.GeometricEquals(correctShape)); })) { Sketch.Shape resultShape = _toCompare.ShapesL.Find(delegate(Sketch.Shape s) { return(s.GeometricEquals(correctShape)); }); ShapeType resultType = resultShape.Type; totalShapes++; if (resultType == originalType) { correctShapes++; } } } return((double)correctShapes / totalShapes); }
private ConfusionMatrix <ShapeType> computeRecognitionConfusionMatrix() { ConfusionMatrix <ShapeType> result = new ConfusionMatrix <ShapeType>(); foreach (Sketch.Shape correctShape in _original.Shapes) { if (IGNORE_CLASSIFICATIONS.Contains(correctShape.Classification.ToLower())) { continue; } ShapeType originalType = correctShape.Type; if (originalType.Equals(new ShapeType())) // skip unidentified shapes { continue; } if (_toCompare.ShapesL.Exists(delegate(Sketch.Shape s) { return(s.GeometricEquals(correctShape)); })) { Sketch.Shape resultShape = _toCompare.ShapesL.Find(delegate(Sketch.Shape s) { return(s.GeometricEquals(correctShape)); }); ShapeType resultType = resultShape.Type; // Record stats result.increment(originalType, resultType); } } return(result); }
/// <summary> /// Find the two closest shapes to the given shape in the sketch. /// </summary> /// <param name="shape">Given shape</param> /// <param name="sketch">Sketch we are working with</param> /// <returns>List of the two closest shapes to the given shape</returns> private List <Sketch.Shape> twoClosest(Sketch.Shape shape, Sketch.Sketch sketch) { double d1 = double.PositiveInfinity; // closer of the two double d2 = double.PositiveInfinity; // farther of the two Sketch.Shape closest1 = null; Sketch.Shape closest2 = null; foreach (Sketch.Shape otherShape in sketch.Shapes) { if (otherShape != shape) // Make sure the shape isn't close to itself { double d = sketch.minDistance(shape, otherShape); if (d < d1) // then d is also less than d2 { d2 = d1; closest2 = closest1; d1 = d; closest1 = otherShape; } else if (d < d2) // if we're here, then d1 < d < d2 { d2 = d; closest2 = otherShape; } } } List <Sketch.Shape> closeShapes = new List <Sketch.Shape>(); closeShapes.Add(closest1); closeShapes.Add(closest2); return(closeShapes); }
/// <summary> /// Draws "ghost" gate for the supplied shape and sets the hoverGate (does nothing if shape is not /// a gate) /// </summary> /// <param name="gate"></param> /// <param name="pos"></param> public void DrawFeedback(Sketch.Shape shape, bool isHoverGate = false, bool drawingOneGate = true) { if (!Domain.LogicDomain.IsGate(shape.Type) || ShapeToGate.ContainsKey(shape)) { return; } KeyValuePair <List <string>, List <string> > shapeIO; ShapeToIO.TryGetValue(shape, out shapeIO); GhostGate ghostGate = new GhostGate(shape, ref sketchPanel, ref gateDrawer, gateRotated, shapeIO); ShapeToGate[shape] = ghostGate; ghostGate.SubscribeEvents(); currGhosts.Add(ghostGate); if (isHoverGate) { hoverGate = ghostGate; //if (shape.UserLabeled) // hoverGate.ShowDrawingAdvice(); } if (shape.UserLabeled) { ghostGate.ShowDrawingAdvice(); } }
/// <summary> /// Returns whether or not the strokes that make up a shape were /// drawn consecutively /// </summary> /// <param name="sketch">The sketch that contains the shape</param> /// <param name="shape">The shape to be checked</param> /// <returns>Whether or not the shape was drawn consecutively</returns> public bool isConsecutive(Sketch.Sketch sketch, Sketch.Shape shape) { Substroke[] strokes = shape.Substrokes; Substroke sub1 = strokes[0]; bool inShape = false; int shapeIndex = 0; for (int i = 0; i < sketch.Substrokes.Length; i++) { Substroke str = sketch.Substrokes[i]; if (!inShape && str.Equals(sub1)) { inShape = true; } if (shapeIndex == strokes.Length) { break; } if (inShape) { sub1 = strokes[shapeIndex]; if (!sub1.Equals(str)) { return(false); } shapeIndex++; } } return(true); }
private List <Tuple <Shape, ShapeType> > computeRecognitionMistakes() { List <Tuple <Shape, ShapeType> > result = new List <Tuple <Shape, ShapeType> >(); foreach (Sketch.Shape correctShape in _original.Shapes) { ShapeType originalType = correctShape.Type; if (originalType.Equals(new ShapeType())) // skip unidentified shapes { continue; } if (_toCompare.ShapesL.Exists(delegate(Sketch.Shape s) { return(s.GeometricEquals(correctShape)); })) { Sketch.Shape resultShape = _toCompare.ShapesL.Find(delegate(Sketch.Shape s) { return(s.GeometricEquals(correctShape)); }); ShapeType resultType = resultShape.Type; if (resultType != originalType) { result.Add(Tuple.Create(resultShape, originalType)); } } } return(result); }
/// <summary> /// Change the value of a specified input in the circuit /// and update the ink colors accordingly /// </summary> /// <param name="name">the name of the input used in the circuit</param> /// <param name="value">the value (0 or 1) to set it to</param> private void setInputValue(Sketch.Shape inputShape, int value) { sketchPanel.Circuit.setInputValue(inputShape, value); colorWiresByValue(); if (displayingClean) { ShowToggles(); } }
/// <summary> /// Update the recognizer to learn from an example shape. /// /// Precondition: shape has a valid type /// </summary> /// <param name="shape">the shape to learn</param> public override void learnFromExample(Sketch.Shape shape) { // If we made a mistake in recognition, make note of that BitmapSymbol bs = base.findTemplate(shape.TemplateName); if (bs != null && bs.SymbolType != shape.Type) { _templateUsage[bs.SymbolType][bs] -= errorSubtraction; } // Update the list of templates accordingly removeExample(shape.Type); Add(shape.Type, shape.SubstrokesL, shape.createBitmap(100, 100, true)); }
/// <summary> /// Returns the bounding box for the given shape in ink space coordinates. Optionall colors the strokes /// the default color for non-wire symbols. /// <see cref="UIConstants.TextFeedbackMechanismSymbolColor"/> /// </summary> /// <param name="shape">the shape for which the bounding box will be calculated</param> /// <param name="substroke2InkTable">A hashtable mapping Ink Stroke IDs to Sketch Substroke IDs. /// This hashtable allows the Feedback Mechanism to assocate Ink Strokes drawn to screen /// with their corresponding (labeled) Sketch substrokes.</param> /// <param name="colorStrokes">True iff the microsoft ink strokes should be colored while calculating /// the bounding box (for efficiency's sake).</param> /// <returns>A System.Drawing.Rectangle representing the bounding box /// for the given shape (in ink space coordinates)</returns> private Rectangle calculateBoundingBox(Sketch.Shape shape, Hashtable substroke2InkTable, bool colorStrokes) { // Compute the bounding box of all the ink strokes // associated with this shape Rectangle boundingBox = Rectangle.Empty; foreach (Sketch.Substroke substroke in shape.Substrokes) { if (!substroke2InkTable.Contains(substroke.XmlAttrs.Id)) { // If, for some reason, there is no ink stroke // that corresponds to this substroke, skip it continue; } Microsoft.Ink.Stroke iStroke = (Microsoft.Ink.Stroke)substroke2InkTable[substroke.XmlAttrs.Id]; if (colorStrokes) { iStroke.DrawingAttributes.Color = UIConstants.TextFeedbackMechanismSymbolColor; } Rectangle strokeBox = iStroke.GetBoundingBox(); if (boundingBox.IsEmpty) { boundingBox = strokeBox; } else { if (strokeBox.X < boundingBox.X) { boundingBox.X = strokeBox.X; } if (strokeBox.Y < boundingBox.Y) { boundingBox.Y = strokeBox.Y; } if (strokeBox.Bottom > boundingBox.Bottom) { boundingBox.Height = strokeBox.Bottom - boundingBox.Y; } if (strokeBox.Right > boundingBox.Right) { boundingBox.Width = strokeBox.Right - boundingBox.X; } } } return(boundingBox); }
/// <summary> /// Returns a list of all the points that make up a shape /// </summary> /// <param name="shape">The shape</param> /// <returns>a list of all the points within the shape</returns> public List <Point> findPointsInShape(Sketch.Shape shape) { List <Substroke> subs = shape.SubstrokesL; List <Point> points = new List <Point>(); foreach (Substroke sub in subs) { foreach (Point p in sub.Points) { points.Add(p); } } return(points); }
/// <summary> /// Recognizes a shape and updates values in shape (through setRecognitionResults). /// Also notes which template was used for template ranking purposes. /// </summary> /// <param name="shape"></param> /// <param name="featureSketch"></param> /// <returns></returns> public override RecognitionInterfaces.RecognitionResult recognize(Sketch.Shape shape, Featurefy.FeatureSketch featureSketch) { // Gaaghaghagahga // C# has one flaw, and I found it: // http://www.simple-talk.com/community/blogs/simonc/archive/2010/07/14/93495.aspx // In short, we have to cast the result from the base class because that method // must return a generic "RecognitionResult" and cannot return the better // "ImageRecognitionResult," even though doing so would be perfectly type-safe. =( ImageRecognitionResult result = (ImageRecognitionResult)base.recognize(shape, featureSketch); ShapeType type = result.Type; string templateName = result.TemplateName; ++_templateUsage[type][base.findTemplate(templateName)]; // increment the number of times that BitmapSymbol has been used return(result); }
/// <summary> /// Prints errors from parsing the circuit. /// </summary> private void printErrors() { if (_parseErrors.Count == 0) { return; } Console.WriteLine("The following errors arose in parsing the circuit:"); foreach (ParseError error in _parseErrors) { Console.WriteLine(error.Explanation); Sketch.Shape errShape = error.Where; } Console.WriteLine(); }
/// <summary> /// Refreshes the underlying data structures corresponding to the inkStroke after the /// inkStroke has been modified (moved or resized) /// </summary> /// <param name="inkStroke">The Ink Stroke to modify</param> public void UpdateInkStroke(System.Windows.Ink.Stroke inkStroke) { // Delete the old stroke but save its shape for the new Stroke Sketch.Shape oldShape = GetSketchSubstrokeByInk(inkStroke).ParentShape; DeleteStroke(inkStroke); // Add the new stroke to the Sketch and put it in the old Shape AddStroke(inkStroke); Sketch.Substroke newSubstroke = GetSketchSubstrokeByInk(inkStroke); if (oldShape != null) { oldShape.AddSubstroke(newSubstroke); if (!Sketch.ShapesL.Contains(oldShape)) { Sketch.AddShape(oldShape); } } }
/// <summary> /// Update the Naive Bayes classifier to learn from an example shape. /// </summary> /// <param name="shape"></param> public void Learn(Sketch.Shape shape) { double believedOrientation; Dictionary <string, object> features = GetIndRecognitionResults(shape, out believedOrientation); // Since the combo classifier is trained with such a large // number of training examples (1600+), each additional // learning example has an extremely small effect. // However, we want the error-corrected learning to have a // more meaningful effect. So, we add the same example // multiple times to the bayes classifier. for (int i = 0; i < 5; i++) // MAGIC NUMBER { _comboClassifier.AddExample(shape.Type, features); } _comboClassifier.UpdateClassifier(); }
/// <summary> /// Returns the time it took to draw the shape /// </summary> /// <param name="shape">The shape</param> /// <returns>total time it took to draw the shape in ms</returns> public ulong findShapeTime(Sketch.Shape shape) { double minTime = Double.PositiveInfinity; double maxTime = Double.NegativeInfinity; List <Point> points = findPointsInShape(shape); foreach (Point p in points) { if ((Double)(p.Time) < minTime) { minTime = p.Time; } if ((Double)(p.Time) > maxTime) { maxTime = (Double)(p.Time); } } return((ulong)(maxTime - minTime)); }
/// <summary> /// Update the input mapping dictionary to reflect an input /// recently added to the circuit /// </summary> /// <param name="input">The shape (label) corresponding to the input</param> private void addInput(Sketch.Shape inputShape) { if (debug) { Console.WriteLine("Trying to add " + inputShape.Type.Name + " called " + inputShape.Name + " to inputs"); } ListSet <String> strokeIDs = new ListSet <String>(); foreach (Sketch.Substroke substroke in inputShape.Substrokes) { strokeIDs.Add(sketchPanel.InkSketch.GetInkStrokeIDBySubstroke(substroke)); } if (!inputMapping.ContainsKey(inputShape)) { inputMapping.Add(inputShape, strokeIDs); } }
/// <summary> /// Update the output mapping dictionary to reflect an output /// recently added to the circuit /// </summary> /// <param name="ouput">The shape (label) corresponding to the output</param> public void addOutput(Sketch.Shape outputShape) { if (debug) { Console.WriteLine("Trying to add " + outputShape.Type + " called " + outputShape.Name + " to outputs"); } ListSet <String> strokeIDs = new ListSet <string>(); foreach (Sketch.Substroke substroke in outputShape.Substrokes) { strokeIDs.Add(sketchPanel.InkSketch.GetInkStrokeIDBySubstroke(substroke)); } if (!outputMapping.ContainsKey(outputShape.Name)) { outputMapping.Add(outputShape.Name, strokeIDs); } }
/// <summary> /// Uses the RecognitionInterfaces.Recognizer recognize method /// which recognizes and assigns the type of a shape. This /// implementation allows the use of the learnFromExample /// method in Interface Functions. /// </summary> /// <param name="shape">The shape to recognize</param> /// <param name="featureSketch">The featureSketch to use</param> public override void recognize(Sketch.Shape shape, Featurefy.FeatureSketch featureSketch) { BitmapSymbol unknown = new BitmapSymbol(shape.SubstrokesL); List <SymbolRank> results = unknown.Recognize(_templates); if (results.Count > 0) { // Populate the dictionary of alterateTypes with all of the ShapeTypes in results Dictionary <ShapeType, double> alternateTypes = new Dictionary <ShapeType, double>(); if (debug) { Console.WriteLine("\nRecognition results: "); } foreach (SymbolRank result in results) { if (!alternateTypes.ContainsKey(result.SymbolType)) { alternateTypes.Add(result.SymbolType, getProbability(result.SymbolType, results)); } if (debug) { Console.WriteLine(result.SymbolType + " with template " + result.SymbolName); } } ShapeType type = results[0].SymbolType; // the most likely type float probability = (float)alternateTypes[type]; // grab the probability of our most likely type alternateTypes.Remove(type); // the most likely type is NOT an alternate shape.setRecognitionResults( type, probability, alternateTypes, results[0].BestOrientation, results[0].SymbolName); } }
private ConfusionMatrix <ShapeType> computeRecognitionConfusionMatrix() { ConfusionMatrix <ShapeType> result = new ConfusionMatrix <ShapeType>(); foreach (Sketch.Shape correctShape in _original.Shapes) { ShapeType originalType = correctShape.Type; if (_toCompare.ShapesL.Exists(delegate(Sketch.Shape s) { return(s.Equals(correctShape)); })) { Sketch.Shape resultShape = _toCompare.ShapesL.Find(delegate(Sketch.Shape s) { return(s.Equals(correctShape)); }); ShapeType resultType = resultShape.Type; // Record stats result.increment(originalType, resultType); } } return(result); }
/// <summary> /// Once strokes are grouped, turn the stroke pairs into shapes. /// </summary> /// <param name="pairs">The list of stroke pairs that should be merged into shapes.</param> /// <param name="sketch">The sketch the strokes exist in; where the shapes should be created.</param> private void makeShapes(List <Sketch.StrokePair> pairs, Sketch.Sketch sketch) { foreach (Sketch.StrokePair pair in pairs) { Sketch.Shape shape1 = pair.Item1.ParentShape; Sketch.Shape shape2 = pair.Item2.ParentShape; // If they've already been merged, forget it. if (shape1 == shape2) { continue; } // If either one is null, we have a problem! if (shape1 == null || shape2 == null) { throw new Exception("Cannot merge substrokes " + pair.Item1 + " and " + pair.Item2 + "; one or both are missing a parent shape"); } // Don't merge any user-specified shapes. if (shape1.AlreadyGrouped || shape2.AlreadyGrouped) { continue; } sketch.mergeShapes(shape1, shape2); } #if DEBUG Console.WriteLine("Your sketch has " + sketch.Shapes.Length + " distinct shapes."); int gates = 0; foreach (Sketch.Shape shape in sketch.Shapes) { if (shape.Classification == LogicDomain.GATE_CLASS) { gates++; } } Console.WriteLine(gates + " of those shapes are gates."); #endif }
/// <summary> /// Draws "ghost" gate for the supplied shape /// </summary> /// <param name="gate"></param> /// <param name="pos"></param> public void DrawFeedback(Sketch.Shape shape, bool isHoverGate = false) { if (!Domain.LogicDomain.IsGate(shape.Type)) { return; } GhostGate ghostGate = new GhostGate(shape, ref sketchPanel, ref gateDrawer); ghostGate.SubscribeEvents(); currGhosts.Add(ghostGate); if (isHoverGate) { hoverGate = ghostGate; } if (shape.UserLabeled) { ghostGate.ShowDrawingAdvice(); } }
/// <summary> /// Determines if shape2 is contained in the boundingbox of shape1 /// more accurately it checks that at most some fraction of the points are not contained /// </summary> /// <param name="shape2"></param> /// <param name="shape1"></param> /// <returns></returns> private bool contains(Sketch.Shape shape1, Sketch.Shape shape2) { RectangleF bbox1 = Featurefy.Compute.BoundingBox(shape1.Points.ToArray()); int badPts = 0; foreach (Sketch.Point pt in shape2.Points) { System.Drawing.PointF pf = pt.SysDrawPointF; if (!(bbox1.Contains(pf))) { badPts++; } } //THIS IS A MAGIC NUMBER I PULLED OUT OF THE AIR double MAXIMUM_BAD_PERCENT = .2; if (shape2.Points.Count != 0) { return(MAXIMUM_BAD_PERCENT > badPts / shape2.Points.Count); } return(true); }
public ParseError(string errorExplanation, Sketch.Shape shape) { _errorExplanation = errorExplanation; _userExplanation = null; _shape = shape; }
/// <summary> /// Recognizes a shape and updates values in shape (through setRecognitionResults). /// Also notes which template was used for template ranking purposes. /// </summary> /// <param name="shape">The shape to recognize</param> /// <param name="featureSketch">The featureSketch containing the shape. /// Never used in this function, but kept around because this function is overriding another.</param> public override void recognize(Sketch.Shape shape, Featurefy.FeatureSketch featureSketch) { base.recognize(shape, featureSketch); //++_templateUsage[shape.Type][base.findTemplate(shape.TemplateName)]; // increment the number of times that BitmapSymbol has been used }
public Sketch.Shape align(Sketch.Shape shape) { Sketch.Shape aligned = shape.Clone(); string imId = aligned.Id.ToString(); Dictionary <Substroke, int> votes = new Dictionary <Substroke, int>(); foreach (Substroke s in aligned.SubstrokesL) { votes.Add(s, 0); } foreach (AlignFeature af in _tf.Keys) { double goodness = Double.PositiveInfinity; // Best match so far Substroke lbest = null; foreach (Substroke ss in aligned.SubstrokesL) { double current = match(ss, aligned, af); if (current < goodness) { lbest = ss; goodness = current; } } ++votes[lbest]; } int maxvote = 0; Substroke best = null; foreach (KeyValuePair <Substroke, int> kvp in votes) { if (kvp.Value > maxvote) { maxvote = kvp.Value; best = kvp.Key; } } if (best == null) { return(shape); } // Step 1 is to uniformly scale so that the keystroke and the match stroke are the same length double scaleFactor = _template.SpatialLength / best.SpatialLength; aligned.scale(scaleFactor); // Next, let's find some points in the template Point TTop = _template.PointsL[0]; Point TBottom; findFarthestPointFrom(TTop, _template, out TBottom); if (TBottom.Y > TTop.Y) { swapPts(ref TTop, ref TBottom); } double TMiddleX = (TTop.X + TBottom.X) / 2.0; double TMiddleY = (TTop.Y + TBottom.Y) / 2.0; double TCentroidX = _shape.Centroid[0]; double TCentroidY = _shape.Centroid[1]; // Now some points in the match Point BTop = best.PointsL[0]; Point BBottom; findFarthestPointFrom(BTop, best, out BBottom); if (BBottom.Y > BTop.Y) { swapPts(ref BTop, ref BBottom); } double BMiddleX = (BTop.X + BBottom.X) / 2.0; double BMiddleY = (BTop.Y + BBottom.Y) / 2.0; double BCentroidX = aligned.Centroid[0]; double BCentroidY = aligned.Centroid[1]; // The first step in alignment is rotation. We want the angle between the line BMiddle-BCentroid and horizontal to // be the same as the angle between the line TMiddle-TCentroid and horizontal. double ttheta = Math.Atan2(TCentroidY - TMiddleY, TCentroidX - TMiddleX); double btheta = Math.Atan2(BCentroidY - BMiddleY, BCentroidX - BMiddleX); double deltaTheta = ttheta - btheta; aligned.rotate(deltaTheta); /* * BMiddleX = (BTop.X + BBottom.X) / 2.0; * BMiddleY = (BTop.Y + BBottom.Y) / 2.0; * BCentroidX = shape.Centroid[0]; * BCentroidY = shape.Centroid[1]; * btheta = Math.Atan2(BCentroidY - BMiddleY, BCentroidX - BMiddleX); * * Substroke middleToCenter = new Substroke(new Point[2] { * new Point((float)BMiddleX, (float)BMiddleY), new Point((float)BCentroidX,(float)BCentroidY) * }, new XmlStructs.XmlShapeAttrs(true)); * middleToCenter.XmlAttrs.Time = (ulong)DateTime.Now.Ticks; * aligned.AddSubstroke(middleToCenter); * // Finally, we want to scale along the Middle-Centroid axis such that the lengths of the Middle-Centroid lines are the same * scaleFactor = Math.Sqrt(Math.Pow(TCentroidX - TMiddleX, 2) + Math.Pow(TCentroidY - TMiddleY, 2)) / Math.Sqrt(Math.Pow(BCentroidX - BMiddleX, 2) + Math.Pow(BCentroidY - BMiddleY, 2)); * aligned.rotate(-btheta); * SymbolRec.Image.Image image = new SymbolRec.Image.Image(64, 64, aligned); * image.writeToBitmap(String.Format("{0}\\output\\shape_{1}_postrotate.png", System.IO.Directory.GetCurrentDirectory(), imId)); * aligned.transform(new MathNet.Numerics.LinearAlgebra.Matrix(new double[][] { new double[] { 1, 0, 0 }, new double[] { 0, scaleFactor, 0 }, new double[] { 0, 0, 1 } })); * image = new SymbolRec.Image.Image(64, 64, aligned); * image.writeToBitmap(String.Format("{0}\\output\\shape_{1}_postaxialscale.png", System.IO.Directory.GetCurrentDirectory(), imId)); * aligned.rotate(ttheta); * image = new SymbolRec.Image.Image(64, 64, aligned); * image.writeToBitmap(String.Format("{0}\\output\\shape_{1}_postnextrotate.png", System.IO.Directory.GetCurrentDirectory(), imId)); */ return(aligned); }
/// <summary> /// Returns the number of substrokes in a shape /// </summary> /// <param name="shape">The shape</param> /// <returns>The number of substrokes in the shape</returns> public int numSubStrokes(Sketch.Shape shape) { return(shape.Substrokes.Length); }