private static async Task <TResponse> RunTaskWithAutoRetryOnQuotaLimitExceededError <TResponse>(Func <Task <TResponse> > action) { int retriesLeft = FaceServiceHelper.RetryCountOnQuotaLimitError; int delay = FaceServiceHelper.RetryDelayOnQuotaLimitError; TResponse response = default(TResponse); while (true) { try { response = await action(); break; } catch (FaceAPIException exception) when(exception.HttpStatus == (System.Net.HttpStatusCode) 429 && retriesLeft > 0) { ErrorTrackingHelper.TrackException(exception, "Face API throttling error"); if (retriesLeft == 1 && Throttled != null) { Throttled(); } await Task.Delay(delay); retriesLeft--; delay *= 2; continue; } } return(response); }
public static async Task ResetFaceLists() { faceLists = new Dictionary <string, FaceListInfo>(); try { IEnumerable <FaceListMetadata> metadata = await FaceServiceHelper.GetFaceListsAsync(FaceListsUserDataFilter); foreach (var item in metadata) { await FaceServiceHelper.DeleteFaceListAsync(item.FaceListId); } } catch (Exception e) { ErrorTrackingHelper.TrackException(e, "Error resetting face lists"); } }
public async Task DetectFacesAsync(bool detectFaceAttributes = false, bool detectFaceLandmarks = false) { try { if (this.ImageUrl != null) { this.DetectedFaces = await FaceServiceHelper.DetectAsync( this.ImageUrl, returnFaceId : true, returnFaceLandmarks : detectFaceLandmarks, returnFaceAttributes : detectFaceAttributes?DefaultFaceAttributeTypes : null); } else if (this.GetImageStreamCallback != null) { this.DetectedFaces = await FaceServiceHelper.DetectAsync( this.GetImageStreamCallback, returnFaceId : true, returnFaceLandmarks : detectFaceLandmarks, returnFaceAttributes : detectFaceAttributes?DefaultFaceAttributeTypes : null); } if (this.FilterOutSmallFaces) { this.DetectedFaces = this.DetectedFaces.Where(f => CoreUtil.IsFaceBigEnoughForDetection(f.FaceRectangle.Height, this.DecodedImageHeight)); } } catch (Exception e) { ErrorTrackingHelper.TrackException(e, "Face API DetectAsync error"); this.DetectedFaces = Enumerable.Empty <Face>(); if (this.ShowDialogOnFaceApiErrors) { await ErrorTrackingHelper.GenericApiCallExceptionHandler(e, "Face detection failed."); } } finally { this.OnFaceDetectionCompleted(); } }
public static async Task Initialize() { faceLists = new Dictionary <string, FaceListInfo>(); try { IEnumerable <FaceListMetadata> metadata = await FaceServiceHelper.GetFaceListsAsync(FaceListsUserDataFilter); foreach (var item in metadata) { faceLists.Add(item.FaceListId, new FaceListInfo { FaceListId = item.FaceListId, LastMatchTimestamp = DateTime.Now }); } } catch (Exception e) { ErrorTrackingHelper.TrackException(e, "Face API GetFaceListsAsync error"); } }
public async Task FindSimilarPersistedFacesAsync() { this.SimilarFaceMatches = Enumerable.Empty <SimilarFaceMatch>(); if (this.DetectedFaces == null || !this.DetectedFaces.Any()) { return; } List <SimilarFaceMatch> result = new List <SimilarFaceMatch>(); foreach (Face detectedFace in this.DetectedFaces) { try { SimilarPersistedFace similarPersistedFace = await FaceListManager.FindSimilarPersistedFaceAsync(this.GetImageStreamCallback, detectedFace.FaceId, detectedFace); if (similarPersistedFace != null) { result.Add(new SimilarFaceMatch { Face = detectedFace, SimilarPersistedFace = similarPersistedFace }); } } catch (Exception e) { ErrorTrackingHelper.TrackException(e, "FaceListManager.FindSimilarPersistedFaceAsync error"); if (this.ShowDialogOnFaceApiErrors) { await ErrorTrackingHelper.GenericApiCallExceptionHandler(e, "Failure finding similar faces"); } } } this.SimilarFaceMatches = result; }
public static async Task <SimilarPersistedFace> FindSimilarPersistedFaceAsync(Func <Task <Stream> > imageStreamCallback, Guid faceId, Face face) { if (faceLists == null) { await Initialize(); } Tuple <SimilarPersistedFace, string> bestMatch = null; bool foundMatch = false; foreach (var faceListId in faceLists.Keys) { try { SimilarPersistedFace similarFace = (await FaceServiceHelper.FindSimilarAsync(faceId, faceListId))?.FirstOrDefault(); if (similarFace == null) { continue; } foundMatch = true; if (bestMatch != null) { // We already found a match for this face in another list. Replace the previous one if the new confidence is higher. if (bestMatch.Item1.Confidence < similarFace.Confidence) { bestMatch = new Tuple <SimilarPersistedFace, string>(similarFace, faceListId); } } else { bestMatch = new Tuple <SimilarPersistedFace, string>(similarFace, faceListId); } } catch (Exception e) { // Catch errors with individual face lists so we can continue looping through all lists. Maybe an answer will come from // another one. ErrorTrackingHelper.TrackException(e, "Face API FindSimilarAsync error"); } } if (!foundMatch) { // If we are here we didnt' find a match, so let's add the face to the first FaceList that we can add it to. We // might create a new list if none exist, and if all lists are full we will delete the oldest face list (based on when we // last matched anything on it) so that we can add the new one. double maxAngle = 30; if (face.FaceAttributes.HeadPose != null && (Math.Abs(face.FaceAttributes.HeadPose.Yaw) > maxAngle || Math.Abs(face.FaceAttributes.HeadPose.Pitch) > maxAngle || Math.Abs(face.FaceAttributes.HeadPose.Roll) > maxAngle)) { // This isn't a good frontal shot, so let's not use it as the primary example face for this person return(null); } if (!faceLists.Any()) { // We don't have any FaceLists yet. Create one string newFaceListId = Guid.NewGuid().ToString(); await FaceServiceHelper.CreateFaceListAsync(newFaceListId, "ManagedFaceList", FaceListsUserDataFilter); faceLists.Add(newFaceListId, new FaceListInfo { FaceListId = newFaceListId, LastMatchTimestamp = DateTime.Now }); } AddPersistedFaceResult addResult = null; bool failedToAddToNonFullList = false; foreach (var faceList in faceLists) { if (faceList.Value.IsFull) { continue; } try { addResult = await FaceServiceHelper.AddFaceToFaceListAsync(faceList.Key, imageStreamCallback, face.FaceRectangle); break; } catch (Exception ex) { if (ex is FaceAPIException && ((FaceAPIException)ex).ErrorCode == "403") { // FaceList is full. Continue so we can try again with the next FaceList faceList.Value.IsFull = true; continue; } else { failedToAddToNonFullList = true; break; } } } if (addResult == null && !failedToAddToNonFullList) { // We were not able to add the face to an existing list because they were all full. // If possible, let's create a new list now and add the new face to it. If we can't (e.g. we already maxed out on list count), // let's delete an old list, create a new one and add the new face to it. if (faceLists.Count == 64) { // delete oldest face list var oldestFaceList = faceLists.OrderBy(fl => fl.Value.LastMatchTimestamp).FirstOrDefault(); faceLists.Remove(oldestFaceList.Key); await FaceServiceHelper.DeleteFaceListAsync(oldestFaceList.Key); } // create new list string newFaceListId = Guid.NewGuid().ToString(); await FaceServiceHelper.CreateFaceListAsync(newFaceListId, "ManagedFaceList", FaceListsUserDataFilter); faceLists.Add(newFaceListId, new FaceListInfo { FaceListId = newFaceListId, LastMatchTimestamp = DateTime.Now }); // Add face to new list addResult = await FaceServiceHelper.AddFaceToFaceListAsync(newFaceListId, imageStreamCallback, face.FaceRectangle); } if (addResult != null) { bestMatch = new Tuple <SimilarPersistedFace, string>(new SimilarPersistedFace { Confidence = 1, PersistedFaceId = addResult.PersistedFaceId }, null); } } return(bestMatch?.Item1); }
public async Task IdentifyFacesAsync() { this.IdentifiedPersons = Enumerable.Empty <IdentifiedPerson>(); Guid[] detectedFaceIds = this.DetectedFaces?.Select(f => f.FaceId).ToArray(); if (detectedFaceIds != null && detectedFaceIds.Any()) { List <IdentifiedPerson> result = new List <IdentifiedPerson>(); IEnumerable <PersonGroup> personGroups = Enumerable.Empty <PersonGroup>(); try { personGroups = await FaceServiceHelper.GetPersonGroupsAsync(PeopleGroupsUserDataFilter); } catch (Exception e) { ErrorTrackingHelper.TrackException(e, "Face API GetPersonGroupsAsync error"); if (this.ShowDialogOnFaceApiErrors) { await ErrorTrackingHelper.GenericApiCallExceptionHandler(e, "Failure getting PersonGroups"); } } foreach (var group in personGroups) { try { IdentifyResult[] groupResults = await FaceServiceHelper.IdentifyAsync(group.PersonGroupId, detectedFaceIds); foreach (var match in groupResults) { if (!match.Candidates.Any()) { continue; } Person person = await FaceServiceHelper.GetPersonAsync(group.PersonGroupId, match.Candidates[0].PersonId); IdentifiedPerson alreadyIdentifiedPerson = result.FirstOrDefault(p => p.Person.PersonId == match.Candidates[0].PersonId); if (alreadyIdentifiedPerson != null) { // We already tagged this person in another group. Replace the existing one if this new one if the confidence is higher. if (alreadyIdentifiedPerson.Confidence < match.Candidates[0].Confidence) { alreadyIdentifiedPerson.Person = person; alreadyIdentifiedPerson.Confidence = match.Candidates[0].Confidence; alreadyIdentifiedPerson.FaceId = match.FaceId; } } else { result.Add(new IdentifiedPerson { Person = person, Confidence = match.Candidates[0].Confidence, FaceId = match.FaceId }); } } } catch (Exception e) { // Catch errors with individual groups so we can continue looping through all groups. Maybe an answer will come from // another one. ErrorTrackingHelper.TrackException(e, "Face API IdentifyAsync error"); if (this.ShowDialogOnFaceApiErrors) { await ErrorTrackingHelper.GenericApiCallExceptionHandler(e, "Failure identifying faces"); } } } this.IdentifiedPersons = result; } this.OnFaceRecognitionCompleted(); }