/// <summary> /// Tests an entire batch of files. See comments atop MainForm.TestBatch_Click(). /// </summary> /// <param name="subject">Subject identification.</param> /// <param name="speed">"fast", "medium", or "slow"</param> /// <param name="categories">A list of gesture categories that each contain lists of prototypes (examples) within that gesture category.</param> /// <param name="dir">The directory into which to write the output files.</param> /// <param name="protractor">If true, uses Protractor instead of Golden Section Search.</param> /// <returns>The two filenames of the output file if successful; null otherwise. The main results are in string[0], /// while the detailed recognition results are in string[1].</returns> public string[] TestBatch(string subject, string speed, List <Category> categories, string dir, bool protractor) { StreamWriter mw = null; // main results writer StreamWriter dw = null; // detailed results writer string[] filenames = new string[2]; try { // set up a main results file and detailed results file int start = Environment.TickCount; filenames[0] = String.Format("{0}\\$1({1})_main_{2}.txt", dir, protractor ? "protractor" : "gss", start); // main results (small file) filenames[1] = String.Format("{0}\\$1({1})_nbest_{2}.txt", dir, protractor ? "protractor" : "gss", start); // recognition details (large file) mw = new StreamWriter(filenames[0], false, Encoding.UTF8); mw.WriteLine("Subject = {0}, Recognizer = $1, Search = {1}, Speed = {2}, StartTime(ms) = {3}", subject, protractor ? "protractor" : "gss", speed, start); mw.WriteLine("Subject Recognizer Search Speed NumTraining GestureType RecognitionRate\n"); dw = new StreamWriter(filenames[1], false, Encoding.UTF8); dw.WriteLine("Subject = {0}, Recognizer = $1, Search = {1}, Speed = {2}, StartTime(ms) = {3}", subject, protractor ? "protractor" : "gss", speed, start); dw.WriteLine("Correct? NumTrain Tested 1stCorrect Pts Ms Angle : (NBestNames) [NBestScores]\n"); // determine the number of gesture categories and the number of examples in each one int numCategories = categories.Count; int numExamples = categories[0].NumExamples; double totalTests = (numExamples - 1) * NumRandomTests; // outermost loop: trains on N=1..9, tests on 10-N (for e.g., numExamples = 10) for (int n = 1; n <= numExamples - 1; n++) { // storage for the final avg results for each category for this N double[] results = new double[numCategories]; // run a number of tests at this particular N number of training examples for (int r = 0; r < NumRandomTests; r++) { _gestures.Clear(); // clear any (old) loaded prototypes // load (train on) N randomly selected gestures in each category for (int i = 0; i < numCategories; i++) { int[] chosen = RandomEx.Array(0, numExamples - 1, n, true); // select N unique indices for (int j = 0; j < chosen.Length; j++) { Unistroke p = categories[i][chosen[j]]; // get the prototype from this category at chosen[j] _gestures.Add(p.Name, p); // load the randomly selected test gestures into the recognizer } } // testing loop on all unloaded gestures in each category. creates a recognition // rate (%) by averaging the binary outcomes (correct, incorrect) for each test. for (int i = 0; i < numCategories; i++) { // pick a random unloaded gesture in this category for testing. // instead of dumbly picking, first find out what indices aren't // loaded, and then randomly pick from those. int[] notLoaded = new int[numExamples - n]; for (int j = 0, k = 0; j < numExamples; j++) { Unistroke g = categories[i][j]; if (!_gestures.ContainsKey(g.Name)) { notLoaded[k++] = j; // jth gesture in categories[i] is not loaded } } int chosen = RandomEx.Integer(0, notLoaded.Length - 1); // index Unistroke p = categories[i][notLoaded[chosen]]; // gesture to test Debug.Assert(!_gestures.ContainsKey(p.Name)); // do the recognition! List <PointF> testPts = GeotrigEx.RotatePoints( // spin gesture randomly TimePointF.ConvertList(p.RawPoints), GeotrigEx.Degrees2Radians(RandomEx.Integer(0, 359)) ); NBestList result = this.Recognize(TimePointF.ConvertList(testPts), protractor); string category = Category.ParseName(result.Name); int correct = (category == categories[i].Name) ? 1 : 0; dw.WriteLine("{0} {1} {2} {3} {4} {5} {6:F1}{7} : ({8}) [{9}]", correct, // Correct? n, // NumTrain p.Name, // Tested FirstCorrect(p.Name, result.Names), // 1stCorrect p.RawPoints.Count, // Pts p.Duration, // Ms Math.Round(result.Angle, 1), (char)176, // Angle tweaking : result.NamesString, // (NBestNames) result.ScoresString); // [NBestScores] results[i] += correct; } // provide feedback as to how many tests have been performed thus far. double testsSoFar = ((n - 1) * NumRandomTests) + r; //TODO //ProgressChangedEvent(this, new ProgressEventArgs(testsSoFar / totalTests)); // callback } // // now create the final results for this N and write them to a file // for (int i = 0; i < numCategories; i++) { results[i] /= (double)NumRandomTests; // normalize by the number of tests at this N Category c = (Category)categories[i]; // Subject Recognizer Search Speed NumTraining GestureType RecognitionRate mw.WriteLine("{0} $1 {1} {2} {3} {4} {5:F3}", subject, protractor ? "protractor" : "gss", speed, n, c.Name, Math.Round(results[i], 3) ); } } // time-stamp the end of the processing int end = Environment.TickCount; mw.WriteLine("\nEndTime(ms) = {0}, Minutes = {1:F2}", end, Math.Round((end - start) / 60000.0, 2)); dw.WriteLine("\nEndTime(ms) = {0}, Minutes = {1:F2}", end, Math.Round((end - start) / 60000.0, 2)); } catch (Exception ex) { Console.WriteLine(ex.Message); filenames = null; } finally { if (mw != null) { mw.Close(); } if (dw != null) { dw.Close(); } } return(filenames); }
/// <summary> /// Constructs a FittsSession instance. A FittsSession contains conditions for a Fitts' law study, /// which, in turn, contain a set of trials. A constructed instance contains a list of conditions /// in sequence, which themselves contain a list of trials. /// </summary> /// <param name="o">The options that configure this session obtained from the OptionsForm dialog.</param> public SessionData(OptionsForm.Options o) { // // Set the condition variables that define this test. // _subject = o.Subject; _circular = o.Is2D; _a = o.A; _w = o.W; _mtpct = o.MTPct; _intercept = o.Intercept; _slope = o.Slope; _screen = Screen.PrimaryScreen.Bounds; _conditions = new List <ConditionData>(); // // Create the order of conditions. Nesting is mt%[a[w]]]. // int[] a_order, w_order, mt_order; if (o.Randomize) // randomize the order of conditions { a_order = RandomEx.Array(0, o.A.Length - 1, o.A.Length, true); w_order = RandomEx.Array(0, o.W.Length - 1, o.W.Length, true); if (o.MTPct != null) { mt_order = RandomEx.Array(0, o.MTPct.Length - 1, o.MTPct.Length, true); while (o.MTPct[mt_order[0]] < StatsEx.Mean(o.MTPct)) // enforce that the first MT% condition is >avg(MT%) { mt_order = RandomEx.Array(0, o.MTPct.Length - 1, o.MTPct.Length, true); } } else { mt_order = null; } } else // in-order arrays { a_order = new int[o.A.Length]; for (int i = 0; i < o.A.Length; i++) { a_order[i] = i; } w_order = new int[o.W.Length]; for (int i = 0; i < o.W.Length; i++) { w_order[i] = i; } mt_order = (o.MTPct != null) ? new int[o.MTPct.Length] : null; for (int i = 0; o.MTPct != null && i < o.MTPct.Length; i++) { mt_order[i] = i; } } // // Create the ordered condition list for the first block of all conditions (i.e., one time through). // int n = 0; for (int i = 0; o.MTPct == null || i < o.MTPct.Length; i++) { for (int j = 0; j < o.A.Length; j++) { for (int k = 0; k < o.W.Length; k++) { double pct = (o.MTPct != null) ? o.MTPct[mt_order[i]] : -1.0; // MT% double fitts = o.Intercept + o.Slope * Math.Log((double)o.A[a_order[j]] / o.W[w_order[k]] + 1.0, 2.0); long mt = (o.MTPct != null) ? (long)fitts : -1L; // Fitts' law predicted movement time ConditionData cd = new ConditionData(0, n++, o.A[a_order[j]], o.W[w_order[k]], pct, mt, o.Trials, o.Practice, o.Is2D); _conditions.Add(cd); } } if (o.MTPct == null) { break; } } // // Now possibly replicate the created order multiple times. // int nConditions = _conditions.Count; // number of conditions in one block for (int b = 1; b < o.Blocks; b++) { for (int c = 0; c < nConditions; c++) { ConditionData fx = _conditions[c]; ConditionData fc = new ConditionData(b, c, fx.A, fx.W, fx.MTPct, fx.MTPred, o.Trials, o.Practice, o.Is2D); _conditions.Add(fc); } } }