3
3
import numpy as np
4
4
import queue
5
5
import serial
6
- import serial .tools .list_ports
7
- import threading
6
+ import sys
8
7
import time
8
+ import traceback
9
+ import threading
10
+ from enum import Enum
11
+ import serial .tools .list_ports
9
12
from lang_manager import LocaleStringManager as lang
10
13
11
14
from colorama import Fore
12
15
from config import BabbleConfig , BabbleSettingsConfig
13
- from utils .misc_utils import get_camera_index_by_name , list_camera_names , is_nt
14
- from enum import Enum
15
- import sys
16
+ from utils .misc_utils import get_camera_index_by_name , list_camera_names
17
+
18
+ from vivefacialtracker .vivetracker import ViveTracker
19
+ from vivefacialtracker .camera_controller import FTCameraController
16
20
17
21
WAIT_TIME = 0.1
18
22
BUFFER_SIZE = 32768
24
28
# packet (packet-size bytes)
25
29
ETVR_HEADER = b"\xff \xa0 \xff \xa1 "
26
30
ETVR_HEADER_LEN = 6
31
+ PORTS = ("COM" , "/dev/ttyACM" )
27
32
28
33
29
34
class CameraState (Enum ):
@@ -54,13 +59,15 @@ def __init__(
54
59
self .cancellation_event = cancellation_event
55
60
self .current_capture_source = config .capture_source
56
61
self .cv2_camera : "cv2.VideoCapture" = None
62
+ self .vft_camera : FTCameraController = None
57
63
58
64
self .serial_connection = None
59
65
self .last_frame_time = time .time ()
60
66
self .fps = 0
61
67
self .bps = 0
62
68
self .start = True
63
69
self .buffer = b""
70
+ self .frame_number = 0
64
71
self .FRAME_SIZE = [0 , 0 ]
65
72
66
73
self .error_message = f'{ Fore .YELLOW } [{ lang ._instance .get_string ("log.warn" )} ] { lang ._instance .get_string ("info.enterCaptureOne" )} {{}} { lang ._instance .get_string ("info.enterCaptureTwo" )} { Fore .RESET } '
@@ -78,6 +85,8 @@ def run(self):
78
85
print (
79
86
f'{ Fore .CYAN } [{ lang ._instance .get_string ("log.info" )} ] { lang ._instance .get_string ("info.exitCaptureThread" )} { Fore .RESET } '
80
87
)
88
+ if self .vft_camera is not None :
89
+ self .vft_camera .close ()
81
90
return
82
91
should_push = True
83
92
# If things aren't open, retry until they are. Don't let read requests come in any earlier
@@ -86,8 +95,15 @@ def run(self):
86
95
self .config .capture_source is not None
87
96
and self .config .capture_source != ""
88
97
):
89
- ports = ("COM" , "/dev/ttyACM" )
90
- if any (x in str (self .config .capture_source ) for x in ports ):
98
+ isSerial = any (x in str (self .config .capture_source ) for x in PORTS )
99
+
100
+ if isSerial :
101
+ if self .cv2_camera is not None :
102
+ self .cv2_camera .release ()
103
+ self .cv2_camera = None
104
+ if self .vft_camera is not None :
105
+ self .vft_camera .close ()
106
+ self .device_is_vft = False ;
91
107
if (
92
108
self .serial_connection is None
93
109
or self .camera_status == CameraState .DISCONNECTED
@@ -96,25 +112,51 @@ def run(self):
96
112
port = self .config .capture_source
97
113
self .current_capture_source = port
98
114
self .start_serial_connection (port )
99
- else :
100
- if (
115
+ elif ViveTracker .is_device_vive_tracker (self .config .capture_source ):
116
+ if self .cv2_camera is not None :
117
+ self .cv2_camera .release ()
118
+ self .cv2_camera = None
119
+ self .device_is_vft = True ;
120
+
121
+ if self .vft_camera is None :
122
+ print (self .error_message .format (self .config .capture_source ))
123
+ # capture_source is an index into a list of devices, so it should be treated as such
124
+ if self .cancellation_event .wait (WAIT_TIME ):
125
+ return
126
+ try :
127
+ # Only create the camera once, reuse it
128
+ self .vft_camera = FTCameraController (get_camera_index_by_name (self .config .capture_source ))
129
+ self .vft_camera .open ()
130
+ should_push = False
131
+ except Exception :
132
+ print (traceback .format_exc ())
133
+ if self .vft_camera is not None :
134
+ self .vft_camera .close ()
135
+ else :
136
+ # If the camera is already open it don't spam it!!
137
+ if (not self .vft_camera .is_open ):
138
+ self .vft_camera .open ()
139
+ should_push = False
140
+ elif (
101
141
self .cv2_camera is None
102
142
or not self .cv2_camera .isOpened ()
103
143
or self .camera_status == CameraState .DISCONNECTED
104
- or self .config .capture_source != self .current_capture_source
144
+ or get_camera_index_by_name ( self .config .capture_source ) != self .current_capture_source
105
145
):
146
+ if self .vft_camera is not None :
147
+ self .vft_camera .close ()
148
+ self .device_is_vft = False ;
149
+
106
150
print (self .error_message .format (self .config .capture_source ))
107
151
# This requires a wait, otherwise we can error and possible screw up the camera
108
152
# firmware. Fickle things.
109
153
if self .cancellation_event .wait (WAIT_TIME ):
110
154
return
111
-
155
+
112
156
if self .config .capture_source not in self .camera_list :
113
157
self .current_capture_source = self .config .capture_source
114
158
else :
115
- self .current_capture_source = get_camera_index_by_name (
116
- self .config .capture_source
117
- )
159
+ self .current_capture_source = get_camera_index_by_name (self .config .capture_source )
118
160
119
161
if self .config .use_ffmpeg :
120
162
self .cv2_camera = cv2 .VideoCapture (
@@ -139,9 +181,12 @@ def run(self):
139
181
self .cv2_camera .set (
140
182
cv2 .CAP_PROP_FPS , self .settings .gui_cam_framerate
141
183
)
184
+
142
185
should_push = False
143
186
else :
144
187
# We don't have a capture source to try yet, wait for one to show up in the GUI.
188
+ if self .vft_camera is not None :
189
+ self .vft_camera .close ()
145
190
if self .cancellation_event .wait (WAIT_TIME ):
146
191
self .camera_status = CameraState .DISCONNECTED
147
192
return
@@ -151,24 +196,35 @@ def run(self):
151
196
if should_push and not self .capture_event .wait (timeout = 0.02 ):
152
197
continue
153
198
if self .config .capture_source is not None :
154
- ports = ("COM" , "/dev/ttyACM" )
155
- if any (x in str (self .config .capture_source ) for x in ports ):
199
+ if isSerial :
156
200
self .get_serial_camera_picture (should_push )
157
201
else :
158
202
self .__del__ ()
159
- self .get_cv2_camera_picture (should_push )
203
+ self .get_camera_picture (should_push )
160
204
if not should_push :
161
205
# if we get all the way down here, consider ourselves connected
162
206
self .camera_status = CameraState .CONNECTED
163
207
164
- def get_cv2_camera_picture (self , should_push ):
208
+ def get_camera_picture (self , should_push ):
165
209
try :
166
- ret , image = self .cv2_camera .read ()
167
- if not ret :
168
- self .cv2_camera .set (cv2 .CAP_PROP_POS_FRAMES , 0 )
169
- raise RuntimeError (lang ._instance .get_string ("error.frame" ))
210
+ image = None
211
+ # Is the current camera a Vive Facial Tracker and have we opened a connection to it before?
212
+ if self .vft_camera is not None and self .device_is_vft :
213
+ image = self .vft_camera .get_image ()
214
+ if image is None :
215
+ return
216
+ self .frame_number = self .frame_number + 1
217
+ elif self .cv2_camera is not None and self .cv2_camera .isOpened ():
218
+ ret , image = self .cv2_camera .read ()
219
+ if not ret :
220
+ self .cv2_camera .set (cv2 .CAP_PROP_POS_FRAMES , 0 )
221
+ raise RuntimeError (lang ._instance .get_string ("error.frame" ))
222
+ self .frame_number = self .cv2_camera .get (cv2 .CAP_PROP_POS_FRAMES ) + 1
223
+ else :
224
+ # Switching from a Vive Facial Tracker to a CV2 camera
225
+ return
226
+
170
227
self .FRAME_SIZE = image .shape
171
- frame_number = self .cv2_camera .get (cv2 .CAP_PROP_POS_FRAMES )
172
228
# Calculate FPS
173
229
current_frame_time = time .time () # Should be using "time.perf_counter()", not worth ~3x cycles?
174
230
delta_time = current_frame_time - self .last_frame_time
@@ -179,8 +235,9 @@ def get_cv2_camera_picture(self, should_push):
179
235
self .bps = image .nbytes * self .fps
180
236
181
237
if should_push :
182
- self .push_image_to_queue (image , frame_number + 1 , self .fps )
238
+ self .push_image_to_queue (image , self . frame_number , self .fps )
183
239
except Exception :
240
+ FTCameraController ._logger .exception ("get_image" )
184
241
print (
185
242
f'{ Fore .YELLOW } [{ lang ._instance .get_string ("log.warn" )} ] { lang ._instance .get_string ("warn.captureProblem" )} { Fore .RESET } '
186
243
)
0 commit comments