📄
src/Integrations/FaceCamera/FaceDetectionService.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
using System; using System.Threading; using Emgu.CV; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace MMirror.Integrations.FaceCamera; public sealed class FaceDetectionService : IDisposable { private readonly ILogger<FaceDetectionService> logger; private readonly VideoCapture videoCapture; private readonly FaceDetectorYN faceDetector; private readonly Lock watchLock = new(); private Action<ReadOnlySpan<FaceDetection>>? faceDetected; public event Action<ReadOnlySpan<FaceDetection>> FaceDetected { add { using var scope = watchLock.EnterScope(); if (faceDetected is null) { videoCapture.Start(); } faceDetected += value; } remove { using var scope = watchLock.EnterScope(); faceDetected -= value; if (faceDetected is null) { videoCapture.Stop(); } } } public FaceDetectionService(ILogger<FaceDetectionService> logger, IOptions<CameraOptions> options) { this.logger = logger; videoCapture = new(options.Value.Index); videoCapture.ImageGrabbed += OnImageGrabbed; faceDetector = new FaceDetectorYN( model: options.Value.Model, config: options.Value.Config, inputSize: new(videoCapture.Width, videoCapture.Height) ); } private void OnImageGrabbed(object? sender, EventArgs e) { using var frame = new Mat(); if (!videoCapture.Retrieve(frame)) { logger.LogError("Could not retrieve frame on frame event."); return; } using var detections = new Mat(); if (faceDetector.Detect(frame, detections) is not 1) { logger.LogError("Facial detection failed."); return; } var faces = detections.GetSpan<FaceDetection>(); faceDetected?.Invoke(faces); } public void Dispose() { videoCapture.Dispose(); faceDetector.Dispose(); } }