public void Setup() { var loader = new ImageSetLoader(); models = loader.Load(); Directory.CreateDirectory("Output"); foreach (var model in models) { model.Image.Save($@"Output\{model.Name}.png"); } }
// out parameters can't be used in anonymous methods, so a separate pointer to backgroundWorker is required for return to the caller private bool TryBeginImageFolderLoad(string imageSetFolderPath, string selectedFolderPath) { List <FileInfo> filesToAdd = new List <FileInfo>(); List <string> filesSkipped = new List <string>(); // Generate FileInfo list for every single image / video file in the folder path (including subfolders). These become the files to add to the database // PERFORMANCE - takes modest but noticable time to do if there are a huge number of files. // TO DO: PUT THIS IN THE SHOW PROGRESS LOOP Util.FilesFolders.GetAllImageAndVideoFilesInFolderAndSubfolders(selectedFolderPath, filesToAdd); if (filesToAdd.Count == 0) { // No images were found in the root folder or subfolders, so there is nothing to do Dialogs.ImageSetLoadingNoImagesOrVideosWereFoundDialog(this, selectedFolderPath); return(false); } if (this.State.MetadataAskOnLoad) { Cursor cursor = Mouse.OverrideCursor; PopulateFieldsWithMetadataOnLoad populateField = new PopulateFieldsWithMetadataOnLoad(this, this.DataHandler.FileDatabase, filesToAdd[0].FullName); if (this.ShowDialogAndCheckIfChangesWereMade(populateField)) { this.State.MetadataOnLoad = populateField.MetadataOnLoad; } Mouse.OverrideCursor = cursor; } // Load all the files (matching allowable file types) found in the folder // Show image previews of the files to the user as they are individually loaded // Generally, Background worker examines each image, and extracts data from it which it stores in a data structure, which in turn is used to compose bulk database inserts. // PERFORMANCE This is likely the place that the best performance increases can be gained by transforming its foreach loop into a Parallel.ForEach. // Indeed, you will see commented out remnants of a Parallel.ForEach in the code where this was done, but using it introduced errors. //#pragma warning disable CA2000 // Dispose objects before losing scope. Reason: Not required as Dispose on BackgroundWorker doesn't do anything BackgroundWorker backgroundWorker = new BackgroundWorker() { WorkerReportsProgress = true }; //#pragma warning restore CA2000 // Dispose objects before losing scope // folderLoadProgress contains data to be used to provide feedback on the folder loading state FolderLoadProgress folderLoadProgress = new FolderLoadProgress(filesToAdd.Count) { TotalPasses = 2, CurrentPass = 1 }; backgroundWorker.DoWork += (ow, ea) => { ImageSetLoader loader = new ImageSetLoader(imageSetFolderPath, filesToAdd, this.DataHandler); backgroundWorker.ReportProgress(0, folderLoadProgress); // If the DoWork delegate is async, this is considered finished before the actual image set is loaded. // Instead of an async DoWork and an await here, wait for the loading to finish. loader.LoadAsync(backgroundWorker.ReportProgress, folderLoadProgress, 500).Wait(); filesSkipped = loader.ImagesSkippedAsFilePathTooLong; backgroundWorker.ReportProgress(0, folderLoadProgress); }; backgroundWorker.ProgressChanged += (o, ea) => { // this gets called on the UI thread this.ImageSetPane.IsActive = true; if (filesSkipped.Count > 0) { Dialogs.FilePathTooLongDialog(this, filesSkipped); } if (folderLoadProgress.CurrentPass == 1 && folderLoadProgress.CurrentFile == 0) { // skip the 0th file of the 1st pass, as there is not really much of interest to show return; } string message = (folderLoadProgress.TotalPasses > 1) ? String.Format("Pass {0}/{1}{2}", folderLoadProgress.CurrentPass, folderLoadProgress.TotalPasses, Environment.NewLine) : String.Empty; if (folderLoadProgress.CurrentPass == 1 && folderLoadProgress.CurrentFile == folderLoadProgress.TotalFiles) { message = String.Format("{0}Finalizing analysis of {1} files - could take several minutes ", message, folderLoadProgress.TotalFiles); } else { string what = (folderLoadProgress.CurrentPass == 1) ? "Analyzing file" : "Adding files to database"; message = (folderLoadProgress.CurrentPass == 2 && folderLoadProgress.CurrentFile == 0) ? String.Format("{0}{1} ...", message, what) : String.Format("{0}{1} {2} of {3} ({4})", message, what, folderLoadProgress.CurrentFile, folderLoadProgress.TotalFiles, folderLoadProgress.CurrentFileName); } this.UpdateFolderLoadProgress(this.BusyCancelIndicator, folderLoadProgress.BitmapSource, ea.ProgressPercentage, message, false, false); this.StatusBar.SetCurrentFile(folderLoadProgress.CurrentFile); this.StatusBar.SetCount(folderLoadProgress.TotalFiles); }; backgroundWorker.RunWorkerCompleted += async(o, ea) => { // BackgroundWorker aborts execution on an exception and transfers it to completion for handling // If something went wrong rethrow the error so the user knows there's a problem. Otherwise what would happen is either // 1) some or all of the folder load file scan progress displays but no files get added to the database as the insert is skipped // 2) only some of the files get inserted and the rest are silently dropped // Both of these outcomes result in quite poor user experience and are best avoided. if (ea.Error != null) { throw new FileLoadException("Folder loading failed unexpectedly. See inner exception for details.", ea.Error); } // Create an index on RelativePath, File,and RelativePath/File if it doesn't already exist this.DataHandler.FileDatabase.IndexCreateForFileAndRelativePathIfNotExists(); // Show the file slider this.FileNavigatorSlider.Visibility = Visibility.Visible; await this.OnFolderLoadingCompleteAsync(true).ConfigureAwait(true); // Do some final things // Note that if the magnifier is enabled, we temporarily hide so it doesn't appear in the background bool saveMagnifierState = this.MarkableCanvas.MagnifiersEnabled; this.MarkableCanvas.MagnifiersEnabled = false; this.StatusBar.SetMessage(folderLoadProgress.TotalFiles + " files are now loaded"); this.MarkableCanvas.MagnifiersEnabled = saveMagnifierState; // If we want to import old data from the ImageData.xml file, we can do it here... // Check to see if there is an ImageData.xml file in here. If there is, ask the user // if we want to load the data from that... if (File.Exists(Path.Combine(this.FolderPath, Constant.File.XmlDataFileName))) { ImportImageSetXmlFile importLegacyXmlDialog = new ImportImageSetXmlFile(this); bool?dialogResult = importLegacyXmlDialog.ShowDialog(); if (dialogResult == true) { Tuple <int, int> SuccessSkippedFileCounter = ImageDataXml.Read(Path.Combine(this.FolderPath, Constant.File.XmlDataFileName), this.DataHandler.FileDatabase); await this.FilesSelectAndShowAsync(this.DataHandler.FileDatabase.ImageSet.MostRecentFileID, this.DataHandler.FileDatabase.ImageSet.FileSelection).ConfigureAwait(true); // to regenerate the controls and markers for this image Dialogs.ImageSetLoadingDataImportedFromOldXMLFileDialog(this, SuccessSkippedFileCounter.Item1, SuccessSkippedFileCounter.Item2); } } // Stop the ExifToolManager if it was invoked while loading files, which can occurs when populating metadata to a file via the EXIFTool on load. this.State.ExifToolManager.Stop(); this.BusyCancelIndicator.IsBusy = false; // Hide the busy indicator }; // Set up the user interface to show feedback this.BusyCancelIndicator.IsBusy = true; // Display the busy indicator this.FileNavigatorSlider.Visibility = Visibility.Collapsed; // First feedback message this.UpdateFolderLoadProgress(GlobalReferences.BusyCancelIndicator, null, 0, String.Format("Initializing...{0}Analyzing and loading {1} files ", Environment.NewLine, filesToAdd.Count), false, false); this.StatusBar.SetMessage("Loading folders..."); backgroundWorker.RunWorkerAsync(); return(true); }