diff --git a/BabbleApp/camera.py b/BabbleApp/camera.py index 7dfe1f9..0f02fdc 100644 --- a/BabbleApp/camera.py +++ b/BabbleApp/camera.py @@ -143,13 +143,12 @@ def run(self): self.cv2_camera is None or not self.cv2_camera.isOpened() or self.camera_status == CameraState.DISCONNECTED - #or get_camera_index_by_name(self.config.capture_source) != self.current_capture_source - or self.config.capture_source != self.current_capture_source + or get_camera_index_by_name(self.config.capture_source) != self.current_capture_source ): if self.vft_camera is not None: self.vft_camera.close() self.device_is_vft = False - + print(self.error_message.format(self.config.capture_source)) # This requires a wait, otherwise we can error and possible screw up the camera # firmware. Fickle things. @@ -221,7 +220,7 @@ def get_camera_picture(self, should_push): return self.frame_number = self.frame_number + 1 elif self.cv2_camera is not None and self.cv2_camera.isOpened(): - ret, image = self.cv2_camera.read() # MJPEG Stream reconnects are currently limited by the hard coded 30 second timeout time on VideoCapture.read(). We can get around this by recompiling OpenCV or using a custom MJPEG stream imp. + ret, image = self.cv2_camera.read() # MJPEG Stream reconnects are currently limited by the hard coded 30 second timeout time on VideoCapture.read(). We can get around this by recompiling OpenCV or using a custom MJPEG stream imp. if ret and image is not None: if not ret: if not self.http: @@ -366,4 +365,4 @@ def push_image_to_queue(self, image, frame_number, fps): f'{Fore.YELLOW}[{lang._instance.get_string("log.warn")}] {lang._instance.get_string("warn.backpressure1")} {qsize}. {lang._instance.get_string("warn.backpressure2")}{Fore.RESET}' ) self.camera_output_outgoing.put((self.clamp_max_res(image), frame_number, fps)) - self.capture_event.clear() \ No newline at end of file + self.capture_event.clear() diff --git a/BabbleApp/camera_widget.py b/BabbleApp/camera_widget.py index 1d8f833..ae59781 100644 --- a/BabbleApp/camera_widget.py +++ b/BabbleApp/camera_widget.py @@ -62,7 +62,7 @@ def __init__(self, widget_id: Tab, main_config: BabbleConfig, osc_queue: Queue): self.capture_event = Event() self.capture_queue = Queue(maxsize=2) self.roi_queue = Queue(maxsize=2) - self.image_queue = Queue(maxsize=500) # This is needed to prevent the UI from freezing during widget changes. + self.image_queue = Queue(maxsize=500) # This is needed to prevent the UI from freezing during widget changes. self.babble_cnn = BabbleProcessor( self.config, @@ -285,7 +285,7 @@ def start(self): # self.babble_landmark_thread.start() self.camera_thread = Thread(target=self.camera.run) self.camera_thread.start() - + def stop(self): # If we're not running yet, bail @@ -322,7 +322,7 @@ def render(self, window, event, values): if any(x in str(value) for x in ports): self.config.capture_source = value else: - if is_valid_int_input(value): + if is_valid_int_input(value): self.config.capture_source = int(value) else: self.config.capture_source = value @@ -422,7 +422,7 @@ def render(self, window, event, values): if self.maybe_image is None: # Skip rendering or use a default/placeholder image return # Or handle appropriately - + output = self.maybe_image[0].shape self.config.roi_window_x = 0 self.config.roi_window_y = 0 diff --git a/BabbleApp/utils/misc_utils.py b/BabbleApp/utils/misc_utils.py index 83cc5e6..a9d9189 100644 --- a/BabbleApp/utils/misc_utils.py +++ b/BabbleApp/utils/misc_utils.py @@ -1,4 +1,3 @@ -import typing import serial import serial.tools.list_ports import sys @@ -10,6 +9,7 @@ import subprocess import sounddevice as sd import soundfile as sf +import contextlib bg_color_highlight = "#424042" bg_color_clear = "#242224" @@ -17,6 +17,7 @@ onnx_providers = [ "DmlExecutionProvider", "CUDAExecutionProvider", + "CoreMLExecutionProvider", "CPUExecutionProvider", ] @@ -34,7 +35,7 @@ def is_valid_float_input(value): def is_valid_int_input(value): # Allow empty string, negative sign, or an integer number return bool(re.match(r"^-?\d*$", value)) - + def list_camera_names(): cam_list = graph.get_input_devices() cam_names = [] @@ -43,21 +44,34 @@ def list_camera_names(): cam_names = cam_names + list_serial_ports() return cam_names +@contextlib.contextmanager +def suppress_stderr(): + """Context manager to suppress stderr (used for OpenCV warnings).""" + with open(os.devnull, 'w') as devnull: + old_stderr_fd = os.dup(2) + os.dup2(devnull.fileno(), 2) + try: + yield + finally: + os.dup2(old_stderr_fd, 2) + os.close(old_stderr_fd) + def list_cameras_opencv(): """Use OpenCV to check available cameras by index (fallback for Linux/macOS)""" index = 0 arr = [] - while True: - cap = cv2.VideoCapture(index) - if not cap.read()[0]: - break - else: - arr.append(f"/dev/video{index}") - cap.release() - index += 1 + with suppress_stderr(): # tell OpenCV not to throw "cannot find camera" while we probe for cameras + while True: + cap = cv2.VideoCapture(index) + if not cap.read()[0]: + cap.release() + break + else: + arr.append(f"/dev/video{index}") + cap.release() + index += 1 return arr - def is_uvc_device(device): """Check if the device is a UVC video device (not metadata)""" try: @@ -174,4 +188,3 @@ def playSound(file): def ensurePath(): if os.path.exists(os.path.join(os.getcwd(), "BabbleApp")): os.chdir(os.path.join(os.getcwd(), "BabbleApp")) -