using DlibDotNet; using DlibDotNet.Dnn; using View_by_Distance.FaceRecognitionDotNet.Models; namespace View_by_Distance.FaceRecognitionDotNet.Dlib.Python; internal sealed class FaceRecognitionModelV1 { #region Methods public static Matrix ComputeFaceDescriptor(LossMetric net, Image img, FullObjectDetection face, int numberOfJitters) { FullObjectDetection[]? faces = [face]; return ComputeFaceDescriptors(net, img, faces, numberOfJitters).First(); } public static IEnumerable> ComputeFaceDescriptors(LossMetric net, Image img, IEnumerable faces, int numberOfJitters) { Image[]? batchImage = [img]; IEnumerable[]? batchFaces = [faces]; return BatchComputeFaceDescriptors(net, batchImage, batchFaces, numberOfJitters).First(); } public static IEnumerable>> BatchComputeFaceDescriptors(LossMetric net, IList batchImages, IList> batchFaces, int numberOfJitters) { if (batchImages.Count != batchFaces.Count) throw new ArgumentException("The array of images and the array of array of locations must be of the same size"); foreach (IEnumerable? faces in batchFaces) foreach (FullObjectDetection? f in faces) { if (f.Parts is not 68 and not 5) throw new ArgumentException("The full_object_detection must use the iBUG 300W 68 point face landmark style or dlib's 5 point style."); } List>>? faceChipsArray = new(batchImages.Count); List>? faceChips = []; for (int i = 0; i < batchImages.Count; ++i) { IEnumerable? faces = batchFaces[i]; Image? img = batchImages[i]; List? dets = new(faces.Count()); foreach (FullObjectDetection? f in faces) dets.Add(DlibDotNet.Dlib.GetFaceChipDetails(f, 150, 0.25)); Array>? thisImageFaceChips = DlibDotNet.Dlib.ExtractImageChips(img.Matrix, dets); foreach (Matrix? chip in thisImageFaceChips) faceChips.Add(chip); faceChipsArray.Add(thisImageFaceChips); foreach (ChipDetails? det in dets) det.Dispose(); } List>>? faceDescriptors = []; for (int i = 0, count = batchImages.Count; i < count; i++) faceDescriptors.Add([]); if (numberOfJitters <= 1) { // extract descriptors and convert from float vectors to double vectors OutputLabels>? descriptors = net.Operator(faceChips, 16); int index = 0; Matrix[]? list = descriptors.Select(matrix => matrix).ToArray(); for (int i = 0; i < batchFaces.Count; ++i) for (int j = 0; j < batchFaces[i].Count(); ++j) faceDescriptors[i].Add(DlibDotNet.Dlib.MatrixCast(list[index++])); if (index != list.Length) throw new ApplicationException(); } else { // extract descriptors and convert from float vectors to double vectors int index = 0; for (int i = 0; i < batchFaces.Count; ++i) for (int j = 0; j < batchFaces[i].Count(); ++j) { Matrix[]? tmp = JitterImage(faceChips[index++], numberOfJitters).ToArray(); using (OutputLabels>? tmp2 = net.Operator(tmp, 16)) using (MatrixOp? mat = DlibDotNet.Dlib.Mat(tmp2)) { Matrix? r = DlibDotNet.Dlib.Mean(mat); faceDescriptors[i].Add(r); } foreach (Matrix? matrix in tmp) matrix.Dispose(); } if (index != faceChips.Count) throw new ApplicationException(); } if (faceChipsArray.Count > 0) { foreach (Array>? array in faceChipsArray) { foreach (Matrix? faceChip in array) faceChip.Dispose(); array.Dispose(); } } return faceDescriptors; } #region Helpers private static readonly Rand _Rand = new(); private static IEnumerable> JitterImage(Matrix img, int numberOfJitters) { List>? crops = []; for (int i = 0; i < numberOfJitters; ++i) crops.Add(DlibDotNet.Dlib.JitterImage(img, _Rand)); return crops; } #endregion #endregion }