pyorbbec 1.0.1.6__py3-none-any.whl → 1.0.1.8__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.
Files changed (49) hide show
  1. {pyorbbec-1.0.1.6.dist-info → pyorbbec-1.0.1.8.dist-info}/METADATA +1 -1
  2. pyorbbec-1.0.1.8.dist-info/RECORD +59 -0
  3. pyorbbecsdk/OrbbecSDKConfig.xml +2332 -0
  4. pyorbbecsdk/examples/.gitkeep +0 -0
  5. pyorbbecsdk/examples/OrbbecSDK.dll +0 -0
  6. pyorbbecsdk/examples/OrbbecSDK.lib +0 -0
  7. pyorbbecsdk/examples/README.md +26 -0
  8. pyorbbecsdk/examples/__pycache__/utils.cpython-313.pyc +0 -0
  9. pyorbbecsdk/examples/callback.py +303 -0
  10. pyorbbecsdk/examples/color.py +64 -0
  11. pyorbbecsdk/examples/coordinate_transform.py +184 -0
  12. pyorbbecsdk/examples/depth.py +107 -0
  13. pyorbbecsdk/examples/depth_work_mode.py +50 -0
  14. pyorbbecsdk/examples/device_firmware_update.py +155 -0
  15. pyorbbecsdk/examples/device_optional_depth_presets_update.py +142 -0
  16. pyorbbecsdk/examples/enumerate.py +118 -0
  17. pyorbbecsdk/examples/extensions/depthengine/depthengine.dll +0 -0
  18. pyorbbecsdk/examples/extensions/depthengine/depthengine.lib +0 -0
  19. pyorbbecsdk/examples/extensions/filters/FilterProcessor.dll +0 -0
  20. pyorbbecsdk/examples/extensions/filters/ob_priv_filter.dll +0 -0
  21. pyorbbecsdk/examples/extensions/firmwareupdater/firmwareupdater.dll +0 -0
  22. pyorbbecsdk/examples/extensions/frameprocessor/ob_frame_processor.dll +0 -0
  23. pyorbbecsdk/examples/hdr.py +216 -0
  24. pyorbbecsdk/examples/hot_plug.py +160 -0
  25. pyorbbecsdk/examples/hw_d2c_align.py +135 -0
  26. pyorbbecsdk/examples/imu.py +60 -0
  27. pyorbbecsdk/examples/infrared.py +115 -0
  28. pyorbbecsdk/examples/logger.py +55 -0
  29. pyorbbecsdk/examples/metadata.py +64 -0
  30. pyorbbecsdk/examples/multi_device.py +169 -0
  31. pyorbbecsdk/examples/multi_streams.py +219 -0
  32. pyorbbecsdk/examples/net_device.py +158 -0
  33. pyorbbecsdk/examples/playback.py +277 -0
  34. pyorbbecsdk/examples/point_cloud.py +90 -0
  35. pyorbbecsdk/examples/post_processing.py +119 -0
  36. pyorbbecsdk/examples/preset.py +67 -0
  37. pyorbbecsdk/examples/pyorbbecsdk.cp313-win_amd64.pyd +0 -0
  38. pyorbbecsdk/examples/quick_start.py +90 -0
  39. pyorbbecsdk/examples/recorder.py +236 -0
  40. pyorbbecsdk/examples/requirements.txt +9 -0
  41. pyorbbecsdk/examples/save_image_to_disk.py +106 -0
  42. pyorbbecsdk/examples/sync_align.py +109 -0
  43. pyorbbecsdk/examples/two_devices_sync.py +233 -0
  44. pyorbbecsdk/examples/utils.py +127 -0
  45. pyorbbec-1.0.1.6.dist-info/RECORD +0 -17
  46. {pyorbbec-1.0.1.6.dist-info → pyorbbec-1.0.1.8.dist-info}/WHEEL +0 -0
  47. {pyorbbec-1.0.1.6.dist-info → pyorbbec-1.0.1.8.dist-info}/licenses/LICENSE +0 -0
  48. {pyorbbec-1.0.1.6.dist-info → pyorbbec-1.0.1.8.dist-info}/licenses/NOTICE +0 -0
  49. {pyorbbec-1.0.1.6.dist-info → pyorbbec-1.0.1.8.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,55 @@
1
+ # ******************************************************************************
2
+ # Copyright (c) 2024 Orbbec 3D Technology, Inc
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http:# www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ # ******************************************************************************
16
+
17
+ import os
18
+ from pyorbbecsdk import *
19
+ import time
20
+
21
+ def main():
22
+ # Set console logger (INFO level)
23
+ # if you DO NOT want to see the log message in console, you can set the log level to OBLogLevel.NONE
24
+ Context.set_logger_to_console(OBLogLevel.INFO)
25
+
26
+ # Set file logger (DEBUG level)
27
+ log_path = "Log/Custom/"
28
+ os.makedirs(log_path, exist_ok=True) # Ensure log directory exists
29
+ Context.set_logger_to_file(OBLogLevel.INFO, log_path)
30
+
31
+
32
+ # Configure streams
33
+ config = Config()
34
+ pipeline = Pipeline()
35
+ # Get and enable depth stream configuration
36
+ depth_profiles = pipeline.get_stream_profile_list(OBSensorType.DEPTH_SENSOR)
37
+ depth_profile = depth_profiles.get_default_video_stream_profile()
38
+ config.enable_stream(depth_profile)
39
+
40
+ # Get and enable color stream configuration
41
+ color_profiles = pipeline.get_stream_profile_list(OBSensorType.COLOR_SENSOR)
42
+ color_profile = color_profiles.get_default_video_stream_profile()
43
+ config.enable_stream(color_profile)
44
+
45
+ # Start pipeline
46
+ pipeline.start(config)
47
+ time.sleep(1)
48
+ # Stop pipeline
49
+ pipeline.stop()
50
+
51
+ print("\nPress any key to exit.")
52
+ input() # Wait for user input to exit
53
+
54
+ if __name__ == "__main__":
55
+ main()
@@ -0,0 +1,64 @@
1
+ # ******************************************************************************
2
+ # Copyright (c) 2024 Orbbec 3D Technology, Inc
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http:# www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ # ******************************************************************************
16
+
17
+ from pyorbbecsdk import *
18
+
19
+ ESC_KEY = 27
20
+
21
+ def main():
22
+ # Initialize Pipeline
23
+ pipeline = Pipeline()
24
+ # Start Pipeline
25
+ pipeline.start()
26
+ print("Pipeline started. Press Ctrl+C to exit.")
27
+
28
+ frame_counter = 0 # Add frame counter
29
+
30
+ while True:
31
+ try:
32
+ # Get frameSet from Pipeline
33
+ frame_set = pipeline.wait_for_frames(1000)
34
+ if frame_set is None:
35
+ continue
36
+
37
+ frame_counter += 1 # Increment counter
38
+
39
+ # Only print metadata every 30 frames
40
+ if frame_counter % 30 == 0:
41
+ for i in range(len(frame_set)):
42
+ frame = frame_set[i]
43
+
44
+ # Print frame metadata
45
+ print(f"Frame type: {frame.get_type()}")
46
+ metadata_types = [getattr(OBFrameMetadataType, attr) for attr in dir(OBFrameMetadataType)
47
+ if not attr.startswith('__') and isinstance(getattr(OBFrameMetadataType, attr), OBFrameMetadataType)]
48
+
49
+ for metadata_type in metadata_types:
50
+ if frame.has_metadata(metadata_type):
51
+ metadata_value = frame.get_metadata_value(metadata_type)
52
+ print(f" Metadata type: {metadata_type.name}, value: {metadata_value}")
53
+
54
+ except KeyboardInterrupt:
55
+ break
56
+ except Exception as e:
57
+ print("An error occurred:", e)
58
+ break
59
+
60
+ pipeline.stop()
61
+ print("Pipeline stopped.")
62
+
63
+ if __name__ == "__main__":
64
+ main()
@@ -0,0 +1,169 @@
1
+ # ******************************************************************************
2
+ # Copyright (c) 2024 Orbbec 3D Technology, Inc
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http:# www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ # ******************************************************************************
16
+
17
+ from queue import Queue
18
+ from typing import List
19
+
20
+ import cv2
21
+ import numpy as np
22
+
23
+ from pyorbbecsdk import *
24
+ from utils import frame_to_bgr_image
25
+
26
+ MAX_DEVICES = 2
27
+ curr_device_cnt = 0
28
+
29
+ MAX_QUEUE_SIZE = 5
30
+ ESC_KEY = 27
31
+
32
+ color_frames_queue: List[Queue] = [Queue() for _ in range(MAX_DEVICES)]
33
+ depth_frames_queue: List[Queue] = [Queue() for _ in range(MAX_DEVICES)]
34
+ has_color_sensor: List[bool] = [False for _ in range(MAX_DEVICES)]
35
+ stop_rendering = False
36
+
37
+
38
+ def on_new_frame_callback(frames: FrameSet, index: int):
39
+ global color_frames_queue, depth_frames_queue
40
+ global MAX_QUEUE_SIZE
41
+ assert index < MAX_DEVICES
42
+ color_frame = frames.get_color_frame()
43
+ depth_frame = frames.get_depth_frame()
44
+ if color_frame is not None:
45
+ if color_frames_queue[index].qsize() >= MAX_QUEUE_SIZE:
46
+ color_frames_queue[index].get()
47
+ color_frames_queue[index].put(color_frame)
48
+ if depth_frame is not None:
49
+ if depth_frames_queue[index].qsize() >= MAX_QUEUE_SIZE:
50
+ depth_frames_queue[index].get()
51
+ depth_frames_queue[index].put(depth_frame)
52
+
53
+
54
+ def rendering_frames():
55
+ global color_frames_queue, depth_frames_queue
56
+ global curr_device_cnt
57
+ global stop_rendering
58
+ while not stop_rendering:
59
+ for i in range(curr_device_cnt):
60
+ color_frame = None
61
+ depth_frame = None
62
+ if not color_frames_queue[i].empty():
63
+ color_frame = color_frames_queue[i].get()
64
+ if not depth_frames_queue[i].empty():
65
+ depth_frame = depth_frames_queue[i].get()
66
+ if color_frame is None and depth_frame is None:
67
+ continue
68
+ color_image = None
69
+ depth_image = None
70
+ color_width, color_height = 0, 0
71
+ if color_frame is not None:
72
+ color_width, color_height = color_frame.get_width(), color_frame.get_height()
73
+ color_image = frame_to_bgr_image(color_frame)
74
+ if depth_frame is not None:
75
+ width = depth_frame.get_width()
76
+ height = depth_frame.get_height()
77
+ scale = depth_frame.get_depth_scale()
78
+ depth_format = depth_frame.get_format()
79
+ if depth_format != OBFormat.Y16:
80
+ print("depth format is not Y16")
81
+ continue
82
+
83
+ try:
84
+ depth_data = np.frombuffer(depth_frame.get_data(), dtype=np.uint16)
85
+ depth_data = depth_data.reshape((height, width))
86
+ except ValueError:
87
+ print("Failed to reshape depth data")
88
+ continue
89
+
90
+ depth_data = depth_data.astype(np.float32) * scale
91
+
92
+ depth_image = cv2.normalize(depth_data, None, 0, 255, cv2.NORM_MINMAX,
93
+ dtype=cv2.CV_8U)
94
+ depth_image = cv2.applyColorMap(depth_image, cv2.COLORMAP_JET)
95
+
96
+ if color_image is not None and depth_image is not None:
97
+ window_size = (color_width // 2, color_height // 2)
98
+ color_image = cv2.resize(color_image, window_size)
99
+ depth_image = cv2.resize(depth_image, window_size)
100
+ image = np.hstack((color_image, depth_image))
101
+ elif depth_image is not None and not has_color_sensor[i]:
102
+ image = depth_image
103
+ else:
104
+ continue
105
+ cv2.imshow("Device {}".format(i), image)
106
+ key = cv2.waitKey(1)
107
+ if key == ord('q') or key == ESC_KEY:
108
+ stop_rendering = True
109
+ break
110
+ cv2.destroyAllWindows()
111
+
112
+
113
+ def start_streams(pipelines: List[Pipeline], configs: List[Config]):
114
+ index = 0
115
+ for pipeline, config in zip(pipelines, configs):
116
+ print("Starting device {}".format(index))
117
+ pipeline.start(config, lambda frame_set, curr_index=index: on_new_frame_callback(frame_set,
118
+ curr_index))
119
+ index += 1
120
+
121
+
122
+ def stop_streams(pipelines: List[Pipeline]):
123
+ for pipeline in pipelines:
124
+ pipeline.stop()
125
+
126
+
127
+ def main():
128
+ ctx = Context()
129
+ device_list = ctx.query_devices()
130
+ global curr_device_cnt
131
+ curr_device_cnt = device_list.get_count()
132
+ if curr_device_cnt == 0:
133
+ print("No device connected")
134
+ return
135
+ if curr_device_cnt > MAX_DEVICES:
136
+ print("Too many devices connected")
137
+ return
138
+ pipelines: List[Pipeline] = []
139
+ configs: List[Config] = []
140
+ global has_color_sensor
141
+ for i in range(device_list.get_count()):
142
+ device = device_list.get_device_by_index(i)
143
+ pipeline = Pipeline(device)
144
+ config = Config()
145
+ try:
146
+ profile_list = pipeline.get_stream_profile_list(OBSensorType.COLOR_SENSOR)
147
+ color_profile: VideoStreamProfile = profile_list.get_default_video_stream_profile()
148
+ config.enable_stream(color_profile)
149
+ has_color_sensor[i] = True
150
+ except OBError as e:
151
+ print(e)
152
+ has_color_sensor[i] = False
153
+ profile_list = pipeline.get_stream_profile_list(OBSensorType.DEPTH_SENSOR)
154
+ depth_profile = profile_list.get_default_video_stream_profile()
155
+ config.enable_stream(depth_profile)
156
+ pipelines.append(pipeline)
157
+ configs.append(config)
158
+ global stop_rendering
159
+ start_streams(pipelines, configs)
160
+ try:
161
+ rendering_frames()
162
+ except KeyboardInterrupt:
163
+ stop_rendering = True
164
+ finally:
165
+ stop_streams(pipelines)
166
+ cv2.destroyAllWindows()
167
+
168
+ if __name__ == "__main__":
169
+ main()
@@ -0,0 +1,219 @@
1
+ # ******************************************************************************
2
+ # Copyright (c) 2024 Orbbec 3D Technology, Inc
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http:# www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ # ******************************************************************************
16
+
17
+ import cv2
18
+ import numpy as np
19
+ from pyorbbecsdk import *
20
+ from utils import frame_to_bgr_image
21
+
22
+ # cached frames for better visualization
23
+ cached_frames = {
24
+ 'color': None,
25
+ 'depth': None,
26
+ 'left_ir': None,
27
+ 'right_ir': None,
28
+ 'ir': None
29
+ }
30
+
31
+ def setup_camera():
32
+ """Setup camera and stream configuration"""
33
+ pipeline = Pipeline()
34
+ config = Config()
35
+ device = pipeline.get_device()
36
+
37
+ # Try to enable all possible sensors
38
+ video_sensors = [
39
+ OBSensorType.COLOR_SENSOR,
40
+ OBSensorType.DEPTH_SENSOR,
41
+ OBSensorType.IR_SENSOR,
42
+ OBSensorType.LEFT_IR_SENSOR,
43
+ OBSensorType.RIGHT_IR_SENSOR
44
+ ]
45
+ sensor_list = device.get_sensor_list()
46
+ for sensor in range(len(sensor_list)):
47
+ try:
48
+ sensor_type = sensor_list[sensor].get_type()
49
+ if sensor_type in video_sensors:
50
+ config.enable_stream(sensor_type)
51
+ except:
52
+ continue
53
+
54
+ pipeline.start(config)
55
+ return pipeline
56
+
57
+ def setup_imu():
58
+ """Setup IMU configuration"""
59
+ pipeline = Pipeline()
60
+ config = Config()
61
+ config.enable_accel_stream()
62
+ config.enable_gyro_stream()
63
+ pipeline.start(config)
64
+ return pipeline
65
+
66
+ def process_color(frame):
67
+ """Process color image"""
68
+ frame = frame if frame else cached_frames['color']
69
+ cached_frames['color'] = frame
70
+ return frame_to_bgr_image(frame) if frame else None
71
+
72
+
73
+ def process_depth(frame):
74
+ """Process depth image"""
75
+ frame = frame if frame else cached_frames['depth']
76
+ cached_frames['depth'] = frame
77
+ if not frame:
78
+ return None
79
+ try:
80
+ depth_data = np.frombuffer(frame.get_data(), dtype=np.uint16)
81
+ depth_data = depth_data.reshape(frame.get_height(), frame.get_width())
82
+ depth_image = cv2.normalize(depth_data, None, 0, 255, cv2.NORM_MINMAX, dtype=cv2.CV_8U)
83
+ return cv2.applyColorMap(depth_image, cv2.COLORMAP_JET)
84
+ except ValueError:
85
+ return None
86
+
87
+
88
+ def process_ir(ir_frame):
89
+ """Process IR frame (left or right) to RGB image"""
90
+ if ir_frame is None:
91
+ return None
92
+ ir_frame = ir_frame.as_video_frame()
93
+ ir_data = np.asanyarray(ir_frame.get_data())
94
+ width = ir_frame.get_width()
95
+ height = ir_frame.get_height()
96
+ ir_format = ir_frame.get_format()
97
+
98
+ if ir_format == OBFormat.Y8:
99
+ ir_data = np.resize(ir_data, (height, width, 1))
100
+ data_type = np.uint8
101
+ image_dtype = cv2.CV_8UC1
102
+ max_data = 255
103
+ elif ir_format == OBFormat.MJPG:
104
+ ir_data = cv2.imdecode(ir_data, cv2.IMREAD_UNCHANGED)
105
+ data_type = np.uint8
106
+ image_dtype = cv2.CV_8UC1
107
+ max_data = 255
108
+ if ir_data is None:
109
+ print("decode mjpeg failed")
110
+ return None
111
+ ir_data = np.resize(ir_data, (height, width, 1))
112
+ else:
113
+ ir_data = np.frombuffer(ir_data, dtype=np.uint16)
114
+ data_type = np.uint16
115
+ image_dtype = cv2.CV_16UC1
116
+ max_data = 255
117
+ ir_data = np.resize(ir_data, (height, width, 1))
118
+
119
+ cv2.normalize(ir_data, ir_data, 0, max_data, cv2.NORM_MINMAX, dtype=image_dtype)
120
+ ir_data = ir_data.astype(data_type)
121
+ return cv2.cvtColor(ir_data, cv2.COLOR_GRAY2RGB)
122
+
123
+ def get_imu_text(frame, name):
124
+ """Format IMU data"""
125
+ if not frame:
126
+ return []
127
+ return [
128
+ f"{name} x: {frame.get_x():.2f}",
129
+ f"{name} y: {frame.get_y():.2f}",
130
+ f"{name} z: {frame.get_z():.2f}"
131
+ ]
132
+
133
+
134
+ def create_display(frames, width=1280, height=720):
135
+ """Create display window"""
136
+ display = np.zeros((height, width, 3), dtype=np.uint8)
137
+ h, w = height // 2, width // 2
138
+
139
+ # Process video frames
140
+ if 'color' in frames and frames['color'] is not None:
141
+ display[0:h, 0:w] = cv2.resize(frames['color'], (w, h))
142
+
143
+ if 'depth' in frames and frames['depth'] is not None:
144
+ display[0:h, w:] = cv2.resize(frames['depth'], (w, h))
145
+
146
+ if 'ir' in frames and frames['ir'] is not None:
147
+ display[h:, 0:w] = cv2.resize(frames['ir'], (w, h))
148
+
149
+ # Display IMU data
150
+ if 'imu' in frames:
151
+ y_offset = h + 20
152
+ for data_type in ['accel', 'gyro']:
153
+ text_lines = get_imu_text(frames['imu'].get(data_type), data_type.title())
154
+ for i, line in enumerate(text_lines):
155
+ cv2.putText(display, line, (w + 10, y_offset + i * 20),
156
+ cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
157
+ y_offset += 80
158
+
159
+ return display
160
+
161
+
162
+ def main():
163
+ # Window settings
164
+ WINDOW_NAME = "MultiStream Viewer"
165
+ DISPLAY_WIDTH = 1280
166
+ DISPLAY_HEIGHT = 720
167
+
168
+ # Initialize camera
169
+ pipeline = setup_camera()
170
+ imu_pipeline = setup_imu()
171
+ cv2.namedWindow(WINDOW_NAME, cv2.WINDOW_NORMAL)
172
+ cv2.resizeWindow(WINDOW_NAME, DISPLAY_WIDTH, DISPLAY_HEIGHT)
173
+ while True:
174
+ # Get all frames
175
+ frames = pipeline.wait_for_frames(100)
176
+ if not frames:
177
+ continue
178
+ # Process different frame types
179
+ processed_frames = {'color': process_color(frames.get_color_frame()),
180
+ 'depth': process_depth(frames.get_depth_frame())}
181
+
182
+ # Process IR image: try stereo IR first, fallback to mono if unavailable
183
+ try:
184
+ left = process_ir(frames.get_frame(OBFrameType.LEFT_IR_FRAME).as_video_frame())
185
+ right = process_ir(frames.get_frame(OBFrameType.RIGHT_IR_FRAME).as_video_frame())
186
+ if left is not None and right is not None:
187
+ processed_frames['ir'] = np.hstack((left, right))
188
+ except:
189
+ ir_frame = frames.get_ir_frame()
190
+ if ir_frame:
191
+ processed_frames['ir'] = process_ir(ir_frame.as_video_frame())
192
+
193
+ # Process IMU data
194
+ imu_frames = imu_pipeline.wait_for_frames(100)
195
+ if not imu_frames:
196
+ continue
197
+ accel = imu_frames.get_frame(OBFrameType.ACCEL_FRAME)
198
+ gyro = imu_frames.get_frame(OBFrameType.GYRO_FRAME)
199
+ if accel and gyro:
200
+ processed_frames['imu'] = {
201
+ 'accel': accel.as_accel_frame(),
202
+ 'gyro': gyro.as_gyro_frame()
203
+ }
204
+
205
+ # create display
206
+ display = create_display(processed_frames, DISPLAY_WIDTH, DISPLAY_HEIGHT)
207
+ cv2.imshow(WINDOW_NAME, display)
208
+
209
+ # check exit key
210
+ key = cv2.waitKey(1) & 0xFF
211
+ if key in [ord('q'), 27]: # q or ESC
212
+ break
213
+
214
+ pipeline.stop()
215
+ cv2.destroyAllWindows()
216
+
217
+
218
+ if __name__ == "__main__":
219
+ main()
@@ -0,0 +1,158 @@
1
+ # ******************************************************************************
2
+ # Copyright (c) 2024 Orbbec 3D Technology, Inc
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http:# www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ # ******************************************************************************
16
+
17
+ import platform
18
+ import cv2
19
+ import numpy as np
20
+ import av
21
+ import io
22
+ import threading
23
+ import time
24
+ import pygame
25
+ import os
26
+ from pyorbbecsdk import (Pipeline, Context, Config, OBSensorType, OBFormat, OBError)
27
+ from utils import frame_to_bgr_image
28
+
29
+ ESC_KEY = 27
30
+
31
+ def get_stream_profile(pipeline, sensor_type, width, height, fmt, fps):
32
+ profile_list = pipeline.get_stream_profile_list(sensor_type)
33
+ try:
34
+ profile = profile_list.get_video_stream_profile(width, height, fmt, fps)
35
+ except OBError:
36
+ profile = profile_list.get_default_video_stream_profile()
37
+ return profile
38
+
39
+ def decode_h26x_frame(decoder, byte_data):
40
+ try:
41
+ packet = av.Packet(byte_data)
42
+ frames = decoder.decode(packet)
43
+ for frame in frames:
44
+ return frame.to_ndarray(format='bgr24')
45
+ except av.AVError as e:
46
+ print(f"Decoding error: {e}")
47
+ return None
48
+
49
+ class FrameProcessor(threading.Thread):
50
+ def __init__(self, decoder, display_width, display_height):
51
+ super().__init__()
52
+ self.decoder = decoder
53
+ self.latest_frame = None
54
+ self.processed_frame = None
55
+ self.lock = threading.Lock()
56
+ self.running = True
57
+ self.daemon = True
58
+ self.display_width = display_width
59
+ self.display_height = display_height
60
+
61
+ def run(self):
62
+ while self.running:
63
+ with self.lock:
64
+ if self.latest_frame is not None:
65
+ color_image = decode_h26x_frame(self.decoder, self.latest_frame)
66
+ if color_image is not None:
67
+ # Resize the image to 1080p
68
+ resized_image = cv2.resize(color_image, (self.display_width, self.display_height))
69
+ rgb_image = cv2.cvtColor(resized_image, cv2.COLOR_BGR2RGB)
70
+ self.processed_frame = rgb_image
71
+ self.latest_frame = None
72
+ time.sleep(0.001)
73
+
74
+ def update_frame(self, frame):
75
+ with self.lock:
76
+ self.latest_frame = frame
77
+
78
+ def get_processed_frame(self):
79
+ with self.lock:
80
+ return self.processed_frame
81
+
82
+ def stop(self):
83
+ self.running = False
84
+
85
+ def main():
86
+ ctx = Context()
87
+ ip = input("Enter the IP address of the device (default: 192.168.1.10): ") or "192.168.1.10"
88
+ device = ctx.create_net_device(ip, 8090)
89
+ if device is None:
90
+ print("Failed to create net device")
91
+ return
92
+
93
+ config = Config()
94
+ pipeline = Pipeline(device)
95
+
96
+ # Set up 4K capture
97
+ color_profile = get_stream_profile(pipeline, OBSensorType.COLOR_SENSOR, 3840, 2160, OBFormat.H264, 25)
98
+ config.enable_stream(color_profile)
99
+
100
+ pipeline.start(config)
101
+
102
+ color_codec_name = 'h264' if color_profile.get_format() == OBFormat.H264 else 'hevc'
103
+ try:
104
+ decoder = av.codec.CodecContext.create(color_codec_name, 'r')
105
+ except av.AVError as e:
106
+ print(f"Failed to create decoder for {color_codec_name}: {e}")
107
+ pipeline.stop()
108
+ return
109
+
110
+ # Set display resolution to 720p
111
+ display_width, display_height = 1280, 720
112
+ frame_processor = FrameProcessor(decoder, display_width, display_height)
113
+ frame_processor.start()
114
+
115
+ pygame.init()
116
+ screen = pygame.display.set_mode((display_width, display_height))
117
+ pygame.display.set_caption("4K Net Device Viewer (720p Display)")
118
+ clock = pygame.time.Clock()
119
+
120
+ running = True
121
+ try:
122
+ while running:
123
+ for event in pygame.event.get():
124
+ if event.type == pygame.QUIT:
125
+ running = False
126
+ elif event.type == pygame.KEYDOWN:
127
+ if event.key == pygame.K_ESCAPE:
128
+ running = False
129
+
130
+ if not running:
131
+ break
132
+
133
+ frames = pipeline.wait_for_frames(100)
134
+ if frames:
135
+ color_frame = frames.get_color_frame()
136
+ if color_frame:
137
+ byte_data = color_frame.get_data()
138
+ if len(byte_data) > 0:
139
+ frame_processor.update_frame(byte_data)
140
+
141
+ processed_frame = frame_processor.get_processed_frame()
142
+ if processed_frame is not None:
143
+ surf = pygame.surfarray.make_surface(processed_frame.swapaxes(0, 1))
144
+ screen.blit(surf, (0, 0))
145
+ pygame.display.flip()
146
+
147
+ clock.tick(30) # Limit to 30 FPS
148
+
149
+ finally:
150
+ print("Stopping frame processor...")
151
+ frame_processor.stop()
152
+ print("Stopping pipeline...")
153
+ pipeline.stop()
154
+ print("Exiting the program...")
155
+ os._exit(0)
156
+
157
+ if __name__ == "__main__":
158
+ main()