/// <summary> /// Pick image folder and detect all faces in these images /// </summary> /// <param name="sender">Event sender</param> /// <param name="e">Event arguments</param> private async void FolderPicker_Click(object sender, RoutedEventArgs e) { bool groupExists = false; MainWindow mainWindow = Window.GetWindow(this) as MainWindow; string subscriptionKey = mainWindow._scenariosControl.SubscriptionKey; string endpoint = mainWindow._scenariosControl.SubscriptionEndpoint; var faceServiceClient = new FaceServiceClient(subscriptionKey, endpoint); try { MainWindow.Log("Request: Large Face List {0} will be used to build a person database. Checking whether the large face list exists.", this._largeFaceListId); await faceServiceClient.GetLargeFaceListAsync(this._largeFaceListId); groupExists = true; MainWindow.Log("Response: Face List {0} exists.", this._largeFaceListId); } catch (FaceAPIException ex) { if (ex.ErrorCode != "LargeFaceListNotFound") { MainWindow.Log("Response: {0}. {1}", ex.ErrorCode, ex.ErrorMessage); return; } else { MainWindow.Log("Response: Large Face List {0} did not exist previously.", this._largeFaceListId); } } if (groupExists) { var cleanLargeFaceList = System.Windows.MessageBox.Show(string.Format("Requires a clean up for large face list \"{0}\" before setting up a new large face list. Click OK to proceed, large face list \"{0}\" will be cleared.", this._largeFaceListId), "Warning", MessageBoxButton.OKCancel); if (cleanLargeFaceList == MessageBoxResult.OK) { await faceServiceClient.DeleteLargeFaceListAsync(this._largeFaceListId); this._largeFaceListId = Guid.NewGuid().ToString(); } else { return; } } OpenFaceButton.IsEnabled = false; // Show folder picker System.Windows.Forms.FolderBrowserDialog dlg = new System.Windows.Forms.FolderBrowserDialog(); var result = dlg.ShowDialog(); bool forceContinue = false; if (result == System.Windows.Forms.DialogResult.OK) { // Enumerate all ".jpg" files in the folder, call detect List <Task> tasks = new List <Task>(); FacesCollection.Clear(); TargetFaces.Clear(); FindSimilarMatchPersonCollection.Clear(); FindSimilarMatchFaceCollection.Clear(); SelectedFile = null; // Set the suggestion count is intent to minimum the data preparation step only, // it's not corresponding to service side constraint const int SuggestionCount = 10; int processCount = 0; MainWindow.Log("Request: Preparing, detecting faces in chosen folder."); await faceServiceClient.CreateLargeFaceListAsync(this._largeFaceListId, this._largeFaceListId, "large face list for sample"); var imageList = new ConcurrentBag <string>( Directory.EnumerateFiles(dlg.SelectedPath, "*.*", SearchOption.AllDirectories) .Where(s => s.ToLower().EndsWith(".jpg") || s.ToLower().EndsWith(".png") || s.ToLower().EndsWith(".bmp") || s.ToLower().EndsWith(".gif"))); string img; int invalidImageCount = 0; while (imageList.TryTake(out img)) { tasks.Add(Task.Factory.StartNew( async(obj) => { var imgPath = obj as string; // Call detection using (var fStream = File.OpenRead(imgPath)) { try { var faces = await faceServiceClient.AddFaceToLargeFaceListAsync(this._largeFaceListId, fStream); return(new Tuple <string, ClientContract.AddPersistedFaceResult>(imgPath, faces)); } catch (FaceAPIException ex) { // if operation conflict, retry. if (ex.ErrorCode.Equals("ConcurrentOperationConflict")) { imageList.Add(imgPath); return(null); } // if operation cause rate limit exceed, retry. else if (ex.ErrorCode.Equals("RateLimitExceeded")) { imageList.Add(imgPath); return(null); } else if (ex.ErrorMessage.Contains("more than 1 face in the image.")) { Interlocked.Increment(ref invalidImageCount); } // Here we simply ignore all detection failure in this sample // You may handle these exceptions by check the Error.Error.Code and Error.Message property for ClientException object return(new Tuple <string, ClientContract.AddPersistedFaceResult>(imgPath, null)); } } }, img).Unwrap().ContinueWith((detectTask) => { var res = detectTask?.Result; if (res?.Item2 == null) { return; } // Update detected faces on UI this.Dispatcher.Invoke( new Action <ObservableCollection <Face>, string, ClientContract.AddPersistedFaceResult>( UIHelper.UpdateFace), FacesCollection, res.Item1, res.Item2); })); processCount++; if (processCount >= SuggestionCount && !forceContinue) { var continueProcess = System.Windows.Forms.MessageBox.Show( "The images loaded have reached the recommended count, may take long time if proceed. Would you like to continue to load images?", "Warning", System.Windows.Forms.MessageBoxButtons.YesNo); if (continueProcess == System.Windows.Forms.DialogResult.Yes) { forceContinue = true; } else { break; } } if (tasks.Count >= _maxConcurrentProcesses || imageList.IsEmpty) { await Task.WhenAll(tasks); tasks.Clear(); } } if (invalidImageCount > 0) { MainWindow.Log("Warning: more or less than one face is detected in {0} images, can not add to large face list.", invalidImageCount); } MainWindow.Log("Response: Success. Total {0} faces are detected.", FacesCollection.Count); try { // Start train large face list. MainWindow.Log("Request: Training Large Face List \"{0}\"", this._largeFaceListId); await faceServiceClient.TrainLargeFaceListAsync(this._largeFaceListId); // Wait until train completed while (true) { await Task.Delay(1000); var status = await faceServiceClient.GetLargeFaceListTrainingStatusAsync(this._largeFaceListId); MainWindow.Log("Response: {0}. Large Face List \"{1}\" training process is {2}", "Success", this._largeFaceListId, status.Status); if (status.Status != Contract.Status.Running) { break; } } OpenFaceButton.IsEnabled = true; } catch (FaceAPIException ex) { MainWindow.Log("Response: {0}. {1}", ex.ErrorCode, ex.ErrorMessage); } } GC.Collect(); }