framesource 0.3.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.
- frame_processors/__init__.py +37 -0
- frame_source/__init__.py +50 -0
- framesource/__init__.py +93 -0
- framesource/_msmf_config.py +26 -0
- framesource/_version.py +24 -0
- framesource/discovery.py +109 -0
- framesource/errors.py +82 -0
- framesource/factory.py +290 -0
- framesource/processors/__init__.py +34 -0
- framesource/processors/equirectangular360_processor.py +577 -0
- framesource/processors/fisheye2equirectangular_processor.py +328 -0
- framesource/processors/frame_processor.py +30 -0
- framesource/processors/hyperspectral_processor.py +23 -0
- framesource/processors/realsense_depth_processor.py +90 -0
- framesource/py.typed +0 -0
- framesource/sources/__init__.py +40 -0
- framesource/sources/audiospectrogram_capture.py +1068 -0
- framesource/sources/basler_capture.py +477 -0
- framesource/sources/folder_capture.py +920 -0
- framesource/sources/genicam_capture.py +681 -0
- framesource/sources/huateng_capture.py +245 -0
- framesource/sources/ipcamera_capture.py +254 -0
- framesource/sources/mvsdk.py +2454 -0
- framesource/sources/realsense_capture.py +565 -0
- framesource/sources/screen_capture.py +800 -0
- framesource/sources/video_capture_base.py +560 -0
- framesource/sources/video_file_capture.py +259 -0
- framesource/sources/webcam_capture.py +511 -0
- framesource/sources/ximea_capture.py +299 -0
- framesource/threading_utils.py +790 -0
- framesource-0.3.0.dist-info/METADATA +787 -0
- framesource-0.3.0.dist-info/RECORD +37 -0
- framesource-0.3.0.dist-info/WHEEL +5 -0
- framesource-0.3.0.dist-info/licenses/LICENSE +21 -0
- framesource-0.3.0.dist-info/scm_file_list.json +276 -0
- framesource-0.3.0.dist-info/scm_version.json +8 -0
- framesource-0.3.0.dist-info/top_level.txt +3 -0
|
@@ -0,0 +1,565 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import warnings
|
|
3
|
+
from typing import Any, Optional
|
|
4
|
+
|
|
5
|
+
import cv2
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
from ..errors import MissingDependencyError
|
|
9
|
+
from ..processors.realsense_depth_processor import (
|
|
10
|
+
RealsenseDepthProcessor,
|
|
11
|
+
RealsenseProcessingOutput,
|
|
12
|
+
)
|
|
13
|
+
from .video_capture_base import VideoCaptureBase
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class RealsenseCapture(VideoCaptureBase):
|
|
19
|
+
has_discovery = True
|
|
20
|
+
supports_exposure = True
|
|
21
|
+
supports_gain = True
|
|
22
|
+
supports_depth = True
|
|
23
|
+
|
|
24
|
+
def _read_implementation(self) -> tuple[bool, Optional[np.ndarray]]:
|
|
25
|
+
"""
|
|
26
|
+
Read a single frame from the realsense camera.
|
|
27
|
+
Returns:
|
|
28
|
+
Tuple[bool, Optional[np.ndarray]]: (success, frame)
|
|
29
|
+
"""
|
|
30
|
+
if not self.is_connected or self.pipeline is None:
|
|
31
|
+
return False, None
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
# Wait for a coherent pair of frames: depth and color
|
|
35
|
+
frames = self.pipeline.wait_for_frames()
|
|
36
|
+
aligned = self._align.process(frames)
|
|
37
|
+
|
|
38
|
+
depth_frame = frames.get_depth_frame()
|
|
39
|
+
color_frame = frames.get_color_frame()
|
|
40
|
+
aligned_depth_frame = aligned.get_depth_frame()
|
|
41
|
+
aligned_color_frame = aligned.get_color_frame()
|
|
42
|
+
|
|
43
|
+
if not depth_frame or not color_frame:
|
|
44
|
+
return False, None
|
|
45
|
+
except RuntimeError as e:
|
|
46
|
+
# Handle case where pipeline has been stopped
|
|
47
|
+
if "wait_for_frames cannot be called before start()" in str(e):
|
|
48
|
+
return False, None
|
|
49
|
+
else:
|
|
50
|
+
raise # Re-raise other runtime errors
|
|
51
|
+
|
|
52
|
+
# if depth_frame and color_frame:
|
|
53
|
+
# print("Depth Intrinsics:", depth_frame.profile.as_video_stream_profile().get_intrinsics()) # noqa: E501
|
|
54
|
+
# print("Color Intrinsics:", color_frame.profile.as_video_stream_profile().get_intrinsics()) # noqa: E501
|
|
55
|
+
|
|
56
|
+
frame_dict = {
|
|
57
|
+
"raw_color": color_frame,
|
|
58
|
+
"raw_depth": depth_frame,
|
|
59
|
+
"aligned_depth": aligned_depth_frame,
|
|
60
|
+
"aligned_color": aligned_color_frame,
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return True, frame_dict
|
|
64
|
+
|
|
65
|
+
"""Realsense camera capture using Realsense lib."""
|
|
66
|
+
|
|
67
|
+
def __init__(
|
|
68
|
+
self,
|
|
69
|
+
source: int = 0,
|
|
70
|
+
*,
|
|
71
|
+
width: Optional[int] = None,
|
|
72
|
+
height: Optional[int] = None,
|
|
73
|
+
fps: Optional[float] = None,
|
|
74
|
+
processor: Optional[Any] = None,
|
|
75
|
+
**kwargs,
|
|
76
|
+
):
|
|
77
|
+
"""Initialize the RealSense capture.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
source: Camera serial number (str) or device index (int,
|
|
81
|
+
default: 0).
|
|
82
|
+
width: Desired color-stream width in pixels. Falls back to the
|
|
83
|
+
device's maximum supported width when omitted (default:
|
|
84
|
+
None). Applied on :meth:`connect`.
|
|
85
|
+
height: Desired color-stream height in pixels. Falls back to the
|
|
86
|
+
device's maximum supported height when omitted (default:
|
|
87
|
+
None). Applied on :meth:`connect`.
|
|
88
|
+
fps: Desired stream frame rate. Falls back to the device's
|
|
89
|
+
maximum supported frame rate when omitted (default: None).
|
|
90
|
+
Applied on :meth:`connect`.
|
|
91
|
+
processor: Frame processor attached at construction time.
|
|
92
|
+
Defaults to a
|
|
93
|
+
:class:`~framesource.processors.realsense_depth_processor.RealsenseDepthProcessor`
|
|
94
|
+
configured for RGB output when not provided.
|
|
95
|
+
**kwargs: Additional passthrough options stored on ``self.config``.
|
|
96
|
+
"""
|
|
97
|
+
# Preserve the historical ``self.config`` contents: only forward
|
|
98
|
+
# explicitly-provided values so the ``self.config.get(...)`` defaults
|
|
99
|
+
# below keep their old meaning.
|
|
100
|
+
for _key, _val in (
|
|
101
|
+
("width", width),
|
|
102
|
+
("height", height),
|
|
103
|
+
("fps", fps),
|
|
104
|
+
("processor", processor),
|
|
105
|
+
):
|
|
106
|
+
if _val is not None:
|
|
107
|
+
kwargs[_key] = _val
|
|
108
|
+
super().__init__(source, **kwargs)
|
|
109
|
+
self.pipeline = None
|
|
110
|
+
self.device = None
|
|
111
|
+
self.profile = None
|
|
112
|
+
self._align = None
|
|
113
|
+
|
|
114
|
+
self.w = self.config.get("width", 0)
|
|
115
|
+
self.h = self.config.get("height", 0)
|
|
116
|
+
self.fps = self.config.get("fps", 0)
|
|
117
|
+
|
|
118
|
+
self._max_width = 0
|
|
119
|
+
self._max_height = 0
|
|
120
|
+
self._max_fps = 0
|
|
121
|
+
|
|
122
|
+
self._default_processor = self.config.get("processor", None)
|
|
123
|
+
if self._default_processor is None:
|
|
124
|
+
self._default_processor = RealsenseDepthProcessor(
|
|
125
|
+
output_format=RealsenseProcessingOutput.RGB
|
|
126
|
+
)
|
|
127
|
+
self.attach_processor(self._default_processor)
|
|
128
|
+
|
|
129
|
+
self.source = source if isinstance(source, int) else 0
|
|
130
|
+
|
|
131
|
+
if "is_mono" in kwargs:
|
|
132
|
+
logger.warning(
|
|
133
|
+
"'is_mono' argument is only used for certain industrial cameras "
|
|
134
|
+
"and has no effect for realsense camera."
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
def connect(self) -> bool:
|
|
138
|
+
"""Connect to realsense camera."""
|
|
139
|
+
try:
|
|
140
|
+
import pyrealsense2 as rs
|
|
141
|
+
except ImportError as e:
|
|
142
|
+
raise MissingDependencyError("pyrealsense2", extra="realsense", details=str(e)) from e
|
|
143
|
+
|
|
144
|
+
try:
|
|
145
|
+
# Configure depth and color streams
|
|
146
|
+
self.pipeline = rs.pipeline()
|
|
147
|
+
self._align = rs.align(rs.stream.color)
|
|
148
|
+
config = rs.config()
|
|
149
|
+
|
|
150
|
+
serial_number = self.source if isinstance(self.source, str) else None
|
|
151
|
+
device_index = self.source if isinstance(self.source, int) else 0
|
|
152
|
+
|
|
153
|
+
if serial_number:
|
|
154
|
+
config.enable_device(serial_number)
|
|
155
|
+
elif device_index is not None:
|
|
156
|
+
ctx = rs.context()
|
|
157
|
+
devices = ctx.query_devices()
|
|
158
|
+
|
|
159
|
+
if device_index < 0 or device_index >= len(devices):
|
|
160
|
+
raise ValueError(f"Invalid device index: {device_index}")
|
|
161
|
+
selected_serial = devices[device_index].get_info(rs.camera_info.serial_number)
|
|
162
|
+
config.enable_device(selected_serial)
|
|
163
|
+
|
|
164
|
+
# Get device product line for setting a supporting resolution
|
|
165
|
+
pipeline_wrapper = rs.pipeline_wrapper(self.pipeline)
|
|
166
|
+
pipeline_profile = config.resolve(pipeline_wrapper)
|
|
167
|
+
self.device: rs.device = pipeline_profile.get_device()
|
|
168
|
+
|
|
169
|
+
serial_number = self.device.get_info(rs.camera_info.serial_number)
|
|
170
|
+
device_name = self.device.get_info(rs.camera_info.name)
|
|
171
|
+
device_product_line = str(self.device.get_info(rs.camera_info.product_line))
|
|
172
|
+
|
|
173
|
+
found_rgb = False
|
|
174
|
+
max_res = (0, 0, 0)
|
|
175
|
+
for s in self.device.query_sensors():
|
|
176
|
+
if any(p.stream_type() == rs.stream.color for p in s.get_stream_profiles()):
|
|
177
|
+
found_rgb = True
|
|
178
|
+
for profile in s.get_stream_profiles():
|
|
179
|
+
if profile.stream_type() == rs.stream.color:
|
|
180
|
+
vp = profile.as_video_stream_profile()
|
|
181
|
+
res = (vp.width(), vp.height(), vp.fps())
|
|
182
|
+
if res > max_res:
|
|
183
|
+
max_res = res
|
|
184
|
+
if not found_rgb:
|
|
185
|
+
print("The demo requires Depth camera with Color sensor")
|
|
186
|
+
exit(0)
|
|
187
|
+
|
|
188
|
+
self._max_width = max_res[0]
|
|
189
|
+
self._max_height = max_res[1]
|
|
190
|
+
self._max_fps = max_res[2]
|
|
191
|
+
|
|
192
|
+
width = self.w if self.w > 0 else self._max_width
|
|
193
|
+
height = self.h if self.h > 0 else self._max_height
|
|
194
|
+
fps = self.fps if self.fps > 0 else self._max_fps
|
|
195
|
+
|
|
196
|
+
# Enable depth stream without specifying resolution - let it auto-select
|
|
197
|
+
# (depth sensor often has different max resolution than color sensor)
|
|
198
|
+
config.enable_stream(rs.stream.depth, rs.format.z16, int(fps))
|
|
199
|
+
config.enable_stream(rs.stream.color, width, height, rs.format.bgr8, int(fps))
|
|
200
|
+
|
|
201
|
+
# Start streaming
|
|
202
|
+
self.profile = self.pipeline.start(config)
|
|
203
|
+
|
|
204
|
+
self.is_connected = True
|
|
205
|
+
logger.info(
|
|
206
|
+
f"Connected to realsense camera {self.source}, {device_name} "
|
|
207
|
+
f"({device_product_line} line) (Serial: {serial_number})"
|
|
208
|
+
)
|
|
209
|
+
return True
|
|
210
|
+
except Exception as e:
|
|
211
|
+
logger.error(f"Error connecting to realsense camera: {e}")
|
|
212
|
+
return False
|
|
213
|
+
|
|
214
|
+
def disconnect(self) -> bool:
|
|
215
|
+
"""Disconnect from realsense camera."""
|
|
216
|
+
try:
|
|
217
|
+
if self.pipeline is not None:
|
|
218
|
+
self.pipeline.stop()
|
|
219
|
+
self.is_connected = False
|
|
220
|
+
logger.info("Disconnected from realsense camera")
|
|
221
|
+
return True
|
|
222
|
+
except Exception as e:
|
|
223
|
+
logger.error(f"Error disconnecting from realsense camera: {e}")
|
|
224
|
+
return False
|
|
225
|
+
|
|
226
|
+
def set_exposure(self, value: float) -> bool:
|
|
227
|
+
"""Set exposure (1.0 to 165000 for most realsense cameras)."""
|
|
228
|
+
if not self.is_connected or self.pipeline is None:
|
|
229
|
+
return False
|
|
230
|
+
|
|
231
|
+
try:
|
|
232
|
+
self._exposure = value
|
|
233
|
+
import pyrealsense2 as rs
|
|
234
|
+
|
|
235
|
+
for s in self.device.query_sensors():
|
|
236
|
+
if s.supports(rs.option.exposure):
|
|
237
|
+
s.set_option(rs.option.exposure, int(value))
|
|
238
|
+
logger.info(f"Set exposure to {int(value)}")
|
|
239
|
+
else:
|
|
240
|
+
logger.info(f"Not setting exposure on sensor {s}, it is not supported.")
|
|
241
|
+
|
|
242
|
+
return True
|
|
243
|
+
except Exception as e:
|
|
244
|
+
logger.error(f"Error setting exposure: {e}")
|
|
245
|
+
return False
|
|
246
|
+
|
|
247
|
+
def get_exposure(self) -> Optional[float]:
|
|
248
|
+
"""Get current exposure."""
|
|
249
|
+
if not self.is_connected or self.pipeline is None:
|
|
250
|
+
return None
|
|
251
|
+
|
|
252
|
+
try:
|
|
253
|
+
import pyrealsense2 as rs
|
|
254
|
+
|
|
255
|
+
for s in self.device.query_sensors():
|
|
256
|
+
if s.supports(rs.option.exposure):
|
|
257
|
+
exposure = s.get_option(rs.option.exposure)
|
|
258
|
+
return float(exposure)
|
|
259
|
+
except Exception:
|
|
260
|
+
return self._exposure
|
|
261
|
+
|
|
262
|
+
def set_gain(self, value: float) -> bool:
|
|
263
|
+
"""Set gain (16-248 for most realsense cameras)."""
|
|
264
|
+
if not self.is_connected or self.pipeline is None:
|
|
265
|
+
return False
|
|
266
|
+
|
|
267
|
+
try:
|
|
268
|
+
self._gain = value
|
|
269
|
+
import pyrealsense2 as rs
|
|
270
|
+
|
|
271
|
+
for s in self.device.query_sensors():
|
|
272
|
+
if s.supports(rs.option.gain):
|
|
273
|
+
s.set_option(rs.option.gain, int(value))
|
|
274
|
+
logger.info(f"Set gain to {int(value)}")
|
|
275
|
+
else:
|
|
276
|
+
logger.info(f"Not setting gain on sensor {s}, it is not supported.")
|
|
277
|
+
|
|
278
|
+
return True
|
|
279
|
+
except Exception as e:
|
|
280
|
+
logger.error(f"Error setting gain: {e}")
|
|
281
|
+
return False
|
|
282
|
+
|
|
283
|
+
def get_gain(self) -> Optional[float]:
|
|
284
|
+
"""Get current gain."""
|
|
285
|
+
if not self.is_connected or self.pipeline is None:
|
|
286
|
+
return None
|
|
287
|
+
|
|
288
|
+
try:
|
|
289
|
+
import pyrealsense2 as rs
|
|
290
|
+
|
|
291
|
+
for s in self.device.query_sensors():
|
|
292
|
+
if s.supports(rs.option.gain):
|
|
293
|
+
gain = s.get_option(rs.option.gain)
|
|
294
|
+
return float(gain)
|
|
295
|
+
except Exception:
|
|
296
|
+
return self._gain
|
|
297
|
+
|
|
298
|
+
def get_exposure_range(self) -> Optional[tuple[float, float]]:
|
|
299
|
+
"""Get current exposure."""
|
|
300
|
+
if not self.is_connected or self.pipeline is None:
|
|
301
|
+
return None
|
|
302
|
+
|
|
303
|
+
try:
|
|
304
|
+
import pyrealsense2 as rs
|
|
305
|
+
|
|
306
|
+
for s in self.device.query_sensors():
|
|
307
|
+
if s.supports(rs.option.exposure):
|
|
308
|
+
exposure = s.get_option_range(rs.option.exposure)
|
|
309
|
+
return float(exposure.min), float(exposure.max)
|
|
310
|
+
except Exception:
|
|
311
|
+
return 0.0, 0.0
|
|
312
|
+
|
|
313
|
+
def get_gain_range(self) -> Optional[tuple[float, float]]:
|
|
314
|
+
"""Get current gain."""
|
|
315
|
+
if not self.is_connected or self.pipeline is None:
|
|
316
|
+
return None
|
|
317
|
+
|
|
318
|
+
try:
|
|
319
|
+
import pyrealsense2 as rs
|
|
320
|
+
|
|
321
|
+
for s in self.device.query_sensors():
|
|
322
|
+
if s.supports(rs.option.gain):
|
|
323
|
+
gain = s.get_option_range(rs.option.gain)
|
|
324
|
+
return float(gain.min), float(gain.max)
|
|
325
|
+
except Exception:
|
|
326
|
+
return 0.0, 0.0
|
|
327
|
+
|
|
328
|
+
def _get_active_profile(self):
|
|
329
|
+
import pyrealsense2 as rs
|
|
330
|
+
|
|
331
|
+
color_stream = self.profile.get_stream(rs.stream.color)
|
|
332
|
+
video_profile = color_stream.as_video_stream_profile()
|
|
333
|
+
return video_profile
|
|
334
|
+
|
|
335
|
+
def get_frame_size(self) -> Optional[tuple[int, int]]:
|
|
336
|
+
"""Get frame size."""
|
|
337
|
+
if not self.is_connected or self.profile is None:
|
|
338
|
+
return None
|
|
339
|
+
|
|
340
|
+
# Get current resolution
|
|
341
|
+
video_profile = self._get_active_profile()
|
|
342
|
+
width = video_profile.width()
|
|
343
|
+
height = video_profile.height()
|
|
344
|
+
|
|
345
|
+
return width, height
|
|
346
|
+
|
|
347
|
+
def set_frame_size(self, width: int, height: int) -> bool:
|
|
348
|
+
"""Set frame size."""
|
|
349
|
+
self.w = width
|
|
350
|
+
self.h = height
|
|
351
|
+
|
|
352
|
+
return True
|
|
353
|
+
|
|
354
|
+
def get_fps(self) -> Optional[float]:
|
|
355
|
+
"""Get FPS."""
|
|
356
|
+
if not self.is_connected or self.profile is None:
|
|
357
|
+
return None
|
|
358
|
+
|
|
359
|
+
video_profile = self._get_active_profile()
|
|
360
|
+
fps = video_profile.fps()
|
|
361
|
+
return fps
|
|
362
|
+
|
|
363
|
+
def set_fps(self, fps: float) -> bool:
|
|
364
|
+
self.fps = fps
|
|
365
|
+
return True
|
|
366
|
+
|
|
367
|
+
def enable_auto_exposure(self, enable: bool = True) -> bool:
|
|
368
|
+
"""
|
|
369
|
+
Enable or disable auto exposure for realsense camera.
|
|
370
|
+
"""
|
|
371
|
+
if not self.is_connected or self.pipeline is None:
|
|
372
|
+
return False
|
|
373
|
+
try:
|
|
374
|
+
import pyrealsense2 as rs
|
|
375
|
+
|
|
376
|
+
for s in self.device.query_sensors():
|
|
377
|
+
# Check if the sensor supports auto-exposure
|
|
378
|
+
if s.supports(rs.option.enable_auto_exposure):
|
|
379
|
+
s.set_option(rs.option.enable_auto_exposure, int(enable))
|
|
380
|
+
logger.info(f"Set auto exposure to {enable}")
|
|
381
|
+
else:
|
|
382
|
+
logger.info(f"Not setting auto exposure on sensor {s}, it is not supported.")
|
|
383
|
+
|
|
384
|
+
return True
|
|
385
|
+
except Exception as e:
|
|
386
|
+
logger.error(f"Error setting auto exposure: {e}")
|
|
387
|
+
return False
|
|
388
|
+
|
|
389
|
+
@classmethod
|
|
390
|
+
def discover(cls) -> list:
|
|
391
|
+
"""
|
|
392
|
+
Discover available RealSense cameras.
|
|
393
|
+
|
|
394
|
+
Returns:
|
|
395
|
+
list: List of dictionaries containing RealSense camera information.
|
|
396
|
+
Each dict contains: {'index': int, 'serial_number': str, 'name': str,
|
|
397
|
+
'product_line': str}
|
|
398
|
+
"""
|
|
399
|
+
devices = []
|
|
400
|
+
|
|
401
|
+
try:
|
|
402
|
+
import pyrealsense2 as rs
|
|
403
|
+
except ImportError:
|
|
404
|
+
logger.warning("pyrealsense2 module not available. Cannot discover RealSense cameras.")
|
|
405
|
+
return []
|
|
406
|
+
|
|
407
|
+
try:
|
|
408
|
+
# Get RealSense context
|
|
409
|
+
ctx = rs.context()
|
|
410
|
+
|
|
411
|
+
# Query all connected devices
|
|
412
|
+
device_list = ctx.query_devices()
|
|
413
|
+
|
|
414
|
+
for index in range(len(device_list)):
|
|
415
|
+
try:
|
|
416
|
+
device = device_list[index]
|
|
417
|
+
device_data = {
|
|
418
|
+
"index": index,
|
|
419
|
+
"serial_number": device.get_info(rs.camera_info.serial_number),
|
|
420
|
+
"name": device.get_info(rs.camera_info.name),
|
|
421
|
+
"product_line": device.get_info(rs.camera_info.product_line),
|
|
422
|
+
}
|
|
423
|
+
devices.append(device_data)
|
|
424
|
+
logger.info(f"Found RealSense camera: {device_data}")
|
|
425
|
+
|
|
426
|
+
except Exception as e:
|
|
427
|
+
logger.warning(f"Could not get info for RealSense device {index}: {e}")
|
|
428
|
+
continue
|
|
429
|
+
|
|
430
|
+
except Exception as e:
|
|
431
|
+
logger.error(f"Error discovering RealSense cameras: {e}")
|
|
432
|
+
|
|
433
|
+
return devices
|
|
434
|
+
|
|
435
|
+
@classmethod
|
|
436
|
+
def get_config_schema(cls) -> dict[str, Any]:
|
|
437
|
+
"""Get configuration schema for RealSense camera capture"""
|
|
438
|
+
warnings.warn(
|
|
439
|
+
"get_config_schema() is deprecated and will be removed in a future release; "
|
|
440
|
+
"UI form schemas belong in the consuming application.",
|
|
441
|
+
DeprecationWarning,
|
|
442
|
+
stacklevel=2,
|
|
443
|
+
)
|
|
444
|
+
return {
|
|
445
|
+
"title": "RealSense Camera Configuration",
|
|
446
|
+
"description": "Configure Intel RealSense depth camera settings",
|
|
447
|
+
"fields": [
|
|
448
|
+
{
|
|
449
|
+
"name": "source",
|
|
450
|
+
"label": "Camera Index",
|
|
451
|
+
"type": "number",
|
|
452
|
+
"min": 0,
|
|
453
|
+
"max": 10,
|
|
454
|
+
"placeholder": "0",
|
|
455
|
+
"description": "Camera device index (0 for first RealSense camera)",
|
|
456
|
+
"required": False,
|
|
457
|
+
"default": 0,
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
"name": "width",
|
|
461
|
+
"label": "Width",
|
|
462
|
+
"type": "number",
|
|
463
|
+
"min": 424,
|
|
464
|
+
"max": 1920,
|
|
465
|
+
"placeholder": "640",
|
|
466
|
+
"description": "Frame width in pixels",
|
|
467
|
+
"required": False,
|
|
468
|
+
"default": 640,
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
"name": "height",
|
|
472
|
+
"label": "Height",
|
|
473
|
+
"type": "number",
|
|
474
|
+
"min": 240,
|
|
475
|
+
"max": 1080,
|
|
476
|
+
"placeholder": "480",
|
|
477
|
+
"description": "Frame height in pixels",
|
|
478
|
+
"required": False,
|
|
479
|
+
"default": 480,
|
|
480
|
+
},
|
|
481
|
+
{
|
|
482
|
+
"name": "fps",
|
|
483
|
+
"label": "Frame Rate (FPS)",
|
|
484
|
+
"type": "number",
|
|
485
|
+
"min": 6,
|
|
486
|
+
"max": 90,
|
|
487
|
+
"placeholder": "30",
|
|
488
|
+
"description": "Frames per second",
|
|
489
|
+
"required": False,
|
|
490
|
+
"default": 30,
|
|
491
|
+
},
|
|
492
|
+
{
|
|
493
|
+
"name": "depth_range_min",
|
|
494
|
+
"label": "Min Depth Range (m)",
|
|
495
|
+
"type": "number",
|
|
496
|
+
"min": 0.1,
|
|
497
|
+
"max": 5.0,
|
|
498
|
+
"step": 0.1,
|
|
499
|
+
"placeholder": "0.3",
|
|
500
|
+
"description": "Minimum depth detection range in meters",
|
|
501
|
+
"required": False,
|
|
502
|
+
"default": 0.3,
|
|
503
|
+
},
|
|
504
|
+
{
|
|
505
|
+
"name": "depth_range_max",
|
|
506
|
+
"label": "Max Depth Range (m)",
|
|
507
|
+
"type": "number",
|
|
508
|
+
"min": 1.0,
|
|
509
|
+
"max": 10.0,
|
|
510
|
+
"step": 0.1,
|
|
511
|
+
"placeholder": "3.0",
|
|
512
|
+
"description": "Maximum depth detection range in meters",
|
|
513
|
+
"required": False,
|
|
514
|
+
"default": 3.0,
|
|
515
|
+
},
|
|
516
|
+
{
|
|
517
|
+
"name": "output_type",
|
|
518
|
+
"label": "Output Type",
|
|
519
|
+
"type": "select",
|
|
520
|
+
"options": [
|
|
521
|
+
{"value": "color", "label": "Color Only"},
|
|
522
|
+
{"value": "depth", "label": "Depth Only"},
|
|
523
|
+
{"value": "both", "label": "Color + Depth"},
|
|
524
|
+
],
|
|
525
|
+
"description": "Type of output frames",
|
|
526
|
+
"required": False,
|
|
527
|
+
"default": "color",
|
|
528
|
+
},
|
|
529
|
+
],
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
if __name__ == "__main__":
|
|
534
|
+
# Example usage
|
|
535
|
+
camera = RealsenseCapture(
|
|
536
|
+
width=640,
|
|
537
|
+
height=480,
|
|
538
|
+
processor=RealsenseDepthProcessor(
|
|
539
|
+
output_format=RealsenseProcessingOutput.ALIGNED_SIDE_BY_SIDE
|
|
540
|
+
),
|
|
541
|
+
)
|
|
542
|
+
|
|
543
|
+
# processor = RealsenseDepthProcessor(
|
|
544
|
+
# output_format=RealsenseProcessingOutput.ALIGNED_SIDE_BY_SIDE
|
|
545
|
+
# )
|
|
546
|
+
# camera.attach_processor(processor)
|
|
547
|
+
|
|
548
|
+
if camera.connect():
|
|
549
|
+
print("Realsense camera connected successfully.")
|
|
550
|
+
print(f"Exposure: {camera.get_exposure()}")
|
|
551
|
+
print(f"Gain: {camera.get_gain()}")
|
|
552
|
+
print(f"Frame size: {camera.get_frame_size()}")
|
|
553
|
+
|
|
554
|
+
# Read a few frames
|
|
555
|
+
while camera.is_connected:
|
|
556
|
+
ret, frame = camera.read()
|
|
557
|
+
if ret:
|
|
558
|
+
cv2.imshow("Realsense camera", frame) # type: ignore
|
|
559
|
+
if cv2.waitKey(1) & 0xFF == ord("q"):
|
|
560
|
+
break
|
|
561
|
+
|
|
562
|
+
camera.stop()
|
|
563
|
+
camera.disconnect()
|
|
564
|
+
else:
|
|
565
|
+
print("Failed to connect to realsense camera.")
|