attr-eomt 1.1.4__tar.gz → 1.2.0__tar.gz
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.
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/PKG-INFO +1 -1
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/eomt/preprocess.py +32 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/eomt/tta.py +9 -5
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/pyproject.toml +1 -1
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/tests/test_smoke.py +14 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/tests/test_tta.py +9 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/.github/workflows/pages.yml +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/.github/workflows/publish.yml +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/.github/workflows/release.yml +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/.github/workflows/tests.yml +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/.gitignore +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/LICENSE +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/README.md +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/configs/coco.yaml +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/docs/assets/00-hero-banner.png +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/docs/assets/00-hero-banner.svg +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/docs/examples/billie_eilish.jpg +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/docs/examples/rosalia.jpg +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/docs/examples/sample.jpg +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/docs/examples/taylor_swift.jpg +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/eomt/__init__.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/eomt/api.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/eomt/aux_cls.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/eomt/box_loss.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/eomt/compress.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/eomt/config.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/eomt/data/__init__.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/eomt/data/coco.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/eomt/data/config.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/eomt/data/download.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/eomt/data/transforms.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/eomt/device.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/eomt/ema.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/eomt/engine/__init__.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/eomt/engine/distributed.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/eomt/engine/match.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/eomt/engine/predict.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/eomt/engine/track.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/eomt/engine/train.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/eomt/engine/validate.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/eomt/loss.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/eomt/model.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/eomt/plotting.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/eomt/postprocess.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/eomt/reid.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/eomt/serialization.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/eomt/visualize.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/sample_data/README.md +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/sample_data/annotations/instances_train.json +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/sample_data/annotations/instances_val.json +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/sample_data/data.yaml +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/sample_data/images/train/.gitkeep +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/sample_data/images/val/.gitkeep +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/scripts/info.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/scripts/match.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/scripts/predict.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/scripts/push_to_hub.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/scripts/track.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/scripts/train.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/scripts/train_detect_coco.sh +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/scripts/val.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/site/index.html +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/tests/test_api.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/tests/test_architecture.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/tests/test_augment.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/tests/test_compress.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/tests/test_ddp.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/tests/test_parity.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/tests/test_recipe.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/tests/test_regression_attr.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/tests/test_reid.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/tests/test_sweep.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/tools/attr_tagger/README.md +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/tools/attr_tagger/tag.py +0 -0
- {attr_eomt-1.1.4 → attr_eomt-1.2.0}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: attr-eomt
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: Standalone EoMT (Encoder-only Mask Transformer) for instance segmentation, with DINOv2 init, COCO training/validation and inference.
|
|
5
5
|
Project-URL: Homepage, https://github.com/imagra93/attr-eomt
|
|
6
6
|
Project-URL: Documentation, https://imagra93.github.io/attr-eomt
|
|
@@ -65,3 +65,35 @@ def preprocess_numpy(
|
|
|
65
65
|
canvas[:nh, :nw] = arr
|
|
66
66
|
chw = np.ascontiguousarray(canvas.transpose(2, 0, 1))
|
|
67
67
|
return chw, make_preprocess_meta(True, (nh, nw), input_size)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def preprocess_torch(
|
|
71
|
+
img_rgb_hwc: np.ndarray,
|
|
72
|
+
input_size: int,
|
|
73
|
+
*,
|
|
74
|
+
letterbox: bool = True,
|
|
75
|
+
mean=None,
|
|
76
|
+
std=None,
|
|
77
|
+
device="cpu",
|
|
78
|
+
):
|
|
79
|
+
""":func:`preprocess_numpy` run with torch on ``device`` (e.g. the model's GPU).
|
|
80
|
+
|
|
81
|
+
The resize is an antialiased bilinear one rounded to uint8, as PIL's, so the result matches
|
|
82
|
+
:func:`preprocess_numpy` within one grey level. Returns ``(chw_float32_tensor, meta)``.
|
|
83
|
+
"""
|
|
84
|
+
import torch
|
|
85
|
+
import torch.nn.functional as F # noqa: N812
|
|
86
|
+
|
|
87
|
+
h, w = img_rgb_hwc.shape[:2]
|
|
88
|
+
if letterbox:
|
|
89
|
+
scale = input_size / max(h, w)
|
|
90
|
+
nh, nw = max(1, round(h * scale)), max(1, round(w * scale))
|
|
91
|
+
else:
|
|
92
|
+
nh, nw = input_size, input_size
|
|
93
|
+
x = torch.from_numpy(np.ascontiguousarray(img_rgb_hwc)).to(device).permute(2, 0, 1)[None].float()
|
|
94
|
+
x = F.interpolate(x, size=(nh, nw), mode="bilinear", antialias=True, align_corners=False).round_()
|
|
95
|
+
mean = torch.as_tensor(IMAGENET_MEAN if mean is None else mean, dtype=torch.float32, device=device).view(3, 1, 1)
|
|
96
|
+
std = torch.as_tensor(IMAGENET_STD if std is None else std, dtype=torch.float32, device=device).view(3, 1, 1)
|
|
97
|
+
chw = x.new_zeros((3, input_size, input_size)) # 0 == mean post-normalize
|
|
98
|
+
chw[:, :nh, :nw] = (x[0] / 255.0 - mean) / std
|
|
99
|
+
return chw, make_preprocess_meta(letterbox, (nh, nw), input_size)
|
|
@@ -38,6 +38,7 @@ repair / replace rules). Attribute probabilities and embeddings are score-weight
|
|
|
38
38
|
from __future__ import annotations
|
|
39
39
|
|
|
40
40
|
import contextlib
|
|
41
|
+
import functools
|
|
41
42
|
import itertools
|
|
42
43
|
import math
|
|
43
44
|
from dataclasses import dataclass
|
|
@@ -47,7 +48,7 @@ import torch
|
|
|
47
48
|
import torch.nn.functional as F # noqa: N812
|
|
48
49
|
|
|
49
50
|
from .postprocess import _masks_to_original, aux_result, aux_value_result, boxes_from_masks, query_scores
|
|
50
|
-
from .preprocess import preprocess_numpy
|
|
51
|
+
from .preprocess import preprocess_numpy, preprocess_torch
|
|
51
52
|
|
|
52
53
|
_AUGMENT_KEYS = {"flip", "scales"}
|
|
53
54
|
_TILES_KEYS = {"size", "overlap", "full"}
|
|
@@ -288,12 +289,16 @@ def predict_views(
|
|
|
288
289
|
amp_dtype: torch.dtype | None = None,
|
|
289
290
|
aux_scopes: dict | None = None,
|
|
290
291
|
embed: bool = False,
|
|
292
|
+
device_preprocess: bool = False,
|
|
291
293
|
) -> dict:
|
|
292
294
|
"""Instances of the RGB ``image`` ``(H, W, 3)`` uint8, merged over the views of ``cfg``.
|
|
293
295
|
|
|
294
296
|
Returns the :func:`~eomt.postprocess.postprocess_instance` dict (``boxes`` / ``scores`` / ``classes`` / ``masks``
|
|
295
297
|
and, with attribute heads, ``aux``), plus ``embed`` ``(N, hidden)`` with ``embed=True``. There is no ``query_idx``:
|
|
296
298
|
a merged instance comes from several queries.
|
|
299
|
+
|
|
300
|
+
With ``device_preprocess=True`` the views are preprocessed on the model's device
|
|
301
|
+
(:func:`~eomt.preprocess.preprocess_torch`) instead of with PIL on the CPU; the inputs match within one grey level.
|
|
297
302
|
"""
|
|
298
303
|
if getattr(model, "family", "instance") != "instance":
|
|
299
304
|
raise NotImplementedError("TTA / tiled inference supports the instance family only.")
|
|
@@ -311,15 +316,14 @@ def predict_views(
|
|
|
311
316
|
view_cells = [_grid_cells(v.rect, g, hg, wg) for v in views]
|
|
312
317
|
|
|
313
318
|
flip_perms = {s.name: s.flip_perm() for s in getattr(model, "aux_specs", []) if s.flip_perm() is not None}
|
|
319
|
+
prep = functools.partial(preprocess_torch, device=dev) if device_preprocess else preprocess_numpy
|
|
314
320
|
found: list[dict] = []
|
|
315
321
|
for size in dict.fromkeys(v.size for v in views):
|
|
316
322
|
idx = [i for i, v in enumerate(views) if v.size == size]
|
|
317
323
|
for start in range(0, len(idx), cfg.batch):
|
|
318
324
|
chunk = idx[start : start + cfg.batch]
|
|
319
|
-
pre = [
|
|
320
|
-
|
|
321
|
-
]
|
|
322
|
-
x = torch.from_numpy(np.stack([chw for chw, _ in pre])).to(dev)
|
|
325
|
+
pre = [prep(_crop(image, views[i]), size, letterbox=letterbox, mean=mean, std=std) for i in chunk]
|
|
326
|
+
x = torch.stack([torch.as_tensor(chw) for chw, _ in pre]).to(dev)
|
|
323
327
|
ctx = torch.autocast(dev.type, dtype=amp_dtype) if amp_dtype is not None else contextlib.nullcontext()
|
|
324
328
|
with ctx:
|
|
325
329
|
out = model(x)
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "attr-eomt"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.2.0"
|
|
8
8
|
description = "Standalone EoMT (Encoder-only Mask Transformer) for instance segmentation, with DINOv2 init, COCO training/validation and inference."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -514,6 +514,20 @@ def test_preprocess_letterbox_meta_and_padding():
|
|
|
514
514
|
assert not meta2["letterbox"] and meta2["content_hw"] == (20, 20)
|
|
515
515
|
|
|
516
516
|
|
|
517
|
+
@pytest.mark.parametrize("letterbox", [True, False])
|
|
518
|
+
def test_preprocess_torch_matches_preprocess_numpy_within_one_grey_level(letterbox):
|
|
519
|
+
import numpy as np
|
|
520
|
+
|
|
521
|
+
from eomt.preprocess import IMAGENET_STD, preprocess_numpy, preprocess_torch
|
|
522
|
+
|
|
523
|
+
img = np.random.default_rng(0).integers(0, 255, (97, 151, 3), dtype=np.uint8)
|
|
524
|
+
chw, meta = preprocess_torch(img, 64, letterbox=letterbox)
|
|
525
|
+
expected, expected_meta = preprocess_numpy(img, 64, letterbox=letterbox)
|
|
526
|
+
assert chw.shape == (3, 64, 64) and chw.dtype == torch.float32 and meta == expected_meta
|
|
527
|
+
diff = np.abs(chw.numpy() - expected) * IMAGENET_STD.reshape(3, 1, 1) * 255 # in grey levels
|
|
528
|
+
assert diff.max() <= 1.01 and diff.mean() < 0.25
|
|
529
|
+
|
|
530
|
+
|
|
517
531
|
def test_letterbox_inverse_crops_content_not_padding():
|
|
518
532
|
from eomt.postprocess import _masks_to_original
|
|
519
533
|
from eomt.preprocess import make_preprocess_meta
|
|
@@ -172,6 +172,15 @@ def test_flip_tta_merges_the_two_views_and_scores_their_agreement():
|
|
|
172
172
|
assert _iou(mean["masks"][0], 14, 35, 56, 91) > 0.95
|
|
173
173
|
|
|
174
174
|
|
|
175
|
+
def test_device_preprocessed_views_find_the_same_instances():
|
|
176
|
+
img = _image(280, 420, 70, 42, 315, 98)
|
|
177
|
+
cfg = TTAConfig(flip=True, scales=(1.0, 1.5), tiles=True, tile_size=140)
|
|
178
|
+
cpu = predict_views(_Blobs(), img, cfg, imgsz=IMGSZ, conf_thres=0.3)
|
|
179
|
+
dev = predict_views(_Blobs(), img, cfg, imgsz=IMGSZ, conf_thres=0.3, device_preprocess=True)
|
|
180
|
+
assert dev["num_detections"] == cpu["num_detections"] == 1
|
|
181
|
+
assert torch.allclose(dev["scores"], cpu["scores"]) and _same(dev["masks"], cpu["masks"])
|
|
182
|
+
|
|
183
|
+
|
|
175
184
|
def test_predict_image_with_tta_keeps_the_result_contract():
|
|
176
185
|
from eomt.engine.predict import predict_image
|
|
177
186
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|