rocket-welder-sdk 1.1.34.dev10__tar.gz → 1.1.34.dev11__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.
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/PKG-INFO +1 -1
- rocket_welder_sdk-1.1.34.dev11/VERSION +1 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/pyproject.toml +2 -2
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk/controllers.py +1 -1
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk/frame_metadata.py +2 -2
- rocket_welder_sdk-1.1.34.dev11/rocket_welder_sdk/high_level/__init__.py +52 -0
- rocket_welder_sdk-1.1.34.dev11/rocket_welder_sdk/high_level/client.py +262 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk/high_level/connection_strings.py +56 -55
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk/high_level/data_context.py +17 -11
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk/high_level/schema.py +44 -27
- rocket_welder_sdk-1.1.34.dev11/rocket_welder_sdk/high_level/transport_protocol.py +238 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk.egg-info/PKG-INFO +1 -1
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk.egg-info/SOURCES.txt +1 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/tests/test_high_level_api.py +136 -77
- rocket_welder_sdk-1.1.34.dev10/VERSION +0 -1
- rocket_welder_sdk-1.1.34.dev10/rocket_welder_sdk/high_level/__init__.py +0 -66
- rocket_welder_sdk-1.1.34.dev10/rocket_welder_sdk/high_level/transport_protocol.py +0 -166
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/MANIFEST.in +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/README.md +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/logo.png +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk/__init__.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk/bytes_size.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk/connection_string.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk/external_controls/__init__.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk/external_controls/contracts.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk/external_controls/contracts_old.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk/gst_metadata.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk/keypoints_protocol.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk/opencv_controller.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk/periodic_timer.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk/py.typed +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk/rocket_welder_client.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk/segmentation_result.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk/session_id.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk/transport/__init__.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk/transport/frame_sink.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk/transport/frame_source.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk/transport/nng_transport.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk/transport/stream_transport.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk/transport/tcp_transport.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk/transport/unix_socket_transport.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk/ui/__init__.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk/ui/controls.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk/ui/icons.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk/ui/ui_events_projection.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk/ui/ui_service.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk/ui/value_types.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk.egg-info/dependency_links.txt +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk.egg-info/requires.txt +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk.egg-info/top_level.txt +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/setup.cfg +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/setup.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/tests/test_bytes_size.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/tests/test_connection_string.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/tests/test_controllers.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/tests/test_external_controls_serialization.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/tests/test_external_controls_serialization_v2.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/tests/test_frame_metadata.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/tests/test_gst_metadata.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/tests/test_icons.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/tests/test_keypoints_cross_platform.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/tests/test_keypoints_protocol.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/tests/test_rocket_welder_client.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/tests/test_segmentation_cross_platform.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/tests/test_segmentation_result.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/tests/test_session_id.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/tests/test_transport_cross_platform.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/tests/test_ui_controls.py +0 -0
- {rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/tests/test_ui_service_happy_path.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rocket-welder-sdk
|
|
3
|
-
Version: 1.1.34.
|
|
3
|
+
Version: 1.1.34.dev11
|
|
4
4
|
Summary: High-performance video streaming SDK for RocketWelder services using ZeroBuffer IPC
|
|
5
5
|
Home-page: https://github.com/modelingevolution/rocket-welder-sdk
|
|
6
6
|
Author: ModelingEvolution
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
1.1.34.dev11
|
|
@@ -82,8 +82,7 @@ show_error_codes = true
|
|
|
82
82
|
show_column_numbers = true
|
|
83
83
|
pretty = true
|
|
84
84
|
exclude = [
|
|
85
|
-
"examples/
|
|
86
|
-
"examples/rocket-welder-client-python-yolo",
|
|
85
|
+
"examples/", # Examples are not packages, exclude from type checking
|
|
87
86
|
]
|
|
88
87
|
|
|
89
88
|
[[tool.mypy.overrides]]
|
|
@@ -148,6 +147,7 @@ ignore = [
|
|
|
148
147
|
[tool.ruff.lint.per-file-ignores]
|
|
149
148
|
"__init__.py" = ["F401"] # imported but unused
|
|
150
149
|
"tests/*" = ["S101"] # use of assert
|
|
150
|
+
"examples/*" = ["N999", "SIM112"] # Module names and env var casing (matches C# SDK)
|
|
151
151
|
|
|
152
152
|
[tool.pytest.ini_options]
|
|
153
153
|
minversion = "7.0"
|
{rocket_welder_sdk-1.1.34.dev10 → rocket_welder_sdk-1.1.34.dev11}/rocket_welder_sdk/controllers.py
RENAMED
|
@@ -338,7 +338,7 @@ class OneWayShmController(IController):
|
|
|
338
338
|
Matches C# CreateMat behavior - creates Mat wrapping the data.
|
|
339
339
|
|
|
340
340
|
Frame data layout from GStreamer zerosink:
|
|
341
|
-
[FrameMetadata (16 bytes)][Pixel Data (
|
|
341
|
+
[FrameMetadata (16 bytes)][Pixel Data (WxHxC bytes)]
|
|
342
342
|
|
|
343
343
|
Args:
|
|
344
344
|
frame: ZeroBuffer frame
|
|
@@ -18,7 +18,7 @@ from __future__ import annotations
|
|
|
18
18
|
|
|
19
19
|
import struct
|
|
20
20
|
from dataclasses import dataclass
|
|
21
|
-
from typing import Optional
|
|
21
|
+
from typing import ClassVar, Dict, Optional
|
|
22
22
|
|
|
23
23
|
# Size of the FrameMetadata structure in bytes
|
|
24
24
|
FRAME_METADATA_SIZE = 16
|
|
@@ -113,7 +113,7 @@ class GstVideoFormat:
|
|
|
113
113
|
GRAY16_BE = 26
|
|
114
114
|
GRAY16_LE = 27
|
|
115
115
|
|
|
116
|
-
_FORMAT_NAMES:
|
|
116
|
+
_FORMAT_NAMES: ClassVar[Dict[int, str]] = {
|
|
117
117
|
0: "UNKNOWN",
|
|
118
118
|
2: "I420",
|
|
119
119
|
3: "YV12",
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""
|
|
2
|
+
High-level API for RocketWelder SDK.
|
|
3
|
+
|
|
4
|
+
Mirrors C# RocketWelder.SDK API for consistent developer experience.
|
|
5
|
+
|
|
6
|
+
Example:
|
|
7
|
+
from rocket_welder_sdk.high_level import RocketWelderClient
|
|
8
|
+
|
|
9
|
+
with RocketWelderClient.from_environment() as client:
|
|
10
|
+
nose = client.keypoints.define_point("nose")
|
|
11
|
+
person = client.segmentation.define_class(1, "person")
|
|
12
|
+
client.start(process_frame)
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from .client import RocketWelderClient, RocketWelderClientOptions
|
|
16
|
+
from .connection_strings import (
|
|
17
|
+
KeyPointsConnectionString,
|
|
18
|
+
SegmentationConnectionString,
|
|
19
|
+
VideoSourceConnectionString,
|
|
20
|
+
VideoSourceType,
|
|
21
|
+
)
|
|
22
|
+
from .data_context import (
|
|
23
|
+
IKeyPointsDataContext,
|
|
24
|
+
ISegmentationDataContext,
|
|
25
|
+
)
|
|
26
|
+
from .schema import (
|
|
27
|
+
IKeyPointsSchema,
|
|
28
|
+
ISegmentationSchema,
|
|
29
|
+
KeyPointDefinition,
|
|
30
|
+
SegmentClass,
|
|
31
|
+
)
|
|
32
|
+
from .transport_protocol import (
|
|
33
|
+
TransportKind,
|
|
34
|
+
TransportProtocol,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
__all__ = [
|
|
38
|
+
"IKeyPointsDataContext",
|
|
39
|
+
"IKeyPointsSchema",
|
|
40
|
+
"ISegmentationDataContext",
|
|
41
|
+
"ISegmentationSchema",
|
|
42
|
+
"KeyPointDefinition",
|
|
43
|
+
"KeyPointsConnectionString",
|
|
44
|
+
"RocketWelderClient",
|
|
45
|
+
"RocketWelderClientOptions",
|
|
46
|
+
"SegmentClass",
|
|
47
|
+
"SegmentationConnectionString",
|
|
48
|
+
"TransportKind",
|
|
49
|
+
"TransportProtocol",
|
|
50
|
+
"VideoSourceConnectionString",
|
|
51
|
+
"VideoSourceType",
|
|
52
|
+
]
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
"""
|
|
2
|
+
RocketWelderClient - High-level API matching C# RocketWelder.SDK.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
with RocketWelderClient.from_environment() as client:
|
|
6
|
+
# Define schema
|
|
7
|
+
nose = client.keypoints.define_point("nose")
|
|
8
|
+
person = client.segmentation.define_class(1, "person")
|
|
9
|
+
|
|
10
|
+
# Start processing
|
|
11
|
+
client.start(process_frame)
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import logging
|
|
17
|
+
from dataclasses import dataclass, field
|
|
18
|
+
from typing import TYPE_CHECKING, Any, Callable, Optional
|
|
19
|
+
|
|
20
|
+
import numpy as np
|
|
21
|
+
import numpy.typing as npt
|
|
22
|
+
from typing_extensions import TypeAlias
|
|
23
|
+
|
|
24
|
+
from .connection_strings import (
|
|
25
|
+
KeyPointsConnectionString,
|
|
26
|
+
SegmentationConnectionString,
|
|
27
|
+
VideoSourceConnectionString,
|
|
28
|
+
)
|
|
29
|
+
from .data_context import (
|
|
30
|
+
IKeyPointsDataContext,
|
|
31
|
+
ISegmentationDataContext,
|
|
32
|
+
KeyPointsDataContext,
|
|
33
|
+
SegmentationDataContext,
|
|
34
|
+
)
|
|
35
|
+
from .schema import (
|
|
36
|
+
IKeyPointsSchema,
|
|
37
|
+
ISegmentationSchema,
|
|
38
|
+
KeyPointsSchema,
|
|
39
|
+
SegmentationSchema,
|
|
40
|
+
)
|
|
41
|
+
from .transport_protocol import TransportKind
|
|
42
|
+
|
|
43
|
+
if TYPE_CHECKING:
|
|
44
|
+
from rocket_welder_sdk.keypoints_protocol import KeyPointsSink
|
|
45
|
+
from rocket_welder_sdk.transport.frame_sink import IFrameSink
|
|
46
|
+
|
|
47
|
+
# Type alias for OpenCV Mat (numpy array)
|
|
48
|
+
Mat: TypeAlias = npt.NDArray[np.uint8]
|
|
49
|
+
|
|
50
|
+
logger = logging.getLogger(__name__)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass
|
|
54
|
+
class RocketWelderClientOptions:
|
|
55
|
+
"""Configuration options for RocketWelderClient."""
|
|
56
|
+
|
|
57
|
+
video_source: VideoSourceConnectionString = field(
|
|
58
|
+
default_factory=VideoSourceConnectionString.default
|
|
59
|
+
)
|
|
60
|
+
keypoints: KeyPointsConnectionString = field(default_factory=KeyPointsConnectionString.default)
|
|
61
|
+
segmentation: SegmentationConnectionString = field(
|
|
62
|
+
default_factory=SegmentationConnectionString.default
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
@classmethod
|
|
66
|
+
def from_environment(cls) -> RocketWelderClientOptions:
|
|
67
|
+
"""Create from environment variables."""
|
|
68
|
+
return cls(
|
|
69
|
+
video_source=VideoSourceConnectionString.from_environment(),
|
|
70
|
+
keypoints=KeyPointsConnectionString.from_environment(),
|
|
71
|
+
segmentation=SegmentationConnectionString.from_environment(),
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class RocketWelderClient:
|
|
76
|
+
"""
|
|
77
|
+
High-level client for RocketWelder SDK.
|
|
78
|
+
|
|
79
|
+
Mirrors C# RocketWelder.SDK.IRocketWelderClient interface.
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
def __init__(self, options: RocketWelderClientOptions) -> None:
|
|
83
|
+
self._options = options
|
|
84
|
+
self._keypoints_schema = KeyPointsSchema()
|
|
85
|
+
self._segmentation_schema = SegmentationSchema()
|
|
86
|
+
self._keypoints_sink: Optional[KeyPointsSink] = None
|
|
87
|
+
self._keypoints_frame_sink: Optional[IFrameSink] = None
|
|
88
|
+
self._segmentation_frame_sink: Optional[IFrameSink] = None
|
|
89
|
+
self._closed = False
|
|
90
|
+
logger.debug("RocketWelderClient created with options: %s", options)
|
|
91
|
+
|
|
92
|
+
@classmethod
|
|
93
|
+
def from_environment(cls) -> RocketWelderClient:
|
|
94
|
+
"""Create client from environment variables."""
|
|
95
|
+
logger.info("Creating RocketWelderClient from environment variables")
|
|
96
|
+
return cls(RocketWelderClientOptions.from_environment())
|
|
97
|
+
|
|
98
|
+
@classmethod
|
|
99
|
+
def create(cls, options: Optional[RocketWelderClientOptions] = None) -> RocketWelderClient:
|
|
100
|
+
"""Create client with explicit options."""
|
|
101
|
+
return cls(options or RocketWelderClientOptions())
|
|
102
|
+
|
|
103
|
+
@property
|
|
104
|
+
def keypoints(self) -> IKeyPointsSchema:
|
|
105
|
+
"""Schema for defining keypoints."""
|
|
106
|
+
return self._keypoints_schema
|
|
107
|
+
|
|
108
|
+
@property
|
|
109
|
+
def segmentation(self) -> ISegmentationSchema:
|
|
110
|
+
"""Schema for defining segmentation classes."""
|
|
111
|
+
return self._segmentation_schema
|
|
112
|
+
|
|
113
|
+
def start(
|
|
114
|
+
self,
|
|
115
|
+
process_frame: Callable[[Mat, ISegmentationDataContext, IKeyPointsDataContext, Mat], None],
|
|
116
|
+
) -> None:
|
|
117
|
+
"""Start with both keypoints and segmentation."""
|
|
118
|
+
self._run_loop(process_frame, use_keypoints=True, use_segmentation=True)
|
|
119
|
+
|
|
120
|
+
def start_keypoints(
|
|
121
|
+
self,
|
|
122
|
+
process_frame: Callable[[Mat, IKeyPointsDataContext, Mat], None],
|
|
123
|
+
) -> None:
|
|
124
|
+
"""Start with keypoints only."""
|
|
125
|
+
self._run_loop(process_frame, use_keypoints=True, use_segmentation=False)
|
|
126
|
+
|
|
127
|
+
def start_segmentation(
|
|
128
|
+
self,
|
|
129
|
+
process_frame: Callable[[Mat, ISegmentationDataContext, Mat], None],
|
|
130
|
+
) -> None:
|
|
131
|
+
"""Start with segmentation only."""
|
|
132
|
+
self._run_loop(process_frame, use_keypoints=False, use_segmentation=True)
|
|
133
|
+
|
|
134
|
+
def _run_loop(
|
|
135
|
+
self,
|
|
136
|
+
process_frame: Callable[..., None],
|
|
137
|
+
use_keypoints: bool,
|
|
138
|
+
use_segmentation: bool,
|
|
139
|
+
) -> None:
|
|
140
|
+
"""Run processing loop."""
|
|
141
|
+
from rocket_welder_sdk.keypoints_protocol import KeyPointsSink
|
|
142
|
+
|
|
143
|
+
logger.info(
|
|
144
|
+
"Starting processing loop (keypoints=%s, segmentation=%s)",
|
|
145
|
+
use_keypoints,
|
|
146
|
+
use_segmentation,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
# Initialize sinks
|
|
150
|
+
if use_keypoints:
|
|
151
|
+
cs = self._options.keypoints
|
|
152
|
+
logger.info("Initializing keypoints sink: %s -> %s", cs.protocol, cs.address)
|
|
153
|
+
self._keypoints_frame_sink = self._create_frame_sink(cs.protocol, cs.address)
|
|
154
|
+
self._keypoints_sink = KeyPointsSink(
|
|
155
|
+
frame_sink=self._keypoints_frame_sink,
|
|
156
|
+
master_frame_interval=cs.master_frame_interval,
|
|
157
|
+
owns_sink=False, # We manage frame sink lifecycle in close()
|
|
158
|
+
)
|
|
159
|
+
logger.debug(
|
|
160
|
+
"KeyPointsSink created with master_frame_interval=%d", cs.master_frame_interval
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
if use_segmentation:
|
|
164
|
+
seg_cs = self._options.segmentation
|
|
165
|
+
logger.info("Initializing segmentation sink: %s -> %s", seg_cs.protocol, seg_cs.address)
|
|
166
|
+
self._segmentation_frame_sink = self._create_frame_sink(seg_cs.protocol, seg_cs.address)
|
|
167
|
+
logger.debug("Segmentation frame sink created")
|
|
168
|
+
|
|
169
|
+
# TODO: Video capture loop - for now raise NotImplementedError
|
|
170
|
+
raise NotImplementedError(
|
|
171
|
+
"Video capture not implemented. Use process_frame_sync() or low-level API."
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
def process_frame_sync(
|
|
175
|
+
self,
|
|
176
|
+
frame_id: int,
|
|
177
|
+
input_frame: Mat,
|
|
178
|
+
output_frame: Mat,
|
|
179
|
+
width: int,
|
|
180
|
+
height: int,
|
|
181
|
+
) -> tuple[Optional[IKeyPointsDataContext], Optional[ISegmentationDataContext]]:
|
|
182
|
+
"""
|
|
183
|
+
Process a single frame synchronously.
|
|
184
|
+
|
|
185
|
+
Returns (keypoints_context, segmentation_context) for the caller to use.
|
|
186
|
+
Caller must call commit() on contexts when done.
|
|
187
|
+
"""
|
|
188
|
+
from rocket_welder_sdk.segmentation_result import SegmentationResultWriter
|
|
189
|
+
|
|
190
|
+
kp_ctx: Optional[IKeyPointsDataContext] = None
|
|
191
|
+
seg_ctx: Optional[ISegmentationDataContext] = None
|
|
192
|
+
|
|
193
|
+
if self._keypoints_sink is not None:
|
|
194
|
+
kp_writer = self._keypoints_sink.create_writer(frame_id)
|
|
195
|
+
kp_ctx = KeyPointsDataContext(frame_id, kp_writer)
|
|
196
|
+
|
|
197
|
+
if self._segmentation_frame_sink is not None:
|
|
198
|
+
seg_writer = SegmentationResultWriter(
|
|
199
|
+
frame_id, width, height, frame_sink=self._segmentation_frame_sink
|
|
200
|
+
)
|
|
201
|
+
seg_ctx = SegmentationDataContext(frame_id, seg_writer)
|
|
202
|
+
|
|
203
|
+
return kp_ctx, seg_ctx
|
|
204
|
+
|
|
205
|
+
def _create_frame_sink(self, protocol: Any, address: str) -> IFrameSink:
|
|
206
|
+
"""Create frame sink from protocol."""
|
|
207
|
+
from rocket_welder_sdk.transport import NngFrameSink
|
|
208
|
+
from rocket_welder_sdk.transport.stream_transport import StreamFrameSink
|
|
209
|
+
from rocket_welder_sdk.transport.unix_socket_transport import UnixSocketFrameSink
|
|
210
|
+
|
|
211
|
+
from .transport_protocol import TransportProtocol
|
|
212
|
+
|
|
213
|
+
if not isinstance(protocol, TransportProtocol):
|
|
214
|
+
raise TypeError(f"Expected TransportProtocol, got {type(protocol)}")
|
|
215
|
+
|
|
216
|
+
if protocol.kind == TransportKind.FILE:
|
|
217
|
+
logger.debug("Creating file sink: %s", address)
|
|
218
|
+
file_handle = open(address, "wb")
|
|
219
|
+
try:
|
|
220
|
+
return StreamFrameSink(file_handle)
|
|
221
|
+
except Exception:
|
|
222
|
+
file_handle.close()
|
|
223
|
+
raise
|
|
224
|
+
elif protocol.kind == TransportKind.SOCKET:
|
|
225
|
+
logger.debug("Creating Unix socket sink: %s", address)
|
|
226
|
+
return UnixSocketFrameSink.connect(address)
|
|
227
|
+
elif protocol.kind in (TransportKind.NNG_PUSH_IPC, TransportKind.NNG_PUSH_TCP):
|
|
228
|
+
logger.debug("Creating NNG pusher: %s", address)
|
|
229
|
+
return NngFrameSink.create_pusher(address)
|
|
230
|
+
elif protocol.kind in (TransportKind.NNG_PUB_IPC, TransportKind.NNG_PUB_TCP):
|
|
231
|
+
logger.debug("Creating NNG publisher: %s", address)
|
|
232
|
+
return NngFrameSink.create_publisher(address)
|
|
233
|
+
else:
|
|
234
|
+
raise ValueError(f"Unsupported protocol: {protocol}")
|
|
235
|
+
|
|
236
|
+
def close(self) -> None:
|
|
237
|
+
"""Release resources."""
|
|
238
|
+
if self._closed:
|
|
239
|
+
return
|
|
240
|
+
|
|
241
|
+
logger.info("Closing RocketWelderClient")
|
|
242
|
+
|
|
243
|
+
# Close frame sinks (KeyPointsSink has owns_sink=False, so we manage lifecycle)
|
|
244
|
+
self._keypoints_sink = None
|
|
245
|
+
if self._keypoints_frame_sink is not None:
|
|
246
|
+
logger.debug("Closing keypoints frame sink")
|
|
247
|
+
self._keypoints_frame_sink.close()
|
|
248
|
+
self._keypoints_frame_sink = None
|
|
249
|
+
|
|
250
|
+
if self._segmentation_frame_sink is not None:
|
|
251
|
+
logger.debug("Closing segmentation frame sink")
|
|
252
|
+
self._segmentation_frame_sink.close()
|
|
253
|
+
self._segmentation_frame_sink = None
|
|
254
|
+
|
|
255
|
+
self._closed = True
|
|
256
|
+
logger.info("RocketWelderClient closed")
|
|
257
|
+
|
|
258
|
+
def __enter__(self) -> RocketWelderClient:
|
|
259
|
+
return self
|
|
260
|
+
|
|
261
|
+
def __exit__(self, *args: object) -> None:
|
|
262
|
+
self.close()
|
|
@@ -6,7 +6,8 @@ Connection string format: protocol://path?param1=value1¶m2=value2
|
|
|
6
6
|
Examples:
|
|
7
7
|
nng+push+ipc://tmp/keypoints?masterFrameInterval=300
|
|
8
8
|
nng+pub+tcp://localhost:5555
|
|
9
|
-
file
|
|
9
|
+
file:///path/to/output.bin
|
|
10
|
+
socket:///tmp/my.sock
|
|
10
11
|
"""
|
|
11
12
|
|
|
12
13
|
from __future__ import annotations
|
|
@@ -144,19 +145,19 @@ class KeyPointsConnectionString:
|
|
|
144
145
|
"""
|
|
145
146
|
Strongly-typed connection string for KeyPoints output.
|
|
146
147
|
|
|
147
|
-
Supported protocols
|
|
148
|
-
-
|
|
149
|
-
-
|
|
150
|
-
-
|
|
148
|
+
Supported protocols:
|
|
149
|
+
- file:///path/to/file.bin - File output (absolute path)
|
|
150
|
+
- socket:///tmp/socket.sock - Unix domain socket
|
|
151
|
+
- nng+push+ipc://tmp/keypoints - NNG Push over IPC
|
|
152
|
+
- nng+push+tcp://host:port - NNG Push over TCP
|
|
151
153
|
|
|
152
154
|
Supported parameters:
|
|
153
155
|
- masterFrameInterval: Interval between master frames (default: 300)
|
|
154
156
|
"""
|
|
155
157
|
|
|
156
158
|
value: str
|
|
157
|
-
protocol:
|
|
158
|
-
|
|
159
|
-
address: str = ""
|
|
159
|
+
protocol: TransportProtocol
|
|
160
|
+
address: str
|
|
160
161
|
master_frame_interval: int = 300
|
|
161
162
|
parameters: Dict[str, str] = field(default_factory=dict)
|
|
162
163
|
|
|
@@ -199,25 +200,26 @@ class KeyPointsConnectionString:
|
|
|
199
200
|
|
|
200
201
|
# Parse protocol and address
|
|
201
202
|
scheme_end = endpoint_part.find("://")
|
|
202
|
-
if scheme_end
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
elif
|
|
217
|
-
#
|
|
218
|
-
address =
|
|
219
|
-
|
|
220
|
-
|
|
203
|
+
if scheme_end <= 0:
|
|
204
|
+
return None
|
|
205
|
+
|
|
206
|
+
schema_str = endpoint_part[:scheme_end]
|
|
207
|
+
path_part = endpoint_part[scheme_end + 3 :] # skip "://"
|
|
208
|
+
|
|
209
|
+
protocol = TransportProtocol.try_parse(schema_str)
|
|
210
|
+
if protocol is None:
|
|
211
|
+
return None
|
|
212
|
+
|
|
213
|
+
# Build address based on protocol type
|
|
214
|
+
if protocol.is_file:
|
|
215
|
+
# file:///absolute/path -> /absolute/path
|
|
216
|
+
address = path_part if path_part.startswith("/") else "/" + path_part
|
|
217
|
+
elif protocol.is_socket:
|
|
218
|
+
# socket:///tmp/sock -> /tmp/sock
|
|
219
|
+
address = path_part if path_part.startswith("/") else "/" + path_part
|
|
220
|
+
elif protocol.is_nng:
|
|
221
|
+
# NNG protocols need proper address format
|
|
222
|
+
address = protocol.create_nng_address(path_part)
|
|
221
223
|
else:
|
|
222
224
|
return None
|
|
223
225
|
|
|
@@ -230,7 +232,6 @@ class KeyPointsConnectionString:
|
|
|
230
232
|
return cls(
|
|
231
233
|
value=s,
|
|
232
234
|
protocol=protocol,
|
|
233
|
-
is_file=is_file,
|
|
234
235
|
address=address,
|
|
235
236
|
master_frame_interval=master_frame_interval,
|
|
236
237
|
parameters=parameters,
|
|
@@ -245,16 +246,16 @@ class SegmentationConnectionString:
|
|
|
245
246
|
"""
|
|
246
247
|
Strongly-typed connection string for Segmentation output.
|
|
247
248
|
|
|
248
|
-
Supported protocols
|
|
249
|
-
-
|
|
250
|
-
-
|
|
251
|
-
-
|
|
249
|
+
Supported protocols:
|
|
250
|
+
- file:///path/to/file.bin - File output (absolute path)
|
|
251
|
+
- socket:///tmp/socket.sock - Unix domain socket
|
|
252
|
+
- nng+push+ipc://tmp/segmentation - NNG Push over IPC
|
|
253
|
+
- nng+push+tcp://host:port - NNG Push over TCP
|
|
252
254
|
"""
|
|
253
255
|
|
|
254
256
|
value: str
|
|
255
|
-
protocol:
|
|
256
|
-
|
|
257
|
-
address: str = ""
|
|
257
|
+
protocol: TransportProtocol
|
|
258
|
+
address: str
|
|
258
259
|
parameters: Dict[str, str] = field(default_factory=dict)
|
|
259
260
|
|
|
260
261
|
@classmethod
|
|
@@ -296,32 +297,32 @@ class SegmentationConnectionString:
|
|
|
296
297
|
|
|
297
298
|
# Parse protocol and address
|
|
298
299
|
scheme_end = endpoint_part.find("://")
|
|
299
|
-
if scheme_end
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
elif
|
|
314
|
-
#
|
|
315
|
-
address =
|
|
316
|
-
|
|
317
|
-
|
|
300
|
+
if scheme_end <= 0:
|
|
301
|
+
return None
|
|
302
|
+
|
|
303
|
+
schema_str = endpoint_part[:scheme_end]
|
|
304
|
+
path_part = endpoint_part[scheme_end + 3 :] # skip "://"
|
|
305
|
+
|
|
306
|
+
protocol = TransportProtocol.try_parse(schema_str)
|
|
307
|
+
if protocol is None:
|
|
308
|
+
return None
|
|
309
|
+
|
|
310
|
+
# Build address based on protocol type
|
|
311
|
+
if protocol.is_file:
|
|
312
|
+
# file:///absolute/path -> /absolute/path
|
|
313
|
+
address = path_part if path_part.startswith("/") else "/" + path_part
|
|
314
|
+
elif protocol.is_socket:
|
|
315
|
+
# socket:///tmp/sock -> /tmp/sock
|
|
316
|
+
address = path_part if path_part.startswith("/") else "/" + path_part
|
|
317
|
+
elif protocol.is_nng:
|
|
318
|
+
# NNG protocols need proper address format
|
|
319
|
+
address = protocol.create_nng_address(path_part)
|
|
318
320
|
else:
|
|
319
321
|
return None
|
|
320
322
|
|
|
321
323
|
return cls(
|
|
322
324
|
value=s,
|
|
323
325
|
protocol=protocol,
|
|
324
|
-
is_file=is_file,
|
|
325
326
|
address=address,
|
|
326
327
|
parameters=parameters,
|
|
327
328
|
)
|
|
@@ -17,7 +17,7 @@ if TYPE_CHECKING:
|
|
|
17
17
|
from rocket_welder_sdk.keypoints_protocol import IKeyPointsWriter
|
|
18
18
|
from rocket_welder_sdk.segmentation_result import SegmentationResultWriter
|
|
19
19
|
|
|
20
|
-
from .schema import
|
|
20
|
+
from .schema import KeyPointDefinition, SegmentClass
|
|
21
21
|
|
|
22
22
|
# Type aliases
|
|
23
23
|
Point = Tuple[int, int]
|
|
@@ -37,12 +37,12 @@ class IKeyPointsDataContext(ABC):
|
|
|
37
37
|
pass
|
|
38
38
|
|
|
39
39
|
@abstractmethod
|
|
40
|
-
def add(self, point:
|
|
40
|
+
def add(self, point: KeyPointDefinition, x: int, y: int, confidence: float) -> None:
|
|
41
41
|
"""
|
|
42
42
|
Add a keypoint detection for this frame.
|
|
43
43
|
|
|
44
44
|
Args:
|
|
45
|
-
point:
|
|
45
|
+
point: KeyPointDefinition from schema definition
|
|
46
46
|
x: X coordinate in pixels
|
|
47
47
|
y: Y coordinate in pixels
|
|
48
48
|
confidence: Detection confidence (0.0 to 1.0)
|
|
@@ -50,17 +50,22 @@ class IKeyPointsDataContext(ABC):
|
|
|
50
50
|
pass
|
|
51
51
|
|
|
52
52
|
@abstractmethod
|
|
53
|
-
def add_point(self, point:
|
|
53
|
+
def add_point(self, point: KeyPointDefinition, position: Point, confidence: float) -> None:
|
|
54
54
|
"""
|
|
55
55
|
Add a keypoint detection using a Point tuple.
|
|
56
56
|
|
|
57
57
|
Args:
|
|
58
|
-
point:
|
|
58
|
+
point: KeyPointDefinition from schema definition
|
|
59
59
|
position: (x, y) tuple
|
|
60
60
|
confidence: Detection confidence (0.0 to 1.0)
|
|
61
61
|
"""
|
|
62
62
|
pass
|
|
63
63
|
|
|
64
|
+
@abstractmethod
|
|
65
|
+
def commit(self) -> None:
|
|
66
|
+
"""Commit the context (called automatically when delegate returns)."""
|
|
67
|
+
pass
|
|
68
|
+
|
|
64
69
|
|
|
65
70
|
class ISegmentationDataContext(ABC):
|
|
66
71
|
"""
|
|
@@ -92,6 +97,11 @@ class ISegmentationDataContext(ABC):
|
|
|
92
97
|
"""
|
|
93
98
|
pass
|
|
94
99
|
|
|
100
|
+
@abstractmethod
|
|
101
|
+
def commit(self) -> None:
|
|
102
|
+
"""Commit the context (called automatically when delegate returns)."""
|
|
103
|
+
pass
|
|
104
|
+
|
|
95
105
|
|
|
96
106
|
class KeyPointsDataContext(IKeyPointsDataContext):
|
|
97
107
|
"""Implementation of keypoints data context."""
|
|
@@ -101,8 +111,6 @@ class KeyPointsDataContext(IKeyPointsDataContext):
|
|
|
101
111
|
frame_id: int,
|
|
102
112
|
writer: IKeyPointsWriter,
|
|
103
113
|
) -> None:
|
|
104
|
-
from .schema import KeyPoint # noqa: F401
|
|
105
|
-
|
|
106
114
|
self._frame_id = frame_id
|
|
107
115
|
self._writer = writer
|
|
108
116
|
|
|
@@ -110,11 +118,11 @@ class KeyPointsDataContext(IKeyPointsDataContext):
|
|
|
110
118
|
def frame_id(self) -> int:
|
|
111
119
|
return self._frame_id
|
|
112
120
|
|
|
113
|
-
def add(self, point:
|
|
121
|
+
def add(self, point: KeyPointDefinition, x: int, y: int, confidence: float) -> None:
|
|
114
122
|
"""Add a keypoint detection for this frame."""
|
|
115
123
|
self._writer.append(point.id, x, y, confidence)
|
|
116
124
|
|
|
117
|
-
def add_point(self, point:
|
|
125
|
+
def add_point(self, point: KeyPointDefinition, position: Point, confidence: float) -> None:
|
|
118
126
|
"""Add a keypoint detection using a Point tuple."""
|
|
119
127
|
self._writer.append_point(point.id, position, confidence)
|
|
120
128
|
|
|
@@ -131,8 +139,6 @@ class SegmentationDataContext(ISegmentationDataContext):
|
|
|
131
139
|
frame_id: int,
|
|
132
140
|
writer: SegmentationResultWriter,
|
|
133
141
|
) -> None:
|
|
134
|
-
from .schema import SegmentClass # noqa: F401
|
|
135
|
-
|
|
136
142
|
self._frame_id = frame_id
|
|
137
143
|
self._writer = writer
|
|
138
144
|
|