easydetect 0.7.2__tar.gz → 0.7.3__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.
Files changed (87) hide show
  1. {easydetect-0.7.2 → easydetect-0.7.3}/PKG-INFO +1 -1
  2. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/__init__.py +1 -1
  3. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/model.py +17 -0
  4. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/trainer.py +4 -0
  5. {easydetect-0.7.2 → easydetect-0.7.3}/pyproject.toml +1 -1
  6. {easydetect-0.7.2 → easydetect-0.7.3}/tests/test_train_export.py +17 -0
  7. {easydetect-0.7.2 → easydetect-0.7.3}/tools/bench_finetune.py +9 -3
  8. {easydetect-0.7.2 → easydetect-0.7.3}/tools/profile_train.py +9 -1
  9. {easydetect-0.7.2 → easydetect-0.7.3}/.gitignore +0 -0
  10. {easydetect-0.7.2 → easydetect-0.7.3}/LICENSE +0 -0
  11. {easydetect-0.7.2 → easydetect-0.7.3}/NOTICE +0 -0
  12. {easydetect-0.7.2 → easydetect-0.7.3}/README.md +0 -0
  13. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/benchmark.py +0 -0
  14. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/cli.py +0 -0
  15. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/data/__init__.py +0 -0
  16. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/data/augment.py +0 -0
  17. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/data/dataset.py +0 -0
  18. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/data/keypoints.py +0 -0
  19. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/data/labels.py +0 -0
  20. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/downloads.py +0 -0
  21. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/errors.py +0 -0
  22. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/exporter.py +0 -0
  23. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/metrics.py +0 -0
  24. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/nn/__init__.py +0 -0
  25. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/nn/decoder.py +0 -0
  26. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/nn/denoising.py +0 -0
  27. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/nn/dfine_net.py +0 -0
  28. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/nn/encoder.py +0 -0
  29. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/nn/hgnetv2.py +0 -0
  30. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/nn/ops.py +0 -0
  31. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/nn/posenet.py +0 -0
  32. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/nn/sam_decoder.py +0 -0
  33. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/plotting.py +0 -0
  34. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/pose.py +0 -0
  35. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/pose_trainer.py +0 -0
  36. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/predictor.py +0 -0
  37. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/results.py +0 -0
  38. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/seg_trainer.py +0 -0
  39. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/segment.py +0 -0
  40. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/serve.py +0 -0
  41. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/sources.py +0 -0
  42. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/tracker.py +0 -0
  43. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/utils/__init__.py +0 -0
  44. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/utils/checkpoint.py +0 -0
  45. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/utils/loss.py +0 -0
  46. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/utils/ops.py +0 -0
  47. {easydetect-0.7.2 → easydetect-0.7.3}/easydetect/validator.py +0 -0
  48. {easydetect-0.7.2 → easydetect-0.7.3}/tests/__init__.py +0 -0
  49. {easydetect-0.7.2 → easydetect-0.7.3}/tests/conftest.py +0 -0
  50. {easydetect-0.7.2 → easydetect-0.7.3}/tests/pose_toy.py +0 -0
  51. {easydetect-0.7.2 → easydetect-0.7.3}/tests/test_augment.py +0 -0
  52. {easydetect-0.7.2 → easydetect-0.7.3}/tests/test_benchmark.py +0 -0
  53. {easydetect-0.7.2 → easydetect-0.7.3}/tests/test_cli.py +0 -0
  54. {easydetect-0.7.2 → easydetect-0.7.3}/tests/test_coco2yolo.py +0 -0
  55. {easydetect-0.7.2 → easydetect-0.7.3}/tests/test_custom_pose.py +0 -0
  56. {easydetect-0.7.2 → easydetect-0.7.3}/tests/test_custom_seg.py +0 -0
  57. {easydetect-0.7.2 → easydetect-0.7.3}/tests/test_dataset.py +0 -0
  58. {easydetect-0.7.2 → easydetect-0.7.3}/tests/test_downloads.py +0 -0
  59. {easydetect-0.7.2 → easydetect-0.7.3}/tests/test_lab_api.py +0 -0
  60. {easydetect-0.7.2 → easydetect-0.7.3}/tests/test_model.py +0 -0
  61. {easydetect-0.7.2 → easydetect-0.7.3}/tests/test_nn.py +0 -0
  62. {easydetect-0.7.2 → easydetect-0.7.3}/tests/test_onnxruntime.py +0 -0
  63. {easydetect-0.7.2 → easydetect-0.7.3}/tests/test_plotting.py +0 -0
  64. {easydetect-0.7.2 → easydetect-0.7.3}/tests/test_pose.py +0 -0
  65. {easydetect-0.7.2 → easydetect-0.7.3}/tests/test_predictor.py +0 -0
  66. {easydetect-0.7.2 → easydetect-0.7.3}/tests/test_results.py +0 -0
  67. {easydetect-0.7.2 → easydetect-0.7.3}/tests/test_results_summary.py +0 -0
  68. {easydetect-0.7.2 → easydetect-0.7.3}/tests/test_robustness.py +0 -0
  69. {easydetect-0.7.2 → easydetect-0.7.3}/tests/test_segment.py +0 -0
  70. {easydetect-0.7.2 → easydetect-0.7.3}/tests/test_segment_lowres.py +0 -0
  71. {easydetect-0.7.2 → easydetect-0.7.3}/tests/test_serve.py +0 -0
  72. {easydetect-0.7.2 → easydetect-0.7.3}/tests/test_sources.py +0 -0
  73. {easydetect-0.7.2 → easydetect-0.7.3}/tests/test_sources_threads.py +0 -0
  74. {easydetect-0.7.2 → easydetect-0.7.3}/tests/test_tracker.py +0 -0
  75. {easydetect-0.7.2 → easydetect-0.7.3}/tools/bench.py +0 -0
  76. {easydetect-0.7.2 → easydetect-0.7.3}/tools/build_mirror.py +0 -0
  77. {easydetect-0.7.2 → easydetect-0.7.3}/tools/coco2yolo.py +0 -0
  78. {easydetect-0.7.2 → easydetect-0.7.3}/tools/compare_yolo.py +0 -0
  79. {easydetect-0.7.2 → easydetect-0.7.3}/tools/convert_dfine.py +0 -0
  80. {easydetect-0.7.2 → easydetect-0.7.3}/tools/convert_sam.py +0 -0
  81. {easydetect-0.7.2 → easydetect-0.7.3}/tools/eval_exports.py +0 -0
  82. {easydetect-0.7.2 → easydetect-0.7.3}/tools/hub_README.md +0 -0
  83. {easydetect-0.7.2 → easydetect-0.7.3}/tools/make_demo.py +0 -0
  84. {easydetect-0.7.2 → easydetect-0.7.3}/tools/make_toyset.py +0 -0
  85. {easydetect-0.7.2 → easydetect-0.7.3}/tools/pi_kit.py +0 -0
  86. {easydetect-0.7.2 → easydetect-0.7.3}/tools/train_pose.py +0 -0
  87. {easydetect-0.7.2 → easydetect-0.7.3}/tools/voc2yolo.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: easydetect
3
- Version: 0.7.2
3
+ Version: 0.7.3
4
4
  Summary: Easy real-time object detection you can ship: D-FINE with PyTorch training, OpenVINO inference on CPU/GPU/NPU, and a browser app to label and train. Apache-2.0 throughout.
5
5
  Project-URL: Homepage, https://github.com/themakerrobot/easydetect
6
6
  Project-URL: Source, https://github.com/themakerrobot/easydetect
@@ -24,7 +24,7 @@ needs numpy/opencv/openvino/pyyaml; training adds
24
24
 
25
25
  from __future__ import annotations
26
26
 
27
- __version__ = "0.7.2"
27
+ __version__ = "0.7.3"
28
28
 
29
29
  from .errors import DownloadError, EasyDetectError, ModelNotFoundError
30
30
  from .metrics import BoxMetrics, DetMetrics
@@ -457,6 +457,11 @@ class Detector:
457
457
  self.names = {int(k): str(v) for k, v in ckpt.get("names", {}).items()}
458
458
  self.net = DFINENet(ckpt["variant"], ckpt["num_classes"], pretrained_backbone=False)
459
459
  self.net.load_state_dict(ckpt["model"])
460
+ # trained with fewer queries: runs (and fine-tunes again) with as many
461
+ if ckpt.get("queries") and ckpt["queries"] != self.net.decoder.num_queries:
462
+ self.net.trim(queries=int(ckpt["queries"]))
463
+ if ckpt.get("denoising") is not None:
464
+ self.net.decoder.num_denoising = int(ckpt["denoising"])
460
465
  self.net.eval()
461
466
  self.ir_path = None
462
467
  self.predictor = None
@@ -505,6 +510,8 @@ class Detector:
505
510
  seg_epochs: int = 20,
506
511
  seg_batch: int = 16,
507
512
  seg_lr: float = 1e-4,
513
+ queries: int | None = None,
514
+ denoising: int | None = None,
508
515
  **kwargs: Any,
509
516
  ) -> Path:
510
517
  """Train on a data.yaml dataset. Returns the path of ``best.pt``.
@@ -518,6 +525,12 @@ class Detector:
518
525
  ``on_epoch_end`` is called with a dict of the epoch's numbers — the
519
526
  same row that lands in ``results.csv``.
520
527
 
528
+ ``queries`` (of 300) and ``denoising`` (training-only queries, 100)
529
+ make every step cheaper; on a CPU most of a step is spent on them, not
530
+ on the picture. ``queries=100, denoising=30`` trains dfine-n about
531
+ twice as fast on a laptop and suits scenes with a handful of objects;
532
+ the model keeps ``queries`` (it runs faster too).
533
+
521
534
  A keypoint dataset (``kpt_shape`` in its data.yaml, Ultralytics' pose
522
535
  format) also trains a keypoint network after the detector: on crops of
523
536
  the labelled boxes, from the detector's backbone, for ``pose_epochs``
@@ -561,6 +574,10 @@ class Detector:
561
574
  elif self.net.num_classes != cfg["nc"]:
562
575
  self.net = self._reheaded_net(cfg["nc"])
563
576
 
577
+ if queries is not None:
578
+ self.net.trim(queries=int(queries))
579
+ if denoising is not None:
580
+ self.net.decoder.num_denoising = int(denoising)
564
581
  device = _torch_device(device)
565
582
  trainer = Trainer(
566
583
  self.net,
@@ -436,6 +436,8 @@ class Trainer:
436
436
  # a resumed run started where its first attempt did
437
437
  "start": old.get("start") or self.origin,
438
438
  "imgsz": self.imgsz,
439
+ "queries": self.net.decoder.num_queries,
440
+ "denoising": self.net.decoder.num_denoising,
439
441
  "batch": self.batch,
440
442
  "epochs": self.epochs,
441
443
  "patience": self.patience,
@@ -736,6 +738,8 @@ class Trainer:
736
738
  "scaler": self.scaler.state_dict() if self.amp else None,
737
739
  "variant": net.variant,
738
740
  "num_classes": net.num_classes,
741
+ "queries": net.decoder.num_queries,
742
+ "denoising": net.decoder.num_denoising,
739
743
  "names": self.cfg["names"],
740
744
  "epoch": epoch,
741
745
  "best": self.best,
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "easydetect"
7
- version = "0.7.2"
7
+ version = "0.7.3"
8
8
  description = "Easy real-time object detection you can ship: D-FINE with PyTorch training, OpenVINO inference on CPU/GPU/NPU, and a browser app to label and train. Apache-2.0 throughout."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -477,3 +477,20 @@ def test_multiscale_mosaic_and_mixup_train_and_are_recorded(dataset, tmp_path, m
477
477
  assert any("mixup" in line for line in setup["augment"])
478
478
  assert any("random size, 32–64" in line for line in setup["augment"])
479
479
  assert (32, 32) in sizes # the batches went through at the drawn size
480
+
481
+
482
+ def test_fewer_queries_train_and_stay_with_the_model(dataset, tmp_path, monkeypatch):
483
+ monkeypatch.setattr("easydetect.nn.hgnetv2.HGNetv2.load_imagenet", lambda self, name, url: None)
484
+ model = Detector("dfine-n", verbose=False, pretrained=False)
485
+ best = model.train(data=str(dataset), project=str(tmp_path), epochs=1, imgsz=64, batch=2,
486
+ workers=0, device="cpu", val=False, amp=False, queries=40, denoising=10)
487
+ again = Detector(str(best), verbose=False)
488
+ again._ensure_net()
489
+ assert again.net.decoder.num_queries == 40 and again.net.decoder.num_denoising == 10
490
+ # at 160 px the encoder has more than 300 candidates: only the trim limits it
491
+ onnx = again.export(format="onnx", imgsz=160, out_dir=tmp_path / "x", verbose=False)
492
+ import onnxruntime as ort
493
+
494
+ out = ort.InferenceSession(str(onnx)).run(None, {"images": np.zeros((1, 3, 160, 160),
495
+ np.float32)})
496
+ assert any(o.shape[1] == 40 for o in out if o.ndim == 3)
@@ -141,14 +141,17 @@ def train_dfine(data: Path, out: Path, args) -> tuple[Path, float]:
141
141
  # in a process of its own, as the yolo command is: a crash in what training
142
142
  # leaves behind (loader workers, CUDA) then takes only that process down
143
143
  started = time.time()
144
- code = ("import sys; from easydetect import Detector; "
144
+ extra = {k: v for k, v in (("queries", args.queries), ("denoising", args.denoising),
145
+ ("freeze", args.freeze)) if v is not None}
146
+ code = ("import json, sys; from easydetect import Detector; "
145
147
  "print('BEST', Detector(sys.argv[1], verbose=False).train(data=sys.argv[2], "
146
148
  "epochs=int(sys.argv[3]), imgsz=int(sys.argv[4]), batch=int(sys.argv[5]), "
147
149
  "patience=int(sys.argv[3]), project=sys.argv[6], name=sys.argv[1], "
148
- "workers=int(sys.argv[7])))")
150
+ "workers=int(sys.argv[7]), **json.loads(sys.argv[8])))")
149
151
  best = None
150
152
  with subprocess.Popen([sys.executable, "-c", code, args.dfine, str(data), str(args.epochs),
151
- str(args.imgsz), str(args.batch), str(out), str(args.workers)],
153
+ str(args.imgsz), str(args.batch), str(out), str(args.workers),
154
+ json.dumps(extra)],
152
155
  env=UNBUFFERED, stdout=subprocess.PIPE, text=True) as proc:
153
156
  for line in proc.stdout: # passed on as it comes
154
157
  print(line, end="", flush=True)
@@ -276,6 +279,9 @@ def main():
276
279
  parser.add_argument("--yolo", default="yolo26s.pt")
277
280
  parser.add_argument("--dfine", default="dfine-s")
278
281
  parser.add_argument("--device", default="CPU", help="OpenVINO device for scoring")
282
+ parser.add_argument("--queries", type=int, help="D-FINE decoder queries (of 300)")
283
+ parser.add_argument("--denoising", type=int, help="D-FINE denoising queries (100)")
284
+ parser.add_argument("--freeze", help="D-FINE parts not trained (backbone)")
279
285
  parser.add_argument("--rescore", action="store_true",
280
286
  help="score finished datasets again (their trained models are reused)")
281
287
  args = parser.parse_args()
@@ -35,6 +35,9 @@ def main():
35
35
  parser.add_argument("--steps", type=int, default=40)
36
36
  parser.add_argument("--warmup", type=int, default=10)
37
37
  parser.add_argument("--no-amp", action="store_true")
38
+ parser.add_argument("--freeze", default=None, help="backbone, encoder, …: parts not trained")
39
+ parser.add_argument("--queries", type=int, help="decoder queries while training (of 300)")
40
+ parser.add_argument("--denoising", type=int, help="denoising queries while training (100)")
38
41
  parser.add_argument("--trace", action="store_true", help="also PyTorch's operator table")
39
42
  parser.add_argument("--syncs", action="store_true",
40
43
  help="list the lines of easydetect where a step waits for the GPU")
@@ -44,9 +47,14 @@ def main():
44
47
 
45
48
  nc = load_data_yaml(args.data)["nc"]
46
49
  net = DFINENet(args.model, nc)
50
+ if args.queries:
51
+ net.trim(queries=args.queries)
52
+ if args.denoising is not None:
53
+ net.decoder.num_denoising = args.denoising
47
54
  with tempfile.TemporaryDirectory() as tmp:
48
55
  tr = Trainer(net, args.data, epochs=1, imgsz=args.imgsz, batch=args.batch,
49
- workers=args.workers, project=tmp, amp=not args.no_amp)
56
+ workers=args.workers, project=tmp, amp=not args.no_amp,
57
+ freeze=args.freeze)
50
58
  ds = DetDataset(args.data, "train", args.imgsz, augment=True)
51
59
  dl = tr._loader(ds)
52
60
  net = tr.net.to(tr.device).train()
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