Added Class Library FaceRecognitionDotNet
This commit is contained in:
111
FaceRecognitionDotNet/Dlib/Python/CnnFaceDetectionModelV1.cs
Normal file
111
FaceRecognitionDotNet/Dlib/Python/CnnFaceDetectionModelV1.cs
Normal file
@ -0,0 +1,111 @@
|
||||
using DlibDotNet;
|
||||
using DlibDotNet.Dnn;
|
||||
using View_by_Distance.Shared.Models.Stateless;
|
||||
|
||||
namespace View_by_Distance.FaceRecognitionDotNet.Dlib.Python;
|
||||
|
||||
internal sealed class CnnFaceDetectionModelV1
|
||||
{
|
||||
|
||||
#region Methods
|
||||
|
||||
public static IEnumerable<MModRect> Detect(LossMmod net, Image image, int upsampleNumTimes)
|
||||
{
|
||||
using PyramidDown? pyr = new(2);
|
||||
List<MModRect>? rects = new();
|
||||
|
||||
// Copy the data into dlib based objects
|
||||
using Matrix<RgbPixel>? matrix = new();
|
||||
Mode type = image.Mode;
|
||||
switch (type)
|
||||
{
|
||||
case Mode.Greyscale:
|
||||
case Mode.Rgb:
|
||||
DlibDotNet.Dlib.AssignImage(image.Matrix, matrix);
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException("Unsupported image type, must be 8bit gray or RGB image.");
|
||||
}
|
||||
|
||||
// Upsampling the image will allow us to detect smaller faces but will cause the
|
||||
// program to use more RAM and run longer.
|
||||
int levels = upsampleNumTimes;
|
||||
while (levels > 0)
|
||||
{
|
||||
levels--;
|
||||
DlibDotNet.Dlib.PyramidUp<PyramidDown>(matrix, 2);
|
||||
}
|
||||
|
||||
OutputLabels<IEnumerable<MModRect>>? dets = net.Operator(matrix);
|
||||
|
||||
// Scale the detection locations back to the original image size
|
||||
// if the image was upscaled.
|
||||
foreach (MModRect? d in dets.First())
|
||||
{
|
||||
DRectangle drect = pyr.RectDown(new DRectangle(d.Rect), (uint)upsampleNumTimes);
|
||||
d.Rect = new Rectangle((int)drect.Left, (int)drect.Top, (int)drect.Right, (int)drect.Bottom);
|
||||
rects.Add(d);
|
||||
}
|
||||
|
||||
return rects;
|
||||
}
|
||||
|
||||
public static IEnumerable<IEnumerable<MModRect>> DetectMulti(LossMmod net, IEnumerable<Image> images, int upsampleNumTimes, int batchSize = 128)
|
||||
{
|
||||
List<Matrix<RgbPixel>>? destImages = new();
|
||||
List<IEnumerable<MModRect>>? allRects = new();
|
||||
|
||||
try
|
||||
{
|
||||
using PyramidDown? pyr = new(2);
|
||||
// Copy the data into dlib based objects
|
||||
foreach (Image? image in images)
|
||||
{
|
||||
Matrix<RgbPixel>? matrix = new();
|
||||
Mode type = image.Mode;
|
||||
switch (type)
|
||||
{
|
||||
case Mode.Greyscale:
|
||||
case Mode.Rgb:
|
||||
DlibDotNet.Dlib.AssignImage(image.Matrix, matrix);
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException("Unsupported image type, must be 8bit gray or RGB image.");
|
||||
}
|
||||
|
||||
for (int i = 0; i < upsampleNumTimes; i++)
|
||||
DlibDotNet.Dlib.PyramidUp(matrix);
|
||||
|
||||
destImages.Add(matrix);
|
||||
}
|
||||
|
||||
for (int i = 1; i < destImages.Count; i++)
|
||||
if (destImages[i - 1].Columns != destImages[i].Columns || destImages[i - 1].Rows != destImages[i].Rows)
|
||||
throw new ArgumentException("Images in list must all have the same dimensions.");
|
||||
|
||||
OutputLabels<IEnumerable<MModRect>>? dets = net.Operator(destImages, (ulong)batchSize);
|
||||
foreach (IEnumerable<MModRect>? det in dets)
|
||||
{
|
||||
List<MModRect>? rects = new();
|
||||
foreach (MModRect? d in det)
|
||||
{
|
||||
DRectangle drect = pyr.RectDown(new DRectangle(d.Rect), (uint)upsampleNumTimes);
|
||||
d.Rect = new Rectangle((int)drect.Left, (int)drect.Top, (int)drect.Right, (int)drect.Bottom);
|
||||
rects.Add(d);
|
||||
}
|
||||
|
||||
allRects.Add(rects);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
foreach (Matrix<RgbPixel>? matrix in destImages)
|
||||
matrix.Dispose();
|
||||
}
|
||||
|
||||
return allRects;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
129
FaceRecognitionDotNet/Dlib/Python/FaceRecognitionModelV1.cs
Normal file
129
FaceRecognitionDotNet/Dlib/Python/FaceRecognitionModelV1.cs
Normal file
@ -0,0 +1,129 @@
|
||||
using DlibDotNet;
|
||||
using DlibDotNet.Dnn;
|
||||
|
||||
namespace View_by_Distance.FaceRecognitionDotNet.Dlib.Python;
|
||||
|
||||
internal sealed class FaceRecognitionModelV1
|
||||
{
|
||||
|
||||
#region Methods
|
||||
|
||||
public static Matrix<double> ComputeFaceDescriptor(LossMetric net, Image img, FullObjectDetection face, int numJitters)
|
||||
{
|
||||
FullObjectDetection[]? faces = new[] { face };
|
||||
return ComputeFaceDescriptors(net, img, faces, numJitters).First();
|
||||
}
|
||||
|
||||
public static IEnumerable<Matrix<double>> ComputeFaceDescriptors(LossMetric net, Image img, IEnumerable<FullObjectDetection> faces, int numJitters)
|
||||
{
|
||||
Image[]? batchImage = new[] { img };
|
||||
IEnumerable<FullObjectDetection>[]? batchFaces = new[] { faces };
|
||||
return BatchComputeFaceDescriptors(net, batchImage, batchFaces, numJitters).First();
|
||||
}
|
||||
|
||||
public static IEnumerable<IEnumerable<Matrix<double>>> BatchComputeFaceDescriptors(LossMetric net,
|
||||
IList<Image> batchImages,
|
||||
IList<IEnumerable<FullObjectDetection>> batchFaces,
|
||||
int numJitters)
|
||||
{
|
||||
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<FullObjectDetection>? 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<Array<Matrix<RgbPixel>>>? faceChipsArray = new(batchImages.Count);
|
||||
List<Matrix<RgbPixel>>? faceChips = new();
|
||||
for (int i = 0; i < batchImages.Count; ++i)
|
||||
{
|
||||
IEnumerable<FullObjectDetection>? faces = batchFaces[i];
|
||||
Image? img = batchImages[i];
|
||||
|
||||
List<ChipDetails>? dets = new(faces.Count());
|
||||
foreach (FullObjectDetection? f in faces)
|
||||
dets.Add(DlibDotNet.Dlib.GetFaceChipDetails(f, 150, 0.25));
|
||||
|
||||
Array<Matrix<RgbPixel>>? thisImageFaceChips = DlibDotNet.Dlib.ExtractImageChips<RgbPixel>(img.Matrix, dets);
|
||||
foreach (Matrix<RgbPixel>? chip in thisImageFaceChips)
|
||||
faceChips.Add(chip);
|
||||
faceChipsArray.Add(thisImageFaceChips);
|
||||
|
||||
foreach (ChipDetails? det in dets)
|
||||
det.Dispose();
|
||||
}
|
||||
|
||||
List<List<Matrix<double>>>? faceDescriptors = new();
|
||||
for (int i = 0, count = batchImages.Count; i < count; i++)
|
||||
faceDescriptors.Add(new List<Matrix<double>>());
|
||||
|
||||
if (numJitters <= 1)
|
||||
{
|
||||
// extract descriptors and convert from float vectors to double vectors
|
||||
OutputLabels<Matrix<float>>? descriptors = net.Operator(faceChips, 16);
|
||||
int index = 0;
|
||||
Matrix<float>[]? 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<double>(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<RgbPixel>[]? tmp = JitterImage(faceChips[index++], numJitters).ToArray();
|
||||
using (OutputLabels<Matrix<float>>? tmp2 = net.Operator(tmp, 16))
|
||||
using (MatrixOp? mat = DlibDotNet.Dlib.Mat(tmp2))
|
||||
{
|
||||
Matrix<double>? r = DlibDotNet.Dlib.Mean<double>(mat);
|
||||
faceDescriptors[i].Add(r);
|
||||
}
|
||||
|
||||
foreach (Matrix<RgbPixel>? matrix in tmp)
|
||||
matrix.Dispose();
|
||||
}
|
||||
|
||||
if (index != faceChips.Count)
|
||||
throw new ApplicationException();
|
||||
}
|
||||
|
||||
if (faceChipsArray.Any())
|
||||
{
|
||||
foreach (Array<Matrix<RgbPixel>>? array in faceChipsArray)
|
||||
{
|
||||
foreach (Matrix<RgbPixel>? faceChip in array)
|
||||
faceChip.Dispose();
|
||||
array.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
return faceDescriptors;
|
||||
}
|
||||
|
||||
#region Helpers
|
||||
|
||||
private static readonly Rand _Rand = new();
|
||||
|
||||
private static IEnumerable<Matrix<RgbPixel>> JitterImage(Matrix<RgbPixel> img, int numJitters)
|
||||
{
|
||||
List<Matrix<RgbPixel>>? crops = new();
|
||||
for (int i = 0; i < numJitters; ++i)
|
||||
crops.Add(DlibDotNet.Dlib.JitterImage(img, _Rand));
|
||||
|
||||
return crops;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
169
FaceRecognitionDotNet/Dlib/Python/SimpleObjectDetector.cs
Normal file
169
FaceRecognitionDotNet/Dlib/Python/SimpleObjectDetector.cs
Normal file
@ -0,0 +1,169 @@
|
||||
using DlibDotNet;
|
||||
using View_by_Distance.Shared.Models.Stateless;
|
||||
|
||||
namespace View_by_Distance.FaceRecognitionDotNet.Dlib.Python;
|
||||
|
||||
internal sealed class SimpleObjectDetector
|
||||
{
|
||||
|
||||
#region Methods
|
||||
|
||||
public static IEnumerable<Rectangle> RunDetectorWithUpscale1(FrontalFaceDetector detector,
|
||||
Image img,
|
||||
uint upsamplingAmount,
|
||||
double adjustThreshold,
|
||||
List<double> detectionConfidences,
|
||||
List<ulong> weightIndices)
|
||||
{
|
||||
List<Rectangle>? rectangles = new();
|
||||
|
||||
if (img.Mode == Mode.Greyscale)
|
||||
{
|
||||
Matrix<byte>? greyscaleMatrix = img.Matrix as Matrix<byte>;
|
||||
if (upsamplingAmount == 0)
|
||||
{
|
||||
detector.Operator(greyscaleMatrix, out IEnumerable<RectDetection>? rectDetections, adjustThreshold);
|
||||
|
||||
RectDetection[]? dets = rectDetections.ToArray();
|
||||
SplitRectDetections(dets, rectangles, detectionConfidences, weightIndices);
|
||||
|
||||
foreach (RectDetection? rectDetection in dets)
|
||||
rectDetection.Dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
using PyramidDown? pyr = new(2);
|
||||
Matrix<byte>? temp = null;
|
||||
|
||||
try
|
||||
{
|
||||
DlibDotNet.Dlib.PyramidUp(greyscaleMatrix, pyr, out temp);
|
||||
|
||||
uint levels = upsamplingAmount - 1;
|
||||
while (levels > 0)
|
||||
{
|
||||
levels--;
|
||||
DlibDotNet.Dlib.PyramidUp(temp);
|
||||
}
|
||||
|
||||
detector.Operator(temp, out IEnumerable<RectDetection>? rectDetections, adjustThreshold);
|
||||
|
||||
RectDetection[]? dets = rectDetections.ToArray();
|
||||
foreach (RectDetection? t in dets)
|
||||
t.Rect = pyr.RectDown(t.Rect, upsamplingAmount);
|
||||
|
||||
SplitRectDetections(dets, rectangles, detectionConfidences, weightIndices);
|
||||
|
||||
foreach (RectDetection? rectDetection in dets)
|
||||
rectDetection.Dispose();
|
||||
}
|
||||
finally
|
||||
{
|
||||
temp?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
return rectangles;
|
||||
}
|
||||
else
|
||||
{
|
||||
Matrix<RgbPixel>? rgbMatrix = img.Matrix as Matrix<RgbPixel>;
|
||||
if (upsamplingAmount == 0)
|
||||
{
|
||||
detector.Operator(rgbMatrix, out IEnumerable<RectDetection>? rectDetections, adjustThreshold);
|
||||
|
||||
RectDetection[]? dets = rectDetections.ToArray();
|
||||
SplitRectDetections(dets, rectangles, detectionConfidences, weightIndices);
|
||||
|
||||
foreach (RectDetection? rectDetection in dets)
|
||||
rectDetection.Dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
using PyramidDown? pyr = new(2);
|
||||
Matrix<RgbPixel>? temp = null;
|
||||
|
||||
try
|
||||
{
|
||||
DlibDotNet.Dlib.PyramidUp(rgbMatrix, pyr, out temp);
|
||||
|
||||
uint levels = upsamplingAmount - 1;
|
||||
while (levels > 0)
|
||||
{
|
||||
levels--;
|
||||
DlibDotNet.Dlib.PyramidUp(temp);
|
||||
}
|
||||
|
||||
detector.Operator(temp, out IEnumerable<RectDetection>? rectDetections, adjustThreshold);
|
||||
|
||||
RectDetection[]? dets = rectDetections.ToArray();
|
||||
foreach (RectDetection? t in dets)
|
||||
t.Rect = pyr.RectDown(t.Rect, upsamplingAmount);
|
||||
|
||||
SplitRectDetections(dets, rectangles, detectionConfidences, weightIndices);
|
||||
|
||||
foreach (RectDetection? rectDetection in dets)
|
||||
rectDetection.Dispose();
|
||||
}
|
||||
finally
|
||||
{
|
||||
temp?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
return rectangles;
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<Tuple<Rectangle, double>> RunDetectorWithUpscale2(FrontalFaceDetector detector,
|
||||
Image image,
|
||||
uint upsamplingAmount)
|
||||
{
|
||||
if (detector == null)
|
||||
throw new ArgumentNullException(nameof(detector));
|
||||
if (image == null)
|
||||
throw new ArgumentNullException(nameof(image));
|
||||
|
||||
detector.ThrowIfDisposed();
|
||||
image.ThrowIfDisposed();
|
||||
|
||||
List<double>? detectionConfidences = new();
|
||||
List<ulong>? weightIndices = new();
|
||||
const double adjustThreshold = 0.0;
|
||||
|
||||
Rectangle[]? rects = RunDetectorWithUpscale1(detector,
|
||||
image,
|
||||
upsamplingAmount,
|
||||
adjustThreshold,
|
||||
detectionConfidences,
|
||||
weightIndices).ToArray();
|
||||
|
||||
int index = 0;
|
||||
foreach (Rectangle rect in rects)
|
||||
yield return new Tuple<Rectangle, double>(rect, detectionConfidences[index++]);
|
||||
}
|
||||
|
||||
#region Helpers
|
||||
|
||||
private static void SplitRectDetections(RectDetection[] rectDetections,
|
||||
List<Rectangle> rectangles,
|
||||
List<double> detectionConfidences,
|
||||
List<ulong> weightIndices)
|
||||
{
|
||||
rectangles.Clear();
|
||||
detectionConfidences.Clear();
|
||||
weightIndices.Clear();
|
||||
|
||||
foreach (RectDetection? rectDetection in rectDetections)
|
||||
{
|
||||
rectangles.Add(rectDetection.Rect);
|
||||
detectionConfidences.Add(rectDetection.DetectionConfidence);
|
||||
weightIndices.Add(rectDetection.WeightIndex);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
Reference in New Issue
Block a user