animal_detection 0.0.3
animal_detection: ^0.0.3 copied to clipboard
On-device animal detection, species classification, and body pose estimation using TensorFlow Lite.
animal_detection
On-device animal detection, species classification, and body pose estimation using TensorFlow Lite. Detects animals, classifies species/breed, and extracts 24 SuperAnimal body keypoints using a multi-stage pipeline (SSD detection, MobileNetV3 classification, RTMPose/HRNet pose estimation). Completely local: no remote API, just pure on-device, offline detection.
Features #
- Animal body detection with bounding box
- Species and breed classification (dog, cat, fox, bear, etc.)
- 24-point SuperAnimal body pose estimation (spine, neck, tail, limbs)
- Two pose model variants: RTMPose-S (fast, bundled) and HRNet-w32 (accurate, downloaded on demand)
- Truly cross-platform: compatible with Android, iOS, macOS, Windows, and Linux
- Configurable performance with XNNPACK, GPU, and CoreML acceleration
Quick Start #
import 'package:animal_detection/animal_detection.dart';
final detector = AnimalDetector();
await detector.initialize();
// imageBytes is a Uint8List of encoded image data (PNG, JPG, BMP, WebP, etc.)
final animals = await detector.detect(imageBytes);
for (final animal in animals) {
print('${animal.species} (${animal.breed}) at ${animal.boundingBox} score=${animal.score}');
if (animal.pose != null) {
print('Keypoints: ${animal.pose!.landmarks.length}');
}
}
await detector.dispose();
Configuration Options #
The AnimalDetector constructor accepts several configuration options:
final detector = AnimalDetector(
poseModel: AnimalPoseModel.rtmpose, // Pose model variant
enablePose: true, // Enable body pose estimation
cropMargin: 0.20, // Margin around detected body for pose crop
detThreshold: 0.5, // SSD detection score threshold
performanceConfig: PerformanceConfig.disabled, // Performance optimization (default: disabled)
);
| Option | Type | Default | Description |
|---|---|---|---|
poseModel |
AnimalPoseModel |
rtmpose |
Pose model variant |
enablePose |
bool |
true |
Whether to run pose estimation |
cropMargin |
double |
0.20 |
Margin around detected body crop (0.0-1.0) |
detThreshold |
double |
0.5 |
SSD detection score threshold (0.0-1.0) |
performanceConfig |
PerformanceConfig |
disabled |
Hardware acceleration config |
Pose Model Variants #
| Model | Size | Decoder | Accuracy |
|---|---|---|---|
| RTMPose-S (default) | 11.6 MB | SimCC-based | Fast |
| HRNet-w32 | 54.6 MB | Heatmap-based | Most accurate |
HRNet is downloaded on first use and cached locally. You can track download progress:
final detector = AnimalDetector(poseModel: AnimalPoseModel.hrnet);
await detector.initialize(
onDownloadProgress: (model, received, total) {
print('$model: ${(received / total * 100).toStringAsFixed(1)}%');
},
);
Detection Result #
Each detected animal is returned as an Animal object:
| Field | Type | Description |
|---|---|---|
boundingBox |
BoundingBox |
Body bounding box in absolute pixel coordinates |
score |
double |
SSD detector confidence (0.0-1.0) |
species |
String? |
Predicted species (e.g. "dog", "cat") |
breed |
String? |
Predicted breed (e.g. "golden_retriever", "tabby") |
speciesConfidence |
double? |
Species classifier confidence (0.0-1.0) |
pose |
AnimalPose? |
Body pose keypoints (null if pose estimation disabled) |
imageWidth |
int |
Width of the source image in pixels |
imageHeight |
int |
Height of the source image in pixels |
Supported Species #
The classifier recognizes the following species and breeds (mapped from ImageNet classes):
| Species | Breed Count | Examples |
|---|---|---|
| Dog | 151 | Chihuahua, Labrador, German Shepherd, Golden Retriever, Pug, Dalmatian |
| Cat | 5 | Tabby, Tiger Cat, Persian, Siamese, Egyptian |
| Fox | 4 | Red Fox, Kit Fox, Arctic Fox, Grey Fox |
| Bear | 4 | Brown Bear, American Black Bear, Polar Bear, Sloth Bear |
| Rabbit | 3 | Wood Rabbit, Hare, Angora |
| Cow | 3 | Ox, Water Buffalo, Bison |
| Sheep | 3 | Ram, Bighorn, Ibex |
| Deer | 3 | Hartebeest, Impala, Gazelle |
| Horse | 1 | Sorrel |
| Zebra | 1 | Zebra |
Animals detected but not matching a known species are labeled "unknown_animal".
Performance #
Hardware acceleration can be configured via PerformanceConfig:
// XNNPACK: 2-5x CPU speedup via SIMD (NEON on ARM, AVX on x86) — all platforms
final detector = AnimalDetector(
performanceConfig: PerformanceConfig.xnnpack(),
);
// GPU acceleration — Android, iOS
final detector = AnimalDetector(
performanceConfig: PerformanceConfig.gpu(),
);
// CoreML acceleration — iOS, macOS
final detector = AnimalDetector(
performanceConfig: PerformanceConfig.coreml(),
);
await detector.initialize();
Body Pose Keypoints (24-Point) #
The pose property returns an AnimalPose object with up to 24 SuperAnimal body keypoints.
Keypoint Groups #
| Group | Count | Points |
|---|---|---|
| Neck/Throat | 4 | Neck base, neck end, throat base, throat end |
| Spine | 3 | Back base (withers), back middle, back end |
| Tail | 2 | Tail base, tail tip |
| Front legs | 6 | Left/right thigh, knee, paw |
| Back legs | 6 | Left/right thigh, knee, paw |
| Body | 3 | Belly bottom, body middle left/right |
Accessing Keypoints #
final Animal animal = animals.first;
if (animal.pose != null) {
// Iterate through all keypoints
for (final kp in animal.pose!.landmarks) {
print('${kp.type.name}: (${kp.x}, ${kp.y}) confidence=${kp.confidence}');
}
// Access a specific keypoint
final tail = animal.pose!.getLandmark(AnimalPoseLandmarkType.tailEnd);
if (tail != null) {
print('Tail tip at (${tail.x}, ${tail.y})');
}
}
Drawing the Skeleton #
Use the animalPoseConnections constant to draw skeleton lines between connected keypoints:
for (final connection in animalPoseConnections) {
final from = animal.pose!.getLandmark(connection[0]);
final to = animal.pose!.getLandmark(connection[1]);
if (from != null && to != null) {
canvas.drawLine(Offset(from.x, from.y), Offset(to.x, to.y), paint);
}
}
Bounding Boxes #
The boundingBox property returns a BoundingBox object representing the animal body bounding box in absolute pixel coordinates.
final BoundingBox boundingBox = animal.boundingBox;
// Access edges
final double left = boundingBox.left;
final double top = boundingBox.top;
final double right = boundingBox.right;
final double bottom = boundingBox.bottom;
// Calculate dimensions
final double width = boundingBox.right - boundingBox.left;
final double height = boundingBox.bottom - boundingBox.top;
print('Box: ($left, $top) to ($right, $bottom)');
print('Size: $width x $height');
Model Details #
| Model | Size | Input | Purpose |
|---|---|---|---|
| SSD body detector | ~4 MB | 320x320 | Animal body detection and bounding box |
| Species classifier | ~9 MB | 224x224 | Species and breed classification |
| RTMPose-S | 11.6 MB | 256x256 | 24-point body pose estimation (fast) |
| HRNet-w32 | 54.6 MB | 256x256 | 24-point body pose estimation (accurate) |
OpenCV Mat Input #
For advanced use cases (e.g. camera frames), you can pass an OpenCV Mat directly:
import 'package:animal_detection/animal_detection.dart';
final mat = imdecode(imageBytes, IMREAD_COLOR);
final animals = await detector.detectFromMat(
mat,
imageWidth: mat.cols,
imageHeight: mat.rows,
);
Background Isolates #
To run detection in a background isolate, use initializeFromBuffers to avoid asset loading issues:
await detector.initializeFromBuffers(
bodyDetectorBytes: bodyModelBytes,
classifierBytes: classifierModelBytes,
speciesMappingJson: speciesMappingJsonString,
poseModelBytes: poseModelBytes, // optional
);
HRNet Cache Management #
Check if the HRNet model is already cached or clear the model cache:
final cached = await AnimalDetector.isHrnetCached();
await ModelDownloader.clearCache(); // Deletes all cached models
Error Handling #
| Exception | When |
|---|---|
StateError |
detect() or detectFromMat() called before initialize() |
HttpException |
HRNet model download fails (non-200 status) |
If image decoding fails or no animals are detected, detect() returns an empty list.
Credits #
Body detection and pose models based on SuperAnimal pretrained models.
Example #
The sample code from the pub.dev example tab includes a Flutter app that paints detections onto an image: bounding boxes, species labels, and 24-point body pose keypoints.