sef 0.2.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.
- sef/__init__.py +62 -0
- sef/__main__.py +5 -0
- sef/api/__init__.py +27 -0
- sef/api/config.py +159 -0
- sef/api/decorators.py +143 -0
- sef/api/function_adapters.py +129 -0
- sef/api/orchestrator.py +203 -0
- sef/api/pipeline.py +300 -0
- sef/api/registry.py +65 -0
- sef/api/stage_refs.py +211 -0
- sef/builtin/Main.py +83 -0
- sef/builtin/__init__.py +28 -0
- sef/builtin/_optional_dependencies.py +71 -0
- sef/builtin/analyzers/ArUco/ArucoMarkerDisplacementAnalyzer.py +159 -0
- sef/builtin/analyzers/ArUco/ArucoMarkerRelativeMotionAnalyzer.py +170 -0
- sef/builtin/analyzers/ArUco/__init__.py +0 -0
- sef/builtin/analyzers/COCO_pose/COCOPoseStreamAnalyzer.py +152 -0
- sef/builtin/analyzers/COCO_pose/__init__.py +0 -0
- sef/builtin/analyzers/NoAnalyzer.py +34 -0
- sef/builtin/analyzers/multiple_tracker/MultiObjectBarrierCountingAnalyzer.py +93 -0
- sef/builtin/analyzers/multiple_tracker/MultipleDistanceAnalyzer.py +61 -0
- sef/builtin/analyzers/multiple_tracker/__init__.py +0 -0
- sef/builtin/analyzers/optical_flow/DenseOpticalFlowVectorFieldAnalyzer.py +80 -0
- sef/builtin/analyzers/optical_flow/SparseOpticalFlowTrajectoryAnalyzer.py +81 -0
- sef/builtin/analyzers/optical_flow/__init__.py +0 -0
- sef/builtin/analyzers/playback/TrackingPlaybackAnalyzer.py +115 -0
- sef/builtin/analyzers/playback/__init__.py +0 -0
- sef/builtin/analyzers/single_tracker/HoriziontalPositionAnalyzer.py +41 -0
- sef/builtin/analyzers/single_tracker/HorizontalFrequencyAnalyzer.py +53 -0
- sef/builtin/analyzers/single_tracker/HorizontalVelocityAnalyzer.py +60 -0
- sef/builtin/analyzers/single_tracker/VerticalFrequencyAnalyzer.py +53 -0
- sef/builtin/analyzers/single_tracker/VerticalPositionAnalyzer.py +41 -0
- sef/builtin/analyzers/single_tracker/VerticalPositionStreamAnalyzer.py +78 -0
- sef/builtin/analyzers/single_tracker/VerticalVelocityAnalyzer.py +60 -0
- sef/builtin/analyzers/single_tracker/__init__.py +0 -0
- sef/builtin/branching_rules/NewTrackBranchingRule.py +57 -0
- sef/builtin/exporters/IntermediateFrameArtifactExporter.py +98 -0
- sef/builtin/exporters/OpenCVFrameBufferVideoExporter.py +179 -0
- sef/builtin/frame_extractors/OpenCVBufferedFrameExtractor.py +104 -0
- sef/builtin/frame_extractors/OpenCVWebcamFrameExtractor.py +125 -0
- sef/builtin/frame_processors/ColorStabilizationFrameProcessor.py +786 -0
- sef/builtin/frame_processors/DynamicObjectRemovalFrameProcessor.py +3 -0
- sef/builtin/frame_processors/OpenCV/OpenCVBackgroundReplacementFrameProcessor.py +79 -0
- sef/builtin/frame_processors/OpenCV/OpenCVBackgroundSubtractionFrameProcessor.py +93 -0
- sef/builtin/frame_processors/OpenCV/OpenCVDynamicBackgroundReplacementFrameProcessor.py +120 -0
- sef/builtin/frame_processors/OpenCV/OpenCVDynamicInpaintFrameProcessor.py +134 -0
- sef/builtin/frame_processors/OpenCV/OpenCVGrayFrameProcessor.py +24 -0
- sef/builtin/frame_processors/OpenCV/OpenCVHistogramEqualizationFrameProcessor.py +34 -0
- sef/builtin/frame_processors/OpenCV/OpenCVInpaintFrameProcessor.py +75 -0
- sef/builtin/frame_processors/OpenCV/OpenCVResizeFrameProcessor.py +39 -0
- sef/builtin/frame_processors/OpenCV/OpenCVRotateFrameProcessor.py +49 -0
- sef/builtin/frame_processors/OpenCV/OpenCVZoomFrameProcessor.py +56 -0
- sef/builtin/frame_processors/OpenCV/__init__.py +0 -0
- sef/builtin/frame_processors/RealtimeFrameTapProcessor.py +88 -0
- sef/builtin/frame_processors/SmoothingFrameProcessor.py +61 -0
- sef/builtin/frame_processors/__init__.py +0 -0
- sef/builtin/frame_processors/dynamic_object_removal/__init__.py +34 -0
- sef/builtin/frame_processors/dynamic_object_removal/background_estimator.py +98 -0
- sef/builtin/frame_processors/dynamic_object_removal/config.py +116 -0
- sef/builtin/frame_processors/dynamic_object_removal/foreground_mask_extractor.py +56 -0
- sef/builtin/frame_processors/dynamic_object_removal/mask_refiner.py +93 -0
- sef/builtin/frame_processors/dynamic_object_removal/processor.py +374 -0
- sef/builtin/frame_processors/dynamic_object_removal/region_reconstructor.py +42 -0
- sef/builtin/frame_processors/motion_magnification/PhaseMagnificationFrameProcessor.py +488 -0
- sef/builtin/frame_processors/motion_magnification/__init__.py +0 -0
- sef/builtin/live_analyzers/LiveVerticalPositionAnalyzer.py +46 -0
- sef/builtin/live_analyzers/__init__.py +0 -0
- sef/builtin/registry.py +196 -0
- sef/builtin/retry_policies/ExponentialBackoffRetryPolicy.py +53 -0
- sef/builtin/retry_policies/FixedRetryPolicy.py +30 -0
- sef/builtin/retry_policies/NoRetryPolicy.py +3 -0
- sef/builtin/retry_policies/__init__.py +9 -0
- sef/builtin/signal_cleaners/ArUco/ArucoTemporalStabilizerCleaner.py +242 -0
- sef/builtin/signal_cleaners/ArUco/__init__.py +0 -0
- sef/builtin/signal_cleaners/COCO_pose/COCOSkeletonNormalizationSignalCleaner.py +109 -0
- sef/builtin/signal_cleaners/COCO_pose/__init__.py +0 -0
- sef/builtin/signal_cleaners/__init__.py +0 -0
- sef/builtin/signal_cleaners/optical_flow/OpticalFlowOutlierCleaner.py +76 -0
- sef/builtin/signal_cleaners/optical_flow/__init__.py +0 -0
- sef/builtin/signal_cleaners/single_tracker/MovingAverageCleaner.py +46 -0
- sef/builtin/signal_cleaners/single_tracker/MovingAverageStreamSignalCleaner.py +72 -0
- sef/builtin/signal_cleaners/single_tracker/OutlierRejectionCleaner.py +78 -0
- sef/builtin/signal_cleaners/single_tracker/SignalWidenerCleaner.py +43 -0
- sef/builtin/signal_cleaners/single_tracker/__init__.py +0 -0
- sef/builtin/signal_extractors/ArucoMarkerSignalExtractor.py +721 -0
- sef/builtin/signal_extractors/NoSignalExtractor.py +49 -0
- sef/builtin/signal_extractors/OpenCVBufferedSignalExtractor.py +147 -0
- sef/builtin/signal_extractors/OpenCVDenseOpticalFlowSignalExtractor.py +133 -0
- sef/builtin/signal_extractors/OpenCVMultiManualSignalExtractor.py +157 -0
- sef/builtin/signal_extractors/OpenCVMultiObjectSignalExtractor.py +456 -0
- sef/builtin/signal_extractors/OpenCVSparseOpticalFlowSignalExtractor.py +197 -0
- sef/builtin/signal_extractors/OpenCVStreamSignalExtractor.py +162 -0
- sef/builtin/signal_extractors/YOLOSkeletonCOCOStreamSignalExtractor.py +170 -0
- sef/builtin/visualizers/ArUco/ArucoAnnotatedVideoVisualizer.py +129 -0
- sef/builtin/visualizers/ArUco/__init__.py +0 -0
- sef/builtin/visualizers/COCO_pose/COCOPoseFrameRenderer.py +167 -0
- sef/builtin/visualizers/COCO_pose/OpenCVCOCOPoseRealtimeVisualizer.py +184 -0
- sef/builtin/visualizers/COCO_pose/OpenCVCOCOTennisPoseRealtimeVisualizer.py +192 -0
- sef/builtin/visualizers/COCO_pose/RealtimeCOCOPoseFrameVisualizer.py +118 -0
- sef/builtin/visualizers/COCO_pose/__init__.py +0 -0
- sef/builtin/visualizers/Matplotlib/MatplotlibArtifactVisualizer.py +67 -0
- sef/builtin/visualizers/Matplotlib/MatplotlibArucoMotionVisualizer.py +178 -0
- sef/builtin/visualizers/Matplotlib/MatplotlibFunctionStreamVisualizer.py +111 -0
- sef/builtin/visualizers/Matplotlib/MatplotlibFunctionVisualizer.py +68 -0
- sef/builtin/visualizers/Matplotlib/MatplotlibHeatmapVisualizer.py +86 -0
- sef/builtin/visualizers/Matplotlib/MatplotlibHistogramVisualizer.py +57 -0
- sef/builtin/visualizers/Matplotlib/MatplotlibTrajectoryVisualizer.py +83 -0
- sef/builtin/visualizers/Matplotlib/MatplotlibVectorFieldVisualizer.py +93 -0
- sef/builtin/visualizers/Matplotlib/__init__.py +0 -0
- sef/builtin/visualizers/TrackingVideoVisualizer.py +652 -0
- sef/builtin/visualizers/__init__.py +0 -0
- sef/builtin/visualizers/intermediate_frames/IntermediateFramesGridVisualizer.py +94 -0
- sef/builtin/visualizers/intermediate_frames/IntermediateFramesVisualizer.py +141 -0
- sef/builtin/visualizers/intermediate_frames/__init__.py +0 -0
- sef/cli/__init__.py +5 -0
- sef/cli/__main__.py +5 -0
- sef/cli/commands.py +1551 -0
- sef/cli/output.py +97 -0
- sef/core/__init__.py +63 -0
- sef/core/_lazy_exports.py +31 -0
- sef/core/artifacts/Frame.py +38 -0
- sef/core/artifacts/Signal.py +18 -0
- sef/core/artifacts/__init__.py +162 -0
- sef/core/artifacts/buffer/DataBuffer.py +154 -0
- sef/core/artifacts/buffer/FrameBuffer.py +148 -0
- sef/core/artifacts/buffer/SignalBuffer.py +172 -0
- sef/core/artifacts/buffer/__init__.py +36 -0
- sef/core/artifacts/data/ArucoDisplacementData.py +238 -0
- sef/core/artifacts/data/ArucoRelativeMotionData.py +38 -0
- sef/core/artifacts/data/COCOPoseFrameData.py +31 -0
- sef/core/artifacts/data/COCOPoseTennisFrameData.py +32 -0
- sef/core/artifacts/data/CategoryData.py +24 -0
- sef/core/artifacts/data/NoData.py +10 -0
- sef/core/artifacts/data/TrackingPlaybackData.py +66 -0
- sef/core/artifacts/data/TrajectoryData.py +23 -0
- sef/core/artifacts/data/TwoDimGraphData.py +19 -0
- sef/core/artifacts/data/TwoDimPointData.py +19 -0
- sef/core/artifacts/data/VectorFieldGraphData.py +26 -0
- sef/core/artifacts/data/__init__.py +89 -0
- sef/core/artifacts/intermediate_frame/IntermediateFrameArtifacts.py +111 -0
- sef/core/artifacts/intermediate_frame/IntermediateFrameComposition.py +336 -0
- sef/core/artifacts/intermediate_frame/__init__.py +67 -0
- sef/core/artifacts/mask/MaskArtifacts.py +433 -0
- sef/core/artifacts/mask/MaskOperations.py +128 -0
- sef/core/artifacts/mask/__init__.py +47 -0
- sef/core/artifacts/signal_sample/ArucoMarkerSignalSample.py +49 -0
- sef/core/artifacts/signal_sample/BoxSignalSample.py +20 -0
- sef/core/artifacts/signal_sample/COCOSkeletonSignalSample.py +32 -0
- sef/core/artifacts/signal_sample/DenseOpticalFlowSignalSample.py +35 -0
- sef/core/artifacts/signal_sample/MultiManualSignalSample.py +15 -0
- sef/core/artifacts/signal_sample/MultiObjectSignalSample.py +34 -0
- sef/core/artifacts/signal_sample/SparseOpticalFlowSignalSample.py +25 -0
- sef/core/artifacts/signal_sample/__init__.py +64 -0
- sef/core/enum/FrameRotation.py +7 -0
- sef/core/errors.py +276 -0
- sef/core/events/Event.py +54 -0
- sef/core/events/EventBus.py +85 -0
- sef/core/events/PipelineEvent.py +114 -0
- sef/core/events/PipelineLifecycleEvent.py +66 -0
- sef/core/events/__init__.py +49 -0
- sef/core/interfaces/BufferContracts.py +84 -0
- sef/core/interfaces/IAnalyzer.py +40 -0
- sef/core/interfaces/IData.py +5 -0
- sef/core/interfaces/IEventEmitter.py +110 -0
- sef/core/interfaces/IFrameBufferProcessor.py +44 -0
- sef/core/interfaces/IFrameExporter.py +71 -0
- sef/core/interfaces/IFrameExtractor.py +48 -0
- sef/core/interfaces/ILiveAnalyzer.py +19 -0
- sef/core/interfaces/ISignal.py +48 -0
- sef/core/interfaces/ISignalCleaner.py +37 -0
- sef/core/interfaces/ISignalExtractor.py +41 -0
- sef/core/interfaces/ISignalSample.py +30 -0
- sef/core/interfaces/ISingleFrameProcessor.py +35 -0
- sef/core/interfaces/IVisualizer.py +48 -0
- sef/core/interfaces/StageCapabilities.py +71 -0
- sef/core/interfaces/StreamingContracts.py +138 -0
- sef/core/interfaces/__init__.py +96 -0
- sef/core/interfaces/pipeline/IBranchingRule.py +73 -0
- sef/core/interfaces/pipeline/IEventBus.py +55 -0
- sef/core/interfaces/pipeline/IPipelineFactory.py +36 -0
- sef/core/interfaces/pipeline/IPipelineMonitor.py +55 -0
- sef/core/interfaces/pipeline/IPipelineOutputStore.py +29 -0
- sef/core/interfaces/pipeline/IPipelineRunner.py +52 -0
- sef/core/interfaces/pipeline/IPipelineValidator.py +22 -0
- sef/core/interfaces/pipeline/IRetryPolicy.py +55 -0
- sef/core/interfaces/pipeline/__init__.py +66 -0
- sef/core/pipeline/AnalysisSegmentExecutor.py +359 -0
- sef/core/pipeline/BranchingCoordinator.py +110 -0
- sef/core/pipeline/ConfigPipelineBuilder.py +403 -0
- sef/core/pipeline/DefaultPipelineFactory.py +31 -0
- sef/core/pipeline/FluentPipelineBuilder.py +176 -0
- sef/core/pipeline/FrameProcessingStage.py +78 -0
- sef/core/pipeline/FrameSegmentExecutor.py +303 -0
- sef/core/pipeline/InMemoryPipelineMonitor.py +91 -0
- sef/core/pipeline/InMemoryPipelineOutputStore.py +33 -0
- sef/core/pipeline/IntermediateFrameCapture.py +210 -0
- sef/core/pipeline/LatencyPolicy.py +331 -0
- sef/core/pipeline/NoRetryPolicy.py +17 -0
- sef/core/pipeline/Pipeline.py +152 -0
- sef/core/pipeline/PipelineBoundaryMaterializer.py +125 -0
- sef/core/pipeline/PipelineBuffers.py +48 -0
- sef/core/pipeline/PipelineCodeExporter.py +102 -0
- sef/core/pipeline/PipelineComponentCapabilities.py +89 -0
- sef/core/pipeline/PipelineConfigExporter.py +533 -0
- sef/core/pipeline/PipelineConfigVersioning.py +226 -0
- sef/core/pipeline/PipelineContext.py +210 -0
- sef/core/pipeline/PipelineErrors.py +49 -0
- sef/core/pipeline/PipelineEventInjector.py +57 -0
- sef/core/pipeline/PipelineExecutionLookahead.py +64 -0
- sef/core/pipeline/PipelineExecutionPlan.py +144 -0
- sef/core/pipeline/PipelineExecutionPlanner.py +285 -0
- sef/core/pipeline/PipelineExecutionPolicy.py +226 -0
- sef/core/pipeline/PipelineExecutionResources.py +45 -0
- sef/core/pipeline/PipelineExecutionResult.py +25 -0
- sef/core/pipeline/PipelineExportUtils.py +168 -0
- sef/core/pipeline/PipelineOrchestrator.py +188 -0
- sef/core/pipeline/PipelineOutputAssembler.py +94 -0
- sef/core/pipeline/PipelineRunSnapshot.py +33 -0
- sef/core/pipeline/PipelineRuntimeState.py +40 -0
- sef/core/pipeline/PipelineStageExecutor.py +44 -0
- sef/core/pipeline/SegmentedPipelineExecutor.py +147 -0
- sef/core/pipeline/SignalSegmentExecutor.py +172 -0
- sef/core/pipeline/SingleFrameProcessorAdapter.py +208 -0
- sef/core/pipeline/StreamRuntimeConfig.py +122 -0
- sef/core/pipeline/ThreadedPipelineRunner.py +322 -0
- sef/core/pipeline/VisualizationExecutor.py +178 -0
- sef/core/pipeline/VisualizerBinding.py +53 -0
- sef/core/pipeline/__init__.py +99 -0
- sef/core/plugins/PluginRegistry.py +348 -0
- sef/core/plugins/__init__.py +19 -0
- sef/core/pose/COCOSkeletonNormalizer.py +164 -0
- sef/core/pose/__init__.py +11 -0
- sef/core/realtime/IRealtimeFrameSink.py +21 -0
- sef/core/realtime/LatestRealtimeFrameStore.py +132 -0
- sef/core/realtime/NullRealtimeFrameSink.py +19 -0
- sef/core/realtime/RealtimeFrame.py +73 -0
- sef/core/realtime/__init__.py +24 -0
- sef/core/utils/OpenCVBarrierSelector.py +144 -0
- sef/core/utils/OpenCVDisplayUtils.py +64 -0
- sef/core/utils/OpenCVMaskSelector.py +206 -0
- sef/core/utils/OpenCVMultiStartBoxSelector.py +96 -0
- sef/core/utils/OpenCVStartBoxSelector.py +74 -0
- sef/core/visualization/PipelineOutputs.py +52 -0
- sef/core/visualization/PipelineRunMetadata.py +29 -0
- sef/core/visualization/VisualArtifact.py +259 -0
- sef/core/visualization/VisualizationContext.py +30 -0
- sef/core/visualization/__init__.py +65 -0
- sef-0.2.0.dist-info/METADATA +52 -0
- sef-0.2.0.dist-info/RECORD +253 -0
- sef-0.2.0.dist-info/WHEEL +5 -0
- sef-0.2.0.dist-info/entry_points.txt +2 -0
- sef-0.2.0.dist-info/licenses/LICENSE +13 -0
- sef-0.2.0.dist-info/top_level.txt +1 -0
sef/__init__.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""Public Pythonic entrypoint for SEF.
|
|
2
|
+
|
|
3
|
+
Use this module for concise workflows:
|
|
4
|
+
|
|
5
|
+
```python
|
|
6
|
+
import sef
|
|
7
|
+
|
|
8
|
+
outputs = (
|
|
9
|
+
sef.pipeline("quickstart")
|
|
10
|
+
.frames("demo_frames", frame_count=3)
|
|
11
|
+
.signals("demo_signals")
|
|
12
|
+
.analyze("sample_count")
|
|
13
|
+
.visualize("summary_text")
|
|
14
|
+
.run()
|
|
15
|
+
)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Advanced users can still import stable lower-level contracts from
|
|
19
|
+
``sef.core`` or ``sef.core``.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from sef.api import (
|
|
23
|
+
OrchestratorFacade,
|
|
24
|
+
PipelineFacade,
|
|
25
|
+
analyzer,
|
|
26
|
+
cleaner,
|
|
27
|
+
default_registry,
|
|
28
|
+
frame_extractor,
|
|
29
|
+
from_config,
|
|
30
|
+
load_config,
|
|
31
|
+
normalize_config,
|
|
32
|
+
orchestrator,
|
|
33
|
+
pipeline,
|
|
34
|
+
processor,
|
|
35
|
+
register_user_plugin,
|
|
36
|
+
signal_extractor,
|
|
37
|
+
video,
|
|
38
|
+
visualizer,
|
|
39
|
+
webcam,
|
|
40
|
+
)
|
|
41
|
+
from sef.core import PipelineExecutionError
|
|
42
|
+
|
|
43
|
+
__all__ = [
|
|
44
|
+
"OrchestratorFacade",
|
|
45
|
+
"PipelineFacade",
|
|
46
|
+
"PipelineExecutionError",
|
|
47
|
+
"analyzer",
|
|
48
|
+
"cleaner",
|
|
49
|
+
"default_registry",
|
|
50
|
+
"frame_extractor",
|
|
51
|
+
"from_config",
|
|
52
|
+
"load_config",
|
|
53
|
+
"normalize_config",
|
|
54
|
+
"orchestrator",
|
|
55
|
+
"pipeline",
|
|
56
|
+
"processor",
|
|
57
|
+
"register_user_plugin",
|
|
58
|
+
"signal_extractor",
|
|
59
|
+
"video",
|
|
60
|
+
"visualizer",
|
|
61
|
+
"webcam",
|
|
62
|
+
]
|
sef/__main__.py
ADDED
sef/api/__init__.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""High-level Pythonic SEF API."""
|
|
2
|
+
|
|
3
|
+
from sef.api.config import load_config, normalize_config
|
|
4
|
+
from sef.api.decorators import analyzer, cleaner, frame_extractor, processor, signal_extractor, visualizer
|
|
5
|
+
from sef.api.orchestrator import OrchestratorFacade, orchestrator
|
|
6
|
+
from sef.api.pipeline import PipelineFacade, from_config, pipeline, video, webcam
|
|
7
|
+
from sef.api.registry import default_registry, register_user_plugin
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"OrchestratorFacade",
|
|
11
|
+
"PipelineFacade",
|
|
12
|
+
"analyzer",
|
|
13
|
+
"cleaner",
|
|
14
|
+
"default_registry",
|
|
15
|
+
"frame_extractor",
|
|
16
|
+
"from_config",
|
|
17
|
+
"load_config",
|
|
18
|
+
"normalize_config",
|
|
19
|
+
"orchestrator",
|
|
20
|
+
"pipeline",
|
|
21
|
+
"processor",
|
|
22
|
+
"register_user_plugin",
|
|
23
|
+
"signal_extractor",
|
|
24
|
+
"video",
|
|
25
|
+
"visualizer",
|
|
26
|
+
"webcam",
|
|
27
|
+
]
|
sef/api/config.py
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any, Mapping
|
|
6
|
+
|
|
7
|
+
from sef.core.pipeline.PipelineConfigVersioning import normalize_pipeline_config
|
|
8
|
+
from sef.core.pipeline.PipelineErrors import ConfigSchemaError
|
|
9
|
+
|
|
10
|
+
_PIPELINE_STAGE_KEYS = frozenset(
|
|
11
|
+
{
|
|
12
|
+
"frame_extractor",
|
|
13
|
+
"frame_processors",
|
|
14
|
+
"frame_cleaners",
|
|
15
|
+
"signal_extractor",
|
|
16
|
+
"signal_cleaners",
|
|
17
|
+
"analyzers",
|
|
18
|
+
"visualizers",
|
|
19
|
+
"intermediate_frames",
|
|
20
|
+
"runtime",
|
|
21
|
+
}
|
|
22
|
+
)
|
|
23
|
+
_FRAME_SOURCE_CONFIG_KEYS = ("resize", "stride", "max_frames")
|
|
24
|
+
_TRACKING_SIGNAL_EXTRACTORS = frozenset(
|
|
25
|
+
{
|
|
26
|
+
"opencv_tracker",
|
|
27
|
+
"opencv_stream_tracker",
|
|
28
|
+
"opencv_multi_tracker",
|
|
29
|
+
}
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def load_config(path: str | Path) -> dict[str, Any]:
|
|
34
|
+
"""
|
|
35
|
+
Load a SEF pipeline config from JSON or YAML and return the canonical schema.
|
|
36
|
+
|
|
37
|
+
The returned mapping is safe to pass to ``sef.from_config`` or
|
|
38
|
+
``ConfigPipelineBuilder``. YAML support intentionally lives here instead of
|
|
39
|
+
in the execution core so the runtime remains independent from file formats.
|
|
40
|
+
"""
|
|
41
|
+
config_path = Path(path)
|
|
42
|
+
raw_text = config_path.read_text(encoding="utf-8")
|
|
43
|
+
suffix = config_path.suffix.lower()
|
|
44
|
+
if suffix == ".json":
|
|
45
|
+
loaded = json.loads(raw_text)
|
|
46
|
+
elif suffix in {".yaml", ".yml"}:
|
|
47
|
+
loaded = _load_yaml(raw_text, config_path)
|
|
48
|
+
else:
|
|
49
|
+
raise ConfigSchemaError(
|
|
50
|
+
f"Unsupported config file extension '{config_path.suffix}'. Use .json, .yaml, or .yml.",
|
|
51
|
+
path=str(config_path),
|
|
52
|
+
)
|
|
53
|
+
return normalize_config(loaded)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def normalize_config(config: Mapping[str, Any]) -> dict[str, Any]:
|
|
57
|
+
"""
|
|
58
|
+
Normalize user-facing config shapes into the public SEF schema.
|
|
59
|
+
|
|
60
|
+
This is the single compatibility entry point for CLI, UI adapters, and
|
|
61
|
+
facade-based config execution. It accepts both a full top-level config and a
|
|
62
|
+
bare pipeline section, then applies public schema version normalization.
|
|
63
|
+
"""
|
|
64
|
+
root = _ensure_root_config(config)
|
|
65
|
+
versioned = normalize_pipeline_config(root).source_config()
|
|
66
|
+
pipeline = dict(versioned["pipeline"])
|
|
67
|
+
_move_frame_source_options(pipeline)
|
|
68
|
+
_inject_frame_source_path(pipeline)
|
|
69
|
+
versioned["pipeline"] = pipeline
|
|
70
|
+
return versioned
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _load_yaml(raw_text: str, config_path: Path) -> Any:
|
|
74
|
+
try:
|
|
75
|
+
import yaml
|
|
76
|
+
except ImportError as exc: # pragma: no cover - exercised only in broken installs
|
|
77
|
+
raise ConfigSchemaError(
|
|
78
|
+
"YAML config loading requires PyYAML. Install SEF with its declared runtime dependencies.",
|
|
79
|
+
path=str(config_path),
|
|
80
|
+
cause=exc,
|
|
81
|
+
) from exc
|
|
82
|
+
|
|
83
|
+
return yaml.safe_load(raw_text)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _ensure_root_config(config: Mapping[str, Any]) -> dict[str, Any]:
|
|
87
|
+
if not isinstance(config, Mapping):
|
|
88
|
+
raise ConfigSchemaError("'config' must be a mapping.", path="config")
|
|
89
|
+
root = dict(config)
|
|
90
|
+
if "pipeline" in root:
|
|
91
|
+
return root
|
|
92
|
+
if _PIPELINE_STAGE_KEYS.intersection(root):
|
|
93
|
+
return {"pipeline": root}
|
|
94
|
+
raise ConfigSchemaError("Missing required config section 'pipeline'.", path="pipeline")
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _move_frame_source_options(pipeline: dict[str, Any]) -> None:
|
|
98
|
+
frame_extractor = pipeline.get("frame_extractor")
|
|
99
|
+
if not isinstance(frame_extractor, Mapping):
|
|
100
|
+
return
|
|
101
|
+
|
|
102
|
+
entry = dict(frame_extractor)
|
|
103
|
+
params = entry.get("params", {})
|
|
104
|
+
if not isinstance(params, Mapping):
|
|
105
|
+
return
|
|
106
|
+
|
|
107
|
+
normalized_params = dict(params)
|
|
108
|
+
source_config = normalized_params.get("config", {})
|
|
109
|
+
normalized_source_config = dict(source_config) if isinstance(source_config, Mapping) else {}
|
|
110
|
+
|
|
111
|
+
moved = False
|
|
112
|
+
for key in _FRAME_SOURCE_CONFIG_KEYS:
|
|
113
|
+
if key in normalized_params:
|
|
114
|
+
normalized_source_config[key] = normalized_params.pop(key)
|
|
115
|
+
moved = True
|
|
116
|
+
|
|
117
|
+
if moved:
|
|
118
|
+
normalized_params["config"] = normalized_source_config
|
|
119
|
+
entry["params"] = normalized_params
|
|
120
|
+
pipeline["frame_extractor"] = entry
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _inject_frame_source_path(pipeline: dict[str, Any]) -> None:
|
|
124
|
+
frame_source_path = _frame_source_path(pipeline)
|
|
125
|
+
if frame_source_path is None:
|
|
126
|
+
return
|
|
127
|
+
|
|
128
|
+
signal_extractor = pipeline.get("signal_extractor")
|
|
129
|
+
if not isinstance(signal_extractor, Mapping):
|
|
130
|
+
return
|
|
131
|
+
extractor_name = str(signal_extractor.get("name", ""))
|
|
132
|
+
if extractor_name not in _TRACKING_SIGNAL_EXTRACTORS:
|
|
133
|
+
return
|
|
134
|
+
|
|
135
|
+
entry = dict(signal_extractor)
|
|
136
|
+
params = entry.get("params", {})
|
|
137
|
+
if not isinstance(params, Mapping):
|
|
138
|
+
return
|
|
139
|
+
|
|
140
|
+
normalized_params = dict(params)
|
|
141
|
+
extractor_config = normalized_params.get("config", {})
|
|
142
|
+
normalized_extractor_config = dict(extractor_config) if isinstance(extractor_config, Mapping) else {}
|
|
143
|
+
normalized_extractor_config.setdefault("source_path", frame_source_path)
|
|
144
|
+
normalized_params["config"] = normalized_extractor_config
|
|
145
|
+
entry["params"] = normalized_params
|
|
146
|
+
pipeline["signal_extractor"] = entry
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _frame_source_path(pipeline: Mapping[str, Any]) -> str | None:
|
|
150
|
+
frame_extractor = pipeline.get("frame_extractor")
|
|
151
|
+
if not isinstance(frame_extractor, Mapping):
|
|
152
|
+
return None
|
|
153
|
+
params = frame_extractor.get("params", {})
|
|
154
|
+
if not isinstance(params, Mapping):
|
|
155
|
+
return None
|
|
156
|
+
raw_path = params.get("path")
|
|
157
|
+
if not isinstance(raw_path, str) or not raw_path:
|
|
158
|
+
return None
|
|
159
|
+
return raw_path
|
sef/api/decorators.py
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Callable
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from sef.api.function_adapters import (
|
|
7
|
+
FunctionAnalyzer,
|
|
8
|
+
FunctionFrameExtractor,
|
|
9
|
+
FunctionFrameProcessor,
|
|
10
|
+
FunctionSignalCleaner,
|
|
11
|
+
FunctionSignalExtractor,
|
|
12
|
+
FunctionVisualizer,
|
|
13
|
+
)
|
|
14
|
+
from sef.api.registry import register_user_plugin
|
|
15
|
+
from sef.core.plugins import PluginCategory, PluginRegistry
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def frame_extractor(
|
|
19
|
+
name: str | Callable[..., Any] | None = None,
|
|
20
|
+
*,
|
|
21
|
+
registry: PluginRegistry | None = None,
|
|
22
|
+
):
|
|
23
|
+
"""Register a function as a frame extractor."""
|
|
24
|
+
return _decorator(
|
|
25
|
+
name,
|
|
26
|
+
registry=registry,
|
|
27
|
+
category=PluginCategory.FRAME_EXTRACTOR,
|
|
28
|
+
factory_builder=lambda function: (lambda **params: FunctionFrameExtractor(function, params)),
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def signal_extractor(
|
|
33
|
+
name: str | Callable[..., Any] | None = None,
|
|
34
|
+
*,
|
|
35
|
+
registry: PluginRegistry | None = None,
|
|
36
|
+
):
|
|
37
|
+
"""Register a function as a signal extractor."""
|
|
38
|
+
return _decorator(
|
|
39
|
+
name,
|
|
40
|
+
registry=registry,
|
|
41
|
+
category=PluginCategory.SIGNAL_EXTRACTOR,
|
|
42
|
+
factory_builder=lambda function: (lambda **params: FunctionSignalExtractor(function, params)),
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def processor(
|
|
47
|
+
name: str | Callable[..., Any] | None = None,
|
|
48
|
+
*,
|
|
49
|
+
registry: PluginRegistry | None = None,
|
|
50
|
+
accepts_frame: bool = False,
|
|
51
|
+
):
|
|
52
|
+
"""Register a function as a single-frame processor."""
|
|
53
|
+
return _decorator(
|
|
54
|
+
name,
|
|
55
|
+
registry=registry,
|
|
56
|
+
category=PluginCategory.SINGLE_FRAME_PROCESSOR,
|
|
57
|
+
factory_builder=lambda function: (
|
|
58
|
+
lambda accepts_frame=accepts_frame, **params: FunctionFrameProcessor(
|
|
59
|
+
function,
|
|
60
|
+
params,
|
|
61
|
+
accepts_frame=bool(accepts_frame),
|
|
62
|
+
)
|
|
63
|
+
),
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def cleaner(
|
|
68
|
+
name: str | Callable[..., Any] | None = None,
|
|
69
|
+
*,
|
|
70
|
+
registry: PluginRegistry | None = None,
|
|
71
|
+
):
|
|
72
|
+
"""Register a function as a signal cleaner."""
|
|
73
|
+
return _decorator(
|
|
74
|
+
name,
|
|
75
|
+
registry=registry,
|
|
76
|
+
category=PluginCategory.SIGNAL_CLEANER,
|
|
77
|
+
factory_builder=lambda function: (lambda **params: FunctionSignalCleaner(function, params)),
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def analyzer(
|
|
82
|
+
name: str | Callable[..., Any] | None = None,
|
|
83
|
+
*,
|
|
84
|
+
registry: PluginRegistry | None = None,
|
|
85
|
+
):
|
|
86
|
+
"""Register a function as an analyzer."""
|
|
87
|
+
return _decorator(
|
|
88
|
+
name,
|
|
89
|
+
registry=registry,
|
|
90
|
+
category=PluginCategory.ANALYZER,
|
|
91
|
+
factory_builder=lambda function: (lambda **params: FunctionAnalyzer(function, params)),
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def visualizer(
|
|
96
|
+
name: str | Callable[..., Any] | None = None,
|
|
97
|
+
*,
|
|
98
|
+
registry: PluginRegistry | None = None,
|
|
99
|
+
):
|
|
100
|
+
"""Register a function as a visualizer."""
|
|
101
|
+
return _decorator(
|
|
102
|
+
name,
|
|
103
|
+
registry=registry,
|
|
104
|
+
category=PluginCategory.VISUALIZER,
|
|
105
|
+
factory_builder=lambda function: (lambda **params: FunctionVisualizer(function, params)),
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _decorator(
|
|
110
|
+
name: str | Callable[..., Any] | None,
|
|
111
|
+
*,
|
|
112
|
+
registry: PluginRegistry | None,
|
|
113
|
+
category: PluginCategory,
|
|
114
|
+
factory_builder: Callable[[Callable[..., Any]], Callable[..., Any]],
|
|
115
|
+
):
|
|
116
|
+
if callable(name) and not isinstance(name, str):
|
|
117
|
+
function = name
|
|
118
|
+
_register(function.__name__, function, registry, category, factory_builder)
|
|
119
|
+
return function
|
|
120
|
+
|
|
121
|
+
def _wrap(function: Callable[..., Any]):
|
|
122
|
+
plugin_name = name or function.__name__
|
|
123
|
+
_register(str(plugin_name), function, registry, category, factory_builder)
|
|
124
|
+
return function
|
|
125
|
+
|
|
126
|
+
return _wrap
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _register(
|
|
130
|
+
name: str,
|
|
131
|
+
function: Callable[..., Any],
|
|
132
|
+
registry: PluginRegistry | None,
|
|
133
|
+
category: PluginCategory,
|
|
134
|
+
factory_builder: Callable[[Callable[..., Any]], Callable[..., Any]],
|
|
135
|
+
) -> None:
|
|
136
|
+
target = registry.register if registry is not None else register_user_plugin
|
|
137
|
+
target(
|
|
138
|
+
category,
|
|
139
|
+
name,
|
|
140
|
+
factory_builder(function),
|
|
141
|
+
f"Function plugin registered through @{category.value}.",
|
|
142
|
+
metadata={"source": "sef.decorator", "function": f"{function.__module__}.{function.__qualname__}"},
|
|
143
|
+
)
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import inspect
|
|
4
|
+
from collections.abc import Callable
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from sef.core.artifacts.buffer.FrameBuffer import FrameBuffer
|
|
8
|
+
from sef.core.artifacts.Frame import Frame
|
|
9
|
+
from sef.core.interfaces.IAnalyzer import IAnalyzer
|
|
10
|
+
from sef.core.interfaces.IData import IData
|
|
11
|
+
from sef.core.interfaces.IFrameExtractor import IFrameExtractor
|
|
12
|
+
from sef.core.interfaces.ISignal import ISignal
|
|
13
|
+
from sef.core.interfaces.ISignalCleaner import ISignalCleaner
|
|
14
|
+
from sef.core.interfaces.ISignalExtractor import ISignalExtractor
|
|
15
|
+
from sef.core.interfaces.ISingleFrameProcessor import ISingleFrameProcessor
|
|
16
|
+
from sef.core.interfaces.IVisualizer import IVisualizer
|
|
17
|
+
from sef.core.visualization.VisualArtifact import VisualArtifact
|
|
18
|
+
from sef.core.visualization.VisualizationContext import VisualizationContext
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class FunctionFrameExtractor(IFrameExtractor):
|
|
22
|
+
"""Adapt a plain callable into an ``IFrameExtractor`` plugin."""
|
|
23
|
+
|
|
24
|
+
def __init__(self, function: Callable[..., FrameBuffer], params: dict[str, Any], config: dict[str, Any] | None = None) -> None:
|
|
25
|
+
super().__init__(config)
|
|
26
|
+
self._function = function
|
|
27
|
+
self._params = dict(params)
|
|
28
|
+
|
|
29
|
+
def extract(self) -> FrameBuffer:
|
|
30
|
+
return self._function(**self._params)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class FunctionSignalExtractor(ISignalExtractor):
|
|
34
|
+
"""Adapt a plain callable into an ``ISignalExtractor`` plugin."""
|
|
35
|
+
|
|
36
|
+
def __init__(self, function: Callable[..., ISignal], params: dict[str, Any], config: dict[str, Any] | None = None) -> None:
|
|
37
|
+
super().__init__(config)
|
|
38
|
+
self._function = function
|
|
39
|
+
self._params = dict(params)
|
|
40
|
+
|
|
41
|
+
def extract(self, buffer: FrameBuffer) -> ISignal:
|
|
42
|
+
return self._function(buffer, **self._params)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class FunctionSignalCleaner(ISignalCleaner):
|
|
46
|
+
"""Adapt a plain callable into an ``ISignalCleaner`` plugin."""
|
|
47
|
+
|
|
48
|
+
def __init__(self, function: Callable[..., ISignal], params: dict[str, Any], config: dict[str, Any] | None = None) -> None:
|
|
49
|
+
super().__init__(config)
|
|
50
|
+
self._function = function
|
|
51
|
+
self._params = dict(params)
|
|
52
|
+
|
|
53
|
+
def clean(self, signal: ISignal) -> ISignal:
|
|
54
|
+
return self._function(signal, **self._params)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class FunctionAnalyzer(IAnalyzer):
|
|
58
|
+
"""Adapt a plain callable into an ``IAnalyzer`` plugin."""
|
|
59
|
+
|
|
60
|
+
def __init__(self, function: Callable[..., IData], params: dict[str, Any], config: dict[str, Any] | None = None) -> None:
|
|
61
|
+
super().__init__(config)
|
|
62
|
+
self._function = function
|
|
63
|
+
self._params = dict(params)
|
|
64
|
+
|
|
65
|
+
def analyze(self, signal: ISignal) -> IData:
|
|
66
|
+
return self._function(signal, **self._params)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class FunctionVisualizer(IVisualizer):
|
|
70
|
+
"""Adapt a plain callable into an ``IVisualizer`` plugin."""
|
|
71
|
+
|
|
72
|
+
def __init__(
|
|
73
|
+
self,
|
|
74
|
+
function: Callable[..., VisualArtifact | tuple[VisualArtifact, ...] | list[VisualArtifact]],
|
|
75
|
+
params: dict[str, Any],
|
|
76
|
+
config: dict[str, Any] | None = None,
|
|
77
|
+
) -> None:
|
|
78
|
+
super().__init__(config)
|
|
79
|
+
self._function = function
|
|
80
|
+
self._params = dict(params)
|
|
81
|
+
self._accepts_context = "context" in inspect.signature(function).parameters
|
|
82
|
+
|
|
83
|
+
def render(
|
|
84
|
+
self,
|
|
85
|
+
data: IData,
|
|
86
|
+
context: VisualizationContext | None = None,
|
|
87
|
+
) -> tuple[VisualArtifact, ...]:
|
|
88
|
+
if self._accepts_context:
|
|
89
|
+
result = self._function(data, context=context, **self._params)
|
|
90
|
+
else:
|
|
91
|
+
result = self._function(data, **self._params)
|
|
92
|
+
if isinstance(result, VisualArtifact):
|
|
93
|
+
return (result,)
|
|
94
|
+
return tuple(result)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class FunctionFrameProcessor(ISingleFrameProcessor):
|
|
98
|
+
"""
|
|
99
|
+
Adapt a plain callable into a single-frame processor.
|
|
100
|
+
|
|
101
|
+
By default the callable receives the frame image and may return either a
|
|
102
|
+
new image array or a complete ``Frame``. Set ``accepts_frame=True`` when
|
|
103
|
+
the callable needs access to indexes, timestamps, or metadata.
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
def __init__(
|
|
107
|
+
self,
|
|
108
|
+
function: Callable[..., Any],
|
|
109
|
+
params: dict[str, Any],
|
|
110
|
+
*,
|
|
111
|
+
accepts_frame: bool = False,
|
|
112
|
+
config: dict[str, Any] | None = None,
|
|
113
|
+
) -> None:
|
|
114
|
+
super().__init__(config)
|
|
115
|
+
self._function = function
|
|
116
|
+
self._params = dict(params)
|
|
117
|
+
self._accepts_frame = accepts_frame
|
|
118
|
+
|
|
119
|
+
def process(self, frame: Frame) -> Frame:
|
|
120
|
+
source = frame if self._accepts_frame else frame.image
|
|
121
|
+
result = self._function(source, **self._params)
|
|
122
|
+
if isinstance(result, Frame):
|
|
123
|
+
return result
|
|
124
|
+
return Frame(
|
|
125
|
+
image=result,
|
|
126
|
+
index=frame.index,
|
|
127
|
+
timestamp_seconds=frame.timestamp_seconds,
|
|
128
|
+
metadata=dict(frame.metadata),
|
|
129
|
+
)
|