public void BKTreeShouldQueryBestMatchesBelowGivenThreshold() { BKTree <ExampleMetric> tree = new BKTree <ExampleMetric>(); ExampleMetric search = new ExampleMetric(new int[] { 399, 400, 400 }); ExampleMetric best1 = new ExampleMetric(41, new int[] { 400, 400, 400 }); ExampleMetric best2 = new ExampleMetric(42, new int[] { 403, 403, 403 }); ExampleMetric best3 = new ExampleMetric(43, new int[] { 406, 406, 406 }); tree.Add(new ExampleMetric(1, new int[] { 100, 100, 100 })); tree.Add(new ExampleMetric(2, new int[] { 200, 200, 200 })); tree.Add(new ExampleMetric(3, new int[] { 300, 300, 300 })); tree.Add(best1); tree.Add(best2); tree.Add(new ExampleMetric(5, new int[] { 500, 500, 500 })); // Query for match within distance of 1 (best1 is only expected result) IDictionary <ExampleMetric, int> results = tree.Query(search, 1); Assert.AreEqual(1, DistanceMetric.CalculateLeeDistance(search.Data, best1.Data)); Assert.AreEqual(1, results.Values.ElementAt(0)); Assert.AreEqual(41, results.Keys.ElementAt(0).Id); Assert.AreEqual(best1.Data, results.Keys.ElementAt(0).Data); // Query for match within distance of 10 (best1 & best2 are expected results) tree.Add(best3); // exercise adding another node after already queried results = tree.Query(search, 10); Assert.AreEqual(2, results.Count); Assert.AreEqual(1, DistanceMetric.CalculateLeeDistance(search.Data, best1.Data)); Assert.AreEqual(10, DistanceMetric.CalculateLeeDistance(search.Data, best2.Data)); Assert.IsTrue(results.Contains(new KeyValuePair <ExampleMetric, int>(best1, 1))); Assert.IsTrue(results.Contains(new KeyValuePair <ExampleMetric, int>(best2, 10))); // Query for matches within distance of 20 (best1, best2 & best3 are expected results) results = tree.Query(search, 20); Assert.AreEqual(3, results.Count); Assert.AreEqual(1, DistanceMetric.CalculateLeeDistance(search.Data, best1.Data)); Assert.AreEqual(10, DistanceMetric.CalculateLeeDistance(search.Data, best2.Data)); Assert.AreEqual(19, DistanceMetric.CalculateLeeDistance(search.Data, best3.Data)); Assert.IsTrue(results.Contains(new KeyValuePair <ExampleMetric, int>(best1, 1))); Assert.IsTrue(results.Contains(new KeyValuePair <ExampleMetric, int>(best2, 10))); Assert.IsTrue(results.Contains(new KeyValuePair <ExampleMetric, int>(best3, 19))); }
private static void ExecuteSearch(string[] args) { string photoFilePath = GetPhotoPath(args); string databaseMetaTablePath = GetDatabaseMetaTable(args); if (string.IsNullOrWhiteSpace(photoFilePath) || string.IsNullOrWhiteSpace(databaseMetaTablePath)) { PrintHelp("Photo path or database metatable path not provided"); return; } if (File.Exists(photoFilePath) == false) { PrintHelp("Photo file does not exist"); return; } if (File.Exists(databaseMetaTablePath) == false) { PrintHelp("Database MetaTable does not exist"); return; } using (Image frame = Image.FromFile(photoFilePath)) { ulong providedPhotoHash = FrameIndexer.CalculateFramePerceptionHashOnly(frame); VideoFingerPrintDatabaseMetaTableWrapper metaTable = VideoFingerPrintDatabaseMetaTableLoader.Load(databaseMetaTablePath); BKTree <FrameMetricWrapper> bktree = ModelMetricUtils.CreateBKTree(metaTable); IDictionary <FrameMetricWrapper, int> treeResults = bktree.Query( new PhotoMetricWrapper { Photo = new PhotoFingerPrintWrapper { FilePath = photoFilePath, PHash = providedPhotoHash, }, }, 2 ); foreach (KeyValuePair <FrameMetricWrapper, int> kvp in treeResults.OrderBy(e => e.Value)) { FrameMetricWrapper frameWrapper = kvp.Key; int distance = kvp.Value; Console.WriteLine(string.Format("Distance {0} for {1} at Frame {2}", distance, frameWrapper.Video.FilePath, frameWrapper.Frame.FrameNumber)); } } }
private static IDictionary <string, ISet <PhotoFingerPrintWrapper> > MapPhotosToVideos( PhotoFingerPrintDatabaseWrapper photoDatabase, VideoFingerPrintDatabaseMetaTableWrapper metatable ) { IDictionary <string, ISet <PhotoFingerPrintWrapper> > resultMap = new Dictionary <string, ISet <PhotoFingerPrintWrapper> >(); IDictionary <string, VideoFingerPrintWrapper> fileNameToVideoFingerPrintMap = MetaTableUtils.EnumerateVideoFingerPrints(metatable).ToDictionary(e => e.FilePath); BKTree <FrameMetricWrapper> bktree = ModelMetricUtils.CreateBKTree(metatable); foreach (PhotoFingerPrintWrapper photo in photoDatabase.PhotoFingerPrints) { // 1. Find bucket of possible candidates IDictionary <FrameMetricWrapper, int> treeResults = bktree.Query( new PhotoMetricWrapper { Photo = photo, }, DefaultMetricThreshold ); IDictionary <string, ISet <FrameMetricWrapper> > collapsedTreeResults = ModelMetricUtils.CollapseTreeResults(treeResults); // 2. Find most likely result and add it to the bucket if (treeResults.Count > 0) { VideoFingerPrintWrapper mostLikelyVideo = FindMostLikelyVideo(photo, collapsedTreeResults, fileNameToVideoFingerPrintMap); // In the case where we didn't get any results, we just skip this photo and move alone if (mostLikelyVideo == null) { continue; } ISet <PhotoFingerPrintWrapper> bucket; string videoFileName = mostLikelyVideo.FilePath; if (resultMap.TryGetValue(videoFileName, out bucket) == false) { bucket = new HashSet <PhotoFingerPrintWrapper>(); resultMap.Add(videoFileName, bucket); } } } return(resultMap); }