Skip to content

Commit

Permalink
Improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
Webreaper committed Mar 6, 2024
1 parent d61d7ee commit ecdc3dc
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 49 deletions.
3 changes: 2 additions & 1 deletion Damselfly.Core.Utils/ML/ImageDetectResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public class ImageDetectResult
public string ServiceModel { get; set; }
public float Score { get; set; }
public float[] Embeddings { get; set; }

public bool IsNewPerson { get; set; }
public string PersonGuid { get; set; }
public bool IsFace => string.Compare(Tag, "Face", true) == 0;
}
36 changes: 14 additions & 22 deletions Damselfly.Core/Services/ImageRecognitionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,10 @@ public async Task UpdateName(ImageObject faceObject, string name)

await db.SaveChangesAsync("SetName");

if (faceObject.Person.Embeddings != null)
if (faceObject.Person.PersonGuid != null)
{
// Add/update the cache
_peopleCache[faceObject.Person.Embeddings] = faceObject.Person;
_peopleCache[faceObject.Person.PersonGuid] = faceObject.Person;
}

_imageCache.Evict(faceObject.ImageId);
Expand All @@ -146,7 +146,7 @@ public async Task UpdatePerson(Person person, string name)
await db.SaveChangesAsync("SetName");

// Add/update the cache
_peopleCache[person.Embeddings] = person;
_peopleCache[person.PersonGuid] = person;
}

public JobPriorities Priority => JobPriorities.ImageRecognition;
Expand Down Expand Up @@ -287,42 +287,38 @@ private async Task LoadPersonCache(bool force = false)
/// Create the DB entries for people who we don't know about,
/// and then pre-populate the cache with their entries.
/// </summary>
/// <param name="embeddingsToAdd"></param>
/// <param name="detectedFaces"></param>
/// <returns></returns>
public async Task CreateMissingPeople(IEnumerable<string> embeddingsToAdd)
public async Task CreateMissingPeople(IEnumerable<ImageDetectResult> detectedFaces)
{
using var scope = _scopeFactory.CreateScope();
using var db = scope.ServiceProvider.GetService<ImageContext>();

try
{
if ( embeddingsToAdd != null && embeddingsToAdd.Any() )
if ( detectedFaces != null )
{
// Find the people that aren't already in the cache and add new ones
// Be careful - filter out empty ones (shouldn't ever happen, but belt
// and braces
var newEmbeddings = embeddingsToAdd.Select(x => x.Trim())
.Where(x => !string.IsNullOrEmpty(x) && !_peopleCache.ContainsKey(x))
.ToList();
var newFaces = detectedFaces.Where( x => x.IsNewPerson ).ToList();

if ( newEmbeddings.Any() )
if ( newFaces.Any() )
{
Logging.Log($"Adding {newEmbeddings.Count()} person records.");
Logging.Log($"Adding {newFaces.Count()} person records.");

var newPeople = newEmbeddings.Select(x => new Person
var newPeople = newFaces.Select(x => new Person
{
Name = "Unknown",
State = Person.PersonState.Unknown,
LastUpdated = DateTime.UtcNow,
Embeddings = x
Embeddings = string.Join( ",", x.Embeddings),
PersonGuid = x.PersonGuid
}).ToList();

if ( newPeople.Any() )
{
await db.BulkInsert(db.People, newPeople);

// Add or replace the new people in the cache (this should always add)
newPeople.ForEach(x => _peopleCache[x.Embeddings] = x);
newPeople.ForEach(x => _peopleCache[x.PersonGuid] = x);
}
}
}
Expand Down Expand Up @@ -423,12 +419,8 @@ private async Task DetectObjects(ImageMetaData metadata)

var newTags = await CreateNewTags(faces);

// Get a list of the new Embeddings
// TODO this logic may be wrong
var embeddingStrings = faces.Select(x => string.Join(",",x.Embeddings));

// Create any new ones, or pull existing ones back from the cache
await CreateMissingPeople(embeddingStrings);
await CreateMissingPeople(faces);

var newFaces = faces.Select(x => new ImageObject
{
Expand Down
53 changes: 28 additions & 25 deletions Damselfly.ML.FaceONNX/FaceONNXService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public void LoadFaceEmbeddings(Dictionary<string,float[]> embeddings)

private class FaceONNXFace
{
public string? PersonGuid { get; set; }
public string PersonGuid { get; set; }
public Rectangle Rectangle { get; set; }
public float[] Embeddings { get; set; }
public float Score { get; set; }
Expand Down Expand Up @@ -137,55 +137,58 @@ private IEnumerable<FaceONNXFace> GetFacesFromImage(Image<Rgb24> image)
/// <returns></returns>
public async Task<List<ImageDetectResult>> DetectFaces(Image<Rgb24> image)

Check warning on line 138 in Damselfly.ML.FaceONNX/FaceONNXService.cs

View workflow job for this annotation

GitHub Actions / build-server (windows)

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 138 in Damselfly.ML.FaceONNX/FaceONNXService.cs

View workflow job for this annotation

GitHub Actions / build-server (linux)

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
{
List<FaceONNXFace> detectedFaces;

List<ImageDetectResult> detected = new();
var watch = new Stopwatch("FaceOnnxDetection");

try
{
detectedFaces = GetFacesFromImage(image).ToList();
var detectedFaces = GetFacesFromImage(image).ToList();

foreach( var face in detectedFaces )
{
// For each result, loop through and see if we have a match
var (personGuid, similarity) = _embeddings.FromSimilarity(face.Embeddings);

if( personGuid != null )
{
// Looks like we have a match. Yay!
face.PersonGuid = personGuid;
}
else
bool isNewPerson = true;

if( personGuid == null || similarity < 0.75 )
{
// No match, so create a new person GUID
face.PersonGuid = Guid.NewGuid().ToString();
// Add it to the embeddings DB
_embeddings.Add(face.PersonGuid, face.Embeddings);
}
else
{
// Looks like we have a match. Yay!
face.PersonGuid = personGuid;
isNewPerson = false;
}

detected.Add( new ImageDetectResult
{
// TODO - need to disambiguate rectangles...
Rect = new System.Drawing.Rectangle(face.Rectangle.X, face.Rectangle.Y,
face.Rectangle.Width, face.Rectangle.Height),
Score = face.Score,
Tag = "Face",
Service = "FaceONNX",
IsNewPerson = isNewPerson,
PersonGuid = face.PersonGuid,
Embeddings = face.Embeddings
} );
}
}
catch ( Exception ex )
{
Logging.LogError($"Exception during FaceONNX face detection: {ex}");
detectedFaces = new();
}

watch.Stop();

if ( detectedFaces.Any() )
Logging.Log($" FaceONNAX Detected {detectedFaces.Count()} faces in {watch.ElapsedTime}ms");
if ( detected.Any() )
Logging.Log($" FaceONNAX Detected {detected.Count()} faces in {watch.ElapsedTime}ms");

// Convert to the caller's type.
var result = detectedFaces.Select( x => new ImageDetectResult
{
// TODO - need to disambiguate rectangles...
Rect = new System.Drawing.Rectangle(x.Rectangle.X, x.Rectangle.Y, x.Rectangle.Width, x.Rectangle.Height),
Score = x.Score,
Tag = "Face",
Service = "FaceONNX",
Embeddings = x.Embeddings
}).ToList();

return result;
return detected;
}
}
2 changes: 1 addition & 1 deletion Damselfly.Web.Client/wwwroot/version.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
const CACHE_VERSION='4.1.0-20240306085257'
const CACHE_VERSION='4.1.0-20240306121028'

0 comments on commit ecdc3dc

Please sign in to comment.