detectFromMat method

Future<List<Animal>> detectFromMat(
  1. Mat image, {
  2. required int imageWidth,
  3. required int imageHeight,
})

Detects animals in an OpenCV Mat image.

Runs the pipeline: SSD detection -> classify -> optional pose estimation.

Returns a list of Animal objects.

Throws StateError if called before initialize.

Implementation

Future<List<Animal>> detectFromMat(
  cv.Mat image, {
  required int imageWidth,
  required int imageHeight,
}) async {
  if (!_isInitialized) {
    throw StateError(
        'AnimalDetector not initialized. Call initialize() first.');
  }

  // Stage 1: SSD body detection
  final detections = await _bodyDetector!.detect(
    image,
    scoreThreshold: detThreshold,
  );
  if (detections.isEmpty) return <Animal>[];

  final animals = <Animal>[];

  for (final (bbox, score) in detections) {
    String? species;
    String? breed;
    double? speciesConfidence;
    AnimalPose? pose;

    // Stage 2: species classification on the original (unexpanded) bbox
    final origBw = (bbox.right - bbox.left).toInt();
    final origBh = (bbox.bottom - bbox.top).toInt();
    if (origBw >= 1 && origBh >= 1) {
      final classifyCrop = image.region(
        cv.Rect(
          bbox.left.toInt(),
          bbox.top.toInt(),
          origBw,
          origBh,
        ),
      );
      try {
        final (sp, br, conf) = await _classifier!.classify(classifyCrop);
        species = sp;
        breed = br;
        speciesConfidence = conf;
      } finally {
        classifyCrop.dispose();
      }
    }

    // Stage 3: body pose estimation on the expanded crop
    if (enablePose && _poseEstimator != null) {
      final (cx1, cy1, cx2, cy2) = ImageUtils.expandBox(
        bbox.left,
        bbox.top,
        bbox.right,
        bbox.bottom,
        cropMargin,
        imageWidth,
        imageHeight,
      );

      final int cropW = cx2 - cx1;
      final int cropH = cy2 - cy1;
      if (cropW >= 1 && cropH >= 1) {
        final expandedCrop = image.region(cv.Rect(cx1, cy1, cropW, cropH));
        try {
          pose = await _poseEstimator!.estimate(
            expandedCrop,
            cropX: cx1,
            cropY: cy1,
          );
        } finally {
          expandedCrop.dispose();
        }
      }
    }

    animals.add(Animal(
      boundingBox: bbox,
      score: score,
      species: species,
      breed: breed,
      speciesConfidence: speciesConfidence,
      pose: pose,
      imageWidth: imageWidth,
      imageHeight: imageHeight,
    ));
  }

  return animals;
}