Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions BabbleApp/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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()
self.capture_event.clear()
8 changes: 4 additions & 4 deletions BabbleApp/camera_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
37 changes: 25 additions & 12 deletions BabbleApp/utils/misc_utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import typing
import serial
import serial.tools.list_ports
import sys
Expand All @@ -10,13 +9,15 @@
import subprocess
import sounddevice as sd
import soundfile as sf
import contextlib

bg_color_highlight = "#424042"
bg_color_clear = "#242224"

onnx_providers = [
"DmlExecutionProvider",
"CUDAExecutionProvider",
"CoreMLExecutionProvider",
"CPUExecutionProvider",
]

Expand All @@ -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 = []
Expand All @@ -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:
Expand Down Expand Up @@ -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"))