oriented-det 0.1.0__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 (115) hide show
  1. export/__init__.py +9 -0
  2. export/ort_runtime.py +67 -0
  3. export/postprocess.py +151 -0
  4. export/scripts/__init__.py +6 -0
  5. export/scripts/build_faster_rcnn_savedmodel.py +104 -0
  6. export/scripts/export_onnx.py +210 -0
  7. export/scripts/onnx_to_savedmodel.py +35 -0
  8. export/scripts/predict_savedmodel.py +94 -0
  9. export/scripts/save_predictions_tf.py +447 -0
  10. export/scripts/to_tflite.py +32 -0
  11. export/tests/test_export_onnx_optional.py +82 -0
  12. export/tests/test_export_wrappers.py +105 -0
  13. export/tests/test_faster_rcnn_export_parity.py +201 -0
  14. export/tests/test_ort_runtime.py +41 -0
  15. export/tf_serving_model.py +96 -0
  16. export/val_dataset.py +116 -0
  17. export/wrappers.py +161 -0
  18. oriented_det/__init__.py +77 -0
  19. oriented_det/cli/__init__.py +92 -0
  20. oriented_det/cli/train.py +18 -0
  21. oriented_det/configs/_base_/augmentation.json +21 -0
  22. oriented_det/configs/_base_/datasets/dota_le90.json +21 -0
  23. oriented_det/configs/_base_/fp16.json +5 -0
  24. oriented_det/configs/_base_/models/oriented_rcnn_r50.json +26 -0
  25. oriented_det/configs/_base_/models/rotated_faster_rcnn_r50.json +53 -0
  26. oriented_det/configs/_base_/models/rotated_retinanet_r50.json +30 -0
  27. oriented_det/configs/_base_/preprocessing.json +8 -0
  28. oriented_det/configs/_base_/schedules/1x.json +33 -0
  29. oriented_det/configs/config.schema.json +404 -0
  30. oriented_det/configs/oriented_rcnn/dota_le90_1x.json +121 -0
  31. oriented_det/configs/oriented_rcnn/dota_le90_3x.json +11 -0
  32. oriented_det/configs/rotated_faster_rcnn/dota_le90_1x.json +119 -0
  33. oriented_det/configs/rotated_faster_rcnn/dota_le90_3x.json +9 -0
  34. oriented_det/configs/rotated_retinanet/dota_le90_1x.json +107 -0
  35. oriented_det/configs/rotated_retinanet/dota_le90_3x.json +30 -0
  36. oriented_det/data/__init__.py +89 -0
  37. oriented_det/data/airbus_playground.py +611 -0
  38. oriented_det/data/dota.py +741 -0
  39. oriented_det/data/dota_classes.py +26 -0
  40. oriented_det/data/evaluation.py +648 -0
  41. oriented_det/data/flips.py +115 -0
  42. oriented_det/data/preprocessing.py +335 -0
  43. oriented_det/data/tiling.py +399 -0
  44. oriented_det/data/transforms.py +377 -0
  45. oriented_det/geometry/__init__.py +8 -0
  46. oriented_det/geometry/poly.py +127 -0
  47. oriented_det/geometry/qbox.py +63 -0
  48. oriented_det/geometry/rbox.py +250 -0
  49. oriented_det/geometry/transforms.py +266 -0
  50. oriented_det/models/__init__.py +36 -0
  51. oriented_det/models/backbones/__init__.py +11 -0
  52. oriented_det/models/backbones/resnet_fpn.py +79 -0
  53. oriented_det/models/backbones/utils.py +81 -0
  54. oriented_det/models/bbox_coder.py +355 -0
  55. oriented_det/models/faster_rcnn_inference.py +494 -0
  56. oriented_det/models/horizontal_roi_coder.py +155 -0
  57. oriented_det/models/oriented_rcnn.py +1256 -0
  58. oriented_det/models/oriented_roi.py +1664 -0
  59. oriented_det/models/oriented_rpn.py +2104 -0
  60. oriented_det/models/rotated_retinanet.py +1030 -0
  61. oriented_det/models/utils.py +590 -0
  62. oriented_det/ops/__init__.py +58 -0
  63. oriented_det/ops/gpu_ops.py +1109 -0
  64. oriented_det/ops/iou.py +172 -0
  65. oriented_det/ops/kfiou.py +275 -0
  66. oriented_det/ops/nms.py +202 -0
  67. oriented_det/ops/probiou.py +165 -0
  68. oriented_det/ops/rotated_ops.py +122 -0
  69. oriented_det/ops/utils.py +257 -0
  70. oriented_det/pretrained/__init__.py +23 -0
  71. oriented_det/pretrained/hub.py +249 -0
  72. oriented_det/pretrained/manifest.json +46 -0
  73. oriented_det/runtime/__init__.py +29 -0
  74. oriented_det/runtime/checkpoint.py +274 -0
  75. oriented_det/runtime/collate.py +348 -0
  76. oriented_det/runtime/inference.py +1286 -0
  77. oriented_det/train/__init__.py +102 -0
  78. oriented_det/train/config.py +872 -0
  79. oriented_det/train/engine.py +2933 -0
  80. oriented_det/train/grouped_ce.py +139 -0
  81. oriented_det/train/piecewise_schedule.py +33 -0
  82. oriented_det/train/profiler.py +287 -0
  83. oriented_det/train/utils.py +999 -0
  84. oriented_det/utils/__init__.py +31 -0
  85. oriented_det/utils/config.py +376 -0
  86. oriented_det/utils/device.py +35 -0
  87. oriented_det/utils/logging.py +163 -0
  88. oriented_det/utils/progress.py +62 -0
  89. oriented_det/utils/viz.py +181 -0
  90. oriented_det-0.1.0.dist-info/METADATA +313 -0
  91. oriented_det-0.1.0.dist-info/RECORD +115 -0
  92. oriented_det-0.1.0.dist-info/WHEEL +5 -0
  93. oriented_det-0.1.0.dist-info/entry_points.txt +2 -0
  94. oriented_det-0.1.0.dist-info/licenses/LICENSE +202 -0
  95. oriented_det-0.1.0.dist-info/top_level.txt +3 -0
  96. tools/__init__.py +1 -0
  97. tools/app.py +1284 -0
  98. tools/dataset_stats.py +389 -0
  99. tools/dota_labels_to_comma.py +132 -0
  100. tools/free_gpu.py +142 -0
  101. tools/generate_airbus_playground_csv.py +154 -0
  102. tools/image_demo.py +200 -0
  103. tools/lr_finder.py +771 -0
  104. tools/measure_sampled_riou_error.py +483 -0
  105. tools/playground_to_dota.py +290 -0
  106. tools/pretrained_download.py +49 -0
  107. tools/preview_augmentation.py +510 -0
  108. tools/publish_checkpoint.py +96 -0
  109. tools/save_predictions.py +2350 -0
  110. tools/sync_vendored_configs.py +94 -0
  111. tools/tile_dota.py +447 -0
  112. tools/train.py +2324 -0
  113. tools/train_example.py +244 -0
  114. tools/train_multi_gpu.py +367 -0
  115. tools/visualize_boxes.py +183 -0
@@ -0,0 +1,290 @@
1
+ #!/usr/bin/env python3
2
+ """Export Airbus Playground folder layout to DOTA-format directories (images + labels).
3
+
4
+ Reads from the Playground export structure (dataset_id/samples/..., dataset_id/labels/...),
5
+ converts polygon annotations to DOTA OBB format (8 coords + class + difficulty), and writes
6
+ images and .txt label files. Optionally splits output into train/val by group ratio, or via an
7
+ existing split CSV from ``generate_airbus_playground_csv.py`` (integer fold ids or legacy train/val).
8
+
9
+ Usage:
10
+ # Single output dir (no split)
11
+ python tools/playground_to_dota.py --data-root /path/to/playground_export --output-dir /path/to/dota_out
12
+
13
+ # Train/val split by image group (ratio; independent of generate_airbus_playground_csv folds)
14
+ python tools/playground_to_dota.py --data-root /path/to/export --output-dir /path/to/dota_out --val-ratio 0.2 --seed 42
15
+
16
+ # Use existing split CSV (integer fold ids; val fold 0 by default, or pass --val-split-id)
17
+ python tools/playground_to_dota.py --data-root /path/to/export --output-dir /path/to/dota_out --split-file /path/to/export/split.csv
18
+
19
+ # Label filtering and mapping
20
+ python tools/playground_to_dota.py --data-root /path/to/export --output-dir /path/to/dota_out --ignore-label Confuser --map-label "taxi=car"
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ import argparse
26
+ import csv
27
+ import random
28
+ import shutil
29
+ import sys
30
+ from collections import defaultdict
31
+ from pathlib import Path
32
+ from typing import Dict, List, Optional, Set, Tuple
33
+
34
+ # Ensure oriented_det is importable when run as script
35
+ _REPO_ROOT = Path(__file__).resolve().parent.parent
36
+ if str(_REPO_ROOT) not in sys.path:
37
+ sys.path.insert(0, str(_REPO_ROOT))
38
+
39
+ from oriented_det.data import format_dota_line
40
+ from oriented_det.data.airbus_playground import (
41
+ TileRecord,
42
+ discover_airbus_tiles,
43
+ detect_airbus_split_csv_format,
44
+ _load_airbus_label_rows,
45
+ _image_group_key,
46
+ )
47
+
48
+
49
+ def _unique_tile_basename(tile: TileRecord) -> str:
50
+ """Return a unique basename for the tile (no extension) to avoid collisions across zones."""
51
+ return f"{tile.dataset_id}_{tile.zone_id}_{tile.image_id}_{tile.tile_id}"
52
+
53
+
54
+ def _compute_split_by_ratio(
55
+ tiles: List[TileRecord],
56
+ val_ratio: float,
57
+ seed: int,
58
+ ) -> Set[Tuple[str, str, str]]:
59
+ """Compute which (dataset_id, zone_id, image_id) groups are validation from a ratio."""
60
+ groups: Dict[Tuple[str, str, str], List[TileRecord]] = defaultdict(list)
61
+ for tile in tiles:
62
+ groups[_image_group_key(tile)].append(tile)
63
+ group_keys = sorted(groups.keys())
64
+ rng = random.Random(seed)
65
+ rng.shuffle(group_keys)
66
+ n_val = max(1, int(round(len(group_keys) * val_ratio)))
67
+ return set(group_keys[:n_val])
68
+
69
+
70
+ def _load_split_file(split_path: Path, *, val_split_id: int = 0) -> Dict[str, str]:
71
+ """Load split CSV: tile_relpath -> 'train' | 'val'.
72
+
73
+ Supports legacy ``train`` / ``val`` strings or integer fold ids (same convention as
74
+ ``generate_airbus_playground_csvs``); for fold ids, tiles whose fold equals ``val_split_id``
75
+ are mapped to ``val``.
76
+ """
77
+ with split_path.open("r", encoding="utf-8", newline="") as f:
78
+ reader = csv.DictReader(f)
79
+ raw_rows = list(reader)
80
+ if not raw_rows:
81
+ return {}
82
+ mode = detect_airbus_split_csv_format([r.get("split", "") for r in raw_rows])
83
+ out: Dict[str, str] = {}
84
+ for row in raw_rows:
85
+ rel = (row.get("tile_relpath") or "").strip()
86
+ if not rel:
87
+ continue
88
+ split_raw = row.get("split") or ""
89
+ if mode == "train_val":
90
+ sv = split_raw.strip().lower()
91
+ if sv in ("train", "val"):
92
+ out[rel] = sv
93
+ else:
94
+ try:
95
+ sid = int(str(split_raw).strip())
96
+ except ValueError:
97
+ continue
98
+ out[rel] = "val" if sid == val_split_id else "train"
99
+ return out
100
+
101
+
102
+ def run(
103
+ data_root: Path,
104
+ output_dir: Path,
105
+ *,
106
+ val_ratio: Optional[float] = None,
107
+ seed: int = 42,
108
+ split_file: Optional[Path] = None,
109
+ val_split_id: int = 0,
110
+ ignore_labels: Optional[List[str]] = None,
111
+ map_labels: Optional[Dict[str, str]] = None,
112
+ dry_run: bool = False,
113
+ ) -> Dict[str, int]:
114
+ """Export Playground export to DOTA dirs. Returns stats dict (tiles_written, train_count, val_count, objects_total, objects_skipped)."""
115
+ root = Path(data_root).resolve()
116
+ out = Path(output_dir).resolve()
117
+
118
+ if not root.is_dir():
119
+ raise FileNotFoundError(f"Data root is not a directory: {root}")
120
+
121
+ tiles = discover_airbus_tiles(root)
122
+ if not tiles:
123
+ raise FileNotFoundError(f"No Airbus Playground tiles found under {root}")
124
+
125
+ # Resolve train/val assignment
126
+ use_split_dirs = False
127
+ val_keys: Optional[Set[Tuple[str, str, str]]] = None
128
+ split_by_tile: Optional[Dict[str, str]] = None
129
+
130
+ if split_file is not None:
131
+ if not split_file.exists():
132
+ raise FileNotFoundError(f"Split file not found: {split_file}")
133
+ split_by_tile = _load_split_file(split_file, val_split_id=val_split_id)
134
+ if not split_by_tile:
135
+ raise ValueError("Split file has no valid tile_relpath -> split rows.")
136
+ use_split_dirs = True
137
+ elif val_ratio is not None:
138
+ if not (0.0 < val_ratio < 1.0):
139
+ raise ValueError("val_ratio must be in (0, 1).")
140
+ val_keys = _compute_split_by_ratio(tiles, val_ratio, seed)
141
+ use_split_dirs = True
142
+
143
+ ignore_set: Optional[Set[str]] = None
144
+ if ignore_labels:
145
+ ignore_set = set(ignore_labels)
146
+ map_dict = dict(map_labels or {})
147
+
148
+ # Output layout: [output_dir/] [train|val/] images/, labels/
149
+ def out_paths(tile: TileRecord, split: str) -> Tuple[Path, Path]:
150
+ base = _unique_tile_basename(tile)
151
+ if use_split_dirs:
152
+ img_dir = out / split / "images"
153
+ lbl_dir = out / split / "labels"
154
+ else:
155
+ img_dir = out / "images"
156
+ lbl_dir = out / "labels"
157
+ return img_dir / f"{base}.jpg", lbl_dir / f"{base}.txt"
158
+
159
+ def get_split(tile: TileRecord) -> str:
160
+ if split_by_tile is not None:
161
+ return split_by_tile.get(tile.tile_relpath, "train")
162
+ if val_keys is not None:
163
+ return "val" if _image_group_key(tile) in val_keys else "train"
164
+ return "train"
165
+
166
+ stats = {
167
+ "tiles_total": len(tiles),
168
+ "tiles_skipped": 0,
169
+ "tiles_written": 0,
170
+ "train_tiles": 0,
171
+ "val_tiles": 0,
172
+ "objects_total": 0,
173
+ }
174
+
175
+ for tile in sorted(tiles, key=lambda t: t.tile_relpath):
176
+ img_src = root / tile.tile_relpath
177
+ if not img_src.exists():
178
+ stats["tiles_skipped"] += 1
179
+ if not dry_run:
180
+ print(f"Warning: image not found {img_src}", file=sys.stderr)
181
+ continue
182
+
183
+ split = get_split(tile)
184
+ img_dst, lbl_dst = out_paths(tile, split)
185
+
186
+ rows = _load_airbus_label_rows(
187
+ root / tile.label_relpath,
188
+ ignore_labels=ignore_set,
189
+ map_labels=map_dict if map_dict else None,
190
+ )
191
+
192
+ if not dry_run:
193
+ img_dst.parent.mkdir(parents=True, exist_ok=True)
194
+ lbl_dst.parent.mkdir(parents=True, exist_ok=True)
195
+ shutil.copy2(img_src, img_dst)
196
+ # Official DOTA format: comma-separated (x1, y1, x2, y2, x3, y3, x4, y4, category, difficult)
197
+ lines = []
198
+ for dota_coords, class_name, difficult in rows:
199
+ coords = [float(x) for x in dota_coords.split()]
200
+ lines.append(format_dota_line(*coords, class_name, difficult))
201
+ lbl_dst.write_text("\n".join(lines), encoding="utf-8")
202
+
203
+ stats["tiles_written"] += 1
204
+ stats["objects_total"] += len(rows)
205
+ if split == "train":
206
+ stats["train_tiles"] += 1
207
+ else:
208
+ stats["val_tiles"] += 1
209
+
210
+ return stats
211
+
212
+
213
+ def main() -> int:
214
+ parser = argparse.ArgumentParser(
215
+ description="Export Airbus Playground export to DOTA-format directories (images + .txt labels).",
216
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter,
217
+ )
218
+ parser.add_argument("--data-root", type=Path, required=True, help="Root of Playground export (dataset_id/samples/..., labels/...).")
219
+ parser.add_argument("--output-dir", type=Path, required=True, help="Output root for DOTA dirs (images/ and labels/, or train/val subdirs).")
220
+ parser.add_argument(
221
+ "--val-ratio",
222
+ type=float,
223
+ default=None,
224
+ help="Validation ratio by image group (0–1). If set, creates output_dir/train and output_dir/val.",
225
+ )
226
+ parser.add_argument("--seed", type=int, default=42, help="Random seed when using --val-ratio.")
227
+ parser.add_argument(
228
+ "--split-file",
229
+ type=Path,
230
+ default=None,
231
+ help="Path to split CSV (tile_relpath, split). Overrides --val-ratio. Integer split column uses --val-split-id as the val fold.",
232
+ )
233
+ parser.add_argument(
234
+ "--val-split-id",
235
+ type=int,
236
+ default=0,
237
+ help="When --split-file has integer fold ids, treat this fold as validation (default 0).",
238
+ )
239
+ parser.add_argument("--ignore-label", action="append", default=[], dest="ignore_labels", help="Label to ignore (repeatable).")
240
+ parser.add_argument(
241
+ "--map-label",
242
+ action="append",
243
+ default=[],
244
+ dest="map_label_list",
245
+ help="Mapping as src=dst (repeatable). Example: taxi=car",
246
+ )
247
+ parser.add_argument("--dry-run", action="store_true", help="Do not write files; only report stats.")
248
+ args = parser.parse_args()
249
+
250
+ map_labels: Dict[str, str] = {}
251
+ for entry in args.map_label_list:
252
+ if "=" not in entry:
253
+ print(f"Error: invalid --map-label '{entry}'. Use src=dst.", file=sys.stderr)
254
+ return 1
255
+ src, dst = entry.split("=", 1)
256
+ src, dst = src.strip(), dst.strip()
257
+ if not src or not dst:
258
+ print(f"Error: empty src or dst in --map-label '{entry}'.", file=sys.stderr)
259
+ return 1
260
+ map_labels[src] = dst
261
+
262
+ try:
263
+ stats = run(
264
+ args.data_root,
265
+ args.output_dir,
266
+ val_ratio=args.val_ratio,
267
+ seed=args.seed,
268
+ split_file=args.split_file,
269
+ val_split_id=args.val_split_id,
270
+ ignore_labels=args.ignore_labels or None,
271
+ map_labels=map_labels if map_labels else None,
272
+ dry_run=args.dry_run,
273
+ )
274
+ except (FileNotFoundError, ValueError) as e:
275
+ print(f"Error: {e}", file=sys.stderr)
276
+ return 1
277
+
278
+ print("Playground → DOTA export " + ("(dry run)" if args.dry_run else "complete."))
279
+ print(f" Tiles total: {stats['tiles_total']}")
280
+ print(f" Tiles skipped: {stats['tiles_skipped']}")
281
+ print(f" Tiles written: {stats['tiles_written']}")
282
+ if stats["train_tiles"] or stats["val_tiles"]:
283
+ print(f" Train tiles: {stats['train_tiles']}")
284
+ print(f" Val tiles: {stats['val_tiles']}")
285
+ print(f" Objects total: {stats['objects_total']}")
286
+ return 0
287
+
288
+
289
+ if __name__ == "__main__":
290
+ sys.exit(main())
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env python3
2
+ """Download OrientedDet pretrained checkpoints from Hugging Face Hub."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import argparse
7
+ import sys
8
+
9
+
10
+ def main() -> None:
11
+ parser = argparse.ArgumentParser(
12
+ description="Download OrientedDet pretrained checkpoints from Hugging Face Hub."
13
+ )
14
+ sub = parser.add_subparsers(dest="command", required=True)
15
+
16
+ dl = sub.add_parser("download", help="Download one or more registered checkpoints")
17
+ dl.add_argument(
18
+ "assets",
19
+ nargs="+",
20
+ help="Asset name or .pth filename (see `list`)",
21
+ )
22
+
23
+ sub.add_parser("list", help="List registered pretrained assets")
24
+
25
+ args = parser.parse_args()
26
+
27
+ from oriented_det.pretrained import download_asset, list_assets, load_manifest
28
+
29
+ if args.command == "list":
30
+ manifest = load_manifest()
31
+ repo_id = manifest.get("repo_id", "")
32
+ print(f"repo_id: {repo_id}")
33
+ print(f"revision: {manifest.get('revision', 'main')}")
34
+ print("assets:")
35
+ for name, remote in sorted(list_assets().items()):
36
+ print(f" {name} -> {remote}")
37
+ return
38
+
39
+ for asset in args.assets:
40
+ path = download_asset(asset)
41
+ print(f"Downloaded {asset} -> {path}")
42
+
43
+
44
+ if __name__ == "__main__":
45
+ try:
46
+ main()
47
+ except (ImportError, KeyError, RuntimeError, ValueError) as e:
48
+ print(f"Error: {e}", file=sys.stderr)
49
+ raise SystemExit(1) from e