animal_detection

Platform Language: Dart
Pub Version pub points License

Cow demo Sheep demo

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.

Libraries

animal_detection
On-device animal detection, species classification, and body pose estimation using TensorFlow Lite.