facial_recognition_photobooth/CrestronOpenCvSharp/Capture/FacialRecognition.cs

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;
}
}
}