detectFromMat method
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;
}