/// <summary> /// Look at any out-of-context shapes in the sketch and refine them by /// removing a substroke. (We may want to try removing more strokes later). /// /// We assume that the substroke should be a wire, and we remove it /// from the shape. Then we regroup the shape without the substroke, /// re-recognize it, and check to see if it makes sense. If it does, /// keep it. If not, try again with a different substroke. /// </summary> public void StrokeShedRefine(Featurefy.FeatureSketch featureSketch) { foreach (Sketch.Shape shape in featureSketch.Sketch.Shapes) { bool valid = _domain.IsProperlyConnected(shape); // If the shape wansn't specified by the user, doesn't fit // its context, and is a gate if ((!shape.AlreadyGrouped) && (!valid || (shape.Probability < _thresholdProbability)) && (shape.Classification == LogicDomain.GATE_CLASS)) { float bestProb = 0F; Sketch.Substroke bestSubstroke = null; foreach (Sketch.Substroke substroke in shape.Substrokes) { // See how helpful it is to let the shape // shed this stroke float shedProb = strokeShedHelpfulness(shape, substroke, featureSketch); if (shedProb > bestProb) { bestProb = shedProb; bestSubstroke = substroke; } } // If hypotheticaly shedding one of the substrokes // enhanced the recognition of the shape, do it for real //if (bestProb > shape.Probability) // NOT SURE IF WE WANT TO KEEP THIS shedStroke(shape, bestSubstroke, featureSketch); } } }
/// <summary> /// After the sketch has been recognized, we can look at the context of each shape. /// If a shape is out of context (for example, if a NOT gate is connected to three wires), /// then we assign the shape the next most likely label. /// </summary> /// <param name="featureSketch">the sketch to process</param> public virtual void process(Featurefy.FeatureSketch featureSketch) { if (DEBUG) { Console.WriteLine("\nCareful Context Refinement"); } foreach (Sketch.Shape shape in featureSketch.Sketch.Shapes) { bool valid = _domain.IsProperlyConnected(shape); if (valid || shape.AlreadyGrouped) { continue; } ShapeType originalType = shape.Type; string originalName = shape.Name; float originalProbability = shape.Probability; // If a shape only has one connection, // it is assumed to be a label. if (shape.ConnectedShapes.Count == 1) { foreach (Sketch.Substroke substroke in shape.Substrokes) { substroke.Classification = LogicDomain.TEXT_CLASS; } _sketchRecognizer.processShape(shape, featureSketch); if (shape.Probability < 0.5) { // we probably made a mistake, so revert. shape.Type = originalType; shape.Name = originalName; shape.Probability = originalProbability; } } if (Domain.LogicDomain.IsGate(shape.Type)) { nextBestLabel(shape); if (!_domain.IsProperlyConnected(shape)) { // the next best type wasn't properly connected either // so we probably want to stick with our first type shape.Type = originalType; shape.Name = originalName; shape.Probability = originalProbability; } } if (DEBUG && ((originalName != shape.Name) || (originalType != shape.Type))) // if it changed { Console.WriteLine(" " + originalName + " (" + originalType + ") -> " + shape.Name + " (" + shape.Type + "); confidence = " + shape.Probability); } } }
/// <summary> /// Test the validity of each labeled shape. /// </summary> /// <returns>A dictionary mapping shapes to validity.</returns> public Dictionary <Sketch.Shape, bool> TestValidity(Featurefy.FeatureSketch _featuresketch) { Sketch.Sketch sketch = _featuresketch.Sketch; Dictionary <Sketch.Shape, bool> dict = new Dictionary <Sketch.Shape, bool>(); foreach (Sketch.Shape shape in sketch.Shapes) { dict.Add(shape, _domain.IsProperlyConnected(shape)); } return(dict); }
/// <summary> /// Pick out the shapes that still that do not match their context, /// try grouping neighboring strokes into them (i.e. they steal a neighboring stroke), /// and if this puts it in context, keep it. /// /// NOTE: we're only considering gates at the moment /// </summary> public void StrokeStealRefine(Featurefy.FeatureSketch featureSketch) { foreach (Sketch.Shape shape in featureSketch.Sketch.Shapes) { bool valid = _domain.IsProperlyConnected(shape); // If the shape was not specified by the user, doesn't fit its // context, and is a gate if (!valid && !shape.AlreadyGrouped && shape.Classification == LogicDomain.GATE_CLASS) { float bestProb = 0F; Sketch.Substroke bestSubstroke = null; // For each neighboring shape, try stealing a stroke until // one fits well given that the user did not specify it List <Sketch.Shape> neighbors = featureSketch.Sketch.neighboringShapes(shape); foreach (Sketch.Shape neighbor in neighbors) { if (!neighbor.AlreadyGrouped) { foreach (Sketch.Substroke substroke in neighbor.Substrokes) { float stealProb = strokeStealHelpfulness(shape, substroke, featureSketch); if (stealProb > bestProb) { bestProb = stealProb; bestSubstroke = substroke; } } } } // Only steal the substroke that gives the best new shape stealStroke(shape, bestSubstroke, featureSketch); } } }
/// <summary> /// After the sketch has been recognized, we can look at the context of each shape. /// If a shape is out of context (for example, if a NOT gate is connected to three wires), /// then we assign the shape the next most likely label. /// </summary> /// <param name="featureSketch">the sketch to process</param> public void process(Featurefy.FeatureSketch featureSketch) { if (DEBUG) { Console.WriteLine("Context Refinement"); } // Refine three times... (THIS IS A MAGIC NUMBER I PULLED OUT OF THE AIR). int CONTEXT_REFINE_ITERATIONS = 3; for (int i = 0; i < CONTEXT_REFINE_ITERATIONS; i++) { foreach (Sketch.Shape shape in featureSketch.Sketch.Shapes) { bool valid = _domain.IsProperlyConnected(shape); if (valid || shape.AlreadyGrouped) { continue; } ShapeType originalType = shape.Type; string originalName = shape.Name; // If a shape only has one connection, it is assumed to be a label if (shape.ConnectedShapes.Count == 1) { foreach (Sketch.Substroke substroke in shape.Substrokes) { substroke.Classification = "Text"; } _sketchRecognizer.recognize(shape, featureSketch); } else { if (Domain.LogicDomain.IsGate(shape.Type)) { nextBestLabel(shape); } } if (DEBUG && ((originalName != shape.Name) || (originalType != shape.Type))) // if it changed { Console.WriteLine(" " + originalName + " (" + originalType + ") -> " + shape.Name + " (" + shape.Type + ")"); } } } }
/// <summary> /// Goes through every shape in the sketch and creates a text box for it /// </summary> private void MakeTextBlocks() { //System.Console.WriteLine("Making Text Boxes"); toolTips.Clear(); foreach (Sketch.Shape shape in sketchPanel.Sketch.Shapes) { if (shape.Substrokes.Length == 0) { return; } // Set Content and Color Popup newTextBlock = new Popup(); ContextDomain.ContextDomain contextDomain = ContextDomain.CircuitDomain.GetInstance(); // Make text box. Feel free to change FontWeight, FontSize, etc. TextBlock newChild = new TextBlock(); if (debug) { newChild.Text = shape.Name + " (" + shape.Type.Name + ")"; newChild.Text += "\nOrientation: " + shape.Orientation; newChild.Text += "\nTemplate: " + shape.TemplateName; //newChild.Text += "\nConnected shapes: "; //foreach (Sketch.Shape connectedShape in shape.ConnectedShapes) //newChild.Text += connectedShape.Name + ", "; newChild.Text += "\nProperly Connected: " + (contextDomain.IsProperlyConnected(shape) ? "yes" : "no"); } else { newChild.Text = shape.Type.Name; } newChild.Background = Brushes.Transparent; newChild.FontWeight = System.Windows.FontWeights.Bold; newTextBlock.Child = newChild; newTextBlock.IsOpen = false; newTextBlock.AllowsTransparency = true; newTextBlock.Visibility = System.Windows.Visibility.Visible; newTextBlock.PlacementTarget = sketchPanel.InkCanvas; newTextBlock.Placement = PlacementMode.RelativePoint; // Find strokes StrokeCollection strokes = new StrokeCollection(); foreach (Sketch.Substroke sub in shape.Substrokes) { strokes.Add(sketchPanel.InkSketch.GetInkStrokeBySubstroke(sub)); } // Set Position and Add to Canvas and our collection newTextBlock.VerticalOffset = strokes.GetBounds().Top + strokes.GetBounds().Height / 2; newTextBlock.HorizontalOffset = strokes.GetBounds().Left + strokes.GetBounds().Width / 2; subscribed = true; //sketchPanel.InkCanvas.Children.Add(newTextBlock); toolTips.Add(shape, newTextBlock); } }