Metadata-Version: 2.4
Name: skelenet
Version: 0.3.1
Summary: Neural skeleton pose estimation — graph-transformer keypoint detection for any species
Project-URL: Homepage, https://github.com/Ha-Mad/skelenet
Project-URL: Models, https://huggingface.co/Haa-mad
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: torch>=2.0
Requires-Dist: torchvision
Requires-Dist: pillow
Requires-Dist: huggingface_hub>=0.20
Requires-Dist: scipy
Requires-Dist: numpy

# Skelenet

Neural skeleton pose estimation — graph-transformer keypoint detection for any species.

## Install

```bash
pip install skelenet
```

## Quickstart

```python
import skelenet

# Load model (downloads once, cached locally)
model = skelenet.load("Haa-mad/chick-pose-v29")

# Run on a file path
result = skelenet.predict(model, "image.jpg")

# Or run on a numpy array (e.g. from cv2)
result = skelenet.predict(model, frame[..., ::-1])  # cv2 is BGR — flip to RGB
```

## Output

```python
result = skelenet.predict(model, image)

result['keypoints']             # dict: {name: (x, y)} in original image pixels
result['confidence']            # list: per-keypoint confidence score
result['bbox']                  # list: [cx, cy, w, h] normalized to [0, 1]
result['keypoint_names']        # list: ordered keypoint names
result['detection_confidence']  # float: Stage2 box confidence (1.0 if use_internal_detection=False)
result['used_detection']        # bool: whether the internal detector found something
```

### Example output

```python
{
  'keypoints': {
    'beak':       (359.8, 155.2),
    'head':       (364.6, 174.2),
    'back':       (366.9, 156.3),
    'tail':       (360.6, 167.3),
    'left_wing':  (350.7, 161.6),
    'right_wing': (366.5, 147.7),
  },
  'confidence': [0.91, 0.88, 0.95, 0.90, 0.87, 0.89],
  'bbox': [0.505, 0.497, 0.236, 0.487],
  'keypoint_names': ['beak', 'head', 'back', 'tail', 'left_wing', 'right_wing'],
  'detection_confidence': 1.0,
  'used_detection': False,
}
```

## Two modes: pose-only vs. full detection

**Pose-only (default, `use_internal_detection=False`)** — pass an already-cropped
image (e.g. from an external detector like YOLO). Fastest, most accurate keypoints
when you already have a tight crop.

```python
result = skelenet.predict(model, crop)
```

**Full two-stage detection (`use_internal_detection=True`)** — pass a full,
uncropped frame. The model finds the subject itself: a coarse full-frame
locate (Stage 1) followed by a native-resolution crop refine (Stage 2),
then runs pose on that crop. Requires a checkpoint trained with a coarse
locator (v29 and later).

```python
result = skelenet.predict(model, full_frame, use_internal_detection=True)
if not result['used_detection']:
    print("nothing found with enough confidence")
```

## Integrating with a detection pipeline

```python
import skelenet

model = skelenet.load("Haa-mad/chick-pose-v29")

for detection in detections:
    crop = get_crop(frame, detection['bbox'])  # your crop logic
    result = skelenet.predict(model, crop)

    detection['keypoints'] = {
        name: [x, y, result['confidence'][i]]
        for i, (name, (x, y)) in enumerate(result['keypoints'].items())
    }
```

Or skip your own detector entirely and let skelenet find the subject:

```python
result = skelenet.predict(model, full_frame, use_internal_detection=True)
```

## Available models

| Model | Species | Keypoints | HuggingFace |
|-------|---------|-----------|-------------|
| chick-pose-v29 | Chick | beak, head, back, tail, left_wing, right_wing | `Haa-mad/chick-pose-v29` |

## Device

```python
model = skelenet.load("Haa-mad/chick-pose-v29", device="cuda")  # GPU
model = skelenet.load("Haa-mad/chick-pose-v29", device="cpu")   # CPU
```
