seavision-python 0.1.2__tar.gz → 0.1.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.
- {seavision_python-0.1.2 → seavision_python-0.1.3}/PKG-INFO +6 -3
- {seavision_python-0.1.2 → seavision_python-0.1.3}/pyproject.toml +7 -3
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/__init__.py +1 -1
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/detectors/__init__.py +35 -8
- seavision_python-0.1.3/seavision/engine/detectors/cfd/__init__.py +11 -0
- seavision_python-0.1.3/seavision/engine/detectors/cfd/config.py +105 -0
- seavision_python-0.1.3/seavision/engine/detectors/cfd/detector.py +268 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/detectors/yolo/detector.py +3 -3
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision_python.egg-info/PKG-INFO +6 -3
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision_python.egg-info/SOURCES.txt +3 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision_python.egg-info/requires.txt +6 -2
- {seavision_python-0.1.2 → seavision_python-0.1.3}/README.md +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/defaults.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/edge/__init__.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/edge/bundle.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/edge/capture.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/edge/config.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/edge/postprocess.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/edge/runtime.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/edge/writer.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/__init__.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/detectors/base.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/detectors/motion/__init__.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/detectors/motion/background.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/detectors/motion/detector.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/detectors/motion/stabiliser.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/detectors/motion/tracker.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/detectors/sam3/__init__.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/detectors/sam3/config.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/detectors/sam3/detector.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/detectors/sam3/native.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/detectors/yolo/__init__.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/detectors/yolo/config.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/export/__init__.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/export/config.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/export/exporter.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/export/validation.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/postprocessor.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/source/__init__.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/source/base.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/source/discovery.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/source/local.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/source/s3.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/visualiser/__init__.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/visualiser/annotator.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/visualiser/config.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/visualiser/loader.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/visualiser/visualiser.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/visualiser/writer.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/gui/__init__.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/gui/app.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/gui/main_window.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/gui/shared/__init__.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/gui/shared/conversion.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/gui/shared/session_utils.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/gui/validation/__init__.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/gui/validation/button_styles.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/gui/validation/detection_detail.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/gui/validation/detection_rect_item.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/gui/validation/detection_table.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/gui/validation/interactive_frame_scene.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/gui/validation/interactive_frame_view.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/gui/validation/s3_browser.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/gui/validation/seekable_source.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/gui/validation/session.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/gui/validation/tab.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/gui/validation/transport_bar.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/gui/validation/validation_model.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/gui/validation/video_cache.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/gui/validation/video_list.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/gui/validation/video_viewer.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/gui/validation/video_worker.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/pipeline.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/resources/default.yaml +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/run_edge.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/run_export.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/run_pipeline.py +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision_python.egg-info/dependency_links.txt +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision_python.egg-info/entry_points.txt +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/seavision_python.egg-info/top_level.txt +0 -0
- {seavision_python-0.1.2 → seavision_python-0.1.3}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: seavision-python
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: Human-in-the-loop Computer Vision for Marine Environments
|
|
5
5
|
Author: Mario Lambrette
|
|
6
6
|
License: Proprietary
|
|
@@ -35,17 +35,20 @@ Provides-Extra: sam3
|
|
|
35
35
|
Requires-Dist: pillow>=10.0; extra == "sam3"
|
|
36
36
|
Requires-Dist: torch>=2.0; extra == "sam3"
|
|
37
37
|
Requires-Dist: torchvision>=0.20; extra == "sam3"
|
|
38
|
-
Requires-Dist: transformers>=
|
|
38
|
+
Requires-Dist: transformers>=5.1; extra == "sam3"
|
|
39
39
|
Requires-Dist: ultralytics>=8.3.237; extra == "sam3"
|
|
40
|
+
Provides-Extra: cfd
|
|
41
|
+
Requires-Dist: rfdetr>=1.8.3; extra == "cfd"
|
|
40
42
|
Provides-Extra: all
|
|
41
43
|
Requires-Dist: boto3>=1.26; extra == "all"
|
|
42
44
|
Requires-Dist: onnxruntime>=1.16; extra == "all"
|
|
43
45
|
Requires-Dist: pillow>=10.0; extra == "all"
|
|
44
46
|
Requires-Dist: psutil>=5.9; extra == "all"
|
|
45
47
|
Requires-Dist: PySide6<6.9,>=6.5; extra == "all"
|
|
48
|
+
Requires-Dist: rfdetr>=1.8.3; extra == "all"
|
|
46
49
|
Requires-Dist: torch>=2.0; extra == "all"
|
|
47
50
|
Requires-Dist: torchvision>=0.20; extra == "all"
|
|
48
|
-
Requires-Dist: transformers>=
|
|
51
|
+
Requires-Dist: transformers>=5.1; extra == "all"
|
|
49
52
|
Requires-Dist: ultralytics>=8.3.237; extra == "all"
|
|
50
53
|
Provides-Extra: dev
|
|
51
54
|
Requires-Dist: build>=1.2; extra == "dev"
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "seavision-python"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.3"
|
|
8
8
|
description = "Human-in-the-loop Computer Vision for Marine Environments"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -56,10 +56,13 @@ sam3 = [
|
|
|
56
56
|
"pillow>=10.0",
|
|
57
57
|
"torch>=2.0",
|
|
58
58
|
"torchvision>=0.20",
|
|
59
|
-
"transformers>=
|
|
59
|
+
"transformers>=5.1",
|
|
60
60
|
"ultralytics>=8.3.237",
|
|
61
61
|
]
|
|
62
62
|
|
|
63
|
+
# CFD detectore dependencies
|
|
64
|
+
cfd = ["rfdetr>=1.8.3"]
|
|
65
|
+
|
|
63
66
|
# Combined install for workstations needing all SeaVision features.
|
|
64
67
|
all = [
|
|
65
68
|
"boto3>=1.26",
|
|
@@ -67,9 +70,10 @@ all = [
|
|
|
67
70
|
"pillow>=10.0",
|
|
68
71
|
"psutil>=5.9",
|
|
69
72
|
"PySide6>=6.5,<6.9",
|
|
73
|
+
"rfdetr>=1.8.3",
|
|
70
74
|
"torch>=2.0",
|
|
71
75
|
"torchvision>=0.20",
|
|
72
|
-
"transformers>=
|
|
76
|
+
"transformers>=5.1",
|
|
73
77
|
"ultralytics>=8.3.237",
|
|
74
78
|
]
|
|
75
79
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""Detection data structures and detector interface.
|
|
2
2
|
|
|
3
3
|
Base classes (Detection, DetectorBase) are imported eagerly.
|
|
4
|
-
Optional detector implementations (SAM3, YOLO) use lazy imports
|
|
5
|
-
to avoid pulling in torch/ultralytics at package initialisation time.
|
|
4
|
+
Optional detector implementations (SAM3, YOLO, CFD) use lazy imports
|
|
5
|
+
to avoid pulling in torch/ultralytics/rfdetr at package initialisation time.
|
|
6
6
|
This allows lightweight consumers (like the GUI) to import Detection
|
|
7
7
|
without triggering heavy dependency loads.
|
|
8
8
|
"""
|
|
@@ -14,6 +14,7 @@ __all__ = ["Detection", "DetectorBase"]
|
|
|
14
14
|
# Lazy-loaded flags — None means "not yet checked"
|
|
15
15
|
_SAM3_AVAILABLE = None
|
|
16
16
|
_YOLO_AVAILABLE = None
|
|
17
|
+
_CFD_AVAILABLE = None
|
|
17
18
|
|
|
18
19
|
# Names that trigger lazy loading when accessed
|
|
19
20
|
_SAM3_NAMES = frozenset({
|
|
@@ -25,23 +26,27 @@ _YOLO_NAMES = frozenset({
|
|
|
25
26
|
"YOLODetector", "YOLODetectorConfig",
|
|
26
27
|
"YOLO_AVAILABLE",
|
|
27
28
|
})
|
|
29
|
+
_CFD_NAMES = frozenset({
|
|
30
|
+
"CFDDetector", "CFDDetectorConfig",
|
|
31
|
+
"CFD_AVAILABLE",
|
|
32
|
+
})
|
|
28
33
|
|
|
29
34
|
|
|
30
35
|
def __getattr__(name):
|
|
31
36
|
"""Lazy import for optional detector implementations."""
|
|
32
|
-
global _SAM3_AVAILABLE, _YOLO_AVAILABLE
|
|
37
|
+
global _SAM3_AVAILABLE, _YOLO_AVAILABLE, _CFD_AVAILABLE #pylint: disable=global-statement
|
|
33
38
|
|
|
34
39
|
if name in _SAM3_NAMES:
|
|
35
40
|
if _SAM3_AVAILABLE is None:
|
|
36
41
|
try:
|
|
37
|
-
from .sam3 import (
|
|
42
|
+
from .sam3 import ( # pylint: disable=import-outside-toplevel
|
|
38
43
|
SAM3Detector,
|
|
39
44
|
SAM3DetectorConfig,
|
|
40
45
|
PromptType,
|
|
41
46
|
HybridStrategy,
|
|
42
47
|
PrompterConfig,
|
|
43
48
|
PromptConfig,
|
|
44
|
-
)
|
|
49
|
+
) # pylint: disable=import-outside-toplevel
|
|
45
50
|
_SAM3_AVAILABLE = True
|
|
46
51
|
# Inject into module namespace so subsequent access is fast
|
|
47
52
|
globals().update({
|
|
@@ -53,7 +58,7 @@ def __getattr__(name):
|
|
|
53
58
|
"PromptConfig": PromptConfig,
|
|
54
59
|
"SAM3_AVAILABLE": True,
|
|
55
60
|
})
|
|
56
|
-
except Exception:
|
|
61
|
+
except Exception: # pylint: disable=broad-exception-caught
|
|
57
62
|
_SAM3_AVAILABLE = False
|
|
58
63
|
globals()["SAM3_AVAILABLE"] = False
|
|
59
64
|
|
|
@@ -68,14 +73,14 @@ def __getattr__(name):
|
|
|
68
73
|
if name in _YOLO_NAMES:
|
|
69
74
|
if _YOLO_AVAILABLE is None:
|
|
70
75
|
try:
|
|
71
|
-
from .yolo import YOLODetector, YOLODetectorConfig
|
|
76
|
+
from .yolo import YOLODetector, YOLODetectorConfig # pylint: disable=import-outside-toplevel
|
|
72
77
|
_YOLO_AVAILABLE = True
|
|
73
78
|
globals().update({
|
|
74
79
|
"YOLODetector": YOLODetector,
|
|
75
80
|
"YOLODetectorConfig": YOLODetectorConfig,
|
|
76
81
|
"YOLO_AVAILABLE": True,
|
|
77
82
|
})
|
|
78
|
-
except Exception:
|
|
83
|
+
except Exception: # pylint: disable=broad-exception-caught
|
|
79
84
|
_YOLO_AVAILABLE = False
|
|
80
85
|
globals()["YOLO_AVAILABLE"] = False
|
|
81
86
|
|
|
@@ -87,4 +92,26 @@ def __getattr__(name):
|
|
|
87
92
|
)
|
|
88
93
|
return globals()[name]
|
|
89
94
|
|
|
95
|
+
if name in _CFD_NAMES:
|
|
96
|
+
if _CFD_AVAILABLE is None:
|
|
97
|
+
try:
|
|
98
|
+
from .cfd import CFDDetector, CFDDetectorConfig # pylint: disable=import-outside-toplevel
|
|
99
|
+
_CFD_AVAILABLE = True
|
|
100
|
+
globals().update({
|
|
101
|
+
"CFDDetector": CFDDetector,
|
|
102
|
+
"CFDDetectorConfig": CFDDetectorConfig,
|
|
103
|
+
"CFD_AVAILABLE": True,
|
|
104
|
+
})
|
|
105
|
+
except Exception: # pylint: disable=broad-exception-caught
|
|
106
|
+
_CFD_AVAILABLE = False
|
|
107
|
+
globals()["CFD_AVAILABLE"] = False
|
|
108
|
+
|
|
109
|
+
if name == "CFD_AVAILABLE":
|
|
110
|
+
return _CFD_AVAILABLE
|
|
111
|
+
if not _CFD_AVAILABLE:
|
|
112
|
+
raise ImportError(
|
|
113
|
+
f"{name} is not available (CFD dependencies not installed)"
|
|
114
|
+
)
|
|
115
|
+
return globals()[name]
|
|
116
|
+
|
|
90
117
|
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Community Fish Detector (CFD) detector for SeaVision.
|
|
2
|
+
|
|
3
|
+
Provides:
|
|
4
|
+
- CFDDetectorConfig: configuration for the CFD detector.
|
|
5
|
+
- CFDDetector: DetectorBase-compatible wrapper around the CFD RF-DETR models.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .config import CFDDetectorConfig
|
|
9
|
+
from .detector import CFDDetector
|
|
10
|
+
|
|
11
|
+
__all__ = ["CFDDetector", "CFDDetectorConfig"]
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"""Configuration for the Community Fish Detector (CFD) detector.
|
|
2
|
+
|
|
3
|
+
CFD is the marine analogue of MegaDetector: a single-class ("fish") object
|
|
4
|
+
detector. The current, non-deprecated models are RF-DETR checkpoints published
|
|
5
|
+
by the CFD project; the older YOLOv12x checkpoint is deprecated upstream (and
|
|
6
|
+
AGPL-licensed) and is deliberately not supported here.
|
|
7
|
+
|
|
8
|
+
Weights are pinned to a specific GitHub release rather than resolved as
|
|
9
|
+
"latest", so a run is reproducible: a new upstream release cannot silently
|
|
10
|
+
change results. To move to a newer release, bump ``release_tag`` (and the
|
|
11
|
+
per-variant filenames in ``CFD_WEIGHT_FILES`` if they change).
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from dataclasses import dataclass
|
|
17
|
+
from typing import Dict, Optional
|
|
18
|
+
|
|
19
|
+
# --- Pinned release ---------------------------------------------------------
|
|
20
|
+
# Matches the URLs hardcoded in the CFD repo's own test script
|
|
21
|
+
# (test_community_fish_detector.py) for the 2026.07.06 release.
|
|
22
|
+
CFD_RELEASE_TAG = "2026.07.06-release"
|
|
23
|
+
CFD_RELEASE_BASE = (
|
|
24
|
+
"https://github.com/filippovarini/community-fish-detector/"
|
|
25
|
+
"releases/download/{tag}/"
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
# variant -> checkpoint filename within the release.
|
|
29
|
+
CFD_WEIGHT_FILES: Dict[str, str] = {
|
|
30
|
+
"nano": "cfd-rf-detr-nano-640-2026.02.02.cp-011.20260706-release.pth",
|
|
31
|
+
"small": "cfd-rf-detr-small-1024-2026.06.06.cp-016.20260706-release.pth",
|
|
32
|
+
"medium": "cfd-rf-detr-medium-1024-2026.03.24.cp-011.20260706-release.pth",
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
# Reported AP on the CFD validation split. Only useful for ranking the CFD
|
|
36
|
+
# variants against each other; the absolute values are not comparable to any
|
|
37
|
+
# other benchmark (see the CFD README). medium 0.609 > small 0.606 > nano 0.596
|
|
38
|
+
# is within noise on their split — do not over-read the medium/small gap.
|
|
39
|
+
CFD_VARIANT_AP: Dict[str, float] = {"nano": 0.596, "small": 0.606, "medium": 0.609}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass
|
|
43
|
+
class CFDDetectorConfig:
|
|
44
|
+
"""Configuration for :class:`CFDDetector`.
|
|
45
|
+
|
|
46
|
+
Attributes:
|
|
47
|
+
variant: RF-DETR size, one of ``"nano"``, ``"small"``, ``"medium"``.
|
|
48
|
+
This is the accuracy/speed lever. ``nano`` runs at 640 px,
|
|
49
|
+
``small``/``medium`` at 1024 px (resolved from the checkpoint).
|
|
50
|
+
device: Compute device string, e.g. ``"cuda:0"`` or ``"cpu"``.
|
|
51
|
+
threshold: Confidence floor applied by ``predict()``.
|
|
52
|
+
resolution: Optional inference resolution override (square). ``None``
|
|
53
|
+
uses the training resolution recorded in the checkpoint, which is
|
|
54
|
+
the correct default and needs no manual ``imgsz`` plumbing. If set,
|
|
55
|
+
it must be divisible by ``patch_size * num_windows`` (32 for these
|
|
56
|
+
RF-DETR variants) or ``predict()`` will raise. It is NOT RECCOMENDED
|
|
57
|
+
to override this unless you have a specific reason.
|
|
58
|
+
output_labels: If True, emit the model's class name as the detection
|
|
59
|
+
label (``"fish"``). CFD is single-class, so this is constant.
|
|
60
|
+
weights_path: Explicit local ``.pth`` path. If set and it exists, it is
|
|
61
|
+
used directly and no download occurs (overrides ``variant``).
|
|
62
|
+
weights_url: Explicit download URL. If set, overrides the URL resolved
|
|
63
|
+
from ``variant`` + ``release_tag``.
|
|
64
|
+
release_tag: GitHub release tag to pull weights from.
|
|
65
|
+
persist_weights: If True (default), cache the downloaded checkpoint to
|
|
66
|
+
``cache_dir`` and skip the download on subsequent runs. If False,
|
|
67
|
+
download to a temporary file, load, then delete it — no persistent
|
|
68
|
+
disk footprint, at the cost of re-downloading every process start.
|
|
69
|
+
cache_dir: Directory for the cached checkpoint when
|
|
70
|
+
``persist_weights`` is True. ``None`` uses
|
|
71
|
+
``<tempdir>/community-fish-detector``.
|
|
72
|
+
optimize_for_inference: Call ``model.optimize_for_inference()`` after
|
|
73
|
+
load. Off by default, mirroring the CFD repo — its bfloat16 path is
|
|
74
|
+
reported to produce degenerate boxes.
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
variant: str = "medium"
|
|
78
|
+
device: str = "cuda:0"
|
|
79
|
+
threshold: float = 0.001
|
|
80
|
+
resolution: Optional[int] = None
|
|
81
|
+
output_labels: bool = True
|
|
82
|
+
|
|
83
|
+
# Weight source / caching
|
|
84
|
+
weights_path: Optional[str] = None
|
|
85
|
+
weights_url: Optional[str] = None
|
|
86
|
+
release_tag: str = CFD_RELEASE_TAG
|
|
87
|
+
persist_weights: bool = True
|
|
88
|
+
cache_dir: Optional[str] = None
|
|
89
|
+
|
|
90
|
+
# Runtime
|
|
91
|
+
optimize_for_inference: bool = False
|
|
92
|
+
|
|
93
|
+
def __post_init__(self) -> None:
|
|
94
|
+
if self.variant not in CFD_WEIGHT_FILES:
|
|
95
|
+
raise ValueError(
|
|
96
|
+
f"Unknown CFD variant {self.variant!r}. "
|
|
97
|
+
f"Valid variants: {sorted(CFD_WEIGHT_FILES)}."
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
def resolve_weights_url(self) -> str:
|
|
101
|
+
"""Return the download URL for the configured variant/release."""
|
|
102
|
+
if self.weights_url:
|
|
103
|
+
return self.weights_url
|
|
104
|
+
base = CFD_RELEASE_BASE.format(tag=self.release_tag)
|
|
105
|
+
return base + CFD_WEIGHT_FILES[self.variant]
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
"""Community Fish Detector (CFD) detector for SeaVision.
|
|
2
|
+
|
|
3
|
+
Wraps the CFD RF-DETR checkpoints (via the ``rfdetr`` package) to implement the
|
|
4
|
+
SeaVision :class:`DetectorBase` interface, so CFD can be dropped into the
|
|
5
|
+
standard pipeline.
|
|
6
|
+
|
|
7
|
+
Design notes
|
|
8
|
+
------------
|
|
9
|
+
- **Single class.** CFD detects one class, "fish".
|
|
10
|
+
- **Proposal cap.** RF-DETR emits at most ``num_select`` proposals per image
|
|
11
|
+
(300 for these variants). Lowering ``threshold`` cannot exceed that cap, so
|
|
12
|
+
the recall curve is truncated at 300 predictions/frame.
|
|
13
|
+
- **Channel order.** SeaVision passes **BGR** frames (OpenCV convention);
|
|
14
|
+
``rfdetr.predict()`` expects **RGB**. The conversion is handled here, once,
|
|
15
|
+
as a contiguous view flip — passing BGR straight through would silently
|
|
16
|
+
degrade detection. This is the main integration trap and is dealt with in
|
|
17
|
+
``process_frame``.
|
|
18
|
+
- **Resolution.** ``rfdetr.from_checkpoint`` resolves the training resolution
|
|
19
|
+
from checkpoint metadata (1024 for small/medium, 640 for nano), so there is
|
|
20
|
+
no ``imgsz`` to set. An optional override is exposed via the config.
|
|
21
|
+
- **Lazy dependency.** ``rfdetr`` (and its heavy torch stack) is imported only
|
|
22
|
+
on first model load, not at module import, mirroring the other optional
|
|
23
|
+
detectors so lightweight consumers can import SeaVision without it.
|
|
24
|
+
|
|
25
|
+
Example:
|
|
26
|
+
from seavision.engine.detectors.cfd import CFDDetector, CFDDetectorConfig
|
|
27
|
+
|
|
28
|
+
config = CFDDetectorConfig(variant="medium", device="cuda:0")
|
|
29
|
+
with CFDDetector(config) as detector:
|
|
30
|
+
for frame, context in source.iter_frames():
|
|
31
|
+
for det in detector.process_frame(frame, context):
|
|
32
|
+
print(det.label, det.confidence, det.bbox)
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
from __future__ import annotations
|
|
36
|
+
|
|
37
|
+
import logging
|
|
38
|
+
import os
|
|
39
|
+
import tempfile
|
|
40
|
+
import urllib.request
|
|
41
|
+
from typing import Iterator, List, Optional, Tuple
|
|
42
|
+
|
|
43
|
+
import numpy as np
|
|
44
|
+
|
|
45
|
+
from ...source import FrameContext
|
|
46
|
+
from ..base import Detection, DetectorBase
|
|
47
|
+
from .config import CFDDetectorConfig
|
|
48
|
+
|
|
49
|
+
logger = logging.getLogger(__name__)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class CFDDetector(DetectorBase):
|
|
53
|
+
"""RF-DETR-based Community Fish Detector, wrapped for SeaVision.
|
|
54
|
+
|
|
55
|
+
Stateless per frame: no temporal tracking or persistence filtering. The
|
|
56
|
+
model is loaded lazily on the first :meth:`process_frame` call and then
|
|
57
|
+
kept resident; inference does not touch disk after load.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
def __init__(self, config: Optional[CFDDetectorConfig] = None):
|
|
61
|
+
self.config = config or CFDDetectorConfig()
|
|
62
|
+
|
|
63
|
+
# rfdetr model handle (lazy-loaded on first inference).
|
|
64
|
+
self._model = None
|
|
65
|
+
# Cached class-name lookup, resolved once from the model.
|
|
66
|
+
self._class_names: Optional[List[str]] = None
|
|
67
|
+
|
|
68
|
+
logger.info(
|
|
69
|
+
"CFDDetector initialised (variant=%s, device=%s)",
|
|
70
|
+
self.config.variant,
|
|
71
|
+
self.config.device,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# ------------------------------------------------------------------
|
|
75
|
+
# Weight resolution
|
|
76
|
+
# ------------------------------------------------------------------
|
|
77
|
+
|
|
78
|
+
def _resolve_weights(self) -> Tuple[str, bool]:
|
|
79
|
+
"""Return ``(local_path, is_temporary)`` for the checkpoint.
|
|
80
|
+
|
|
81
|
+
Resolution order:
|
|
82
|
+
1. An existing local ``weights_path`` — used directly, no download.
|
|
83
|
+
2. Otherwise download the pinned release URL, either to the cache
|
|
84
|
+
(``persist_weights=True``) or to a temp file that the caller must
|
|
85
|
+
delete after loading (``persist_weights=False``).
|
|
86
|
+
"""
|
|
87
|
+
# 1. Explicit local file.
|
|
88
|
+
if self.config.weights_path:
|
|
89
|
+
if os.path.isfile(self.config.weights_path):
|
|
90
|
+
logger.info("Using local CFD weights: %s", self.config.weights_path)
|
|
91
|
+
return self.config.weights_path, False
|
|
92
|
+
raise FileNotFoundError(
|
|
93
|
+
f"weights_path set but not found: {self.config.weights_path}"
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
url = self.config.resolve_weights_url()
|
|
97
|
+
basename = os.path.basename(url)
|
|
98
|
+
|
|
99
|
+
# 2a. Persistent cache: download once, reuse thereafter.
|
|
100
|
+
if self.config.persist_weights:
|
|
101
|
+
cache_dir = self.config.cache_dir or os.path.join(
|
|
102
|
+
tempfile.gettempdir(), "community-fish-detector"
|
|
103
|
+
)
|
|
104
|
+
os.makedirs(cache_dir, exist_ok=True)
|
|
105
|
+
dest = os.path.join(cache_dir, basename)
|
|
106
|
+
if os.path.isfile(dest):
|
|
107
|
+
logger.info("Using cached CFD weights: %s", dest)
|
|
108
|
+
return dest, False
|
|
109
|
+
logger.info("Downloading CFD weights\n from %s\n to %s", url, dest)
|
|
110
|
+
self._download(url, dest)
|
|
111
|
+
return dest, False
|
|
112
|
+
|
|
113
|
+
# 2b. Ephemeral: download to a temp file, to be deleted after load.
|
|
114
|
+
fd, tmp_path = tempfile.mkstemp(suffix=".pth", prefix="cfd-")
|
|
115
|
+
os.close(fd)
|
|
116
|
+
logger.info(
|
|
117
|
+
"Downloading CFD weights to ephemeral temp file %s (persist_weights=False)",
|
|
118
|
+
tmp_path,
|
|
119
|
+
)
|
|
120
|
+
self._download(url, tmp_path)
|
|
121
|
+
return tmp_path, True
|
|
122
|
+
|
|
123
|
+
@staticmethod
|
|
124
|
+
def _download(url: str, dest: str) -> None:
|
|
125
|
+
"""Download ``url`` to ``dest``, cleaning up a partial file on failure."""
|
|
126
|
+
try:
|
|
127
|
+
urllib.request.urlretrieve(url, dest)
|
|
128
|
+
except Exception:
|
|
129
|
+
if os.path.isfile(dest):
|
|
130
|
+
try:
|
|
131
|
+
os.remove(dest)
|
|
132
|
+
except OSError:
|
|
133
|
+
pass
|
|
134
|
+
raise
|
|
135
|
+
|
|
136
|
+
# ------------------------------------------------------------------
|
|
137
|
+
# Model loading
|
|
138
|
+
# ------------------------------------------------------------------
|
|
139
|
+
|
|
140
|
+
def _ensure_model_loaded(self) -> None:
|
|
141
|
+
"""Lazily import rfdetr, resolve weights, and load the model."""
|
|
142
|
+
if self._model is not None:
|
|
143
|
+
return
|
|
144
|
+
|
|
145
|
+
# Lazy import: keep the heavy torch/rfdetr stack out of import time.
|
|
146
|
+
try:
|
|
147
|
+
from rfdetr import from_checkpoint #type: ignore #pylint: disable=import-outside-toplevel
|
|
148
|
+
except ImportError as exc:
|
|
149
|
+
raise ImportError(
|
|
150
|
+
"The 'rfdetr' package is required for CFDDetector. "
|
|
151
|
+
"Install with: pip install rfdetr>=1.8.3"
|
|
152
|
+
) from exc
|
|
153
|
+
|
|
154
|
+
weights_path, is_temp = self._resolve_weights()
|
|
155
|
+
try:
|
|
156
|
+
logger.info("Loading CFD RF-DETR checkpoint (device=%s)", self.config.device)
|
|
157
|
+
# from_checkpoint resolves the RF-DETR variant, resolution and
|
|
158
|
+
# (single) class count from checkpoint metadata. device is
|
|
159
|
+
# forwarded to the model constructor.
|
|
160
|
+
self._model = from_checkpoint(weights_path, device=self.config.device)
|
|
161
|
+
finally:
|
|
162
|
+
# Ephemeral weights: the checkpoint is now in memory; drop the file.
|
|
163
|
+
if is_temp and os.path.isfile(weights_path):
|
|
164
|
+
try:
|
|
165
|
+
os.remove(weights_path)
|
|
166
|
+
logger.info("Removed ephemeral CFD weights file %s", weights_path)
|
|
167
|
+
except OSError as exc:
|
|
168
|
+
logger.warning("Could not remove temp weights %s: %s", weights_path, exc)
|
|
169
|
+
|
|
170
|
+
if self.config.optimize_for_inference:
|
|
171
|
+
logger.info("Optimising CFD model for inference")
|
|
172
|
+
self._model.optimize_for_inference()
|
|
173
|
+
|
|
174
|
+
# Resolve and cache class names once (single class for CFD).
|
|
175
|
+
try:
|
|
176
|
+
names = self._model.class_names
|
|
177
|
+
self._class_names = list(names) if names else None
|
|
178
|
+
except Exception: # pylint: disable=broad-exception-caught
|
|
179
|
+
self._class_names = None
|
|
180
|
+
|
|
181
|
+
resolved_res = getattr(self._model, "resolution", None)
|
|
182
|
+
logger.info(
|
|
183
|
+
"CFD model loaded (resolution=%s, classes=%s)",
|
|
184
|
+
resolved_res,
|
|
185
|
+
self._class_names,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
def _label_for(self, class_id: int) -> str:
|
|
189
|
+
"""Map a class id to a label, falling back to 'fish' for CFD."""
|
|
190
|
+
if self._class_names and 0 <= class_id < len(self._class_names):
|
|
191
|
+
return str(self._class_names[class_id])
|
|
192
|
+
return "fish"
|
|
193
|
+
|
|
194
|
+
# ------------------------------------------------------------------
|
|
195
|
+
# DetectorBase interface
|
|
196
|
+
# ------------------------------------------------------------------
|
|
197
|
+
|
|
198
|
+
def process_frame(
|
|
199
|
+
self,
|
|
200
|
+
frame: np.ndarray,
|
|
201
|
+
context: FrameContext,
|
|
202
|
+
) -> Iterator[Detection]:
|
|
203
|
+
"""Run CFD on a single frame and yield detections.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
frame: BGR image, shape (H, W, 3), uint8 (OpenCV convention).
|
|
207
|
+
context: Frame metadata.
|
|
208
|
+
|
|
209
|
+
Yields:
|
|
210
|
+
:class:`Detection` per predicted box, in original-frame pixel
|
|
211
|
+
coordinates. Confidence is the RF-DETR score; label is the model
|
|
212
|
+
class name ("fish") when ``output_labels`` is set.
|
|
213
|
+
"""
|
|
214
|
+
self._ensure_model_loaded()
|
|
215
|
+
|
|
216
|
+
# BGR -> RGB. predict() requires RGB; ascontiguousarray gives torch a
|
|
217
|
+
# positive-strided buffer (a bare ::-1 view has negative strides and
|
|
218
|
+
# torch.from_numpy would reject it). include_source_image=False avoids
|
|
219
|
+
# an extra full-frame copy we don't need.
|
|
220
|
+
rgb = np.ascontiguousarray(frame[:, :, ::-1])
|
|
221
|
+
|
|
222
|
+
predict_kwargs = {
|
|
223
|
+
"threshold": self.config.threshold,
|
|
224
|
+
"include_source_image": False,
|
|
225
|
+
}
|
|
226
|
+
if self.config.resolution is not None:
|
|
227
|
+
predict_kwargs["shape"] = (self.config.resolution, self.config.resolution)
|
|
228
|
+
|
|
229
|
+
detections = self._model.predict(rgb, **predict_kwargs) #type: ignore
|
|
230
|
+
# A single-image input returns a single Detections; be defensive.
|
|
231
|
+
if isinstance(detections, list):
|
|
232
|
+
detections = detections[0] if detections else None
|
|
233
|
+
if detections is None or len(detections) == 0:
|
|
234
|
+
return
|
|
235
|
+
|
|
236
|
+
xyxy = detections.xyxy #type: ignore
|
|
237
|
+
conf = getattr(detections, "confidence", None)
|
|
238
|
+
class_id = getattr(detections, "class_id", None)
|
|
239
|
+
|
|
240
|
+
for i in range(len(detections)):
|
|
241
|
+
x1, y1, x2, y2 = (float(v) for v in xyxy[i])
|
|
242
|
+
|
|
243
|
+
confidence: Optional[float] = None
|
|
244
|
+
if conf is not None:
|
|
245
|
+
confidence = float(conf[i])
|
|
246
|
+
|
|
247
|
+
label: Optional[str] = None
|
|
248
|
+
if self.config.output_labels:
|
|
249
|
+
cid = int(class_id[i]) if class_id is not None else 0
|
|
250
|
+
label = self._label_for(cid)
|
|
251
|
+
|
|
252
|
+
yield Detection.from_bbox(
|
|
253
|
+
source_file=context.source_file,
|
|
254
|
+
timestamp=context.timestamp,
|
|
255
|
+
frame_number=context.frame_number,
|
|
256
|
+
x1=x1,
|
|
257
|
+
y1=y1,
|
|
258
|
+
x2=x2,
|
|
259
|
+
y2=y2,
|
|
260
|
+
confidence=confidence,
|
|
261
|
+
label=label,
|
|
262
|
+
track_id=None,
|
|
263
|
+
mask=None,
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
def reset(self) -> None:
|
|
267
|
+
"""No per-video state to clear; the model stays loaded."""
|
|
268
|
+
pass #pylint: disable=unnecessary-pass
|
{seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/detectors/yolo/detector.py
RENAMED
|
@@ -105,7 +105,7 @@ class YOLODetector(DetectorBase):
|
|
|
105
105
|
if self.config.device:
|
|
106
106
|
try:
|
|
107
107
|
self._model.to(self.config.device)
|
|
108
|
-
except Exception as exc:
|
|
108
|
+
except Exception as exc: # pylint: disable=broad-exception-caught
|
|
109
109
|
logger.warning(
|
|
110
110
|
"Failed to move YOLO model to device '%s': %s. "
|
|
111
111
|
"Using default device instead.",
|
|
@@ -155,7 +155,7 @@ class YOLODetector(DetectorBase):
|
|
|
155
155
|
|
|
156
156
|
# Run YOLO inference on the in-memory frame. All saving-related
|
|
157
157
|
# options are explicitly disabled to avoid side effects.
|
|
158
|
-
results_list = self._model.predict(
|
|
158
|
+
results_list = self._model.predict( #type: ignore
|
|
159
159
|
frame,
|
|
160
160
|
imgsz=self.config.imgsz,
|
|
161
161
|
conf=self.config.conf_threshold,
|
|
@@ -183,7 +183,7 @@ class YOLODetector(DetectorBase):
|
|
|
183
183
|
if boxes is None or len(boxes) == 0:
|
|
184
184
|
return
|
|
185
185
|
|
|
186
|
-
for i in range(len(boxes)):
|
|
186
|
+
for i in range(len(boxes)): # pylint: disable=consider-using-enumerate
|
|
187
187
|
box = boxes[i]
|
|
188
188
|
|
|
189
189
|
# xyxy format as numpy array
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: seavision-python
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: Human-in-the-loop Computer Vision for Marine Environments
|
|
5
5
|
Author: Mario Lambrette
|
|
6
6
|
License: Proprietary
|
|
@@ -35,17 +35,20 @@ Provides-Extra: sam3
|
|
|
35
35
|
Requires-Dist: pillow>=10.0; extra == "sam3"
|
|
36
36
|
Requires-Dist: torch>=2.0; extra == "sam3"
|
|
37
37
|
Requires-Dist: torchvision>=0.20; extra == "sam3"
|
|
38
|
-
Requires-Dist: transformers>=
|
|
38
|
+
Requires-Dist: transformers>=5.1; extra == "sam3"
|
|
39
39
|
Requires-Dist: ultralytics>=8.3.237; extra == "sam3"
|
|
40
|
+
Provides-Extra: cfd
|
|
41
|
+
Requires-Dist: rfdetr>=1.8.3; extra == "cfd"
|
|
40
42
|
Provides-Extra: all
|
|
41
43
|
Requires-Dist: boto3>=1.26; extra == "all"
|
|
42
44
|
Requires-Dist: onnxruntime>=1.16; extra == "all"
|
|
43
45
|
Requires-Dist: pillow>=10.0; extra == "all"
|
|
44
46
|
Requires-Dist: psutil>=5.9; extra == "all"
|
|
45
47
|
Requires-Dist: PySide6<6.9,>=6.5; extra == "all"
|
|
48
|
+
Requires-Dist: rfdetr>=1.8.3; extra == "all"
|
|
46
49
|
Requires-Dist: torch>=2.0; extra == "all"
|
|
47
50
|
Requires-Dist: torchvision>=0.20; extra == "all"
|
|
48
|
-
Requires-Dist: transformers>=
|
|
51
|
+
Requires-Dist: transformers>=5.1; extra == "all"
|
|
49
52
|
Requires-Dist: ultralytics>=8.3.237; extra == "all"
|
|
50
53
|
Provides-Extra: dev
|
|
51
54
|
Requires-Dist: build>=1.2; extra == "dev"
|
|
@@ -17,6 +17,9 @@ seavision/engine/__init__.py
|
|
|
17
17
|
seavision/engine/postprocessor.py
|
|
18
18
|
seavision/engine/detectors/__init__.py
|
|
19
19
|
seavision/engine/detectors/base.py
|
|
20
|
+
seavision/engine/detectors/cfd/__init__.py
|
|
21
|
+
seavision/engine/detectors/cfd/config.py
|
|
22
|
+
seavision/engine/detectors/cfd/detector.py
|
|
20
23
|
seavision/engine/detectors/motion/__init__.py
|
|
21
24
|
seavision/engine/detectors/motion/background.py
|
|
22
25
|
seavision/engine/detectors/motion/detector.py
|
|
@@ -9,11 +9,15 @@ onnxruntime>=1.16
|
|
|
9
9
|
pillow>=10.0
|
|
10
10
|
psutil>=5.9
|
|
11
11
|
PySide6<6.9,>=6.5
|
|
12
|
+
rfdetr>=1.8.3
|
|
12
13
|
torch>=2.0
|
|
13
14
|
torchvision>=0.20
|
|
14
|
-
transformers>=
|
|
15
|
+
transformers>=5.1
|
|
15
16
|
ultralytics>=8.3.237
|
|
16
17
|
|
|
18
|
+
[cfd]
|
|
19
|
+
rfdetr>=1.8.3
|
|
20
|
+
|
|
17
21
|
[dev]
|
|
18
22
|
build>=1.2
|
|
19
23
|
pytest>=8.0
|
|
@@ -39,5 +43,5 @@ boto3>=1.26
|
|
|
39
43
|
pillow>=10.0
|
|
40
44
|
torch>=2.0
|
|
41
45
|
torchvision>=0.20
|
|
42
|
-
transformers>=
|
|
46
|
+
transformers>=5.1
|
|
43
47
|
ultralytics>=8.3.237
|
|
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
|
{seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/detectors/motion/__init__.py
RENAMED
|
File without changes
|
{seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/detectors/motion/background.py
RENAMED
|
File without changes
|
{seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/detectors/motion/detector.py
RENAMED
|
File without changes
|
{seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/detectors/motion/stabiliser.py
RENAMED
|
File without changes
|
{seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/detectors/motion/tracker.py
RENAMED
|
File without changes
|
{seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/detectors/sam3/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/detectors/sam3/detector.py
RENAMED
|
File without changes
|
|
File without changes
|
{seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/engine/detectors/yolo/__init__.py
RENAMED
|
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
|
{seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/gui/validation/detection_detail.py
RENAMED
|
File without changes
|
{seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/gui/validation/detection_rect_item.py
RENAMED
|
File without changes
|
{seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/gui/validation/detection_table.py
RENAMED
|
File without changes
|
|
File without changes
|
{seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/gui/validation/interactive_frame_view.py
RENAMED
|
File without changes
|
|
File without changes
|
{seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/gui/validation/seekable_source.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{seavision_python-0.1.2 → seavision_python-0.1.3}/seavision/gui/validation/validation_model.py
RENAMED
|
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
|
{seavision_python-0.1.2 → seavision_python-0.1.3}/seavision_python.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{seavision_python-0.1.2 → seavision_python-0.1.3}/seavision_python.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|