public async Task <List <FaceSendInfo> > IdentifyOrAddPersonWithEmotionsAsync(string groupName, ObservableCollection <IdentifiedFaces> identifiedPersonsIdCollection) { var facesInfo = new List <FaceSendInfo>(); //Loop thru all detected faces from previous steps and fill the facesInfo array //For ease of processing in Azure Stream Analytics we create single level object foreach (var f in this.DetectedFaces) { var fsi = new FaceSendInfo(); //Add emotions var e = CoreUtil.FindEmotionForFace(f, this.DetectedEmotion); FeedFaceInfo(f, fsi, e); //We sen also info how many faces in total were recognized on the picture with current face fsi.facesNo = this.DetectedFaces.Count(); facesInfo.Add(fsi); } //Now we proceed to face recognition/identification //First we create group if it does not exist try { var g = await FaceServiceHelper.CreatePersonGroupIfNoGroupExists(groupName); groupId = g.PersonGroupId; } 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, "Problem creating group"); if (this.ShowDialogOnFaceApiErrors) { await ErrorTrackingHelper.GenericApiCallExceptionHandler(e, "Problem creating group"); } } //We need to find candidate for every face try { IdentifyResult[] groupResults = await this.IdentifyFacesAsync(groupId); //We loop thri all faces again in order to find candidate foreach (var f in this.DetectedFaces) { bool needToRetrain = true; var fi = facesInfo.Where(fin => fin.faceId == f.FaceId.ToString()).FirstOrDefault(); var newPersonID = Guid.NewGuid(); if (groupResults != null && groupResults.Where(gr => gr.FaceId == f.FaceId).Any() && groupResults.Where(gr => gr.FaceId == f.FaceId).FirstOrDefault().Candidates.Any()) { var candidates = groupResults.Where(gr => gr.FaceId == f.FaceId).FirstOrDefault().Candidates.OrderByDescending(ca => ca.Confidence); Person p = new Person(); var can = candidates.FirstOrDefault(); //If we have sufficient confidence, we add Face for person if (can.Confidence >= SettingsHelper.Instance.Confidence) { fi.canid = can.PersonId.ToString(); fi.canconf = can.Confidence; //In order to get also name we need to obtain Person p = await FaceServiceHelper.GetPersonAsync(groupId, can.PersonId); fi.canname = p.Name; var identifiedPersonFromList = identifiedPersonsIdCollection.Where(ip => ip.Id == can.PersonId.ToString()).FirstOrDefault(); //Check whether we did not added too much photos lately, it is not neccesary to add photo for face every time if (identifiedPersonFromList == null) { await AddFaceToPerson(f, p, can.PersonId); } else if (identifiedPersonFromList.NumOfAddedPhotosInLastPeriod < SettingsHelper.Instance.NumberOfPhotoAddsInPeriod) { await AddFaceToPerson(f, p, can.PersonId); identifiedPersonFromList.NumOfAddedPhotosInLastPeriod++; } else if ((DateTime.Now - identifiedPersonFromList.FirstPhotoAddedInLastPeriod).Hours > SettingsHelper.Instance.PhotoAddPeriodSize) { identifiedPersonFromList.NumOfAddedPhotosInLastPeriod = 1; identifiedPersonFromList.FirstPhotoAddedInLastPeriod = DateTime.Now; await AddFaceToPerson(f, p, can.PersonId); } else { needToRetrain = false; } } else { //if not sufficient confidence we also need to check whether there is similar face/ if not create new person await CreatePrsonIfNoSimilarFaceExistsAsync(facesInfo, newPersonID, f); } } else { //if no candidate we also need to check whether there is similar fac,e if not create new person await CreatePrsonIfNoSimilarFaceExistsAsync(facesInfo, newPersonID, f); } try { //We need to train after operation on top of group (addition of photo, person etc.) if (needToRetrain) { await FaceServiceHelper.TrainPersonGroupAsync(groupId); } } catch (Exception e) { // Catch error with training of group ErrorTrackingHelper.TrackException(e, "Problem training group"); if (this.ShowDialogOnFaceApiErrors) { await ErrorTrackingHelper.GenericApiCallExceptionHandler(e, "Problem training group"); } } //Handle the identified persons collection to which we locally save every identified person if (!identifiedPersonsIdCollection.Where(ip => ip.Id == fi.canid).Any()) { identifiedPersonsIdCollection.Add(new IdentifiedFaces() { Id = fi.canid }); } //Increase counter of identifications else if (identifiedPersonsIdCollection.Where(ip => ip.Id == fi.canid).Any()) { identifiedPersonsIdCollection.Where(ip => ip.Id == fi.canid).FirstOrDefault().NumberOfIdentifications++; } //Find faces which were wrongly learned (small number of identifications) var tbd = new List <IdentifiedFaces>(); foreach (var ip in identifiedPersonsIdCollection) { if (ip.NumberOfIdentifications <= SettingsHelper.Instance.NeededFaceIdentNum && (ip.CreatedAt.AddSeconds(SettingsHelper.Instance.DeleteWindow) < DateTime.Now)) { var g = (await FaceServiceHelper.GetPersonGroupsAsync()).Where(gr => gr.Name == groupName).FirstOrDefault(); Person pers = await FaceServiceHelper.GetPersonAsync(g.PersonGroupId, new Guid(ip.Id)); //if we saved insufficient number of faces than delete if (pers.PersistedFaceIds.Length <= SettingsHelper.Instance.NeededFaceIdentNum) { await FaceServiceHelper.DeletePersonAsync(g.PersonGroupId, pers.PersonId); string similarFaceId = ""; using (var db = new KioskDBContext()) { var sfToDelete = db.SimilarFaces.Where(sf => sf.PersonId == pers.PersonId.ToString()).FirstOrDefault(); similarFaceId = sfToDelete.FaceId.ToString(); db.SimilarFaces.Remove(sfToDelete); } await FaceListManager.DeleteFaceFromFaceList(similarFaceId); await FaceServiceHelper.TrainPersonGroupAsync(g.PersonGroupId); tbd.Add(ip); } } } foreach (var iptodelete in tbd) { identifiedPersonsIdCollection.Remove(iptodelete); } } } catch (Exception e) { // Catch error with training of group ErrorTrackingHelper.TrackException(e, "Problem with cognitive services"); if (this.ShowDialogOnFaceApiErrors) { await ErrorTrackingHelper.GenericApiCallExceptionHandler(e, "Problem with cognitive services"); } } return(facesInfo); }