detectFaces static method

Future<FaceDetectionResult> detectFaces(
  1. Uint8List imageBytes, {
  2. double scoreThreshold = 0.55,
  3. double nmsThreshold = -1,
})

Runs face detection on the provided imageBytes.

Implementation

static Future<FaceDetectionResult> detectFaces(
  Uint8List imageBytes, {
  double scoreThreshold = 0.55,
  double nmsThreshold = -1,
}) async {
  if (_session == null || _inputName == null) {
    throw StateError(
      'FaceValidator.initialize() must be called before detection',
    );
  }

  if (imageBytes.isEmpty) {
    return FaceDetectionResult.empty();
  }

  PreprocessedImage? preprocessed;
  OrtValue? inputTensor;
  Map<String, OrtValue> outputs = {};

  try {
    preprocessed = await _preprocess(imageBytes);

    inputTensor = await OrtValue.fromList(preprocessed.tensor, const [
      1,
      1,
      _inputHeight,
      _inputWidth,
    ]);

    outputs = await _session!.run({_inputName!: inputTensor});

    final heatmapValue = outputs[_outputNames[0]];
    final boxValue = outputs[_outputNames[1]];
    final landmarkValue = outputs[_outputNames[2]];

    if (heatmapValue == null || boxValue == null || landmarkValue == null) {
      throw StateError('Model output tensors are missing');
    }

    final heatmapData = Float32List.fromList(
      (await heatmapValue.asFlattenedList()).cast<double>(),
    );
    final boxData = Float32List.fromList(
      (await boxValue.asFlattenedList()).cast<double>(),
    );
    final landmarkData = Float32List.fromList(
      (await landmarkValue.asFlattenedList()).cast<double>(),
    );

    final heatmapShape = heatmapValue.shape;
    final hmHeight = heatmapShape[2];
    final hmWidth = heatmapShape[3];

    final candidates = _decodeDetections(
      heatmapData: heatmapData,
      boxData: boxData,
      landmarkData: landmarkData,
      hmHeight: hmHeight,
      hmWidth: hmWidth,
      scoreThreshold: scoreThreshold,
    );

    final filtered = _applyNmsIfNeeded(candidates, nmsThreshold);

    final faces = filtered
        .map((candidate) => candidate.toFaceDetection(preprocessed!))
        .whereType<FaceDetection>()
        .toList(growable: false);

    return FaceDetectionResult(
      detections: faces,
      originalWidth: preprocessed.originalWidth,
      originalHeight: preprocessed.originalHeight,
    );
  } catch (e) {
    print('Face detection failed: $e');
    return FaceDetectionResult.error();
  } finally {
    if (inputTensor != null) {
      await inputTensor.dispose();
    }
    await Future.wait(
      outputs.values.map((value) => value.dispose()),
      eagerError: false,
    );
  }
}