initialize method

Future<void> initialize({
  1. FaceDetectionModel model = FaceDetectionModel.backCamera,
  2. PerformanceConfig performanceConfig = const PerformanceConfig(),
  3. int meshPoolSize = 3,
  4. bool withSegmentation = false,
  5. SegmentationConfig? segmentationConfig,
  6. bool useCompiledModel = false,
  7. Set<Accelerator> accelerators = const {Accelerator.gpu, Accelerator.cpu},
  8. Precision precision = Precision.fp16,
  9. double minScore = 0.0,
  10. double minFaceSize = 0.0,
  11. bool useLiteRt = false,
  12. String liteRtAccelerator = 'auto',
})

Loads the face detection, face mesh, iris landmark, and embedding models and prepares the interpreters for inference in a background isolate.

This must be called before running any detections. Calling initialize twice without dispose throws StateError.

The model argument specifies which detection model variant to load (for example, FaceDetectionModel.backCamera).

The meshPoolSize parameter controls how many face mesh model instances to create for parallel processing. Default is 3, which allows up to 3 faces to have their meshes computed in parallel. Increase for better multi-face performance (at the cost of ~7-10MB memory per additional instance).

The performanceConfig parameter controls classic Interpreter hardware acceleration delegates when useCompiledModel is false.

The withSegmentation flag initializes the segmentation model at the same time, avoiding an extra model-load step later. Pass segmentationConfig to customize segmentation behaviour.

The useCompiledModel flag opts into the LiteRT CompiledModel engine for the face detection, mesh, iris, embedding, and segmentation models (GPU with CPU fallback, async dispatch). It defaults to false, which uses the classic Interpreter. Use accelerators and precision to control the CompiledModel backend. If a segmentation model fails to compile on the current platform's LiteRT runtime, the segmentation isolate falls back to the Interpreter and prints a debug message.

The minScore and minFaceSize parameters gate which detections are returned. minScore drops faces below a confidence threshold; note the detector already applies an internal floor of 0.5, so only values above 0.5 tighten results further. minFaceSize drops faces whose width, as a fraction of the image width, is below the threshold (matching Google ML Kit's minFaceSize). Both default to 0.0 (no filtering) and must be in the inclusive range [0.0, 1.0], or ArgumentError is thrown. Detections that fail a gate are dropped right after the detector stage, before the per-face mesh, iris and blendshape stages run, so in standard/full modes the gates also skip that per-face landmark cost.

Example:

// Default: classic Interpreter.
final detector = FaceDetector();
await detector.initialize();

// Opt into LiteRT CompiledModel (GPU with CPU fallback).
await detector.initialize(useCompiledModel: true);

// CompiledModel, CPU only.
await detector.initialize(
  useCompiledModel: true,
  accelerators: {Accelerator.cpu},
);

// Include segmentation from the start
await detector.initialize(withSegmentation: true);

Implementation

Future<void> initialize({
  FaceDetectionModel model = FaceDetectionModel.backCamera,
  PerformanceConfig performanceConfig = const PerformanceConfig(),
  int meshPoolSize = 3,
  bool withSegmentation = false,
  SegmentationConfig? segmentationConfig,
  bool useCompiledModel = false,
  Set<Accelerator> accelerators = const {Accelerator.gpu, Accelerator.cpu},
  Precision precision = Precision.fp16,
  double minScore = 0.0,
  double minFaceSize = 0.0,
  // Web-only knobs; accepted here for API parity but ignored on native.
  bool useLiteRt = false,
  String liteRtAccelerator = 'auto',
}) async {
  if (isReady) {
    throw StateError('FaceDetector already initialized');
  }
  // Validate gates before loading any model so bad configuration fails fast.
  // Ordered after the isReady check so a double-initialize still throws the
  // documented StateError regardless of the argument values.
  validateFaceGates(minScore: minScore, minFaceSize: minFaceSize);
  _useCompiledModel = useCompiledModel;
  _accelerators = accelerators;
  _precision = precision;
  _minScore = minScore;
  _minFaceSize = minFaceSize;

  final worker = _FaceDetectorWorker();
  IsolateRpcClient? segmentationRpc;

  try {
    final faceDetectionPath =
        'packages/face_detection_tflite/assets/models/${faceDetectionModelFile(model)}';
    const faceLandmarkPath =
        'packages/face_detection_tflite/assets/models/$kFaceLandmarkModel';
    const irisLandmarkPath =
        'packages/face_detection_tflite/assets/models/$kIrisLandmarkModel';
    const embeddingPath =
        'packages/face_detection_tflite/assets/models/$kEmbeddingModel';
    const faceBlendshapesPath =
        'packages/face_detection_tflite/assets/models/$kFaceBlendshapesModel';

    final assetFutures = [
      rootBundle.load(faceDetectionPath),
      rootBundle.load(faceLandmarkPath),
      rootBundle.load(irisLandmarkPath),
      rootBundle.load(embeddingPath),
      rootBundle.load(faceBlendshapesPath),
    ];

    if (withSegmentation) {
      final effectiveSegModel =
          segmentationConfig?.model ?? SegmentationModel.general;
      final segModelFile = segmentationModelFile(effectiveSegModel);
      assetFutures.add(
        rootBundle.load(
          'packages/face_detection_tflite/assets/models/$segModelFile',
        ),
      );
    }

    final results = await Future.wait(assetFutures);

    await worker.initialize(
      faceDetectionBytes: results[0].buffer.asUint8List(),
      faceLandmarkBytes: results[1].buffer.asUint8List(),
      irisLandmarkBytes: results[2].buffer.asUint8List(),
      embeddingBytes: results[3].buffer.asUint8List(),
      faceBlendshapesBytes: results[4].buffer.asUint8List(),
      model: model,
      performanceConfig: performanceConfig,
      meshPoolSize: meshPoolSize,
      useCompiledModel: useCompiledModel,
      accelerators: accelerators,
      precision: precision,
      minScore: minScore,
      minFaceSize: minFaceSize,
    );

    if (withSegmentation && results.length > 5) {
      final segBytes = results[5].buffer.asUint8List();
      final config = segmentationConfig ?? SegmentationConfig.safe;
      segmentationRpc = IsolateRpcClient();
      await _spawnSegmentationIsolate(
        rpc: segmentationRpc,
        modelBytes: segBytes,
        config: config,
      );
      _segmentationInitialized = true;
    }

    _worker = worker;
    _segmentationRpc = segmentationRpc;
  } catch (e) {
    await segmentationRpc?.disposeGracefully(disposeOp: 'dispose');
    if (worker.isReady) {
      // Graceful: let the detection isolate free its native models instead of
      // being force-killed mid-cleanup (the init-failure path leaked them).
      await worker.disposeGracefully();
    }
    _segmentationInitialized = false;
    rethrow;
  }
}