seavision-python 0.1.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.
- seavision/__init__.py +3 -0
- seavision/defaults.py +38 -0
- seavision/edge/__init__.py +38 -0
- seavision/edge/bundle.py +189 -0
- seavision/edge/capture.py +107 -0
- seavision/edge/config.py +219 -0
- seavision/edge/postprocess.py +185 -0
- seavision/edge/runtime.py +416 -0
- seavision/edge/writer.py +167 -0
- seavision/engine/__init__.py +52 -0
- seavision/engine/detectors/__init__.py +90 -0
- seavision/engine/detectors/base.py +179 -0
- seavision/engine/detectors/motion/__init__.py +14 -0
- seavision/engine/detectors/motion/background.py +92 -0
- seavision/engine/detectors/motion/detector.py +218 -0
- seavision/engine/detectors/motion/stabiliser.py +174 -0
- seavision/engine/detectors/motion/tracker.py +174 -0
- seavision/engine/detectors/sam3/__init__.py +23 -0
- seavision/engine/detectors/sam3/config.py +309 -0
- seavision/engine/detectors/sam3/detector.py +1316 -0
- seavision/engine/detectors/sam3/native.py +490 -0
- seavision/engine/detectors/yolo/__init__.py +12 -0
- seavision/engine/detectors/yolo/config.py +39 -0
- seavision/engine/detectors/yolo/detector.py +236 -0
- seavision/engine/export/__init__.py +24 -0
- seavision/engine/export/config.py +160 -0
- seavision/engine/export/exporter.py +286 -0
- seavision/engine/export/validation.py +314 -0
- seavision/engine/postprocessor.py +738 -0
- seavision/engine/source/__init__.py +16 -0
- seavision/engine/source/base.py +87 -0
- seavision/engine/source/discovery.py +154 -0
- seavision/engine/source/local.py +155 -0
- seavision/engine/source/s3.py +231 -0
- seavision/engine/visualiser/__init__.py +62 -0
- seavision/engine/visualiser/annotator.py +382 -0
- seavision/engine/visualiser/config.py +127 -0
- seavision/engine/visualiser/loader.py +558 -0
- seavision/engine/visualiser/visualiser.py +402 -0
- seavision/engine/visualiser/writer.py +245 -0
- seavision/gui/__init__.py +0 -0
- seavision/gui/app.py +30 -0
- seavision/gui/main_window.py +891 -0
- seavision/gui/shared/__init__.py +0 -0
- seavision/gui/shared/conversion.py +55 -0
- seavision/gui/shared/session_utils.py +37 -0
- seavision/gui/validation/__init__.py +0 -0
- seavision/gui/validation/button_styles.py +144 -0
- seavision/gui/validation/detection_detail.py +164 -0
- seavision/gui/validation/detection_rect_item.py +486 -0
- seavision/gui/validation/detection_table.py +563 -0
- seavision/gui/validation/interactive_frame_scene.py +340 -0
- seavision/gui/validation/interactive_frame_view.py +252 -0
- seavision/gui/validation/s3_browser.py +645 -0
- seavision/gui/validation/seekable_source.py +262 -0
- seavision/gui/validation/session.py +376 -0
- seavision/gui/validation/tab.py +2503 -0
- seavision/gui/validation/transport_bar.py +172 -0
- seavision/gui/validation/validation_model.py +482 -0
- seavision/gui/validation/video_cache.py +215 -0
- seavision/gui/validation/video_list.py +140 -0
- seavision/gui/validation/video_viewer.py +150 -0
- seavision/gui/validation/video_worker.py +422 -0
- seavision/pipeline.py +856 -0
- seavision/resources/default.yaml +139 -0
- seavision/run_edge.py +96 -0
- seavision/run_export.py +176 -0
- seavision/run_pipeline.py +388 -0
- seavision_python-0.1.1.dist-info/METADATA +286 -0
- seavision_python-0.1.1.dist-info/RECORD +73 -0
- seavision_python-0.1.1.dist-info/WHEEL +5 -0
- seavision_python-0.1.1.dist-info/entry_points.txt +5 -0
- seavision_python-0.1.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# Buoy Detection Pipeline - Default Configuration
|
|
3
|
+
# =============================================================================
|
|
4
|
+
#
|
|
5
|
+
# This file contains all configurable options for the detection pipeline.
|
|
6
|
+
# Copy this file and modify as needed for different experiments.
|
|
7
|
+
#
|
|
8
|
+
# Usage:
|
|
9
|
+
# seavision --config config/default.yaml --input ./footage/
|
|
10
|
+
# seavision --config config/default.yaml --min-area 200 # CLI overrides
|
|
11
|
+
#
|
|
12
|
+
# =============================================================================
|
|
13
|
+
|
|
14
|
+
# -----------------------------------------------------------------------------
|
|
15
|
+
# Input Options
|
|
16
|
+
# -----------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
input:
|
|
19
|
+
# Path to video file or directory (can be overridden via CLI --input)
|
|
20
|
+
# path: "./footage/"
|
|
21
|
+
|
|
22
|
+
# Glob pattern for matching videos in a directory
|
|
23
|
+
pattern: "*.ts"
|
|
24
|
+
|
|
25
|
+
# Source type: "local" or "s3"
|
|
26
|
+
source_type: "local"
|
|
27
|
+
|
|
28
|
+
# S3-specific options (only used when source_type is "s3")
|
|
29
|
+
s3:
|
|
30
|
+
bucket: ""
|
|
31
|
+
prefix: ""
|
|
32
|
+
profile_name: "" # AWS SSO profile name (e.g., "m3b-detector")
|
|
33
|
+
|
|
34
|
+
# Process every Nth frame (1 = all frames, 5 = every 5th frame)
|
|
35
|
+
frame_skip: 1
|
|
36
|
+
|
|
37
|
+
# -----------------------------------------------------------------------------
|
|
38
|
+
# Output Options
|
|
39
|
+
# -----------------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
output:
|
|
42
|
+
# Directory where detection CSV files will be saved
|
|
43
|
+
directory: "./output"
|
|
44
|
+
|
|
45
|
+
# Output mode: "per_video" or "single"
|
|
46
|
+
# per_video: Create one CSV per video (e.g., video1_detections.csv)
|
|
47
|
+
# single: All detections in one CSV file
|
|
48
|
+
mode: "per_video"
|
|
49
|
+
|
|
50
|
+
# Filename for single_file mode (ignored in per_video mode)
|
|
51
|
+
single_file_name: "detections.csv"
|
|
52
|
+
|
|
53
|
+
# Whether to overwrite existing output files
|
|
54
|
+
# Set to true to replace existing files, false to raise an error
|
|
55
|
+
overwrite: false
|
|
56
|
+
|
|
57
|
+
# -----------------------------------------------------------------------------
|
|
58
|
+
# Pipeline Options
|
|
59
|
+
# -----------------------------------------------------------------------------
|
|
60
|
+
|
|
61
|
+
# Resume mode: skip videos that already have output CSV files
|
|
62
|
+
# Useful for continuing interrupted processing runs
|
|
63
|
+
resume: false
|
|
64
|
+
|
|
65
|
+
# -----------------------------------------------------------------------------
|
|
66
|
+
# Detector Configuration
|
|
67
|
+
# -----------------------------------------------------------------------------
|
|
68
|
+
|
|
69
|
+
detector:
|
|
70
|
+
# Detector type (currently only "motion" is implemented)
|
|
71
|
+
type: "motion"
|
|
72
|
+
|
|
73
|
+
# Detector-specific configuration
|
|
74
|
+
config:
|
|
75
|
+
# --- Motion Detection Options ---
|
|
76
|
+
|
|
77
|
+
# Enable/disable frame stabilisation (compensates for buoy sway)
|
|
78
|
+
stabilisation_enabled: true
|
|
79
|
+
|
|
80
|
+
# Minimum contour area in pixels (smaller = more sensitive, more noise)
|
|
81
|
+
min_area: 500
|
|
82
|
+
|
|
83
|
+
# Maximum contour area in pixels (larger areas are likely not fish)
|
|
84
|
+
max_area: 5000
|
|
85
|
+
|
|
86
|
+
# Morphological operation kernel size (for noise cleanup)
|
|
87
|
+
morph_kernel_size: 5
|
|
88
|
+
|
|
89
|
+
# Number of morphological operation iterations
|
|
90
|
+
morph_iterations: 2
|
|
91
|
+
|
|
92
|
+
# --- Persistence Tracking Options ---
|
|
93
|
+
|
|
94
|
+
# Enable persistence filtering (require objects to persist across frames)
|
|
95
|
+
persistence_enabled: true
|
|
96
|
+
|
|
97
|
+
# Minimum frames an object must persist before being reported
|
|
98
|
+
min_persistence: 3
|
|
99
|
+
|
|
100
|
+
# Maximum frames an object can be missing before track is dropped
|
|
101
|
+
max_frames_missing: 5
|
|
102
|
+
|
|
103
|
+
# IoU threshold for matching detections between frames
|
|
104
|
+
iou_threshold: 0.3
|
|
105
|
+
|
|
106
|
+
# --- Stabiliser Options ---
|
|
107
|
+
stabiliser:
|
|
108
|
+
# Feature detector type: "ORB" or "AKAZE"
|
|
109
|
+
feature_detector: "ORB"
|
|
110
|
+
|
|
111
|
+
# Maximum number of features to detect per frame
|
|
112
|
+
max_features: 500
|
|
113
|
+
|
|
114
|
+
# Lowe's ratio test threshold for feature matching (0.0 - 1.0)
|
|
115
|
+
# Lower = stricter matching, fewer but better matches
|
|
116
|
+
match_ratio: 0.75
|
|
117
|
+
|
|
118
|
+
# Minimum number of feature matches required for homography
|
|
119
|
+
min_matches: 10
|
|
120
|
+
|
|
121
|
+
# RANSAC reprojection threshold for outlier rejection
|
|
122
|
+
ransac_threshold: 5.0
|
|
123
|
+
|
|
124
|
+
# --- Background Model Options ---
|
|
125
|
+
background:
|
|
126
|
+
# Number of frames used to build the background model
|
|
127
|
+
# Higher = more stable but slower to adapt
|
|
128
|
+
history: 600
|
|
129
|
+
|
|
130
|
+
# Variance threshold for foreground classification
|
|
131
|
+
# Lower = more sensitive to changes
|
|
132
|
+
var_threshold: 16.0
|
|
133
|
+
|
|
134
|
+
# Whether to detect and mark shadows
|
|
135
|
+
detect_shadows: true
|
|
136
|
+
|
|
137
|
+
# Learning rate for background model adaptation
|
|
138
|
+
# -1 = automatic, or 0.0-1.0 where higher = faster adaptation
|
|
139
|
+
learning_rate: -1.0
|
seavision/run_edge.py
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CLI entry point for the SeaVision edge runtime.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
seavision-edge # Use artifact in current directory
|
|
6
|
+
seavision-edge --artifact-dir ./artifact
|
|
7
|
+
seavision-edge --source 0 # Camera 0
|
|
8
|
+
seavision-edge --source video.mp4 # Video file
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import sys
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _load_runtime_class():
|
|
17
|
+
"""Import the runtime only when startup validation has passed."""
|
|
18
|
+
from seavision.edge import EdgeRuntime
|
|
19
|
+
|
|
20
|
+
return EdgeRuntime
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _resolve_artifact_dir(path: str, manifest_filename: str) -> str:
|
|
24
|
+
"""Resolve artifact directory, auto-selecting a nested artifact folder."""
|
|
25
|
+
artifact_dir = Path(path)
|
|
26
|
+
if not artifact_dir.exists() or not artifact_dir.is_dir():
|
|
27
|
+
return path
|
|
28
|
+
|
|
29
|
+
if (artifact_dir / manifest_filename).exists():
|
|
30
|
+
return str(artifact_dir)
|
|
31
|
+
|
|
32
|
+
nested_artifact_dir = artifact_dir / "artifact"
|
|
33
|
+
if (nested_artifact_dir / manifest_filename).exists():
|
|
34
|
+
return str(nested_artifact_dir)
|
|
35
|
+
|
|
36
|
+
return str(artifact_dir)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def main() -> int:
|
|
40
|
+
"""Main entry point."""
|
|
41
|
+
parser = argparse.ArgumentParser(
|
|
42
|
+
prog="seavision-edge",
|
|
43
|
+
description="SeaVision edge inference runtime",
|
|
44
|
+
)
|
|
45
|
+
parser.add_argument(
|
|
46
|
+
"--artifact-dir", default=".",
|
|
47
|
+
help="Path to the exported model artifact directory (default: current directory)",
|
|
48
|
+
)
|
|
49
|
+
parser.add_argument(
|
|
50
|
+
"--source", default=None,
|
|
51
|
+
help="Override video source (camera index or file path)",
|
|
52
|
+
)
|
|
53
|
+
parser.add_argument(
|
|
54
|
+
"--conf", type=float, default=None,
|
|
55
|
+
help="Override confidence threshold",
|
|
56
|
+
)
|
|
57
|
+
parser.add_argument(
|
|
58
|
+
"--frame-skip", type=int, default=None,
|
|
59
|
+
help="Override frame skip",
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
args = parser.parse_args()
|
|
63
|
+
|
|
64
|
+
from seavision.edge import EdgeConfig
|
|
65
|
+
from seavision.edge.config import ARTIFACT_MANIFEST_FILENAME
|
|
66
|
+
|
|
67
|
+
resolved_artifact_dir = _resolve_artifact_dir(
|
|
68
|
+
args.artifact_dir, ARTIFACT_MANIFEST_FILENAME
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
try:
|
|
72
|
+
config = EdgeConfig.from_artifact_dir(resolved_artifact_dir)
|
|
73
|
+
except FileNotFoundError:
|
|
74
|
+
print(
|
|
75
|
+
"ERROR: Artifact directory is incomplete or missing required files: "
|
|
76
|
+
f"{args.artifact_dir}"
|
|
77
|
+
)
|
|
78
|
+
print("Run seavision-export first to create an edge artifact directory.")
|
|
79
|
+
return 1
|
|
80
|
+
|
|
81
|
+
# Apply CLI overrides
|
|
82
|
+
if args.source is not None:
|
|
83
|
+
config.source = args.source
|
|
84
|
+
if args.conf is not None:
|
|
85
|
+
config.conf_threshold = args.conf
|
|
86
|
+
if args.frame_skip is not None:
|
|
87
|
+
config.frame_skip = args.frame_skip
|
|
88
|
+
|
|
89
|
+
EdgeRuntime = _load_runtime_class()
|
|
90
|
+
runtime = EdgeRuntime(config)
|
|
91
|
+
runtime.run()
|
|
92
|
+
return 0
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
if __name__ == "__main__":
|
|
96
|
+
sys.exit(main())
|
seavision/run_export.py
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CLI entry point for model export and edge artifact creation.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
seavision-export --weights models/fish.pt --output ./deployment
|
|
6
|
+
seavision-export --weights models/fish.pt --imgsz 320 --target onnx
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import argparse
|
|
10
|
+
import logging
|
|
11
|
+
import sys
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def parse_args():
|
|
17
|
+
"""Parse command-line arguments."""
|
|
18
|
+
parser = argparse.ArgumentParser(
|
|
19
|
+
prog="seavision-export",
|
|
20
|
+
description="Export a trained YOLO model for edge deployment",
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
parser.add_argument(
|
|
24
|
+
"--weights", required=True,
|
|
25
|
+
help="Path to the trained .pt weights file",
|
|
26
|
+
)
|
|
27
|
+
parser.add_argument(
|
|
28
|
+
"--output", default="./export",
|
|
29
|
+
help="Output directory for exported model and edge artifact (default: ./export)",
|
|
30
|
+
)
|
|
31
|
+
parser.add_argument(
|
|
32
|
+
"--target", default="onnx",
|
|
33
|
+
choices=["onnx", "tflite", "ncnn", "torchscript"],
|
|
34
|
+
help="Export target format (default: onnx)",
|
|
35
|
+
)
|
|
36
|
+
parser.add_argument(
|
|
37
|
+
"--imgsz", type=int, default=640,
|
|
38
|
+
help="Input image size (default: 640)",
|
|
39
|
+
)
|
|
40
|
+
parser.add_argument(
|
|
41
|
+
"--half", action="store_true",
|
|
42
|
+
help="Use float16 quantisation",
|
|
43
|
+
)
|
|
44
|
+
parser.add_argument(
|
|
45
|
+
"--int8", action="store_true",
|
|
46
|
+
help="Use int8 quantisation",
|
|
47
|
+
)
|
|
48
|
+
parser.add_argument(
|
|
49
|
+
"--no-validate", action="store_true",
|
|
50
|
+
help="Skip export validation",
|
|
51
|
+
)
|
|
52
|
+
parser.add_argument(
|
|
53
|
+
"--device", default="cpu",
|
|
54
|
+
help="Device for export (default: cpu)",
|
|
55
|
+
)
|
|
56
|
+
parser.add_argument(
|
|
57
|
+
"--conf", type=float, default=0.25,
|
|
58
|
+
help="Default confidence threshold for edge config (default: 0.25)",
|
|
59
|
+
)
|
|
60
|
+
parser.add_argument(
|
|
61
|
+
"--frame-skip", type=int, default=1,
|
|
62
|
+
help="Default frame skip for edge config (default: 1)",
|
|
63
|
+
)
|
|
64
|
+
parser.add_argument(
|
|
65
|
+
"-v", "--verbose", action="store_true",
|
|
66
|
+
help="Enable debug logging",
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
return parser.parse_args()
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def main() -> int:
|
|
73
|
+
"""Main entry point."""
|
|
74
|
+
args = parse_args()
|
|
75
|
+
|
|
76
|
+
logging.basicConfig(
|
|
77
|
+
level=logging.DEBUG if args.verbose else logging.INFO,
|
|
78
|
+
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
from seavision.engine.export import (
|
|
82
|
+
ExportConfig,
|
|
83
|
+
ExportTarget,
|
|
84
|
+
ModelExporter,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# Map string target to enum
|
|
88
|
+
target_map = {
|
|
89
|
+
"onnx": ExportTarget.ONNX,
|
|
90
|
+
"tflite": ExportTarget.TFLITE,
|
|
91
|
+
"ncnn": ExportTarget.NCNN,
|
|
92
|
+
"torchscript": ExportTarget.TORCHSCRIPT,
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
config = ExportConfig(
|
|
96
|
+
weights_path=args.weights,
|
|
97
|
+
output_dir=args.output,
|
|
98
|
+
target=target_map[args.target],
|
|
99
|
+
imgsz=args.imgsz,
|
|
100
|
+
half=args.half,
|
|
101
|
+
int8=args.int8,
|
|
102
|
+
validate=not args.no_validate,
|
|
103
|
+
device=args.device,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
# --- Export ---
|
|
107
|
+
print(f"Exporting {args.weights} to {args.target.upper()}...")
|
|
108
|
+
exporter = ModelExporter(config)
|
|
109
|
+
result = exporter.export()
|
|
110
|
+
|
|
111
|
+
if not result.success:
|
|
112
|
+
print(f"\nERROR: Export failed: {result.error}")
|
|
113
|
+
return 1
|
|
114
|
+
|
|
115
|
+
print(f"\nExport successful:")
|
|
116
|
+
print(f" Model: {result.exported_path}")
|
|
117
|
+
print(f" Size: {result.model_size_mb:.1f} MB")
|
|
118
|
+
print(f" Classes: {result.num_classes}")
|
|
119
|
+
if result.class_names:
|
|
120
|
+
names = ", ".join(
|
|
121
|
+
f"{k}={v}" for k, v in sorted(result.class_names.items())[:10]
|
|
122
|
+
)
|
|
123
|
+
if len(result.class_names) > 10:
|
|
124
|
+
names += f", ... ({len(result.class_names)} total)"
|
|
125
|
+
print(f" Labels: {names}")
|
|
126
|
+
|
|
127
|
+
if result.validation:
|
|
128
|
+
status = "PASSED" if result.validation.passed else "FAILED"
|
|
129
|
+
print(f" Validation: {status}")
|
|
130
|
+
print(f" {result.validation.details}")
|
|
131
|
+
|
|
132
|
+
from pathlib import Path
|
|
133
|
+
from seavision.edge.bundle import ArtifactBuilder
|
|
134
|
+
from seavision.edge.config import EdgeConfig
|
|
135
|
+
|
|
136
|
+
exported_path = Path(result.exported_path)
|
|
137
|
+
if exported_path.is_dir():
|
|
138
|
+
metadata_path = exported_path / "export_metadata.json"
|
|
139
|
+
else:
|
|
140
|
+
metadata_path = exported_path.with_suffix(".json")
|
|
141
|
+
|
|
142
|
+
if not metadata_path.exists():
|
|
143
|
+
print(f"\nWARNING: Metadata file not found at {metadata_path}")
|
|
144
|
+
print("Skipping artifact creation.")
|
|
145
|
+
return 0
|
|
146
|
+
|
|
147
|
+
edge_config = EdgeConfig(
|
|
148
|
+
conf_threshold=args.conf,
|
|
149
|
+
frame_skip=args.frame_skip,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
artifact_dir = Path(args.output) / "artifact"
|
|
153
|
+
print(f"\nCreating edge artifact: {artifact_dir}")
|
|
154
|
+
|
|
155
|
+
builder = ArtifactBuilder(
|
|
156
|
+
exported_model_path=result.exported_path,
|
|
157
|
+
export_metadata_path=str(metadata_path),
|
|
158
|
+
output_dir=str(artifact_dir),
|
|
159
|
+
edge_config=edge_config,
|
|
160
|
+
)
|
|
161
|
+
artifact_path = builder.build()
|
|
162
|
+
|
|
163
|
+
print(f"\nArtifact created: {artifact_path}")
|
|
164
|
+
print("\nDeployment instructions:")
|
|
165
|
+
print(f" 1. Copy the '{artifact_dir.name}' directory to your Pi:")
|
|
166
|
+
print(f" scp -r {artifact_dir} pi@<pi-ip>:~/")
|
|
167
|
+
print(" 2. Install or update the SeaVision wheel on the Pi:")
|
|
168
|
+
print(" python3 -m pip install '<seavision-wheel>[edge]'")
|
|
169
|
+
print(" 3. Run the detector:")
|
|
170
|
+
print(f" seavision-edge --artifact-dir ~/{artifact_dir.name}")
|
|
171
|
+
|
|
172
|
+
return 0
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
if __name__ == "__main__":
|
|
176
|
+
sys.exit(main())
|