1
1
import platform
2
- import subprocess
3
-
4
2
import cv2
5
3
import numpy as np
6
-
7
- from pyorbbecsdk import (Pipeline , Context , Config , OBSensorType ,
8
- OBFormat , OBError )
4
+ import av
5
+ import io
6
+ import threading
7
+ import time
8
+ import pygame
9
+ import os
10
+ from pyorbbecsdk import (Pipeline , Context , Config , OBSensorType , OBFormat , OBError )
9
11
from utils import frame_to_bgr_image
10
12
11
13
ESC_KEY = 27
12
14
13
- # Only Femto Mega and Gemini2 XL support this sample
14
-
15
15
def get_stream_profile (pipeline , sensor_type , width , height , fmt , fps ):
16
16
profile_list = pipeline .get_stream_profile_list (sensor_type )
17
17
try :
@@ -20,40 +20,55 @@ def get_stream_profile(pipeline, sensor_type, width, height, fmt, fps):
20
20
profile = profile_list .get_default_video_stream_profile ()
21
21
return profile
22
22
23
-
24
- def decode_h265_frame (color_frame , color_format = 'hevc' ):
25
- # This function is only supported on Linux.
26
- # and requires ffmpeg to be installed.
27
- if color_format == 'h265' :
28
- color_format = 'hevc'
29
- elif color_format == 'h264' :
30
- color_format = 'h264' # Actually, this remains unchanged but added for clarity.
31
-
32
- cmd_in = [
33
- 'ffmpeg' ,
34
- '-f' , color_format ,
35
- '-i' , 'pipe:' ,
36
- '-f' , 'rawvideo' ,
37
- '-pix_fmt' , 'bgr24' ,
38
- 'pipe:'
39
- ]
40
-
41
- byte_data = color_frame .get_data ().tobytes ()
42
-
43
- proc = subprocess .Popen (cmd_in , stdin = subprocess .PIPE , stdout = subprocess .PIPE , stderr = subprocess .PIPE )
44
- out , err = proc .communicate (input = byte_data )
45
-
46
- if proc .returncode != 0 :
47
- raise ValueError (f'FFmpeg did not run successfully: { err .decode ()} ' )
48
- if len (out ) == 0 :
49
- return None
50
- decoded_frame = np .frombuffer (out , dtype = np .uint8 ).reshape (color_frame .get_height (), color_frame .get_width (), 3 )
51
- return decoded_frame
52
-
23
+ def decode_h26x_frame (decoder , byte_data ):
24
+ try :
25
+ packet = av .Packet (byte_data )
26
+ frames = decoder .decode (packet )
27
+ for frame in frames :
28
+ return frame .to_ndarray (format = 'bgr24' )
29
+ except av .AVError as e :
30
+ print (f"Decoding error: { e } " )
31
+ return None
32
+
33
+ class FrameProcessor (threading .Thread ):
34
+ def __init__ (self , decoder , display_width , display_height ):
35
+ super ().__init__ ()
36
+ self .decoder = decoder
37
+ self .latest_frame = None
38
+ self .processed_frame = None
39
+ self .lock = threading .Lock ()
40
+ self .running = True
41
+ self .daemon = True
42
+ self .display_width = display_width
43
+ self .display_height = display_height
44
+
45
+ def run (self ):
46
+ while self .running :
47
+ with self .lock :
48
+ if self .latest_frame is not None :
49
+ color_image = decode_h26x_frame (self .decoder , self .latest_frame )
50
+ if color_image is not None :
51
+ # Resize the image to 1080p
52
+ resized_image = cv2 .resize (color_image , (self .display_width , self .display_height ))
53
+ rgb_image = cv2 .cvtColor (resized_image , cv2 .COLOR_BGR2RGB )
54
+ self .processed_frame = rgb_image
55
+ self .latest_frame = None
56
+ time .sleep (0.001 )
57
+
58
+ def update_frame (self , frame ):
59
+ with self .lock :
60
+ self .latest_frame = frame
61
+
62
+ def get_processed_frame (self ):
63
+ with self .lock :
64
+ return self .processed_frame
65
+
66
+ def stop (self ):
67
+ self .running = False
53
68
54
69
def main ():
55
70
ctx = Context ()
56
- ip = input ("Enter the ip address of the device (default: 192.168.1.10): " ) or "192.168.1.10"
71
+ ip = input ("Enter the IP address of the device (default: 192.168.1.10): " ) or "192.168.1.10"
57
72
device = ctx .create_net_device (ip , 8090 )
58
73
if device is None :
59
74
print ("Failed to create net device" )
@@ -62,65 +77,66 @@ def main():
62
77
config = Config ()
63
78
pipeline = Pipeline (device )
64
79
65
- # Setup color stream
66
- color_profile = get_stream_profile (pipeline , OBSensorType .COLOR_SENSOR , 1280 , 0 , OBFormat .MJPG , 10 )
80
+ # Set up 4K capture
81
+ color_profile = get_stream_profile (pipeline , OBSensorType .COLOR_SENSOR , 3840 , 2160 , OBFormat .H264 , 25 )
67
82
config .enable_stream (color_profile )
68
83
69
- # Setup depth stream
70
- depth_profile = get_stream_profile (pipeline , OBSensorType .DEPTH_SENSOR , 640 , 0 , OBFormat .Y16 , 10 )
71
- config .enable_stream (depth_profile )
72
-
73
84
pipeline .start (config )
74
- warning_printed = False
75
85
86
+ color_codec_name = 'h264' if color_profile .get_format () == OBFormat .H264 else 'hevc'
76
87
try :
77
- while True :
78
- frames = pipeline .wait_for_frames (100 )
79
- if not frames :
80
- continue
81
-
82
- color_frame = frames .get_color_frame ()
83
- depth_frame = frames .get_depth_frame ()
84
-
85
- if color_frame and color_frame .get_format () in [OBFormat .H265 , OBFormat .H264 ]:
86
- if platform .system () == 'Linux' :
87
- color_format = 'h265' if color_frame .get_format () == OBFormat .H265 else 'h264'
88
- color_image = decode_h265_frame (color_frame , color_format )
89
- else :
90
- if not warning_printed :
91
- print ("H264 and H265 are not supported on this system." )
92
- warning_printed = True
93
- color_image = None
94
- elif color_frame :
95
- color_image = frame_to_bgr_image (color_frame )
96
- else :
97
- color_image = None
98
-
99
- if depth_frame :
100
- depth_data = np .frombuffer (depth_frame .get_data (), dtype = np .uint16 ).reshape (depth_frame .get_height (),
101
- depth_frame .get_width ())
102
- scale = depth_frame .get_depth_scale ()
103
- depth_data = (depth_data * scale ).astype (np .uint16 )
104
- depth_image = cv2 .normalize (depth_data , None , 0 , 255 , cv2 .NORM_MINMAX , dtype = cv2 .CV_8U )
105
- depth_image = cv2 .applyColorMap (depth_image , cv2 .COLORMAP_JET )
106
- else :
107
- depth_image = None
108
-
109
- if color_image is not None and depth_image is not None :
110
- target_size = (640 , 480 )
111
- images_to_show = [img for img in [color_image , depth_image ] if img is not None ]
112
- # Resize each image to 640x480
113
- images_to_show = [cv2 .resize (img , target_size ) for img in images_to_show ]
114
-
115
- cv2 .imshow ("net_device" , np .hstack (images_to_show ))
116
- key = cv2 .waitKey (1 )
117
- if key in [ord ('q' ), ESC_KEY ]:
88
+ decoder = av .codec .CodecContext .create (color_codec_name , 'r' )
89
+ except av .AVError as e :
90
+ print (f"Failed to create decoder for { color_codec_name } : { e } " )
91
+ pipeline .stop ()
92
+ return
93
+
94
+ # Set display resolution to 720p
95
+ display_width , display_height = 1280 , 720
96
+ frame_processor = FrameProcessor (decoder , display_width , display_height )
97
+ frame_processor .start ()
98
+
99
+ pygame .init ()
100
+ screen = pygame .display .set_mode ((display_width , display_height ))
101
+ pygame .display .set_caption ("4K Net Device Viewer (720p Display)" )
102
+ clock = pygame .time .Clock ()
103
+
104
+ running = True
105
+ try :
106
+ while running :
107
+ for event in pygame .event .get ():
108
+ if event .type == pygame .QUIT :
109
+ running = False
110
+ elif event .type == pygame .KEYDOWN :
111
+ if event .key == pygame .K_ESCAPE :
112
+ running = False
113
+
114
+ if not running :
118
115
break
119
- except KeyboardInterrupt :
120
- pass
116
+
117
+ frames = pipeline .wait_for_frames (100 )
118
+ if frames :
119
+ color_frame = frames .get_color_frame ()
120
+ if color_frame :
121
+ byte_data = color_frame .get_data ()
122
+ if len (byte_data ) > 0 :
123
+ frame_processor .update_frame (byte_data )
124
+
125
+ processed_frame = frame_processor .get_processed_frame ()
126
+ if processed_frame is not None :
127
+ surf = pygame .surfarray .make_surface (processed_frame .swapaxes (0 , 1 ))
128
+ screen .blit (surf , (0 , 0 ))
129
+ pygame .display .flip ()
130
+
131
+ clock .tick (30 ) # Limit to 30 FPS
132
+
121
133
finally :
134
+ print ("Stopping frame processor..." )
135
+ frame_processor .stop ()
136
+ print ("Stopping pipeline..." )
122
137
pipeline .stop ()
123
-
138
+ print ("Exiting the program..." )
139
+ os ._exit (0 )
124
140
125
141
if __name__ == "__main__" :
126
- main ()
142
+ main ()
0 commit comments