161 lines
No EOL
5.7 KiB
C#
161 lines
No EOL
5.7 KiB
C#
using Crestron.SimplSharp;
|
|
using FaceAiSharp;
|
|
using FaceAiSharp.Extensions;
|
|
using PhotoBooth;
|
|
using SixLabors.ImageSharp;
|
|
using SixLabors.ImageSharp.PixelFormats;
|
|
using SixLabors.ImageSharp.Drawing.Processing;
|
|
using SixLabors.ImageSharp.Processing;
|
|
|
|
namespace CrestronOpenCvSharp.Capture;
|
|
|
|
public class FacialRecognition
|
|
{
|
|
private readonly IFaceDetectorWithLandmarks _detector;
|
|
private readonly IFaceEmbeddingsGenerator _recognizer;
|
|
private readonly string? _baseDirectory;
|
|
|
|
private Image<Rgb24>? _image;
|
|
private float[]? _referenceEmbeddings;
|
|
|
|
private readonly Dictionary<string, string> _faceImagesDict;
|
|
private string? FaceImagePath { get; set; }
|
|
|
|
private Contract _contract;
|
|
|
|
private const string BaseUrl = "https://ise2025.local.staal.one/VirtualControl/MA/Rooms/MYFIRSTAI/Html/";
|
|
|
|
public FacialRecognition(string? baseDirectory, Contract contract)
|
|
{
|
|
_baseDirectory = baseDirectory;
|
|
_contract = contract;
|
|
_detector = FaceAiSharpBundleFactory.CreateFaceDetectorWithLandmarks();
|
|
_recognizer = FaceAiSharpBundleFactory.CreateFaceEmbeddingsGenerator();
|
|
|
|
if (_baseDirectory != null)
|
|
{
|
|
FaceImagePath = Path.Combine(_baseDirectory, "aligned.png");
|
|
}
|
|
|
|
// Let's load the default stuff in this dictionary
|
|
_faceImagesDict = new Dictionary<string, string>
|
|
{
|
|
{ "Yuri Staal", "https://ise2025.local.staal.one/VirtualControl/MA/Rooms/MYFIRSTAI/Html/yuri.jpg" },
|
|
{ "Toine C. Leerentveld", "https://ise2025.local.staal.one/VirtualControl/MA/Rooms/MYFIRSTAI/Html/toine.jpg" },
|
|
{ "Oliver Hall", "https://ise2025.local.staal.one/VirtualControl/MA/Rooms/MYFIRSTAI/Html/oliver.jpg" }
|
|
};
|
|
}
|
|
|
|
public bool CheckForFace(string imageFilePath)
|
|
{
|
|
|
|
try
|
|
{
|
|
// Load the photo
|
|
var photo = File.ReadAllBytes(imageFilePath);
|
|
// Convert it
|
|
_image = Image.Load<Rgb24>(photo);
|
|
// Detect faces in this photo
|
|
var faces = _detector.DetectFaces(_image);
|
|
|
|
if (faces.Count != 0)
|
|
{
|
|
foreach (var face in faces)
|
|
{
|
|
var box = face.Box;
|
|
_image.Mutate(ctx => ctx.DrawPolygon(Color.Red, 2, new PointF(box.Left, box.Top), new PointF(box.Right, box.Top), new PointF(box.Right, box.Bottom), new PointF(box.Left, box.Bottom)));
|
|
}
|
|
|
|
var fileName = GenerateUniqueFilename();
|
|
_image.Save(Path.Combine(_baseDirectory!, fileName!));
|
|
_contract.Main.ImagePreview_Url(string.Format($"{BaseUrl}{fileName!}"));
|
|
Console.WriteLine($"Sending URL {string.Format($"{BaseUrl}{fileName!}")}");
|
|
|
|
_recognizer.AlignFaceUsingLandmarks(_image, faces.First().Landmarks!);
|
|
|
|
_referenceEmbeddings = _recognizer.GenerateEmbedding(_image);
|
|
_image.Save(FaceImagePath!);
|
|
Console.WriteLine("Aligned faces!");
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine("No faces were found!");
|
|
}
|
|
|
|
// Return true or false
|
|
return faces.Any();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Console.WriteLine($"Exception detecting faces: {e.Message}");
|
|
throw;
|
|
}
|
|
}
|
|
|
|
public async Task<string?> CompareFaces()
|
|
{
|
|
foreach (var (name, value) in _faceImagesDict)
|
|
{
|
|
var faceImage = await LoadImageAsync(value);
|
|
var detectedFace = _detector.DetectFaces(faceImage).FirstOrDefault();
|
|
|
|
// Generate embedding for the detected face
|
|
_recognizer.AlignFaceUsingLandmarks(faceImage, detectedFace.Landmarks!);
|
|
var faceEmbedding = _recognizer.GenerateEmbedding(faceImage);
|
|
|
|
// Compare embeddings
|
|
var similarity = _referenceEmbeddings?.Dot(faceEmbedding);
|
|
Console.WriteLine($"Similarity with {name}: {similarity}");
|
|
if (similarity >= 0.42)
|
|
{
|
|
//Console.WriteLine("Assessment: Both pictures show the same person.");
|
|
return name;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public void AddPersonToDatabase(string name)
|
|
{
|
|
var shortName = name.Replace(" ", "");
|
|
// Copy the aligned image to a new image
|
|
if (_baseDirectory != null)
|
|
{
|
|
var newFile = Path.Combine(_baseDirectory, $"{shortName}.jpg");
|
|
Console.WriteLine($"Saved new image to {newFile}");
|
|
File.Copy(FaceImagePath!, newFile, overwrite: true);
|
|
}
|
|
|
|
_faceImagesDict.Add(name, $"https://ise2025.local.staal.one/VirtualControl/MA/Rooms/MYFIRSTAI/Html/{shortName}.jpg");
|
|
Console.WriteLine($"Added new image to dictionary");
|
|
}
|
|
|
|
private async Task<Image<Rgb24>> LoadImageAsync(string path)
|
|
{
|
|
using var hc = new HttpClient();
|
|
var imageBytes = await hc.GetByteArrayAsync(path);
|
|
return Image.Load<Rgb24>(imageBytes);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generate a unique filename for the floor plan
|
|
/// </summary>
|
|
/// <returns>A dynamic string with a randomized number appended to "image"</returns>
|
|
private string? GenerateUniqueFilename()
|
|
{
|
|
try
|
|
{
|
|
// We are generating random numbers to append to the filename
|
|
// We do this to force an update of the image in the browser
|
|
var rnd = new Random();
|
|
var num = rnd.Next();
|
|
var fileName = $"image{num}.jpg";
|
|
return fileName;
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
ErrorLog.Exception($"Exception in GenerateUniqueFilename()", exception);
|
|
return null;
|
|
}
|
|
}
|
|
} |