FaceDetector
Find where faces are in an image or a camera frame using the platform's own detector - for framing a profile-photo crop, focusing a preview, or blurring faces before an upload.
@inject Bit.Butil.FaceDetector faceDetectorMDN reference
IsSupported returning false is the common case, and
Detect answers with an empty array rather than throwing.
BarcodeDetector is the one member of the Shape Detection family
with real deployment.
Returns true when the runtime exposes FaceDetector. During prerender/SSR the check returns false rather than throwing, so defer it to OnAfterRenderAsync.
@inject Bit.Butil.FaceDetector faceDetector
if (await faceDetector.IsSupported() is false)
{
// offer the manual crop instead
}Reads one frame of a video, image or canvas element. fastMode trades accuracy for speed, which is what a live preview wants; maxFaces stops the search early when you only need one. Landmarks - eyes, nose, mouth - are the platform's decision, not the browser's, so they are often empty on the same browser version that reports them elsewhere.
IAsyncDisposable
Bit.Butil.MediaDevices mediaDevices
Bit.Butil.FaceDetector faceDetector
<video @ref="_video" autoplay playsinline muted></video>
{
private ElementReference _video;
private MediaStreamHandle? _stream;
private async Task Detect()
{
_stream = await mediaDevices.GetUserMedia(audio: false, video: true);
await _stream!.AttachTo(_video);
var faces = await faceDetector.Detect(_video, maxFaces: 5, fastMode: true);
foreach (var face in faces)
{
// face.X / Y / Width / Height in source pixels, face.Landmarks
}
}
// The camera light stays on until the stream is disposed, whatever the page does.
public async ValueTask DisposeAsync()
{
if (_stream is not null) await _stream.DisposeAsync();
}
}Takes an encoded image's bytes - a PNG or JPEG, not raw pixels - which is what a file the user picked or an image you fetched already is. The decoded bitmap is released as soon as the scan finishes; it holds uncompressed pixels, which would be a real leak in a loop.
var found = await faceDetector.DetectImage(bytes, "image/jpeg", maxFaces: 10);IsSupported up front rather than inferring it from silence.
API reference
ValueTask<bool> IsSupported()ValueTask<DetectedFace[]> Detect(ElementReference element, int maxFaces = 0, bool fastMode = false)ValueTask<DetectedFace[]> DetectImage(byte[] imageBytes, string mimeType = "image/png", int maxFaces = 0, bool fastMode = false)X | Y | Width | Height | LandmarksType | X | Y | PointCount