33import numpy as np
44import queue
55import serial
6- import serial .tools .list_ports
7- import threading
6+ import sys
87import time
8+ import traceback
9+ import threading
10+ from enum import Enum
11+ import serial .tools .list_ports
912from lang_manager import LocaleStringManager as lang
1013
1114from colorama import Fore
1215from 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
1620
1721WAIT_TIME = 0.1
1822BUFFER_SIZE = 32768
2428# packet (packet-size bytes)
2529ETVR_HEADER = b"\xff \xa0 \xff \xa1 "
2630ETVR_HEADER_LEN = 6
31+ PORTS = ("COM" , "/dev/ttyACM" )
2732
2833
2934class CameraState (Enum ):
@@ -54,13 +59,15 @@ def __init__(
5459 self .cancellation_event = cancellation_event
5560 self .current_capture_source = config .capture_source
5661 self .cv2_camera : "cv2.VideoCapture" = None
62+ self .vft_camera : FTCameraController = None
5763
5864 self .serial_connection = None
5965 self .last_frame_time = time .time ()
6066 self .fps = 0
6167 self .bps = 0
6268 self .start = True
6369 self .buffer = b""
70+ self .frame_number = 0
6471 self .FRAME_SIZE = [0 , 0 ]
6572
6673 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):
7885 print (
7986 f'{ Fore .CYAN } [{ lang ._instance .get_string ("log.info" )} ] { lang ._instance .get_string ("info.exitCaptureThread" )} { Fore .RESET } '
8087 )
88+ if self .vft_camera is not None :
89+ self .vft_camera .close ()
8190 return
8291 should_push = True
8392 # 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):
8695 self .config .capture_source is not None
8796 and self .config .capture_source != ""
8897 ):
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 ;
91107 if (
92108 self .serial_connection is None
93109 or self .camera_status == CameraState .DISCONNECTED
@@ -96,25 +112,51 @@ def run(self):
96112 port = self .config .capture_source
97113 self .current_capture_source = port
98114 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 (
101141 self .cv2_camera is None
102142 or not self .cv2_camera .isOpened ()
103143 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
105145 ):
146+ if self .vft_camera is not None :
147+ self .vft_camera .close ()
148+ self .device_is_vft = False ;
149+
106150 print (self .error_message .format (self .config .capture_source ))
107151 # This requires a wait, otherwise we can error and possible screw up the camera
108152 # firmware. Fickle things.
109153 if self .cancellation_event .wait (WAIT_TIME ):
110154 return
111-
155+
112156 if self .config .capture_source not in self .camera_list :
113157 self .current_capture_source = self .config .capture_source
114158 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 )
118160
119161 if self .config .use_ffmpeg :
120162 self .cv2_camera = cv2 .VideoCapture (
@@ -139,9 +181,12 @@ def run(self):
139181 self .cv2_camera .set (
140182 cv2 .CAP_PROP_FPS , self .settings .gui_cam_framerate
141183 )
184+
142185 should_push = False
143186 else :
144187 # 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 ()
145190 if self .cancellation_event .wait (WAIT_TIME ):
146191 self .camera_status = CameraState .DISCONNECTED
147192 return
@@ -151,24 +196,35 @@ def run(self):
151196 if should_push and not self .capture_event .wait (timeout = 0.02 ):
152197 continue
153198 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 :
156200 self .get_serial_camera_picture (should_push )
157201 else :
158202 self .__del__ ()
159- self .get_cv2_camera_picture (should_push )
203+ self .get_camera_picture (should_push )
160204 if not should_push :
161205 # if we get all the way down here, consider ourselves connected
162206 self .camera_status = CameraState .CONNECTED
163207
164- def get_cv2_camera_picture (self , should_push ):
208+ def get_camera_picture (self , should_push ):
165209 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+
170227 self .FRAME_SIZE = image .shape
171- frame_number = self .cv2_camera .get (cv2 .CAP_PROP_POS_FRAMES )
172228 # Calculate FPS
173229 current_frame_time = time .time () # Should be using "time.perf_counter()", not worth ~3x cycles?
174230 delta_time = current_frame_time - self .last_frame_time
@@ -179,8 +235,9 @@ def get_cv2_camera_picture(self, should_push):
179235 self .bps = image .nbytes * self .fps
180236
181237 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 )
183239 except Exception :
240+ FTCameraController ._logger .exception ("get_image" )
184241 print (
185242 f'{ Fore .YELLOW } [{ lang ._instance .get_string ("log.warn" )} ] { lang ._instance .get_string ("warn.captureProblem" )} { Fore .RESET } '
186243 )
0 commit comments