Download src/objectmodel_v1/matching.py from bench-labs/objectmodel-v1: direct link, hf CLI and curl.
- Browser
- Download file 3.63 kB
-
https://huggingface.co/bench-labs/objectmodel-v1/resolve/main/src/objectmodel_v1/matching.py
- Command line
-
hf download hf://bench-labs/objectmodel-v1/src/objectmodel_v1/matching.py
-
curl -L -o matching.py https://huggingface.co/bench-labs/objectmodel-v1/resolve/main/src/objectmodel_v1/matching.py
3.63 kB
| from __future__ import annotations | |
| from collections.abc import Sequence | |
| import numpy as np | |
| import torch | |
| from scipy.optimize import linear_sum_assignment | |
| from torch import Tensor | |
| from .boxes import box_cxcywh_to_xyxy, generalized_box_iou_batched | |
| def hungarian_match( | |
| outputs: dict[str, Tensor], | |
| targets: Sequence[dict[str, Tensor]], | |
| class_cost: float = 2.0, | |
| bbox_cost: float = 5.0, | |
| giou_cost: float = 2.0, | |
| ) -> list[tuple[Tensor, Tensor]]: | |
| """Match predictions to targets with a detached CPU Hungarian solve.""" | |
| return hungarian_match_layers([outputs], targets, class_cost, bbox_cost, giou_cost)[0] | |
| def hungarian_match_layers( | |
| outputs: Sequence[dict[str, Tensor]], | |
| targets: Sequence[dict[str, Tensor]], | |
| class_cost: float = 2.0, | |
| bbox_cost: float = 5.0, | |
| giou_cost: float = 2.0, | |
| ) -> list[list[tuple[Tensor, Tensor]]]: | |
| """Match all decoder layers with one GPU-to-CPU synchronization.""" | |
| if not outputs: | |
| return [] | |
| device = outputs[0]["pred_boxes"].device | |
| target_sizes = [len(target["labels"]) for target in targets] | |
| max_targets = max(target_sizes, default=0) | |
| if max_targets == 0: | |
| empty = torch.empty(0, dtype=torch.int64, device=device) | |
| return [[(empty, empty) for _ in targets] for _ in outputs] | |
| batch_size = len(targets) | |
| padded_boxes = torch.zeros(batch_size, max_targets, 4, dtype=torch.float32, device=device) | |
| padded_labels = torch.zeros(batch_size, max_targets, dtype=torch.int64, device=device) | |
| for batch_index, target in enumerate(targets): | |
| count = target_sizes[batch_index] | |
| if count > 0: | |
| padded_boxes[batch_index, :count] = target["boxes"] | |
| padded_labels[batch_index, :count] = target["labels"] | |
| padded_costs = [] | |
| for layer_output in outputs: | |
| probabilities = layer_output["pred_logits"].sigmoid().detach() | |
| boxes = layer_output["pred_boxes"].detach() | |
| num_queries = boxes.shape[1] | |
| gather_index = padded_labels[:, None, :].expand(-1, num_queries, -1) | |
| class_term = -probabilities.gather(2, gather_index) | |
| bbox_term = torch.cdist(boxes, padded_boxes, p=1) | |
| giou_term = -generalized_box_iou_batched( | |
| box_cxcywh_to_xyxy(boxes), box_cxcywh_to_xyxy(padded_boxes) | |
| ) | |
| cost = (class_cost * class_term + bbox_cost * bbox_term + giou_cost * giou_term).float() | |
| for batch_index in range(batch_size): | |
| padded_costs.append(cost[batch_index]) | |
| costs = torch.stack(padded_costs).cpu().numpy() | |
| cpu_matches: list[tuple[np.ndarray, np.ndarray]] = [] | |
| for cost, target_size in zip(costs, target_sizes * len(outputs), strict=True): | |
| if target_size == 0: | |
| empty = np.empty(0, dtype=np.int64) | |
| cpu_matches.append((empty, empty)) | |
| else: | |
| cpu_matches.append(linear_sum_assignment(cost[:, :target_size])) | |
| lengths = [len(rows) for rows, _ in cpu_matches] | |
| all_rows = torch.as_tensor( | |
| np.concatenate([rows for rows, _ in cpu_matches]), | |
| dtype=torch.int64, | |
| device=device, | |
| ) | |
| all_cols = torch.as_tensor( | |
| np.concatenate([cols for _, cols in cpu_matches]), | |
| dtype=torch.int64, | |
| device=device, | |
| ) | |
| flat_matches = [] | |
| offset = 0 | |
| for length in lengths: | |
| flat_matches.append( | |
| (all_rows[offset : offset + length], all_cols[offset : offset + length]) | |
| ) | |
| offset += length | |
| batch_size = len(targets) | |
| return [ | |
| flat_matches[start : start + batch_size] | |
| for start in range(0, len(flat_matches), batch_size) | |
| ] | |