public Match( DatabaseFin unknownFin, DarwinDatabase db, UpdateDisplayOutlinesDelegate updateOutlines) { if (unknownFin == null) { throw new ArgumentNullException(nameof(unknownFin)); } if (db == null) { throw new ArgumentNullException(nameof(db)); } UnknownFin = new DatabaseFin(unknownFin); Database = db; _updateOutlines = updateOutlines; CurrentFinIndex = 0; MatchResults = new MatchResults(unknownFin.IDCode, unknownFin?.FinFilename, db?.Filename); }
//******************************************************************* // // float Match::matchSingleFin(int registrationMethod, int regSegmentsUsed, // bool categoryToMatch[], bool useFullFinError, // bool useAbsoluteOffsets) // // Attempts to match unknown fin to a single database fin. The // current fin is mDatabase->getItem(mCurrentFin). The match_method // parameter determines which of several outline mapping and // error between match techniques is used. // // now modified to match ONLY those database fins with category designation // indicated in categoryToMatch array // // ***055ER // the new useFullFinError parameter indicates whether or not to use the // entire outlines in the final calculation of the error measure. When false, // only the portion of the outlines between the adjusted leadingeEdgeBegin and // trailingEdgeEnd points is used. When true, the fin mapping is done using // various subsets of the outlines (and errors derived from same), but the // final error (measure of mismatch) uses all outline points from each fin. // This parameter does not affect the original and the trim by 1/10th percent // matching techniques, which always use the entire outline in error calculations // // ***1.3 // New parameter useAbsoluteOffsets, wen true, causes function to step through // the database absolute offset list so that fins are matched in the actual // order that they are stored in the database. This is used ONLY when MATCH // QUEUES are being processed, and it keeps the fin numbers correct even if the // database is later modified. // public float MatchSingleIndividual(List <Category> categoriesToMatch) { if (MatchFactors == null || MatchFactors.Count < 1) { throw new Exception("Match factors haven't been set yet!"); } var stopWatch = System.Diagnostics.Stopwatch.StartNew(); int matchIndex = 0; lock (locker) { // If we have any factors missing updateOutlines, but we know what // the delegate should be, fill them in if (CurrentFinIndex == 0 && _updateOutlines != null && MatchFactors.Any(mf => mf.MatchFactorType == MatchFactorType.Outline && mf.UpdateOutlines == null)) { foreach (var matchFactor in MatchFactors.Where(mf => mf.MatchFactorType == MatchFactorType.Outline && mf.UpdateOutlines == null)) { matchFactor.UpdateOutlines = _updateOutlines; } } // TODO? //if (CurrentFinIndex >= Database.AllFins.Count) // return 100.0f; matchIndex = CurrentFinIndex; CurrentFinIndex += 1; } DatabaseFin thisDBFin = Database.AllFins[matchIndex]; bool tryMatch = categoriesToMatch.Exists(c => c.Name.ToLower() == thisDBFin.DamageCategory.ToLower()); if (tryMatch) { // TODO: This isn't done quite right yet MatchError matchErrorResult = new MatchError(); double errorBetweenFins = 0.0; List <MatchFactorError> rawError = new List <MatchFactorError>(); int factorIndex = 0; try { foreach (var factor in MatchFactors) { var factorResult = factor.FindErrorBetweenIndividuals(UnknownFin, thisDBFin); if (factor.MatchFactorType == MatchFactorType.Outline) { Vector <double> saveRawRatios = null; if (matchErrorResult.RawRatios != null) { saveRawRatios = matchErrorResult.RawRatios; } Vector <double> saveRHat = null; if (matchErrorResult.RHat != null) { saveRHat = matchErrorResult.RHat; } Vector <double> saveDBRHat = null; if (matchErrorResult.DBRHat != null) { saveDBRHat = matchErrorResult.DBRHat; } Vector <double> saveDBRawRatios = null; if (matchErrorResult.DBRawRatios != null) { saveDBRawRatios = matchErrorResult.DBRawRatios; } matchErrorResult = factorResult; if (factorResult.Contour1 != null) { UnknownFin.FinOutline.RemappedChainPoints = factorResult.Contour1; } if (saveRawRatios != null) { matchErrorResult.RawRatios = saveRawRatios; } if (saveRHat != null) { matchErrorResult.RHat = saveRHat; } if (saveDBRawRatios != null) { matchErrorResult.DBRawRatios = saveDBRawRatios; } if (saveDBRHat != null) { matchErrorResult.DBRHat = saveDBRHat; } } else { matchErrorResult.RawRatios = factorResult.RawRatios; matchErrorResult.RHat = factorResult.RHat; matchErrorResult.DBRawRatios = factorResult.DBRawRatios; matchErrorResult.DBRHat = factorResult.DBRHat; } // We're going to rescale this later -- should probably remove this // errorBetweenFins += factor.Weight * result.Error; var matchFactorError = new MatchFactorError { FactorIndex = factorIndex, Error = factorResult.Error, Weight = factor.Weight }; rawError.Add(matchFactorError); lock (locker) { RawErrorTracking.Add(matchFactorError); } factorIndex += 1; } } catch (Exception ex) { Trace.WriteLine(ex); } // Now, store the result Result r = new Result( matchErrorResult.Contour1, //***005CM matchErrorResult.Contour2, //***005CM thisDBFin.ID, // TODO - This image filename stuff is a little ugly. (string.IsNullOrEmpty(thisDBFin.OriginalImageFilename)) ? thisDBFin.ImageFilename : thisDBFin.OriginalImageFilename, thisDBFin.ThumbnailFilenameUri, matchIndex, rawError, errorBetweenFins, thisDBFin.IDCode, thisDBFin.Name, thisDBFin.DamageCategory, thisDBFin.DateOfSighting, thisDBFin.LocationCode); if (matchErrorResult.RHat != null) { r.RHat = matchErrorResult.RHat; } if (matchErrorResult.RawRatios != null) { r.RawRatios = matchErrorResult.RawRatios; } if (matchErrorResult.DBRHat != null) { r.DBRHat = matchErrorResult.DBRHat; } if (matchErrorResult.DBRawRatios != null) { r.DBRawRatios = matchErrorResult.DBRawRatios; } //***1.1 - set indices of beginning, tip and end points used in mapping r.SetMappingControlPoints( matchErrorResult.Contour1ControlPoint1, matchErrorResult.Contour1ControlPoint2, matchErrorResult.Contour1ControlPoint3, // beginning, tip & end of unknown fin matchErrorResult.Contour2ControlPoint1, matchErrorResult.Contour2ControlPoint2, matchErrorResult.Contour2ControlPoint3); // beginning, tip & end of database fin stopWatch.Stop(); lock (locker) { MatchResults.AddResult(r); MatchResults.TimeTaken += stopWatch.ElapsedMilliseconds; } } lock (locker) { int numberDone = MatchResults.Count; if (numberDone >= Database.AllFins.Count) { if (RawErrorTracking != null && RawErrorTracking.Count > 0) { // Now that we're through matching, let's rescale the errors Dictionary <int, float> scaleFactors = new Dictionary <int, float>(); foreach (var idx in RawErrorTracking.Select(r => r.FactorIndex).Distinct()) { double minError = RawErrorTracking.Where(r => r.FactorIndex == idx).Min(r => r.Error); double maxError = RawErrorTracking.Where(r => r.FactorIndex == idx).Max(r => r.Error); // Force minError to 0 if it's not <= already if (minError > 0) { minError = 0; } scaleFactors[idx] = (float)(1 / (maxError - minError)); } foreach (var result in MatchResults.Results) { if (result.RawError != null && result.RawError.Count > 0) { double scaledError = 0.0; foreach (var raw in result.RawError) { scaledError += scaleFactors[raw.FactorIndex] * raw.Error * raw.Weight; } result.Error = scaledError; result.Confidence = 1.0f - scaledError; // For rounding issues so we prevent seeing "-0" in the UI if (result.Confidence < 0) { result.Confidence = 0; } } } } return(1.0f); } return((float)numberDone / Database.AllFins.Count); } }
// 1.1 - the following functions used in MatchQueue context public static MatchResults Load(string fileName, out DarwinDatabase db, out DatabaseFin databaseFin) { MatchResults result = new MatchResults(); db = null; databaseFin = null; if (string.IsNullOrEmpty(fileName)) { throw new ArgumentNullException(nameof(fileName)); } if (!File.Exists(fileName)) { throw new ArgumentOutOfRangeException(nameof(fileName)); } var lines = File.ReadAllLines(fileName); if (lines.Length < 6 || !lines[0].StartsWith("Results for ID:")) { return(result); } result._finID = lines[0].Substring(lines[0].LastIndexOf(":") + 1).Trim(); result.TracedFinFile = lines[1].Substring(lines[1].IndexOf(":") + 1).Trim(); result.DatabaseFile = lines[2].Substring(lines[2].IndexOf(":") + 1).Trim(); db = CatalogSupport.OpenDatabase(result.DatabaseFile, Options.CurrentUserOptions.DefaultCatalogScheme, false); //***2.2 - if we can, determine if database path has just changed drive letter // or some part of path that is a prefix to the SurveyArea. If the database // is in the same SurveyArea and has the same database name, then we can // proceed with the building of the MatchResults //int p = mDatabaseFile.find("surveyAreas"); //string rqdAreaAndDB = mDatabaseFile.substr(p); // survey area and database name //string rqdPreamble = mDatabaseFile.substr(0, p); // strip it to get preamble //string currentDBFile = db->getFilename(); //p = currentDBFile.find("surveyAreas"); //string currentAreaAndDB = currentDBFile.substr(p); // survey area and catalog name //string currentPreamble = currentDBFile.substr(0, p); // strip it to get preamble //if (db.Filename.ToLower() != DatabaseFile.ToLower()) //{ // //cout << mDatabaseFile << endl; // //cout << rqdPreamble << " " << rqdAreaAndDB << endl; // //cout << currentDBFile << endl; // //cout << currentPreamble << " " << currentAreaAndDB << endl; // if (currentAreaAndDB == rqdAreaAndDB) // { // string msg = "The Survey Area and Catalog for the match results appear corrrect,\n"; // msg += "but the path to DARWIN's home folder seems to have changed.\n"; // msg += "Is it OK to open the indicated catalog in the current location\n"; // msg += "as shown below?\n\n"; // msg += (currentPreamble + rqdAreaAndDB); // Trace.WriteLine(msg); // //ErrorDialog *err = new ErrorDialog(msg); // //err->show(); // //***2.2 - path possibly has to be fixed to FIN file as well // if ((currentDBFile != mDatabaseFile) && (currentAreaAndDB == rqdAreaAndDB)) // { // //add preamble of current DARWINHOME to FINs relative location // p = mTracedFinFile.find("surveyAreas"); // string currentFinAreaPlus = mTracedFinFile.substr(p); // mTracedFinFile = currentPreamble + currentFinAreaPlus; // cout << mTracedFinFile << endl; // } // } // else // { // string msg = "The WRONG database is currently loaded for viewing these results ...\n\n"; // msg += "LOADED DB:\n " + db->getFilename() + "\n\n"; // msg += "REQUIRED DB:\n " + mDatabaseFile + "\n\n"; // msg += "Please load the required database from the main window\n"; // msg += "and then reload the desired results file."; // //ErrorDialog *err = new ErrorDialog(msg); // //err->show(); // //***2.22 - replacing own ErrorDialog with GtkMessageDialogs // GtkWidget* errd = gtk_message_dialog_new(NULL, // GTK_DIALOG_DESTROY_WITH_PARENT, // GTK_MESSAGE_ERROR, // GTK_BUTTONS_CLOSE, // msg.c_str()); // gtk_dialog_run(GTK_DIALOG(errd)); // gtk_widget_destroy(errd); // return NULL; // } //} DatabaseFin unkFin = CatalogSupport.OpenFinz(result.TracedFinFile); // get match info on each matched database fin // After skipping some of the headers for (int i = 6; i < lines.Length; i++) { string line = lines[i]; int pos = line.IndexOf("\t"); //string rank = line.Substring(0, pos); line = line.Substring(pos + 1); pos = line.IndexOf("\t"); string error = line.Substring(0, pos); line = line.Substring(pos + 1); pos = line.IndexOf("\t"); string dbFinID = line.Substring(0, pos); line = line.Substring(pos + 1); //cout << "dbFinID[" << dbFinID << "]"; //*** 2.2 - show for now string numStr; int dbFinPosition, uBegin, uTip, uEnd, dbBegin, dbTip, dbEnd; pos = line.IndexOf("\t"); numStr = line.Substring(0, pos); line = line.Substring(pos + 1); dbFinPosition = int.Parse(numStr); //cout << "[" << dbFinPosition << "]" << endl; //*** 2.2 - show for now pos = line.IndexOf("\t"); numStr = line.Substring(0, pos); uBegin = int.Parse(numStr); line = line.Substring(pos + 1); //cout << "[" << uBegin << "]"; pos = line.IndexOf("\t"); numStr = line.Substring(0, pos); uTip = int.Parse(numStr); line = line.Substring(pos + 1); //cout << "[" << uTip << "]"; pos = line.IndexOf("\t"); numStr = line.Substring(0, pos); uEnd = int.Parse(numStr); line = line.Substring(pos + 1); //cout << "[" << uEnd << "]"; pos = line.IndexOf("\t"); numStr = line.Substring(0, pos); dbBegin = int.Parse(numStr); line = line.Substring(pos + 1); //cout << "[" << dbBegin << "]"; pos = line.IndexOf("\t"); numStr = line.Substring(0, pos); dbTip = int.Parse(numStr); line = line.Substring(pos + 1); //cout << "[" << dbTip << "]"; pos = line.IndexOf("\t"); numStr = line.Substring(0, pos); dbEnd = int.Parse(numStr); line = line.Substring(pos + 1); //cout << "[" << dbEnd << "]"; //string damage = line; //cout << "[" << damage << "]" << endl; // The position is written starting at 1, but our index is 0 based DatabaseFin thisDBFin = db.AllFins[dbFinPosition - 1]; // TODO: Should this throw an exception instead? if (thisDBFin.IDCode != dbFinID) { Trace.WriteLine("Disaster " + thisDBFin.IDCode + " " + dbFinID); } FloatContour mappedUnknownContour = unkFin.FinOutline.ChainPoints.MapContour( unkFin.FinOutline.ChainPoints[uTip], unkFin.FinOutline.ChainPoints[uBegin], unkFin.FinOutline.ChainPoints[uEnd], thisDBFin.FinOutline.ChainPoints[dbTip], thisDBFin.FinOutline.ChainPoints[dbBegin], thisDBFin.FinOutline.ChainPoints[dbEnd]); Result r = new Result( mappedUnknownContour, //***1.3 - Mem Leak - constructor make copy now thisDBFin.FinOutline.ChainPoints, //***1.3 - Mem Leak - constructor make copy now thisDBFin.ID, // TODO - This image filename stuff is a little ugly. (string.IsNullOrEmpty(thisDBFin.OriginalImageFilename)) ? thisDBFin.ImageFilename : thisDBFin.OriginalImageFilename, thisDBFin.ThumbnailFilenameUri, dbFinPosition - 1, // position of fin in database double.Parse(error), thisDBFin.IDCode, thisDBFin.Name, thisDBFin.DamageCategory, thisDBFin.DateOfSighting, thisDBFin.LocationCode); r.SetMappingControlPoints( uBegin, uTip, uEnd, // beginning, tip & end of unknown fin dbBegin, dbTip, dbEnd); // beginning, tip & end of database fin result.Results.Add(r); } databaseFin = unkFin; result.SetRankings(); return(result); }