File size: 2,692 Bytes
3decd42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7330a0e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3decd42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
"""Smoke tests for the inference module."""

import importlib.util
import sys
from pathlib import Path
import numpy as np
import pytest

# Ensure src is on path
ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(ROOT / "src"))


def test_model_builds():
    """The SignTransformer can be instantiated and produce logits."""
    from bridgelink_asl.inference import _build_model

    config = {
        "num_classes": 10,
        "d_model": 64,
        "nhead": 2,
        "layers": 2,
        "seq_len": 32,
        "feat_dim": 225,
        "dropout": 0.1,
    }
    model = _build_model(config)
    import torch

    x = torch.randn(2, 32, 225)
    logits = model(x)
    assert logits.shape == (2, 10), f"Expected (2, 10), got {logits.shape}"


def test_cnn_model_builds():
    """The landmark CNN can be instantiated and produce logits."""
    from bridgelink_asl.inference import _build_model
    import torch

    config = {
        "model_type": "landmark_cnn",
        "num_classes": 10,
        "feat_dim": 225,
        "channels": [32, 64, 64],
        "dropout": 0.1,
    }
    model = _build_model(config)
    x = torch.randn(2, 32, 225)
    logits = model(x)
    assert logits.shape == (2, 10), f"Expected (2, 10), got {logits.shape}"


def test_extract_landmarks_from_frame():
    """Landmark extraction returns a 225-d vector from a dummy frame."""
    if importlib.util.find_spec("mediapipe") is None:
        pytest.skip("MediaPipe is not installed in this local test environment")

    from bridgelink_asl.inference import extract_landmarks_from_frame, FEAT_DIM

    # Black frame — MediaPipe probably won't detect hands, but the function
    # should still return a zero-padded vector without crashing.
    frame = np.zeros((480, 640, 3), dtype=np.uint8)
    lm = extract_landmarks_from_frame(frame)
    assert lm.shape == (FEAT_DIM,), f"Expected ({FEAT_DIM},), got {lm.shape}"
    assert lm.dtype == np.float32


def test_runtime_predict():
    """A dummy runtime can classify a random sequence."""
    from bridgelink_asl.inference import _build_model, SignLanguageRuntime
    import torch

    config = {"num_classes": 5, "d_model": 64, "nhead": 2, "layers": 2,
              "seq_len": 32, "feat_dim": 225, "dropout": 0.1}
    model = _build_model(config)
    model.eval()

    labels = ["a", "b", "c", "d", "e"]
    runtime = SignLanguageRuntime(model=model, labels=labels, config=config, device="cpu")

    seq = np.random.randn(32, 225).astype(np.float32)
    label, confidence, top5 = runtime.predict(seq)

    assert label in labels
    assert 0.0 <= confidence <= 1.0
    assert len(top5) == 5
    assert all(name in labels for name, _ in top5)