pydfine 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dfine/__init__.py +48 -0
- dfine/backends/__init__.py +6 -0
- dfine/backends/native/__init__.py +29 -0
- dfine/backends/native/box_ops.py +86 -0
- dfine/backends/native/coco.py +103 -0
- dfine/backends/native/common.py +75 -0
- dfine/backends/native/criterion.py +514 -0
- dfine/backends/native/denoising.py +115 -0
- dfine/backends/native/dfine.py +94 -0
- dfine/backends/native/dfine_decoder.py +956 -0
- dfine/backends/native/dfine_utils.py +130 -0
- dfine/backends/native/dist.py +29 -0
- dfine/backends/native/hgnetv2.py +505 -0
- dfine/backends/native/hybrid_encoder.py +505 -0
- dfine/backends/native/loader.py +70 -0
- dfine/backends/native/matcher.py +139 -0
- dfine/backends/native/ops.py +120 -0
- dfine/backends/native/postprocessor.py +119 -0
- dfine/cli.py +193 -0
- dfine/config.py +393 -0
- dfine/convert.py +265 -0
- dfine/downloads.py +52 -0
- dfine/export/__init__.py +12 -0
- dfine/export/onnx.py +154 -0
- dfine/model.py +591 -0
- dfine/registry.py +134 -0
- dfine/results.py +192 -0
- dfine/track/__init__.py +74 -0
- dfine/track/byte_tracker.py +322 -0
- dfine/track/kalman_filter.py +96 -0
- dfine/train/__init__.py +43 -0
- dfine/train/augment.py +127 -0
- dfine/train/dataset.py +486 -0
- dfine/train/distributed.py +194 -0
- dfine/train/ema.py +67 -0
- dfine/train/evaluator.py +114 -0
- dfine/train/logger.py +143 -0
- dfine/train/scheduler.py +100 -0
- dfine/train/trainer.py +315 -0
- dfine/train/visualizer.py +152 -0
- pydfine-0.0.1.dist-info/METADATA +239 -0
- pydfine-0.0.1.dist-info/RECORD +47 -0
- pydfine-0.0.1.dist-info/WHEEL +5 -0
- pydfine-0.0.1.dist-info/entry_points.txt +2 -0
- pydfine-0.0.1.dist-info/licenses/LICENSE +201 -0
- pydfine-0.0.1.dist-info/licenses/NOTICE +21 -0
- pydfine-0.0.1.dist-info/top_level.txt +1 -0
dfine/__init__.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""dfine — a config-first, ultralytics-style wrapper around D-FINE.
|
|
2
|
+
|
|
3
|
+
The whole model is configured by typed params on one class. Phase 0/1 ships the
|
|
4
|
+
config surface (``DFINEConfig``, presets); ``DFINE`` and ``Results`` land with the
|
|
5
|
+
inference backend (Phase 2) and are exposed lazily so importing this package never
|
|
6
|
+
requires torch until you actually build a model.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from .config import SIZE_PRESETS, SIZES, DFINEConfig, list_presets
|
|
14
|
+
from .convert import yolo_to_coco
|
|
15
|
+
from .registry import list_checkpoints
|
|
16
|
+
|
|
17
|
+
__version__ = "0.0.1"
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"DFINEConfig",
|
|
21
|
+
"SIZE_PRESETS",
|
|
22
|
+
"SIZES",
|
|
23
|
+
"list_presets",
|
|
24
|
+
"list_checkpoints",
|
|
25
|
+
"yolo_to_coco",
|
|
26
|
+
"__version__",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
# Inference symbols live in modules that import torch; expose them lazily so a bare
|
|
30
|
+
# `import dfine` (config/CLI only) never requires the torch extra.
|
|
31
|
+
_LAZY = {
|
|
32
|
+
"DFINE": "model",
|
|
33
|
+
"Results": "results",
|
|
34
|
+
"Boxes": "results",
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def __getattr__(name: str) -> Any:
|
|
39
|
+
if name in _LAZY:
|
|
40
|
+
try:
|
|
41
|
+
mod = __import__(f"dfine.{_LAZY[name]}", fromlist=[name])
|
|
42
|
+
except ImportError as exc: # torch/torchvision/pillow not installed
|
|
43
|
+
raise AttributeError(
|
|
44
|
+
f"dfine.{name} needs the inference deps — install with "
|
|
45
|
+
f"`pip install pydfine[torch]` (missing: {exc.name})."
|
|
46
|
+
) from exc
|
|
47
|
+
return getattr(mod, name)
|
|
48
|
+
raise AttributeError(f"module 'dfine' has no attribute {name!r}")
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Native D-FINE modules (Path A port).
|
|
2
|
+
|
|
3
|
+
Each module is copied from upstream ``D-FINE/src`` with the registry/YAML layer
|
|
4
|
+
removed and a ``from_config`` constructor added. Layer/parameter names match
|
|
5
|
+
upstream so released checkpoints load without a remap.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from .criterion import DFINECriterion
|
|
11
|
+
from .dfine import DFINE
|
|
12
|
+
from .dfine_decoder import DFINETransformer
|
|
13
|
+
from .hgnetv2 import HGNetv2
|
|
14
|
+
from .hybrid_encoder import HybridEncoder
|
|
15
|
+
from .loader import extract_state_dict, load_checkpoint
|
|
16
|
+
from .matcher import HungarianMatcher
|
|
17
|
+
from .postprocessor import DFINEPostProcessor
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"DFINE",
|
|
21
|
+
"DFINECriterion",
|
|
22
|
+
"DFINEPostProcessor",
|
|
23
|
+
"DFINETransformer",
|
|
24
|
+
"HGNetv2",
|
|
25
|
+
"HungarianMatcher",
|
|
26
|
+
"HybridEncoder",
|
|
27
|
+
"extract_state_dict",
|
|
28
|
+
"load_checkpoint",
|
|
29
|
+
]
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""Bounding-box helpers.
|
|
2
|
+
|
|
3
|
+
Ported from ``D-FINE/src/zoo/dfine/box_ops.py``, which originates from DETR
|
|
4
|
+
(© Facebook, Inc.; https://github.com/facebookresearch/detr). Kept verbatim.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import torch
|
|
10
|
+
from torch import Tensor
|
|
11
|
+
from torchvision.ops.boxes import box_area
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def box_cxcywh_to_xyxy(x: Tensor) -> Tensor:
|
|
15
|
+
x_c, y_c, w, h = x.unbind(-1)
|
|
16
|
+
b = [
|
|
17
|
+
(x_c - 0.5 * w.clamp(min=0.0)),
|
|
18
|
+
(y_c - 0.5 * h.clamp(min=0.0)),
|
|
19
|
+
(x_c + 0.5 * w.clamp(min=0.0)),
|
|
20
|
+
(y_c + 0.5 * h.clamp(min=0.0)),
|
|
21
|
+
]
|
|
22
|
+
return torch.stack(b, dim=-1)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def box_xyxy_to_cxcywh(x: Tensor) -> Tensor:
|
|
26
|
+
x0, y0, x1, y1 = x.unbind(-1)
|
|
27
|
+
b = [(x0 + x1) / 2, (y0 + y1) / 2, (x1 - x0), (y1 - y0)]
|
|
28
|
+
return torch.stack(b, dim=-1)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def box_iou(boxes1: Tensor, boxes2: Tensor) -> tuple[Tensor, Tensor]:
|
|
32
|
+
"""IoU plus the union area (modified from torchvision to also return union)."""
|
|
33
|
+
area1 = box_area(boxes1)
|
|
34
|
+
area2 = box_area(boxes2)
|
|
35
|
+
|
|
36
|
+
lt = torch.max(boxes1[:, None, :2], boxes2[:, :2]) # [N,M,2]
|
|
37
|
+
rb = torch.min(boxes1[:, None, 2:], boxes2[:, 2:]) # [N,M,2]
|
|
38
|
+
|
|
39
|
+
wh = (rb - lt).clamp(min=0) # [N,M,2]
|
|
40
|
+
inter = wh[:, :, 0] * wh[:, :, 1] # [N,M]
|
|
41
|
+
|
|
42
|
+
union = area1[:, None] + area2 - inter
|
|
43
|
+
|
|
44
|
+
iou = inter / union
|
|
45
|
+
return iou, union
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def generalized_box_iou(boxes1: Tensor, boxes2: Tensor) -> Tensor:
|
|
49
|
+
"""Generalized IoU (https://giou.stanford.edu/). Boxes in ``xyxy``.
|
|
50
|
+
|
|
51
|
+
Returns an ``[N, M]`` pairwise matrix.
|
|
52
|
+
"""
|
|
53
|
+
# degenerate boxes give inf/nan, so check first
|
|
54
|
+
assert (boxes1[:, 2:] >= boxes1[:, :2]).all()
|
|
55
|
+
assert (boxes2[:, 2:] >= boxes2[:, :2]).all()
|
|
56
|
+
iou, union = box_iou(boxes1, boxes2)
|
|
57
|
+
|
|
58
|
+
lt = torch.min(boxes1[:, None, :2], boxes2[:, :2])
|
|
59
|
+
rb = torch.max(boxes1[:, None, 2:], boxes2[:, 2:])
|
|
60
|
+
|
|
61
|
+
wh = (rb - lt).clamp(min=0) # [N,M,2]
|
|
62
|
+
area = wh[:, :, 0] * wh[:, :, 1]
|
|
63
|
+
|
|
64
|
+
return iou - (area - union) / area
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def masks_to_boxes(masks: Tensor) -> Tensor:
|
|
68
|
+
"""Bounding boxes (``xyxy``) around ``[N, H, W]`` masks."""
|
|
69
|
+
if masks.numel() == 0:
|
|
70
|
+
return torch.zeros((0, 4), device=masks.device)
|
|
71
|
+
|
|
72
|
+
h, w = masks.shape[-2:]
|
|
73
|
+
|
|
74
|
+
y = torch.arange(0, h, dtype=torch.float)
|
|
75
|
+
x = torch.arange(0, w, dtype=torch.float)
|
|
76
|
+
y, x = torch.meshgrid(y, x, indexing="ij")
|
|
77
|
+
|
|
78
|
+
x_mask = masks * x.unsqueeze(0)
|
|
79
|
+
x_max = x_mask.flatten(1).max(-1)[0]
|
|
80
|
+
x_min = x_mask.masked_fill(~(masks.bool()), 1e8).flatten(1).min(-1)[0]
|
|
81
|
+
|
|
82
|
+
y_mask = masks * y.unsqueeze(0)
|
|
83
|
+
y_max = y_mask.flatten(1).max(-1)[0]
|
|
84
|
+
y_min = y_mask.masked_fill(~(masks.bool()), 1e8).flatten(1).min(-1)[0]
|
|
85
|
+
|
|
86
|
+
return torch.stack([x_min, y_min, x_max, y_max], 1)
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""MS-COCO category maps — native D-FINE port.
|
|
2
|
+
|
|
3
|
+
Copied from ``D-FINE/src/data/dataset/coco_dataset.py`` (Apache-2.0, © 2024 The
|
|
4
|
+
D-FINE Authors). COCO's 80 object classes carry non-contiguous ids in the 1..90
|
|
5
|
+
range; models predict a contiguous 0..79 label. ``mscoco_label2category`` maps a
|
|
6
|
+
predicted label back to the original COCO category id (used by the postprocessor
|
|
7
|
+
when ``remap_mscoco_category=True``); ``mscoco_category2name`` gives the display
|
|
8
|
+
name for each COCO id.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"mscoco_category2name",
|
|
15
|
+
"mscoco_category2label",
|
|
16
|
+
"mscoco_label2category",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
mscoco_category2name = {
|
|
20
|
+
1: "person",
|
|
21
|
+
2: "bicycle",
|
|
22
|
+
3: "car",
|
|
23
|
+
4: "motorcycle",
|
|
24
|
+
5: "airplane",
|
|
25
|
+
6: "bus",
|
|
26
|
+
7: "train",
|
|
27
|
+
8: "truck",
|
|
28
|
+
9: "boat",
|
|
29
|
+
10: "traffic light",
|
|
30
|
+
11: "fire hydrant",
|
|
31
|
+
13: "stop sign",
|
|
32
|
+
14: "parking meter",
|
|
33
|
+
15: "bench",
|
|
34
|
+
16: "bird",
|
|
35
|
+
17: "cat",
|
|
36
|
+
18: "dog",
|
|
37
|
+
19: "horse",
|
|
38
|
+
20: "sheep",
|
|
39
|
+
21: "cow",
|
|
40
|
+
22: "elephant",
|
|
41
|
+
23: "bear",
|
|
42
|
+
24: "zebra",
|
|
43
|
+
25: "giraffe",
|
|
44
|
+
27: "backpack",
|
|
45
|
+
28: "umbrella",
|
|
46
|
+
31: "handbag",
|
|
47
|
+
32: "tie",
|
|
48
|
+
33: "suitcase",
|
|
49
|
+
34: "frisbee",
|
|
50
|
+
35: "skis",
|
|
51
|
+
36: "snowboard",
|
|
52
|
+
37: "sports ball",
|
|
53
|
+
38: "kite",
|
|
54
|
+
39: "baseball bat",
|
|
55
|
+
40: "baseball glove",
|
|
56
|
+
41: "skateboard",
|
|
57
|
+
42: "surfboard",
|
|
58
|
+
43: "tennis racket",
|
|
59
|
+
44: "bottle",
|
|
60
|
+
46: "wine glass",
|
|
61
|
+
47: "cup",
|
|
62
|
+
48: "fork",
|
|
63
|
+
49: "knife",
|
|
64
|
+
50: "spoon",
|
|
65
|
+
51: "bowl",
|
|
66
|
+
52: "banana",
|
|
67
|
+
53: "apple",
|
|
68
|
+
54: "sandwich",
|
|
69
|
+
55: "orange",
|
|
70
|
+
56: "broccoli",
|
|
71
|
+
57: "carrot",
|
|
72
|
+
58: "hot dog",
|
|
73
|
+
59: "pizza",
|
|
74
|
+
60: "donut",
|
|
75
|
+
61: "cake",
|
|
76
|
+
62: "chair",
|
|
77
|
+
63: "couch",
|
|
78
|
+
64: "potted plant",
|
|
79
|
+
65: "bed",
|
|
80
|
+
67: "dining table",
|
|
81
|
+
70: "toilet",
|
|
82
|
+
72: "tv",
|
|
83
|
+
73: "laptop",
|
|
84
|
+
74: "mouse",
|
|
85
|
+
75: "remote",
|
|
86
|
+
76: "keyboard",
|
|
87
|
+
77: "cell phone",
|
|
88
|
+
78: "microwave",
|
|
89
|
+
79: "oven",
|
|
90
|
+
80: "toaster",
|
|
91
|
+
81: "sink",
|
|
92
|
+
82: "refrigerator",
|
|
93
|
+
84: "book",
|
|
94
|
+
85: "clock",
|
|
95
|
+
86: "vase",
|
|
96
|
+
87: "scissors",
|
|
97
|
+
88: "teddy bear",
|
|
98
|
+
89: "hair drier",
|
|
99
|
+
90: "toothbrush",
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
mscoco_category2label = {k: i for i, k in enumerate(mscoco_category2name.keys())}
|
|
103
|
+
mscoco_label2category = {v: k for k, v in mscoco_category2label.items()}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""Shared low-level layers for the native D-FINE port.
|
|
2
|
+
|
|
3
|
+
Ported from ``D-FINE/src/nn/backbone/common.py`` (Apache-2.0, © 2024 The D-FINE
|
|
4
|
+
Authors). ``FrozenBatchNorm2d`` itself originates from DETR
|
|
5
|
+
(facebookresearch/detr). Kept verbatim in behaviour and parameter/buffer names so
|
|
6
|
+
upstream checkpoints load unchanged.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import torch
|
|
12
|
+
from torch import nn
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class FrozenBatchNorm2d(nn.Module):
|
|
16
|
+
"""BatchNorm2d with fixed batch statistics and affine params.
|
|
17
|
+
|
|
18
|
+
Adds ``eps`` before ``rsqrt`` (as in torchvision's variant) to avoid NaNs on
|
|
19
|
+
non-resnet backbones.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, num_features: int, eps: float = 1e-5):
|
|
23
|
+
super().__init__()
|
|
24
|
+
n = num_features
|
|
25
|
+
self.register_buffer("weight", torch.ones(n))
|
|
26
|
+
self.register_buffer("bias", torch.zeros(n))
|
|
27
|
+
self.register_buffer("running_mean", torch.zeros(n))
|
|
28
|
+
self.register_buffer("running_var", torch.ones(n))
|
|
29
|
+
self.eps = eps
|
|
30
|
+
self.num_features = n
|
|
31
|
+
|
|
32
|
+
def _load_from_state_dict(
|
|
33
|
+
self,
|
|
34
|
+
state_dict,
|
|
35
|
+
prefix,
|
|
36
|
+
local_metadata,
|
|
37
|
+
strict,
|
|
38
|
+
missing_keys,
|
|
39
|
+
unexpected_keys,
|
|
40
|
+
error_msgs,
|
|
41
|
+
):
|
|
42
|
+
num_batches_tracked_key = prefix + "num_batches_tracked"
|
|
43
|
+
if num_batches_tracked_key in state_dict:
|
|
44
|
+
del state_dict[num_batches_tracked_key]
|
|
45
|
+
super()._load_from_state_dict(
|
|
46
|
+
state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
50
|
+
# Reshapes up front to stay fuser-friendly.
|
|
51
|
+
w = self.weight.reshape(1, -1, 1, 1)
|
|
52
|
+
b = self.bias.reshape(1, -1, 1, 1)
|
|
53
|
+
rv = self.running_var.reshape(1, -1, 1, 1)
|
|
54
|
+
rm = self.running_mean.reshape(1, -1, 1, 1)
|
|
55
|
+
scale = w * (rv + self.eps).rsqrt()
|
|
56
|
+
bias = b - rm * scale
|
|
57
|
+
return x * scale + bias
|
|
58
|
+
|
|
59
|
+
def extra_repr(self) -> str:
|
|
60
|
+
return f"{self.num_features}, eps={self.eps}"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def freeze_norm(module: nn.Module) -> nn.Module:
|
|
64
|
+
"""Recursively replace every ``nn.BatchNorm2d`` with a ``FrozenBatchNorm2d``.
|
|
65
|
+
|
|
66
|
+
Returns the (possibly replaced) module so callers can reassign the root.
|
|
67
|
+
"""
|
|
68
|
+
if isinstance(module, nn.BatchNorm2d):
|
|
69
|
+
frozen = FrozenBatchNorm2d(module.num_features)
|
|
70
|
+
return frozen
|
|
71
|
+
for name, child in module.named_children():
|
|
72
|
+
new_child = freeze_norm(child)
|
|
73
|
+
if new_child is not child:
|
|
74
|
+
setattr(module, name, new_child)
|
|
75
|
+
return module
|