/// <summary> /// Verify the face with the person, get whether these two faces belong to the same person /// </summary> /// <param name="sender">Event sender</param> /// <param name="e">Event argument</param> private async void Face2PersonVerification_Click(object sender, RoutedEventArgs e) { // Call face to face verification, verify REST API supports one face to one person verification only // Here, we handle single face image only if (Person != null && Person.Faces.Count != 0 && RightFaceResultCollection.Count == 1) { PersonVerifyResult = "Verifying..."; var faceId = RightFaceResultCollection[0].FaceId; MainWindow.Log("Request: Verifying face {0} and person {1}", faceId, Person.PersonName); // Call verify REST API with two face id try { var faceServiceClient = FaceServiceClientHelper.GetInstance(this); var res = await faceServiceClient.Face.VerifyFaceToPersonAsync(Guid.Parse(faceId), Guid.Parse(Person.PersonId), null, GroupName); // Verification result contains IsIdentical (true or false) and Confidence (in range 0.0 ~ 1.0), // here we update verify result on UI by PersonVerifyResult binding PersonVerifyResult = string.Format("{0} ({1:0.0})", res.IsIdentical ? "the face belongs to the person" : "the face not belong to the person", res.Confidence); MainWindow.Log("Response: Success. Face {0} {1} person {2}", faceId, res.IsIdentical ? "belong" : "not belong", Person.PersonName); } catch (APIErrorException ex) { MainWindow.Log("Response: {0}. {1}", ex.Body.Error.Code, ex.Body.Error.Message); return; } } else { MessageBox.Show("Verification accepts one person containing face(s) and one face as input, please check.", "Warning", MessageBoxButton.OK); } GC.Collect(); }
/// <summary> /// Pick image for detection, and using the detected face as the face to person verify. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private async void FaceImagePicker_Click(object sender, RoutedEventArgs e) { // Show image picker, show jpg type files only Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog(); dlg.DefaultExt = ".jpg"; dlg.Filter = "Image files(*.jpg, *.png, *.bmp, *.gif) | *.jpg; *.png; *.bmp; *.gif"; var result = dlg.ShowDialog(); if (result.HasValue && result.Value) { PersonVerifyResult = string.Empty; // User already picked one image var pickedImagePath = dlg.FileName; var renderingImage = UIHelper.LoadImageAppliedOrientation(pickedImagePath); var imageInfo = UIHelper.GetImageInfoForRendering(renderingImage); RightImageDisplay2.Source = renderingImage; // Clear last time detection results RightFaceResultCollection.Clear(); PersonVerifyButton.IsEnabled = (FacesCollection.Count != 0 && RightFaceResultCollection.Count != 0); MainWindow.Log("Request: Detecting in {0}", pickedImagePath); var sw = Stopwatch.StartNew(); // Call detection REST API, detect faces inside the image using (var fileStream = File.OpenRead(pickedImagePath)) { try { var faceServiceClient = FaceServiceClientHelper.GetInstance(this); var faces = await faceServiceClient.Face.DetectWithStreamAsync(fileStream, recognitionModel : recognitionModel); // Handle REST API calling error if (faces == null) { return; } MainWindow.Log("Response: Success. Detected {0} face(s) in {1}", faces.Count, pickedImagePath); // Convert detection results into UI binding object for rendering foreach (var face in UIHelper.CalculateFaceRectangleForRendering(faces, MaxImageSize, imageInfo)) { // Detected faces are hosted in result container, will be used in the verification later RightFaceResultCollection.Add(face); } PersonVerifyButton.IsEnabled = (FacesCollection.Count != 0 && RightFaceResultCollection.Count != 0); } catch (APIErrorException ex) { MainWindow.Log("Response: {0}. {1}", ex.Body.Error.Code, ex.Body.Error.Message); return; } } } GC.Collect(); }
/// <summary> /// Verify two detected faces, get whether these two faces belong to the same person /// </summary> /// <param name="sender">Event sender</param> /// <param name="e">Event argument</param> private async void Face2FaceVerification_Click(object sender, RoutedEventArgs e) { // Call face to face verification, verify REST API supports one face to one face verification only // Here, we handle single face image only if (LeftResultCollection.Count == 1 && RightResultCollection.Count == 1) { FaceVerifyResult = "Verifying..."; var faceId1 = LeftResultCollection[0].FaceId; var faceId2 = RightResultCollection[0].FaceId; MainWindow.Log("Request: Verifying face {0} and {1}", faceId1, faceId2); // Call verify REST API with two face id try { var faceServiceClient = FaceServiceClientHelper.GetInstance(this); var res = await faceServiceClient.Face.VerifyFaceToFaceAsync(Guid.Parse(faceId1), Guid.Parse(faceId2)); // Verification result contains IsIdentical (true or false) and Confidence (in range 0.0 ~ 1.0), // here we update verify result on UI by FaceVerifyResult binding FaceVerifyResult = string.Format("Confidence = {0:0.00}, {1}", res.Confidence, res.IsIdentical ? "two faces belong to same person" : "two faces not belong to same person"); MainWindow.Log("Response: Success. Face {0} and {1} {2} to the same person", faceId1, faceId2, res.IsIdentical ? "belong" : "not belong"); } catch (APIErrorException ex) { MainWindow.Log("Response: {0}. {1}", ex.Body.Error.Code, ex.Body.Error.Message); return; } } else { MessageBox.Show("Verification accepts two faces as input, please pick images with only one detectable face in it.", "Warning", MessageBoxButton.OK); } GC.Collect(); }
/// <summary> /// Pick image folder to detect faces and using these faces to create the person database /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private async void PersonImageFolderPicker_Click(object sender, RoutedEventArgs e) { bool groupExists = false; var faceServiceClient = FaceServiceClientHelper.GetInstance(this); // Test whether the group already exists try { MainWindow.Log("Request: Group {0} will be used to build a person database. Checking whether the group exists.", GroupName); await faceServiceClient.LargePersonGroup.GetAsync(GroupName); groupExists = true; MainWindow.Log("Response: Group {0} exists.", GroupName); } catch (APIErrorException ex) { if (ex.Body.Error.Code != "LargePersonGroupNotFound") { MainWindow.Log("Response: {0}. {1}", ex.Body.Error.Code, ex.Body.Error.Message); return; } else { MainWindow.Log("Response: Group {0} did not exist previously.", GroupName); } } if (groupExists) { var cleanGroup = System.Windows.MessageBox.Show(string.Format("Requires a clean up for group \"{0}\" before setting up a new person database. Click OK to proceed, group \"{0}\" will be cleared.", GroupName), "Warning", MessageBoxButton.OKCancel); if (cleanGroup == MessageBoxResult.OK) { await faceServiceClient.LargePersonGroup.DeleteAsync(GroupName); PersonVerifyResult = string.Empty; Person.Faces.Clear(); } else { return; } } // Show folder picker System.Windows.Forms.FolderBrowserDialog dlg = new System.Windows.Forms.FolderBrowserDialog(); var result = dlg.ShowDialog(); // Set the suggestion count is intent to minimum the data preparation step only, // it's not corresponding to service side constraint const int SuggestionCount = 15; if (result == System.Windows.Forms.DialogResult.OK) { FacesCollection.Clear(); PersonVerifyButton.IsEnabled = (FacesCollection.Count != 0 && RightFaceResultCollection.Count != 0); // Call create large person group REST API // Create large person group API call will failed if group with the same name already exists MainWindow.Log("Request: Creating group \"{0}\"", GroupName); try { await faceServiceClient.LargePersonGroup.CreateAsync(GroupName, GroupName, recognitionModel : recognitionModel); MainWindow.Log("Response: Success. Group \"{0}\" created", GroupName); } catch (APIErrorException ex) { MainWindow.Log("Response: {0}. {1}", ex.Body.Error.Code, ex.Body.Error.Message); return; } int processCount = 0; bool forceContinue = false; MainWindow.Log("Request: Preparing person for verification, detecting faces in chosen folder."); // Enumerate top level directories, each directory contains one person's images var tasks = new List <Task>(); var tag = System.IO.Path.GetFileName(dlg.SelectedPath); Person = new Person(); Person.PersonName = tag; var faces = new ObservableCollection <Face>(); Person.Faces = faces; // Call create person REST API, the new create person id will be returned MainWindow.Log("Request: Creating person \"{0}\"", Person.PersonName); Person.PersonId = (await faceServiceClient.LargePersonGroupPerson.CreateAsync(GroupName, Person.PersonName)).PersonId.ToString(); MainWindow.Log("Response: Success. Person \"{0}\" (PersonID:{1}) created", Person.PersonName, Person.PersonId); string img; 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"))); // Enumerate images under the person folder, call detection int invalidImageCount = 0; while (imageList.TryTake(out img)) { tasks.Add(Task.Factory.StartNew( async(obj) => { var imgPath = obj as string; using (var fStream = File.OpenRead(imgPath)) { try { var persistFace = await faceServiceClient.LargePersonGroupPerson.AddFaceFromStreamAsync(GroupName, Guid.Parse(Person.PersonId), fStream, imgPath); return(new Tuple <string, PersistedFace>(imgPath, persistFace)); } catch (APIErrorException ex) { // if operation conflict, retry. if (ex.Body.Error.Code.Equals("ConcurrentOperationConflict")) { imageList.Add(imgPath); return(null); } // if operation cause rate limit exceed, retry. else if (ex.Body.Error.Code.Equals("RateLimitExceeded")) { imageList.Add(imgPath); return(null); } else if (ex.Body.Error.Message.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, PersistedFace>(imgPath, null)); } } }, img).Unwrap().ContinueWith((detectTask) => { // Update detected faces for rendering var detectionResult = detectTask?.Result; if (detectionResult?.Item2 == null) { return; } this.Dispatcher.Invoke( new Action <ObservableCollection <Face>, string, PersistedFace>( UIHelper.UpdateFace), FacesCollection, detectionResult.Item1, detectionResult.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(); } } Person.Faces = FacesCollection; PersonVerifyButton.IsEnabled = (FacesCollection.Count != 0 && RightFaceResultCollection.Count != 0); if (invalidImageCount > 0) { MainWindow.Log("Warning: more or less than one face is detected in {0} images, can not add to face list.", invalidImageCount); } MainWindow.Log("Response: Success. Total {0} faces are detected.", Person.Faces.Count); } GC.Collect(); }
/// <summary> /// Pick image, detect and identify all faces detected /// </summary> /// <param name="sender">Event sender</param> /// <param name="e">Event arguments</param> private async void Identify_Click(object sender, RoutedEventArgs e) { // Show file picker Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog(); dlg.DefaultExt = ".jpg"; dlg.Filter = "Image files(*.jpg, *.png, *.bmp, *.gif) | *.jpg; *.png; *.bmp; *.gif"; var result = dlg.ShowDialog(); if (result.HasValue && result.Value) { // User picked one image // Clear previous detection and identification results TargetFaces.Clear(); var pickedImagePath = dlg.FileName; var renderingImage = UIHelper.LoadImageAppliedOrientation(pickedImagePath); var imageInfo = UIHelper.GetImageInfoForRendering(renderingImage); SelectedFile = renderingImage; var sw = Stopwatch.StartNew(); var faceServiceClient = FaceServiceClientHelper.GetInstance(this); // Call detection REST API using (var fStream = File.OpenRead(pickedImagePath)) { try { var faces = await faceServiceClient.Face.DetectWithStreamAsync(fStream, recognitionModel : recognitionModel, detectionModel : detectionModel); // Convert detection result into UI binding object for rendering foreach (var face in UIHelper.CalculateFaceRectangleForRendering(faces, MaxImageSize, imageInfo)) { TargetFaces.Add(face); } MainWindow.Log("Request: Identifying {0} face(s) in group \"{1}\"", faces.Count, GroupName); // Identify each face // Call identify REST API, the result contains identified person information var identifyResult = await faceServiceClient.Face.IdentifyAsync((from face in faces where face.FaceId != null select face.FaceId.Value).ToList(), null, GroupName); for (int idx = 0; idx < faces.Count; idx++) { // Update identification result for rendering var face = TargetFaces[idx]; var res = identifyResult[idx]; if (res.Candidates.Count > 0 && Persons.Any(p => p.PersonId == res.Candidates[0].PersonId.ToString())) { face.PersonName = Persons.Where(p => p.PersonId == res.Candidates[0].PersonId.ToString()).First().PersonName; } else { face.PersonName = "Unknown"; } } var outString = new StringBuilder(); foreach (var face in TargetFaces) { outString.AppendFormat("Face {0} is identified as {1}. ", face.FaceId, face.PersonName); } MainWindow.Log("Response: Success. {0}", outString); } catch (APIErrorException ex) { MainWindow.Log("Response: {0}. {1}", ex.Body.Error.Code, ex.Body.Error.Message); } } } GC.Collect(); }
/// <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; var faceServiceClient = FaceServiceClientHelper.GetInstance(this); try { MainWindow.Log("Request: Large Face List {0} will be used to build a person database. Checking whether the large face list exists.", _largeFaceListName); await faceServiceClient.LargeFaceList.GetAsync(_largeFaceListName); groupExists = true; MainWindow.Log("Response: Large Face List {0} exists.", _largeFaceListName); } catch (APIErrorException ex) { if (ex.Body.Error.Code != "LargeFaceListNotFound") { MainWindow.Log("Response: {0}. {1}", ex.Body.Error.Code, ex.Body.Error.Message); return; } else { MainWindow.Log("Response: Large Face List {0} did not exist previously.", _largeFaceListName); } } if (groupExists) { var cleanFaceList = 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.", _largeFaceListName), "Warning", MessageBoxButton.OKCancel); if (cleanFaceList == MessageBoxResult.OK) { await faceServiceClient.LargeFaceList.DeleteAsync(_largeFaceListName); } 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.LargeFaceList.CreateAsync(_largeFaceListName, _largeFaceListName, "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.LargeFaceList.AddFaceFromStreamAsync(_largeFaceListName, fStream); return(new Tuple <string, PersistedFace>(imgPath, faces)); } catch (APIErrorException ex) { // if operation conflict, retry. if (ex.Body.Error.Code.Equals("ConcurrentOperationConflict")) { imageList.Add(imgPath); return(null); } // if operation cause rate limit exceed, retry. else if (ex.Body.Error.Code.Equals("RateLimitExceeded")) { imageList.Add(imgPath); return(null); } else if (ex.Body.Error.Message.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, PersistedFace>(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, PersistedFace>( 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 to train the large face list. MainWindow.Log("Request: Training large face list \"{0}\"", _largeFaceListName); await faceServiceClient.LargeFaceList.TrainAsync(_largeFaceListName); // Wait until the training is completed. while (true) { await Task.Delay(1000); var trainingStatus = await faceServiceClient.LargeFaceList.GetTrainingStatusAsync(_largeFaceListName); MainWindow.Log("Response: {0}. Large face list \"{1}\" training process is {2}", "Success", _largeFaceListName, trainingStatus.Status); if (trainingStatus.Status != TrainingStatusType.Running) { if (trainingStatus.Status == TrainingStatusType.Failed) { MainWindow.Log("Response: Training failed with message {0}.", trainingStatus.Message); } break; } } OpenFaceButton.IsEnabled = true; } catch (APIErrorException ex) { MainWindow.Log("Response: {0}. {1}", ex.Body.Error.Code, ex.Body.Error.Message); } } GC.Collect(); }
/// <summary> /// Pick image and call find similar with both two modes for each faces detected /// </summary> /// <param name="sender">Event sender</param> /// <param name="e">Event arguments</param> private async void FindSimilar_Click(object sender, RoutedEventArgs e) { // Show file picker Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog(); dlg.DefaultExt = ".jpg"; dlg.Filter = "Image files (*.jpg, *.png, *.bmp, *.gif) | *.jpg; *.png; *.bmp; *.gif"; var filePicker = dlg.ShowDialog(); if (filePicker.HasValue && filePicker.Value) { // User picked image // Clear previous detection and find similar results TargetFaces.Clear(); FindSimilarMatchPersonCollection.Clear(); FindSimilarMatchFaceCollection.Clear(); var sw = Stopwatch.StartNew(); var pickedImagePath = dlg.FileName; var renderingImage = UIHelper.LoadImageAppliedOrientation(pickedImagePath); var imageInfo = UIHelper.GetImageInfoForRendering(renderingImage); SelectedFile = renderingImage; // Detect all faces in the picked image using (var fStream = File.OpenRead(pickedImagePath)) { MainWindow.Log("Request: Detecting faces in {0}", SelectedFile); var faceServiceClient = FaceServiceClientHelper.GetInstance(this); IList <DetectedFace> faces = await faceServiceClient.Face.DetectWithStreamAsync(fStream); // Update detected faces on UI foreach (var face in UIHelper.CalculateFaceRectangleForRendering(faces, MaxImageSize, imageInfo)) { TargetFaces.Add(face); } MainWindow.Log("Response: Success. Detected {0} face(s) in {1}", faces.Count, SelectedFile); // Find two modes similar faces for each face foreach (var f in faces) { if (f.FaceId == null) { continue; } var faceId = f.FaceId.Value; MainWindow.Log("Request: Finding similar faces in Personal Match Mode for face {0}", faceId); try { // Default mode, call find matchPerson similar REST API, the result contains all the face ids which is personal similar to the query face const int requestCandidatesCount = 4; IList <SimilarFace> result = await faceServiceClient.Face.FindSimilarAsync( faceId, null, _largeFaceListName, maxNumOfCandidatesReturned : requestCandidatesCount); // Update find matchPerson similar results collection for rendering var personSimilarResult = new FindSimilarResult(); personSimilarResult.Faces = new ObservableCollection <Face>(); personSimilarResult.QueryFace = new Face() { ImageFile = SelectedFile, Top = f.FaceRectangle.Top, Left = f.FaceRectangle.Left, Width = f.FaceRectangle.Width, Height = f.FaceRectangle.Height, FaceId = faceId.ToString(), }; foreach (var fr in result) { var candidateFace = FacesCollection.First(ff => ff.FaceId == fr.PersistedFaceId.ToString()); Face newFace = new Face(); newFace.ImageFile = candidateFace.ImageFile; newFace.Confidence = fr.Confidence; newFace.FaceId = candidateFace.FaceId; personSimilarResult.Faces.Add(newFace); } MainWindow.Log("Response: Found {0} similar faces for face {1}", personSimilarResult.Faces.Count, faceId); FindSimilarMatchPersonCollection.Add(personSimilarResult); } catch (APIErrorException ex) { MainWindow.Log("Response: {0}. {1}", ex.Body.Error.Code, ex.Body.Error.Message); } try { // Call find facial match similar REST API, the result faces the top N with the highest similar confidence const int requestCandidatesCount = 4; var result = await faceServiceClient.Face.FindSimilarAsync( faceId, null, _largeFaceListName, maxNumOfCandidatesReturned : requestCandidatesCount, mode : FindSimilarMatchMode.MatchFace); // Update "matchFace" similar results collection for rendering var faceSimilarResults = new FindSimilarResult(); faceSimilarResults.Faces = new ObservableCollection <Face>(); faceSimilarResults.QueryFace = new Face() { ImageFile = SelectedFile, Top = f.FaceRectangle.Top, Left = f.FaceRectangle.Left, Width = f.FaceRectangle.Width, Height = f.FaceRectangle.Height, FaceId = faceId.ToString(), }; foreach (var fr in result) { var candidateFace = FacesCollection.First(ff => ff.FaceId == fr.PersistedFaceId.ToString()); Face newFace = new Face(); newFace.ImageFile = candidateFace.ImageFile; newFace.Confidence = fr.Confidence; newFace.FaceId = candidateFace.FaceId; faceSimilarResults.Faces.Add(newFace); } MainWindow.Log("Response: Found {0} similar faces for face {1}", faceSimilarResults.Faces.Count, faceId); FindSimilarMatchFaceCollection.Add(faceSimilarResults); } catch (APIErrorException ex) { MainWindow.Log("Response: {0}. {1}", ex.Body.Error.Code, ex.Body.Error.Message); } } } } GC.Collect(); }
/// <summary> /// Pick folder, then group detected faces by similarity /// </summary> /// <param name="sender">Event sender</param> /// <param name="e">Event arguments</param> private async void Grouping_Click(object sender, RoutedEventArgs e) { // Show folder picker System.Windows.Forms.FolderBrowserDialog dlg = new System.Windows.Forms.FolderBrowserDialog(); var result = dlg.ShowDialog(); // 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; if (result == System.Windows.Forms.DialogResult.OK) { // User picked one folder List <Task> tasks = new List <Task>(); int processCount = 0; bool forceContinue = false; // Clear previous grouping result GroupedFaces.Clear(); Faces.Clear(); var faceServiceClient = FaceServiceClientHelper.GetInstance(this); MainWindow.Log("Request: Preparing faces for grouping, detecting faces in chosen folder."); 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; while (imageList.TryTake(out img)) { tasks.Add(Task.Factory.StartNew( async(obj) => { var imgPath = obj as string; // Detect faces in image using (var fStream = File.OpenRead(imgPath)) { try { var faces = await faceServiceClient.Face.DetectWithStreamAsync(fStream); return(new Tuple <string, IList <DetectedFace> >(imgPath, faces)); } catch (APIErrorException ex) { // if operation conflict, retry. if (ex.Body.Error.Code.Equals("ConcurrentOperationConflict")) { imageList.Add(imgPath); return(null); } // 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, IList <DetectedFace> >(imgPath, null)); } } }, img).Unwrap().ContinueWith((detectTask) => { // Update detected faces on UI var res = detectTask?.Result; if (res?.Item2 == null) { return; } foreach (var f in res.Item2) { this.Dispatcher.Invoke( new Action <ObservableCollection <Face>, string, DetectedFace>(UIHelper.UpdateFace), Faces, res.Item1, f); } })); if (processCount >= SuggestionCount && !forceContinue) { var continueProcess = System.Windows.Forms.MessageBox.Show("Found many images under chosen folder, may take long time if proceed. Continue?", "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(); } } MainWindow.Log("Response: Success. Total {0} faces are detected.", Faces.Count); try { MainWindow.Log("Request: Grouping {0} faces.", Faces.Count); // Call grouping, the grouping result is a group collection, each group contains similar faces var groupRes = await faceServiceClient.Face.GroupAsync(Faces.Select(f => Guid.Parse(f.FaceId)).ToArray()); // Update grouping results for rendering foreach (var g in groupRes.Groups) { var gg = new GroupingResult() { Faces = new ObservableCollection <Face>(), IsMessyGroup = false, }; foreach (var fr in g) { gg.Faces.Add(Faces.First(f => f.FaceId == fr.ToString())); } GroupedFaces.Add(gg); } // MessyGroup contains all faces which are not similar to any other faces. // Take an extreme case for example: // On grouping faces which are not similar to any other faces, the grouping result will contains only one messy group if (groupRes.MessyGroup.Count > 0) { var messyGroup = new GroupingResult() { Faces = new ObservableCollection <Face>(), IsMessyGroup = true }; foreach (var messy in groupRes.MessyGroup) { messyGroup.Faces.Add(Faces.First(f => f.FaceId == messy.ToString())); } GroupedFaces.Add(messyGroup); } MainWindow.Log("Response: Success. {0} faces are grouped into {1} groups.", Faces.Count, GroupedFaces.Count); } catch (APIErrorException ex) { MainWindow.Log("Response: {0}. {1}", ex.Body.Error.Code, ex.Body.Error.Message); } } GC.Collect(); }
/// <summary> /// Pick image for face detection and set detection result to result container /// </summary> /// <param name="sender">Event sender</param> /// <param name="e">Event argument</param> private async void ImagePicker_Click(object sender, RoutedEventArgs e) { // Show file picker dialog Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog(); dlg.DefaultExt = ".jpg"; dlg.Filter = "Image files (*.jpg, *.png, *.bmp, *.gif) | *.jpg; *.png; *.bmp; *.gif"; var result = dlg.ShowDialog(); if (result.HasValue && result.Value) { // User picked one image var pickedImagePath = dlg.FileName; var renderingImage = UIHelper.LoadImageAppliedOrientation(pickedImagePath); var imageInfo = UIHelper.GetImageInfoForRendering(renderingImage); SelectedFile = renderingImage; // Clear last detection result ResultCollection.Clear(); DetectedFaces.Clear(); DetectedResultsInText = string.Format("Detecting..."); MainWindow.Log("Request: Detecting {0}", pickedImagePath); var sw = Stopwatch.StartNew(); // Call detection REST API using (var fStream = File.OpenRead(pickedImagePath)) { try { var faceServiceClient = FaceServiceClientHelper.GetInstance(this); IList <DetectedFace> faces = await faceServiceClient.Face.DetectWithStreamAsync( fStream, false, true, new List <FaceAttributeType>() { FaceAttributeType.Accessories, FaceAttributeType.Age, FaceAttributeType.Blur, FaceAttributeType.Emotion, FaceAttributeType.Exposure, FaceAttributeType.FacialHair, FaceAttributeType.Gender, FaceAttributeType.Glasses, FaceAttributeType.Hair, FaceAttributeType.HeadPose, FaceAttributeType.Makeup, FaceAttributeType.Noise, FaceAttributeType.Occlusion, FaceAttributeType.Smile }, recognitionModel ); MainWindow.Log("Response: Success. Detected {0} face(s) in {1}", faces.Count, pickedImagePath); DetectedResultsInText = string.Format("{0} face(s) has been detected", faces.Count); foreach (var face in faces) { DetectedFaces.Add(new Face() { ImageFile = renderingImage, Left = face.FaceRectangle.Left, Top = face.FaceRectangle.Top, Width = face.FaceRectangle.Width, Height = face.FaceRectangle.Height, FaceId = face.FaceId?.ToString(), Age = string.Format("{0:#} years old", face.FaceAttributes.Age), Gender = face.FaceAttributes.Gender.ToString(), HeadPose = string.Format("Pitch: {0}, Roll: {1}, Yaw: {2}", Math.Round(face.FaceAttributes.HeadPose.Pitch, 2), Math.Round(face.FaceAttributes.HeadPose.Roll, 2), Math.Round(face.FaceAttributes.HeadPose.Yaw, 2)), FacialHair = string.Format("FacialHair: {0}", face.FaceAttributes.FacialHair.Moustache + face.FaceAttributes.FacialHair.Beard + face.FaceAttributes.FacialHair.Sideburns > 0 ? "Yes" : "No"), Glasses = string.Format("GlassesType: {0}", face.FaceAttributes.Glasses.ToString()), Emotion = $"{GetEmotion(face.FaceAttributes.Emotion)}", Hair = string.Format("Hair: {0}", GetHair(face.FaceAttributes.Hair)), Makeup = string.Format("Makeup: {0}", ((face.FaceAttributes.Makeup.EyeMakeup || face.FaceAttributes.Makeup.LipMakeup) ? "Yes" : "No")), EyeOcclusion = string.Format("EyeOccluded: {0}", ((face.FaceAttributes.Occlusion.EyeOccluded) ? "Yes" : "No")), ForeheadOcclusion = string.Format("ForeheadOccluded: {0}", (face.FaceAttributes.Occlusion.ForeheadOccluded ? "Yes" : "No")), MouthOcclusion = string.Format("MouthOccluded: {0}", (face.FaceAttributes.Occlusion.MouthOccluded ? "Yes" : "No")), Accessories = $"{GetAccessories(face.FaceAttributes.Accessories)}", Blur = string.Format("Blur: {0}", face.FaceAttributes.Blur.BlurLevel.ToString()), Exposure = string.Format("{0}", face.FaceAttributes.Exposure.ExposureLevel.ToString()), Noise = string.Format("Noise: {0}", face.FaceAttributes.Noise.NoiseLevel.ToString()), }); } // Convert detection result into UI binding object for rendering foreach (var face in UIHelper.CalculateFaceRectangleForRendering(faces, MaxImageSize, imageInfo)) { ResultCollection.Add(face); } } catch (APIErrorException ex) { MainWindow.Log("Response: {0}. {1}", ex.Body.Error.Code, ex.Body.Error.Message); GC.Collect(); return; } GC.Collect(); } } }