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.
Files changed (47) hide show
  1. dfine/__init__.py +48 -0
  2. dfine/backends/__init__.py +6 -0
  3. dfine/backends/native/__init__.py +29 -0
  4. dfine/backends/native/box_ops.py +86 -0
  5. dfine/backends/native/coco.py +103 -0
  6. dfine/backends/native/common.py +75 -0
  7. dfine/backends/native/criterion.py +514 -0
  8. dfine/backends/native/denoising.py +115 -0
  9. dfine/backends/native/dfine.py +94 -0
  10. dfine/backends/native/dfine_decoder.py +956 -0
  11. dfine/backends/native/dfine_utils.py +130 -0
  12. dfine/backends/native/dist.py +29 -0
  13. dfine/backends/native/hgnetv2.py +505 -0
  14. dfine/backends/native/hybrid_encoder.py +505 -0
  15. dfine/backends/native/loader.py +70 -0
  16. dfine/backends/native/matcher.py +139 -0
  17. dfine/backends/native/ops.py +120 -0
  18. dfine/backends/native/postprocessor.py +119 -0
  19. dfine/cli.py +193 -0
  20. dfine/config.py +393 -0
  21. dfine/convert.py +265 -0
  22. dfine/downloads.py +52 -0
  23. dfine/export/__init__.py +12 -0
  24. dfine/export/onnx.py +154 -0
  25. dfine/model.py +591 -0
  26. dfine/registry.py +134 -0
  27. dfine/results.py +192 -0
  28. dfine/track/__init__.py +74 -0
  29. dfine/track/byte_tracker.py +322 -0
  30. dfine/track/kalman_filter.py +96 -0
  31. dfine/train/__init__.py +43 -0
  32. dfine/train/augment.py +127 -0
  33. dfine/train/dataset.py +486 -0
  34. dfine/train/distributed.py +194 -0
  35. dfine/train/ema.py +67 -0
  36. dfine/train/evaluator.py +114 -0
  37. dfine/train/logger.py +143 -0
  38. dfine/train/scheduler.py +100 -0
  39. dfine/train/trainer.py +315 -0
  40. dfine/train/visualizer.py +152 -0
  41. pydfine-0.0.1.dist-info/METADATA +239 -0
  42. pydfine-0.0.1.dist-info/RECORD +47 -0
  43. pydfine-0.0.1.dist-info/WHEEL +5 -0
  44. pydfine-0.0.1.dist-info/entry_points.txt +2 -0
  45. pydfine-0.0.1.dist-info/licenses/LICENSE +201 -0
  46. pydfine-0.0.1.dist-info/licenses/NOTICE +21 -0
  47. 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,6 @@
1
+ """Model backends.
2
+
3
+ The native port (Path A) lives in ``dfine.backends.native`` — upstream D-FINE
4
+ ``nn.Module``s copied here with the YAML/registry layer stripped and wired directly
5
+ from :class:`dfine.config.DFINEConfig`.
6
+ """
@@ -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