Skip to content

Commit a407e8f

Browse files
authored
Merge pull request #25 from AdaptiveMotorControlLab/jaap/make_deeplabcut_optional
Make DeepLabCut an optional dependency to fix circular dependency issue
2 parents 26c33e2 + 316e995 commit a407e8f

File tree

7 files changed

+54
-11
lines changed

7 files changed

+54
-11
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ conda activate fmpose_3d
3636
pip install fmpose3d
3737
```
3838

39+
For the animal pipeline, install the optional DeepLabCut dependency:
40+
41+
```bash
42+
pip install "fmpose3d[animals]"
43+
```
44+
3945
## Demos
4046

4147
### Testing on in-the-wild images (humans)

animals/demo/vis_animals.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,15 @@
4646
from fmpose3d.models import get_model
4747
CFM = get_model(args.model_type)
4848

49-
from deeplabcut.pose_estimation_pytorch.apis import superanimal_analyze_images
49+
try:
50+
from deeplabcut.pose_estimation_pytorch.apis import ( # pyright: ignore[reportMissingImports]
51+
superanimal_analyze_images,
52+
)
53+
except ImportError:
54+
raise ImportError(
55+
"DeepLabCut is required for the animal demo. "
56+
"Install it with: pip install \"fmpose3d[animals]\""
57+
) from None
5058

5159
superanimal_name = "superanimal_quadruped"
5260
model_name = "hrnet_w32"

fmpose3d/inference_api/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ result = api.predict("dog.jpg")
4848
print(result.poses_3d.shape) # (1, 26, 3)
4949
```
5050

51+
Before using the animal pipeline, install the optional DeepLabCut dependency:
52+
53+
```bash
54+
pip install "fmpose3d[animals]"
55+
```
56+
5157

5258
## API Documentation
5359

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

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

230+
If DeepLabCut is not installed, calling this estimator raises a clear `ImportError`
231+
with the recommended install command: `pip install "fmpose3d[animals]"`.
232+
224233
- `setup_runtime()` — No-op (DLC loads lazily).
225234
- `predict(frames: ndarray)``(keypoints, scores, valid_frames_mask)` — Returns Animal3D-format 2D keypoints plus a frame-level validity mask.
226235

fmpose3d/inference_api/fmpose3d.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,20 @@ def _compute_valid_frames_mask(
189189
}
190190

191191

192+
def _require_superanimal_analyze_images() -> Callable[..., object]:
193+
"""Return DeepLabCut's SuperAnimal API or raise a clear ImportError."""
194+
try:
195+
from deeplabcut.pose_estimation_pytorch.apis import ( # pyright: ignore[reportMissingImports]
196+
superanimal_analyze_images,
197+
)
198+
except ImportError:
199+
raise ImportError(
200+
"DeepLabCut is required for the animal 2D estimator. "
201+
"Install it with: pip install \"fmpose3d[animals]\""
202+
) from None
203+
return superanimal_analyze_images
204+
205+
192206
class SuperAnimalEstimator:
193207
"""2D pose estimator for animals: DeepLabCut SuperAnimal.
194208
@@ -236,9 +250,7 @@ def predict(
236250
"""
237251
import cv2
238252
import tempfile
239-
from deeplabcut.pose_estimation_pytorch.apis import (
240-
superanimal_analyze_images,
241-
)
253+
superanimal_analyze_images = _require_superanimal_analyze_images()
242254

243255
cfg = self.cfg
244256
num_frames = frames.shape[0]

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,14 @@ dependencies = [
3737
"scikit-image>=0.19.0",
3838
"filterpy>=1.4.5",
3939
"pandas>=1.0.1",
40-
"deeplabcut==3.0.0rc13",
4140
"huggingface_hub>=0.20.0",
4241
]
4342

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

4949
[tool.setuptools]
5050
include-package-data = true

tests/fmpose3d_api/conftest.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
import os
2525
import socket
26+
from importlib.util import find_spec
2627

2728
import pytest
2829

@@ -72,12 +73,7 @@ def weights_ready(filename: str) -> bool:
7273
HUMAN_WEIGHTS_READY: bool = weights_ready(HUMAN_WEIGHTS_FILENAME)
7374
ANIMAL_WEIGHTS_READY: bool = weights_ready(ANIMAL_WEIGHTS_FILENAME)
7475

75-
try:
76-
import deeplabcut # noqa: F401
77-
78-
DLC_AVAILABLE: bool = True
79-
except ImportError:
80-
DLC_AVAILABLE = False
76+
DLC_AVAILABLE: bool = find_spec("deeplabcut") is not None
8177

8278
# ---------------------------------------------------------------------------
8379
# Reusable skip markers

tests/fmpose3d_api/test_fmpose3d.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,18 @@ def test_pose3d_result(self):
741741

742742

743743
class TestSuperAnimalPrediction:
744+
def test_predict_raises_clear_error_without_deeplabcut(self):
745+
"""Missing DLC should raise a clear installation hint."""
746+
estimator = SuperAnimalEstimator()
747+
frames = np.random.randint(0, 255, (1, 64, 64, 3), dtype=np.uint8)
748+
749+
with patch(
750+
"fmpose3d.inference_api.fmpose3d.importlib.util.find_spec",
751+
return_value=None,
752+
):
753+
with pytest.raises(ImportError, match=r"fmpose3d\[animals\]"):
754+
estimator.predict(frames)
755+
744756
def test_predict_returns_zeros_when_no_bodyparts(self):
745757
"""When DLC detects nothing, keypoints are zero-filled."""
746758
pytest.importorskip("deeplabcut")

0 commit comments

Comments
 (0)