Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ conda activate fmpose_3d
pip install fmpose3d
```

For the animal pipeline, install the optional DeepLabCut dependency:

```bash
pip install "fmpose3d[animals]"
```

## Demos

### Testing on in-the-wild images (humans)
Expand Down
10 changes: 9 additions & 1 deletion animals/demo/vis_animals.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,15 @@
from fmpose3d.models import get_model
CFM = get_model(args.model_type)

from deeplabcut.pose_estimation_pytorch.apis import superanimal_analyze_images
try:
from deeplabcut.pose_estimation_pytorch.apis import ( # pyright: ignore[reportMissingImports]
superanimal_analyze_images,
)
except ImportError:
raise ImportError(
"DeepLabCut is required for the animal demo. "
"Install it with: pip install \"fmpose3d[animals]\""
) from None

superanimal_name = "superanimal_quadruped"
model_name = "hrnet_w32"
Expand Down
9 changes: 9 additions & 0 deletions fmpose3d/inference_api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ result = api.predict("dog.jpg")
print(result.poses_3d.shape) # (1, 26, 3)
```

Before using the animal pipeline, install the optional DeepLabCut dependency:

```bash
pip install "fmpose3d[animals]"
```


## API Documentation

Expand Down Expand Up @@ -221,6 +227,9 @@ Default 2D estimator for the human pipeline. Wraps HRNet + YOLO with a COCO →

2D estimator for the animal pipeline. Uses DeepLabCut SuperAnimal and maps quadruped80K keypoints to the 26-joint Animal3D layout.

If DeepLabCut is not installed, calling this estimator raises a clear `ImportError`
with the recommended install command: `pip install "fmpose3d[animals]"`.

- `setup_runtime()` — No-op (DLC loads lazily).
- `predict(frames: ndarray)` → `(keypoints, scores, valid_frames_mask)` — Returns Animal3D-format 2D keypoints plus a frame-level validity mask.

Expand Down
18 changes: 15 additions & 3 deletions fmpose3d/inference_api/fmpose3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,20 @@ def _compute_valid_frames_mask(
}


def _require_superanimal_analyze_images() -> Callable[..., object]:
"""Return DeepLabCut's SuperAnimal API or raise a clear ImportError."""
try:
from deeplabcut.pose_estimation_pytorch.apis import ( # pyright: ignore[reportMissingImports]
superanimal_analyze_images,
)
except ImportError:
raise ImportError(
"DeepLabCut is required for the animal 2D estimator. "
"Install it with: pip install \"fmpose3d[animals]\""
) from None
return superanimal_analyze_images


class SuperAnimalEstimator:
"""2D pose estimator for animals: DeepLabCut SuperAnimal.

Expand Down Expand Up @@ -236,9 +250,7 @@ def predict(
"""
import cv2
import tempfile
from deeplabcut.pose_estimation_pytorch.apis import (
superanimal_analyze_images,
)
superanimal_analyze_images = _require_superanimal_analyze_images()

cfg = self.cfg
num_frames = frames.shape[0]
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@ dependencies = [
"scikit-image>=0.19.0",
"filterpy>=1.4.5",
"pandas>=1.0.1",
"deeplabcut==3.0.0rc13",
"huggingface_hub>=0.20.0",
]

[project.optional-dependencies]
dev = ["pytest>=7.0.0", "black>=22.0.0", "flake8>=4.0.0", "isort>=5.10.0"]
wandb = ["wandb>=0.12.0"]
viz = ["matplotlib>=3.5.0", "opencv-python>=4.5.0"]
animals = ["deeplabcut==3.0.0rc13"]

[tool.setuptools]
include-package-data = true
Expand Down
8 changes: 2 additions & 6 deletions tests/fmpose3d_api/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

import os
import socket
from importlib.util import find_spec

import pytest

Expand Down Expand Up @@ -72,12 +73,7 @@ def weights_ready(filename: str) -> bool:
HUMAN_WEIGHTS_READY: bool = weights_ready(HUMAN_WEIGHTS_FILENAME)
ANIMAL_WEIGHTS_READY: bool = weights_ready(ANIMAL_WEIGHTS_FILENAME)

try:
import deeplabcut # noqa: F401

DLC_AVAILABLE: bool = True
except ImportError:
DLC_AVAILABLE = False
DLC_AVAILABLE: bool = find_spec("deeplabcut") is not None

# ---------------------------------------------------------------------------
# Reusable skip markers
Expand Down
12 changes: 12 additions & 0 deletions tests/fmpose3d_api/test_fmpose3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -741,6 +741,18 @@ def test_pose3d_result(self):


class TestSuperAnimalPrediction:
def test_predict_raises_clear_error_without_deeplabcut(self):
"""Missing DLC should raise a clear installation hint."""
estimator = SuperAnimalEstimator()
frames = np.random.randint(0, 255, (1, 64, 64, 3), dtype=np.uint8)

with patch(
"fmpose3d.inference_api.fmpose3d.importlib.util.find_spec",
return_value=None,
):
with pytest.raises(ImportError, match=r"fmpose3d\[animals\]"):
estimator.predict(frames)

def test_predict_returns_zeros_when_no_bodyparts(self):
"""When DLC detects nothing, keypoints are zero-filled."""
pytest.importorskip("deeplabcut")
Expand Down
Loading