/// <summary> /// Run the recognizer on a sketch. /// /// This method is multithreaded. /// /// Precondition: the sketch has been grouped into shapes. /// /// Postcondition: shape.Classification has been filled in with this recognizer's best guess /// for every shape in the sketch. /// </summary> /// <param name="featureSketch">the sketch to work on</param> public override void process(Featurefy.FeatureSketch featureSketch) { List <Thread> threads = new List <Thread>(_numThreads); int shapesPerWorker = featureSketch.NumShapes / _numThreads; int finalEnd = 0; // fork off (_numThreads - 1) threads, each with shapesPerWorker tasks to perform for (int i = 0; i < _numThreads - 1; i++) { int start = i * shapesPerWorker; int end = start + shapesPerWorker; finalEnd = end; Worker worker = new Worker(_innerRecognizer, start, end, featureSketch); Thread t = new Thread(worker.run); t.Name = "recognition worker thread " + i; threads.Add(t); t.Start(); } // do the rest of the work on this thread Worker myWorker = new Worker(_innerRecognizer, finalEnd, featureSketch.NumShapes, featureSketch); myWorker.run(); // join the other threads to make sure they finished foreach (Thread t in threads) { t.Join(); } }
/// <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> /// Recognizes a given shape and updates the shape accordingly. /// </summary> /// <param name="shape">The shape to recogize</param> /// <param name="featureSketch">A featuresketch</param> public override void recognize(Sketch.Shape shape, Featurefy.FeatureSketch featureSketch) { // Make sure we do nothing if the user specified the label if (shape.AlreadyLabeled) { return; } // Use a particular recognizer based on how the shape was // classified. if (shape.Classification == LogicDomain.WIRE_CLASS) { _wireRecognizer.recognize(shape, featureSketch); } else if (shape.Classification == LogicDomain.TEXT_CLASS) { _textRecognizer.recognize(shape, featureSketch); } else if (shape.Classification == LogicDomain.GATE_CLASS) { _gateRecognizer.recognize(shape, featureSketch); } else { Console.WriteLine( "Error in shape recognition: cannot recognize" + " a shape with the classification \"" + shape.Classification + "\""); } }
public virtual void process(Featurefy.FeatureSketch featureSketch) { foreach (Sketch.Shape shape in featureSketch.Sketch.Shapes) { orient(shape, featureSketch); } }
public void FeatureSketchCreationBehavior() { /* * Problem description: * When a sketch contains duplicated points, the creation of a FeatureSketch * may delete those points. */ Sketch.Sketch sketch = Sketches.newValidSketch(); // Duplicate a point in the sketch Sketch.Shape shape = sketch.Shapes[0]; List <Sketch.Point> points = new List <Sketch.Point>(shape.Substrokes[0].Points); points.Add(points[points.Count - 1]); Sketch.Substroke substroke = new Sketch.Substroke(points); sketch.AddStroke(new Sketch.Stroke(substroke)); shape.AddSubstroke(substroke); Sketch.Sketch clone = sketch.Clone(); Assert.IsTrue(sketch.Equals(clone), "Sketch is not equal to its clone"); Featurefy.FeatureSketch featureSketch = newValidFeatureSketch(new Sketch.Project(sketch)); Assert.IsTrue(sketch.Equals(clone), "FeatureSketch creation modified original sketch"); }
/// <summary> /// Refines the recognition after the shapes have all been recognized. /// /// Precondition: ConnectSketch() has been called. /// </summary> public void RefineSketch(Featurefy.FeatureSketch _featuresketch) { _refinement.process(_featuresketch); if (debug) { Console.WriteLine("Connections:"); foreach (Sketch.Shape shape in _featuresketch.Sketch.Shapes) { Console.Write(" --> " + shape.Name + ": "); bool first = true; foreach (Sketch.Shape connectedShape in shape.ConnectedShapes) { if (!first) { Console.Write(", "); } first = false; Console.Write(connectedShape.Name); } Console.WriteLine(); } } }
/// <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); }
/// <summary> /// Create a new worker. /// </summary> /// <param name="inner">the recognizer to use</param> /// <param name="start">the first shape to recognize (inclusive)</param> /// <param name="end">the last shape to recognize (exclusive)</param> /// <param name="featureSketch">the sketch whose shapes we'll work on</param> public Worker(Recognizer inner, int start, int end, Featurefy.FeatureSketch featureSketch) { _innerRecognizer = inner; _start = start; _end = end; _featureSketch = featureSketch; _shapes = _featureSketch.Sketch.Shapes; }
public static InkToSketchWPF.InkCanvasSketch newInkCanvasSketch() { // Manually construct the necessary data members Sketch.Sketch sketch = new Sketch.Sketch(); Featurefy.FeatureSketch featureSketch = UnitTests.FeaturefyOperations.newValidFeatureSketch(new Sketch.Project(sketch)); InkCanvas inkcanvas = new InkCanvas(); return(new InkToSketchWPF.InkCanvasSketch(inkcanvas, 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> /// Run the recognizer on a sketch. /// /// Precondition: the sketch has been grouped into shapes. /// /// Postcondition: shape.Type and shape.Probability have been filled in with /// this recognizer's best guess for every shape in the sketch. /// </summary> /// <param name="featureSketch">the sketch to work on</param> public void process(Featurefy.FeatureSketch featureSketch) { foreach (Sketch.Shape shape in featureSketch.Sketch.Shapes) { if (shape.AlreadyLabeled || !canRecognize(shape.Classification)) { continue; } processShape(shape, featureSketch); } }
public void process(Featurefy.FeatureSketch featureSketch) { Sketch.Sketch sketch = featureSketch.Sketch; foreach (Sketch.Shape shape in sketch.Shapes) { if (shape.Type != new Domain.ShapeType()) { shape.AlreadyLabeled = true; shape.AlreadyGrouped = true; } } }
/// <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> /// Construct a sketch modification. Computes the benefit of this action /// by computing the difference in the given energy function from /// performing the given sketch operation. /// </summary> public SketchModification(Featurefy.FeatureSketch sketch, ISketchOperation op, Func <Featurefy.FeatureSketch, double> energyFunc) { _sketch = sketch.Sketch; _op = op; double initialEnergy = energyFunc(sketch); op.perform(); double finalEnergy = energyFunc(sketch); op.undo(); _benefit = finalEnergy - initialEnergy; }
/// <summary> /// Classifies only the given substrokes. /// /// Postcondition: substroke.Classification is "Wire," "Gate," "Label," or "Unknown" for /// every substroke in the list. Strokes are not reclassified if they have a parent /// shape with AlreadyLabeled set to true. /// </summary> /// <param name="sketch">the sketch to use</param> /// <param name="featureSketch">the featureSketch to use</param> public virtual void process(Featurefy.FeatureSketch featureSketch, IEnumerable <Sketch.Substroke> substrokes) { foreach (Sketch.Substroke substroke in substrokes) { Sketch.Shape parent = substroke.ParentShape; if (parent != null && parent.AlreadyLabeled) { continue; } substroke.Classification = classify(substroke, featureSketch); } }
/// <summary> /// Classifies all the substrokes in a sketch. /// /// Postcondition: substroke.Classification is "Wire," "Gate," "Label," or "Unknown" for /// every substroke in the sketch. Strokes are not reclassified if they have a parent /// shape with AlreadyLabeled set to true. /// </summary> /// <param name="sketch">the sketch to use</param> public virtual void process(Featurefy.FeatureSketch sketch) { foreach (Sketch.Substroke substroke in sketch.Sketch.Substrokes) { Sketch.Shape parent = substroke.ParentShape; if (parent != null && parent.AlreadyLabeled) { continue; } classify(substroke, sketch); } }
/// <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 + ")"); } } } }
public void process(Featurefy.FeatureSketch featureSketch) { _sketch = featureSketch.Sketch; List <Shape> notbubbles = Data.Utils.filter(_sketch.Shapes, delegate(Shape s) { return(s.Type == LogicDomain.NOTBUBBLE); }); foreach (Shape bubble in notbubbles) { foreach (Shape connected in bubble.ConnectedShapes) { if (tryToMerge(bubble, connected)) { break; } } } }
/// <summary> /// Regroups and recognizes the shape /// </summary> /// <param name="shape"></param> private void regroupShape(Sketch.Shape shape, Featurefy.FeatureSketch _featuresketch) { if (shape.Type == new ShapeType()) { _strokeClassifier.process(_featuresketch, shape.Substrokes); } if (shape.Substrokes.Length > 0) { // Determine the majority classification Dictionary <string, int> counts = new Dictionary <string, int>(); foreach (Sketch.Substroke substroke in shape.Substrokes) { if (!counts.ContainsKey(substroke.Classification)) { counts.Add(substroke.Classification, 0); } counts[substroke.Classification]++; } string bestClassification = LogicDomain.GATE_CLASS; int bestCount = 0; foreach (KeyValuePair <string, int> pair in counts) { if (pair.Value > bestCount) { bestCount = pair.Value; bestClassification = pair.Key; } } // Give all the substrokes the same classification since they are in the same group shape.Classification = bestClassification; if (!shape.AlreadyLabeled) { _sketchRecognizer.processShape(shape, _featuresketch); } } else { Console.WriteLine("WARNING No substrokes available"); } MakeShapeNamesUnique(_featuresketch); }
public virtual void orient(Shape shape, Featurefy.FeatureSketch featureSketch) { ShapeType type = shape.Type; BitmapSymbol defn = new BitmapSymbol(); foreach (BitmapSymbol template in _templates) { if (template.SymbolType == type) { defn = template; break; } } BitmapSymbol unknown = new BitmapSymbol(shape.SubstrokesL); shape.Orientation = unknown.bestOrientation(defn); }
/// <summary> /// Recognizes the text a shape forms, and updates the /// shape with the recognition results (the text and the /// likelihood that the recognition was correct). /// /// This method does not use the featureSketch argument. /// /// Postcondition: the shape is of type IO and its Name /// property tells what text it was recognized as. /// </summary> /// <param name="shape">The shape to recognize</param> /// <param name="featureSketch">Not used.</param> public override void recognize(Sketch.Shape shape, Featurefy.FeatureSketch featureSketch) { // Prepare the shape for recognition SketchOrInkConverter converter = new SketchOrInkConverter(); _microsoftTextRecognizer.Strokes = converter.convertToInk(shape); // Try to recognize the shape RecognitionStatus status; RecognitionResult result; result = _microsoftTextRecognizer.Recognize(out status); // Origanize the results string shapeName = ""; float probability = 0F; if ((result != null) && (status == RecognitionStatus.NoError)) { shapeName = result.TopString; switch (result.TopConfidence) { case RecognitionConfidence.Poor: probability = .1F; break; case RecognitionConfidence.Intermediate: probability = .5F; break; case RecognitionConfidence.Strong: probability = .9F; break; } } // Update the shape to reflect these results if (debug) { Console.WriteLine("Found input/output label: " + shapeName + " (confidence = " + probability + ")"); } shape.setRecognitionResults(LogicDomain.TEXT, probability, shapeName); }
/// <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); } }
/// <summary> /// Create a Recognition Manager for the given sketch panel with default settings. /// Settings are loaded from file settings.txt /// </summary> /// <param name="p">a sketch panel to manage</param> public RecognitionManager(SketchPanelLib.SketchPanel p) { // Load settings from text file string directory = AppDomain.CurrentDomain.BaseDirectory; string SettingsFilename = directory + "//settings.txt"; _filenames = Files.SettingsReader.readSettings(SettingsFilename); // Initialize the recognition machines _domain = ContextDomain.CircuitDomain.GetInstance(); _strokeClassifier = RecognitionPipeline.createDefaultClassifier(); _strokeGrouper = RecognitionPipeline.createDefaultGrouper(); _sketchRecognizer = RecognitionPipeline.createDefaultRecognizer(); _connector = RecognitionPipeline.createDefaultConnector(); _refinement = RecognitionPipeline.createDefaultRefiner(_connector, _sketchRecognizer); // Add events _panel = p; _featuresketch = p.InkSketch.FeatureSketch; }
/// <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> /// Uses the cached result if it is available, or makes a call to the inner /// recognizer otherwise. This method is thread-safe provided that /// 1. the underlying recognizer's "recognize" method is thread-safe /// 2. this method will not be called concurrently on the same shape /// </summary> /// <param name="shape">the shape to recognize</param> /// <param name="featureSketch">the featureSketch to work on</param> public override void recognize(Sketch.Shape shape, Featurefy.FeatureSketch featureSketch) { int hash = shape.GetHashCode(); // Safely determine if the value is already cached. We never remove cached // results, so it is safe to release the lock afterward. bool isCached; lock (_typeResults) isCached = _typeResults.ContainsKey(hash); if (isCached) { // Write the cached results to the shape. Since we can assume that // we don't need to lock the shape, all we need to synchronize on is // the dictionary when we retreive results. ShapeType type; float prob; lock (_typeResults) { type = LogicDomain.getType(_typeResults[hash]); prob = _probabilityResults[hash]; } shape.setRecognitionResults(type, prob); } else { // We only need a lock here when we write results to the dictionary. _innerRecognizer.recognize(shape, featureSketch); lock (_typeResults) { _typeResults.Add(hash, shape.Type.Name); _probabilityResults.Add(hash, shape.Probability); } } }
public virtual void orient(Shape shape, Featurefy.FeatureSketch featureSketch) { ShapeType type = shape.Type; BitmapSymbol defn = new BitmapSymbol(); // TODO: should we use multiple templates, or just one? foreach (BitmapSymbol template in _templates) { if (template.SymbolType == type) { defn = template; break; } } BitmapSymbol unknown = _shapesToSymbols[shape]; shape.Orientation = unknown.bestOrientation(defn); #if JESSI Console.WriteLine("The final shape orientation is " + shape.Orientation); Console.WriteLine(); #endif }
/// <summary> /// Ensure that all the shape names in the sketch are unique. /// </summary> public void MakeShapeNamesUnique(Featurefy.FeatureSketch _featuresketch) { new Refiner.UniqueNamer().process(_featuresketch); }
/// <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 }
/// <summary> /// Rerecognizes the strokes given as a single group /// </summary> public void RerecognizeGroup(Sketch.Shape shape, Featurefy.FeatureSketch _featuresketch) { regroupShape(shape, _featuresketch); shape.ClearConnections(); _connector.connect(shape, _featuresketch.Sketch); }
// It should be noted that the functions to create the ARFF files are somewhat of a hack. They work, on my computer. // They may also work on yours. Hopefully once the ARFF files are created nobody will ever have to use these functions // again, but we're leaving them here anyways. Use at your own risk and feel free to email me if you have questions. // Good luck! -- Jessi Peck, Sketcher 2011, [email protected] /// <summary> /// Create ARFF files for the classifier and grouper from a set of sketches /// </summary> /// <param name="fileList">a list of sketch file names</param> public static void createARFFs(List <string> fileList) { // These dictionaries will store the feature values for each of the ARFF files we're creating Dictionary <double[], string> strokeValues = new Dictionary <double[], string>(); Dictionary <string, Dictionary <double[], string> > groupValues = new Dictionary <string, Dictionary <double[], string> >(); groupValues.Add(TEXT, new Dictionary <double[], string>()); groupValues.Add(WIRE, new Dictionary <double[], string>()); groupValues.Add(GATE, new Dictionary <double[], string>()); // These lists will store the names of the features for the ARFF files we're creating List <string> strokeNames = new List <string>(); List <string> groupNames = new List <string>(); // Stores each stroke's classification. Dictionary <Substroke, string> strokeClassifications = new Dictionary <Substroke, string>(); Dictionary <string, string> settingsFiles = Files.SettingsReader.readSettings(); bool firstFile = true; foreach (string file in fileList) { if (!file.EndsWith(".xml")) { continue; } // only read .xml files //Console.WriteLine("Reading file " + file); // Load the sketch from the file and make it into a feature sketch Sketch.Sketch sketch = new ConverterXML.ReadXML(file).Sketch; Featurefy.FeatureSketch featureSketch = Featurefy.FeatureSketch.MakeFeatureSketch(new Sketch.Project(sketch), settingsFiles); // Get featureSketch to tell you the feature values for each individual stroke in the sketch Dictionary <Substroke, double[]> classificationDict = new Dictionary <Substroke, double[]>(); foreach (Substroke substroke in sketch.Substrokes) { classificationDict.Add(substroke, featureSketch.GetValuesSingle(substroke)); } // Classify each substroke based on what shape it's a part of foreach (Sketch.Shape shape in sketch.Shapes) { string classif = shape.Type.Classification; if (classif == UNKNOWN) { continue; } foreach (Substroke stroke in shape.Substrokes) { strokeValues.Add(classificationDict[stroke], classif); strokeClassifications.Add(stroke, classif); } } #region Bad XML File Catcher // This shouldn't be necessary if your XML files are properly formatted, // but it's here just in case. List <Substroke> badStrokes = new List <Substroke>(); foreach (Substroke strokeToCheck in classificationDict.Keys) { if (classificationDict[strokeToCheck].Length != 27) { Console.WriteLine("OH MAN. GET VALUES SINGLE FAILS."); Console.WriteLine("Your length of features is " + classificationDict[strokeToCheck].Length + " elements long."); badStrokes.Add(strokeToCheck); } } foreach (Substroke naughtyStroke in badStrokes) { strokeValues.Remove(classificationDict[naughtyStroke]); strokeClassifications.Remove(naughtyStroke); classificationDict.Remove(naughtyStroke); Console.WriteLine("You will never see that stroke again."); } #endregion // Get featureSketch to tell you the feature values for each pair of strokes in the sketch Dictionary <string, Dictionary <Featurefy.FeatureStrokePair, double[]> > grouperDict = featureSketch.GetValuesPairwise(strokeClassifications); // You only need to call this once, might as well be on the first file you do. if (firstFile) { strokeNames = featureNames(featureSketch.FeatureListSingle); // set up the list of feature names for stroke classification groupNames = featureNames(featureSketch.FeatureListPair); // set up the list of feature names for stroke grouping firstFile = false; } // Adds feature values for grouping to groupValues getGroupValues(ref groupValues, grouperDict); } // Populate the lists of class names List <string> strokeClasses, groupClasses; classNames(out strokeClasses, out groupClasses); string directory = AppDomain.CurrentDomain.BaseDirectory; // write ARFF for stroke classifier writeARFFfile(directory + STROKE + ARFF, strokeNames, strokeValues, strokeClasses); // write ARFF for stroke grouper x3 writeARFFfile(directory + TEXT_GROUP + ARFF, groupNames, groupValues[TEXT], groupClasses); //writeARFFfile(directory + WIRE_GROUP + ARFF, groupNames, groupValues[WIRE], groupClasses); writeARFFfile(directory + GATE_GROUP + ARFF, groupNames, groupValues[GATE], groupClasses); //Console.WriteLine("ARFF files created!"); }