public void TestVideoIndexerSerialization() { var frameFingerPrint = new FrameFingerPrintWrapper { FrameNumber = 0, PHashCode = 0x1010, EdgeGrayScaleThumb = new byte[] { 0, 1, 0 }, }; var videoFingerPrint = new VideoFingerPrintWrapper { FilePath = "test.mkv", FingerPrints = new[] { frameFingerPrint }, }; var database = new VideoFingerPrintDatabaseWrapper { VideoFingerPrints = new[] { videoFingerPrint }, }; using (var memoryStream = new MemoryStream()) { VideoFingerPrintDatabaseSaver.Save(database, memoryStream); byte[] savedDatabase = memoryStream.ToArray(); VideoFingerPrintDatabaseWrapper reloadedDatabase = VideoFingerPrintDatabaseLoader.Load(savedDatabase); Assert.AreEqual(database, reloadedDatabase); } }
private static void IndexImpl(string databaseMetaTablePath, IEnumerable <string> videoPaths, int numThreads, long maxMemory) { IEnumerable <string> knownHashes = GetKnownDatabaseEntries(databaseMetaTablePath); var skippedFiles = new ConcurrentBag <string>(); using (ChangeErrorMode _ = new ChangeErrorMode(ChangeErrorMode.ErrorModes.FailCriticalErrors | ChangeErrorMode.ErrorModes.NoGpFaultErrorBox)) using (FingerPrintStore store = new FingerPrintStore(databaseMetaTablePath)) { long maxMemoryPerIndexJob = (long)Math.Round((double)maxMemory / numThreads); Parallel.ForEach( videoPaths.Except(knownHashes), new ParallelOptions { MaxDegreeOfParallelism = numThreads }, videoPath => { string fileName = Path.GetFileName(videoPath); try { if (videoPath.Length > 260) // Max file path that mediainfo can handle { Console.WriteLine("File path is too long. Skipping: " + fileName); return; } VideoFingerPrintWrapper videoFingerPrint = Video.VideoIndexer.IndexVideo(videoPath, maxMemoryPerIndexJob); store.AddFingerPrint(videoFingerPrint); } catch (InvalidOperationException e) { Console.WriteLine(string.Format("Unable to hash file {0}. Reason: {1}. Skipping", fileName, e.Message)); skippedFiles.Add(videoPath); } catch (Exception e) { Console.WriteLine(string.Format("Unable to hash file {0}. Reason: {1}. Skipping", fileName, e.Message)); } } ); // Attempting to index skipped files foreach (string skippedFile in skippedFiles) { Console.WriteLine("Attempting to hash skipped file: " + skippedFile); try { VideoFingerPrintWrapper videoFingerPrint = Video.VideoIndexer.IndexVideo(skippedFile, maxMemory); store.AddFingerPrint(videoFingerPrint); } catch (InvalidOperationException) { Console.WriteLine(string.Format("Unable to hash file {0} for the second time. Giving up", skippedFile)); } } store.Shutdown(); store.Wait(); } }
private static void ExecuteCheckDatabase(string[] args) { string maxMemoryArg = GetMaxMemory(args); string databaseMetaTablePath = GetDatabaseMetaTable(args); if (string.IsNullOrWhiteSpace(databaseMetaTablePath)) { PrintHelp("Database metatable path not provided"); return; } if (File.Exists(databaseMetaTablePath) == false) { PrintHelp("Database MetaTable does not exist"); return; } if (string.IsNullOrWhiteSpace(maxMemoryArg)) { PrintHelp("--max-memory must be set"); return; } long maxMemory = 0; if (long.TryParse(maxMemoryArg, out maxMemory) == false) { PrintHelp("--max-memory could not be parsed"); return; } VideoFingerPrintDatabaseMetaTableWrapper metaTable = VideoFingerPrintDatabaseMetaTableLoader.Load(databaseMetaTablePath); var random = new Random(); foreach (string databasePath in metaTable.DatabaseMetaTableEntries.Select(e => e.FileName)) { VideoFingerPrintDatabaseWrapper database = VideoFingerPrintDatabaseLoader.Load(databasePath); int videoFingerPrintSampleCount = (int)Math.Round(database.VideoFingerPrints.Length / 3.0); IEnumerable <VideoFingerPrintWrapper> videoFingerPrints = from fingerPrint in database.VideoFingerPrints where random.Next() % 2 == 0 select fingerPrint; foreach (VideoFingerPrintWrapper videoFingerPrint in videoFingerPrints.Take(videoFingerPrintSampleCount)) { VideoFingerPrintWrapper actualVideoFingerPrint = Video.VideoIndexer.IndexVideo(videoFingerPrint.FilePath, maxMemory); if (Equals(videoFingerPrint, actualVideoFingerPrint) == false) { Console.WriteLine("{0} Fingerprint does not match", Path.GetFileName(videoFingerPrint.FilePath)); } } } }
private static WritableLockBitImage GetFrameFromVideo(VideoFingerPrintWrapper video, int frameNumber) { using (MediaInfoProcess mediaInfoProcess = new MediaInfoProcess(video.FilePath)) { MediaInfo mediaInfo = mediaInfoProcess.Execute(); if (mediaInfo.GetFramerate().Numerator == 0) { throw new Exception("Did not get valid frame rate"); } int width = mediaInfo.GetWidth(); int height = mediaInfo.GetHeight(); var ffmpegProcessSettings = new FFMPEGProcessVideoSettings( video.FilePath, mediaInfo.GetFramerate().Numerator, mediaInfo.GetFramerate().Denominator * 4, FFMPEGMode.SeekFrame ); ffmpegProcessSettings.TargetFrame = frameNumber; byte[] frameBytes = new byte[width * height * 3]; int offset = 0; using ( var ffmpegProcess = new FFMPEGProcess( ffmpegProcessSettings, (stdoutBytes, numBytes) => { Buffer.BlockCopy(stdoutBytes, 0, frameBytes, offset, numBytes); offset += numBytes; }, false ) ) { ffmpegProcess.Execute(); if (offset != 3 * width * height) { throw new Exception("Did not get all bytes to produce valid frame"); } WritableLockBitImage videoFrame = new WritableLockBitImage(width, height, frameBytes); videoFrame.Lock(); return(videoFrame); } } }
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); }
private static VideoFingerPrintWrapper FindMostLikelyVideo( PhotoFingerPrintWrapper photo, IDictionary <string, ISet <FrameMetricWrapper> > treeResults, IDictionary <string, VideoFingerPrintWrapper> nameToVideoMap ) { if (treeResults.Count() == 1) { return(treeResults.First().Value.First().Video); } using (WritableLockBitImage photoAsLockBitImage = new WritableLockBitImage(Image.FromFile(photo.FilePath))) { // Filter on grayscale results first List <FrameMetricWrapper> filteredOnGrayScaleResults = (from videoFrameResults in treeResults from frameMetricWrapper in videoFrameResults.Value where DistanceCalculator.CalculateHammingDistance(photo.EdgeGrayScaleThumb, frameMetricWrapper.Frame.EdgeGrayScaleThumb) < 2 select frameMetricWrapper).ToList(); if (filteredOnGrayScaleResults.Count == 1) { return(filteredOnGrayScaleResults.First().Video); } double currentBestSSIM = 0.0; VideoFingerPrintWrapper currentBestVideo = null; foreach (FrameMetricWrapper FrameMetricWrapper in filteredOnGrayScaleResults) { // Calculate SSIM using (WritableLockBitImage videoFrame = GetFrameFromVideo(FrameMetricWrapper.Video, FrameMetricWrapper.Frame.FrameNumber)) { double possibleBestSSIM = SSIMCalculator.Compute(photoAsLockBitImage, videoFrame); // SSIM must be at least good enough for us to consider if (possibleBestSSIM > currentBestSSIM) { currentBestSSIM = possibleBestSSIM; currentBestVideo = FrameMetricWrapper.Video; } } } return(currentBestVideo); } }
private static Offset <FrameFingerPrint>[] CreateFrameFingerPrintArray(FlatBufferBuilder builder, VideoFingerPrintWrapper videoFingerPrint) { int frameFingerPrintCounter = 0; var frameFingerPrintArray = new Offset <FrameFingerPrint> [videoFingerPrint.FingerPrints.Length]; foreach (FrameFingerPrintWrapper frameFingerPrint in videoFingerPrint.FingerPrints) { VectorOffset grayScaleImageOffset = FrameFingerPrint.CreateEdgeGrayScaleThumbVector(builder, frameFingerPrint.EdgeGrayScaleThumb); FrameFingerPrint.StartFrameFingerPrint(builder); FrameFingerPrint.AddFrameNumber(builder, frameFingerPrint.FrameNumber); FrameFingerPrint.AddPHash(builder, frameFingerPrint.PHashCode); FrameFingerPrint.AddEdgeGrayScaleThumb(builder, grayScaleImageOffset); frameFingerPrintArray[frameFingerPrintCounter] = FrameFingerPrint.EndFrameFingerPrint(builder); frameFingerPrintCounter++; } return(frameFingerPrintArray); }
public void AddFingerPrint(VideoFingerPrintWrapper fingerprint) { _workItems.Add(fingerprint); }