16031aae96
CPU tests Workflow / Testing (ubuntu-latest, 3.12) (push) Failing after 1s
CPU tests Workflow / Testing (ubuntu-latest, 3.13) (push) Failing after 0s
Mypy Type Check / Type Check (push) Failing after 0s
Docs/Test WorkFlow / Test docs build (push) Failing after 1s
PR Conflict Labeler / labeling (push) Failing after 1s
Dependency resolution / Resolve [tflite] extra — Python 3.12 (push) Failing after 0s
Smoke Tests / try-all-models (ubuntu-latest, 3.10) (push) Failing after 0s
Smoke Tests / try-all-models (ubuntu-latest, 3.13) (push) Failing after 1s
CPU tests Workflow / build-pkg (push) Failing after 1s
CPU tests Workflow / Testing (ubuntu-latest, 3.10) (push) Failing after 0s
CPU tests Workflow / Testing (ubuntu-latest, 3.11) (push) Failing after 0s
Smoke Tests / try-all-models (macos-latest, 3.10) (push) Has been cancelled
Smoke Tests / try-all-models (macos-latest, 3.13) (push) Has been cancelled
Smoke Tests / try-all-models (windows-latest, 3.10) (push) Has been cancelled
Smoke Tests / try-all-models (windows-latest, 3.13) (push) Has been cancelled
CPU tests Workflow / Testing (macos-latest, 3.10) (push) Has been cancelled
CPU tests Workflow / Testing (macos-latest, 3.13) (push) Has been cancelled
CPU tests Workflow / Testing (windows-latest, 3.10) (push) Has been cancelled
CPU tests Workflow / Testing (windows-latest, 3.13) (push) Has been cancelled
CPU tests Workflow / testing-guardian (push) Has been cancelled
GPU tests Workflow / Testing (push) Has been cancelled
113 lines
4.2 KiB
Python
113 lines
4.2 KiB
Python
# ------------------------------------------------------------------------
|
|
# RF-DETR
|
|
# Copyright (c) 2025 Roboflow. All Rights Reserved.
|
|
# Licensed under the Apache License, Version 2.0 [see LICENSE for details]
|
|
# ------------------------------------------------------------------------
|
|
"""Tests for keypoint matching costs in HungarianMatcher."""
|
|
|
|
import torch
|
|
|
|
from rfdetr.models.matcher import HungarianMatcher
|
|
|
|
|
|
def _base_outputs(num_queries: int = 2) -> dict[str, torch.Tensor]:
|
|
"""Build minimal detection outputs used across matcher keypoint tests."""
|
|
pred_logits = torch.full((1, num_queries, 1), 5.0, dtype=torch.float32)
|
|
pred_boxes = torch.tensor([0.5, 0.5, 0.2, 0.2], dtype=torch.float32).view(1, 1, 4).repeat(1, num_queries, 1)
|
|
return {
|
|
"pred_logits": pred_logits,
|
|
"pred_boxes": pred_boxes,
|
|
}
|
|
|
|
|
|
def test_matcher_keypoint_cost_list_of_dicts_targets() -> None:
|
|
"""Keypoint matching costs should work with public list-of-dicts targets."""
|
|
matcher = HungarianMatcher(
|
|
cost_class=0.0,
|
|
cost_bbox=1.0,
|
|
cost_giou=0.0,
|
|
num_keypoints_per_class=[1],
|
|
keypoint_l1_loss_coef=10.0,
|
|
keypoint_findable_loss_coef=0.0,
|
|
keypoint_visible_loss_coef=0.0,
|
|
keypoint_nll_loss_coef=0.0,
|
|
)
|
|
outputs = _base_outputs()
|
|
outputs["pred_keypoints"] = torch.zeros((1, 2, 1, 8), dtype=torch.float32)
|
|
outputs["pred_keypoints"][0, 0, 0, :2] = torch.tensor([0.5, 0.5], dtype=torch.float32)
|
|
outputs["pred_keypoints"][0, 1, 0, :2] = torch.tensor([0.0, 0.0], dtype=torch.float32)
|
|
targets = [
|
|
{
|
|
"labels": torch.tensor([0], dtype=torch.int64),
|
|
"boxes": torch.tensor([[0.5, 0.5, 0.2, 0.2]], dtype=torch.float32),
|
|
"keypoints": torch.tensor([[[0.5, 0.5, 2.0]]], dtype=torch.float32),
|
|
}
|
|
]
|
|
|
|
matched_queries, matched_targets = matcher(outputs, targets)[0]
|
|
|
|
assert matched_queries.tolist() == [0]
|
|
assert matched_targets.tolist() == [0]
|
|
|
|
|
|
def test_matcher_keypoint_cost_coefficients_off() -> None:
|
|
"""Zero keypoint coefficients should preserve non-keypoint matching behavior."""
|
|
base_matcher = HungarianMatcher(cost_class=1.0, cost_bbox=1.0, cost_giou=1.0)
|
|
keypoint_matcher = HungarianMatcher(
|
|
cost_class=1.0,
|
|
cost_bbox=1.0,
|
|
cost_giou=1.0,
|
|
num_keypoints_per_class=[1],
|
|
keypoint_l1_loss_coef=0.0,
|
|
keypoint_findable_loss_coef=0.0,
|
|
keypoint_visible_loss_coef=0.0,
|
|
keypoint_nll_loss_coef=0.0,
|
|
)
|
|
outputs = _base_outputs()
|
|
outputs["pred_logits"][0, 0, 0] = 10.0
|
|
outputs["pred_logits"][0, 1, 0] = -10.0
|
|
outputs["pred_boxes"][0, 1, :] = torch.tensor([0.1, 0.1, 0.1, 0.1], dtype=torch.float32)
|
|
targets = [
|
|
{
|
|
"labels": torch.tensor([0], dtype=torch.int64),
|
|
"boxes": torch.tensor([[0.5, 0.5, 0.2, 0.2]], dtype=torch.float32),
|
|
"keypoints": torch.tensor([[[0.0, 0.0, 2.0]]], dtype=torch.float32),
|
|
}
|
|
]
|
|
outputs_with_keypoints = dict(outputs)
|
|
outputs_with_keypoints["pred_keypoints"] = torch.zeros((1, 2, 1, 8), dtype=torch.float32)
|
|
|
|
base_indices = base_matcher(outputs, targets)[0]
|
|
keypoint_indices = keypoint_matcher(outputs_with_keypoints, targets)[0]
|
|
|
|
assert base_indices[0].tolist() == keypoint_indices[0].tolist()
|
|
assert base_indices[1].tolist() == keypoint_indices[1].tolist()
|
|
|
|
|
|
def test_matcher_keypoint_empty_targets() -> None:
|
|
"""Empty keypoint targets should return valid empty match results."""
|
|
matcher = HungarianMatcher(
|
|
cost_class=1.0,
|
|
cost_bbox=1.0,
|
|
cost_giou=1.0,
|
|
num_keypoints_per_class=[1],
|
|
keypoint_l1_loss_coef=1.0,
|
|
keypoint_findable_loss_coef=1.0,
|
|
keypoint_visible_loss_coef=1.0,
|
|
keypoint_nll_loss_coef=1.0,
|
|
)
|
|
outputs = _base_outputs(num_queries=3)
|
|
outputs["pred_keypoints"] = torch.zeros((1, 3, 1, 8), dtype=torch.float32)
|
|
targets = [
|
|
{
|
|
"labels": torch.zeros((0,), dtype=torch.int64),
|
|
"boxes": torch.zeros((0, 4), dtype=torch.float32),
|
|
"keypoints": torch.zeros((0, 1, 3), dtype=torch.float32),
|
|
}
|
|
]
|
|
|
|
matched_queries, matched_targets = matcher(outputs, targets)[0]
|
|
|
|
assert matched_queries.numel() == 0
|
|
assert matched_targets.numel() == 0
|