Skip to content

Commit 54eff83

Browse files
2 parents ce1a063 + 9017457 commit 54eff83

12 files changed

+491
-409
lines changed

BabbleApp/Locale/English/locale.json

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"general.recalibrate": "Recalibrate Address",
1717
"general.recalibrateTooltip": "OSC address we use for recalibrating",
1818
"general.uvcCameraSettings": "UVC Camera Settings",
19+
"general.windowFocus": "Interface Paused",
1920
"general.useRedChannel": "Use Red Channel",
2021
"general.useRedChannelTooltip": "Uses only the red channel for Omnicept capture",
2122
"general.xResolution": "X Resolution",

BabbleApp/Locale/OwO/locale.json

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"general.recalibrate": "Wecawibwate Addwess :3",
1717
"general.recalibrateTooltip": "OSC addwess we use fow wecawibwating OwO",
1818
"general.uvcCameraSettings": "UVC Camewa Settings UwU",
19+
"general.windowFocus": "Intwaface Pawused",
1920
"general.useRedChannel": "Use Wed Channew >w<",
2021
"general.useRedChannelTooltip": "Uses onwy the wed channew fow Omnicept captuwe nya~",
2122
"general.xResolution": "X Wesowution :3",

BabbleApp/Locale/Pirate Speak/locale.json

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"general.recalibrate": "Recalibrate Yer Bearings",
1717
"general.recalibrateTooltip": "OSC bearings we use fer recalibratin'",
1818
"general.uvcCameraSettings": "Spyglass Settings",
19+
"general.windowFocus": "Interfayce Be Paused",
1920
"general.useRedChannel": "Use th' Red Channel",
2021
"general.useRedChannelTooltip": "Uses only th' red channel fer Omnicept plunderin'",
2122
"general.xResolution": "X Marks th' Spot",

BabbleApp/babble_processor.py

+10-4
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ def __init__(
5151
capture_queue_incoming: "queue.Queue(maxsize=2)",
5252
image_queue_outgoing: "queue.Queue(maxsize=2)",
5353
cam_id,
54+
osc_queue: queue.Queue,
5455
):
5556
self.main_config = BabbleSettingsConfig
5657
self.config = config
@@ -63,6 +64,7 @@ def __init__(
6364
self.cancellation_event = cancellation_event
6465
self.capture_event = capture_event
6566
self.cam_id = cam_id
67+
self.osc_queue = osc_queue
6668

6769
# Image state
6870
self.previous_image = None
@@ -130,15 +132,19 @@ def output_images_and_update(self, output_information: CamInfo):
130132
axis=1,
131133
)
132134
self.image_queue_outgoing.put((image_stack, output_information))
135+
if self.image_queue_outgoing.qsize() > 1:
136+
self.image_queue_outgoing.get()
137+
133138
self.previous_image = self.current_image
134139
self.previous_rotation = self.config.rotation_angle
140+
141+
# Relay information to OSC
142+
self.osc_queue.put((None, output_information))
135143
except: # If this fails it likely means that the images are not the same size for some reason.
136144
print(
137145
f'\033[91m[{lang._instance.get_string("log.error")}] {lang._instance.get_string("error.size")}.\033[0m'
138146
)
139147

140-
pass
141-
142148
def capture_crop_rotate_image(self):
143149
# Get our current frame
144150

@@ -225,7 +231,7 @@ def run(self):
225231
self.current_image,
226232
self.current_frame_number,
227233
self.current_fps,
228-
) = self.capture_queue_incoming.get(block=True, timeout=0.2)
234+
) = self.capture_queue_incoming.get(block=True, timeout=0.1)
229235
except queue.Empty:
230236
# print("No image available")
231237
continue
@@ -250,10 +256,10 @@ def run(self):
250256
run_model(self)
251257
if self.settings.use_calibration:
252258
self.output = cal.cal_osc(self, self.output)
253-
254259
# else:
255260
# pass
256261
# print(self.output)
262+
257263
self.output_images_and_update(CamInfo(self.current_algo, self.output))
258264

259265
def get_framesize(self):

BabbleApp/babbleapp.py

+25-2
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ def main():
129129
# Check to see if we have an ROI. If not, bring up ROI finder GUI.
130130

131131
# Spawn worker threads
132-
osc_queue: queue.Queue[tuple[bool, int, int]] = queue.Queue(maxsize=2)
132+
osc_queue: queue.Queue[tuple[bool, int, int]] = queue.Queue(maxsize=10)
133133
osc = VRChatOSC(cancellation_event, osc_queue, config)
134134
osc_thread = threading.Thread(target=osc.run)
135135
# start worker threads
@@ -206,6 +206,8 @@ def main():
206206
background_color=bg_color_highlight,
207207
),
208208
],
209+
# Keep at bottom!
210+
[sg.Text(f'- - - {lang._instance.get_string("general.windowFocus")} - - -', key="-WINFOCUS-", background_color=bg_color_clear, text_color="#F0F0F0", justification="center", expand_x=True, visible=False)],
209211
]
210212

211213
if config.cam_display_id in [Tab.CAM]:
@@ -229,10 +231,12 @@ def main():
229231
f"{appversion}", layout, icon="Images/logo.ico", background_color=bg_color_clear
230232
)
231233

234+
tint = 33
235+
fs = False
232236
# GUI Render loop
233237
while True:
234238
# First off, check for any events from the GUI
235-
event, values = window.read(timeout=1)
239+
event, values = window.read(timeout=tint)
236240

237241
# If we're in either mode and someone hits q, quit immediately
238242
if event in ("Exit", sg.WIN_CLOSED):
@@ -258,6 +262,25 @@ def main():
258262
)
259263
return
260264

265+
try:
266+
# If window isn't in focus increase timeout and stop loop early
267+
if window.TKroot.focus_get():
268+
if fs:
269+
fs = False
270+
tint = 33
271+
window["-WINFOCUS-"].update(visible=False)
272+
window["-WINFOCUS-"].hide_row()
273+
window.refresh()
274+
else:
275+
if not fs:
276+
fs = True
277+
tint = 100
278+
window["-WINFOCUS-"].update(visible=True)
279+
window["-WINFOCUS-"].unhide_row()
280+
continue
281+
except KeyError:
282+
pass
283+
261284
if values[CAM_RADIO_NAME] and config.cam_display_id != Tab.CAM:
262285
cams[0].start()
263286
settings[0].stop()

BabbleApp/babbleapp.spec

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ block_cipher = None
66
a = Analysis(['babbleapp.py'],
77
pathex=[],
88
binaries=[],
9-
datas=[("Audio/*", "Audio"), ("Images/*", "Images/")],
9+
datas=[("Audio", "Audio"), ("Images", "Images"), ("Locale", "Locale"), ("Models", "Models")],
1010
hiddenimports=[],
1111
hookspath=[],
1212
runtime_hooks=[],

BabbleApp/camera.py

+42-68
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,17 @@
1212
from config import BabbleConfig, BabbleSettingsConfig
1313
from utils.misc_utils import get_camera_index_by_name, list_camera_names
1414
from enum import Enum
15+
import sys
1516

1617
WAIT_TIME = 0.1
18+
BUFFER_SIZE = 32768
1719
MAX_RESOLUTION: int = 600
18-
1920
# Serial communication protocol:
20-
# header-begin (2 bytes)
21-
# header-type (2 bytes)
22-
# packet-size (2 bytes)
23-
# packet (packet-size bytes)
24-
ETVR_HEADER = b"\xff\xa0"
25-
ETVR_HEADER_FRAME = b"\xff\xa1"
21+
# header-begin (2 bytes) "\xff\xa0"
22+
# header-type (2 bytes) "\xff\xa1"
23+
# packet-size (2 bytes)
24+
# packet (packet-size bytes)
25+
ETVR_HEADER = b"\xff\xa0\xff\xa1"
2626
ETVR_HEADER_LEN = 6
2727

2828

@@ -48,8 +48,6 @@ def __init__(
4848
self.settings = settings
4949
self.camera_index = camera_index
5050
self.camera_list = list_camera_names()
51-
# The variable is not used
52-
# self.camera_address = config.capture_source
5351
self.camera_status_outgoing = camera_status_outgoing
5452
self.camera_output_outgoing = camera_output_outgoing
5553
self.capture_event = capture_event
@@ -59,15 +57,10 @@ def __init__(
5957

6058
self.serial_connection = None
6159
self.last_frame_time = time.time()
62-
self.frame_number = 0
6360
self.fps = 0
6461
self.bps = 0
6562
self.start = True
6663
self.buffer = b""
67-
self.pf_fps = 0
68-
self.prevft = 0
69-
self.newft = 0
70-
self.fl = [0]
7164
self.FRAME_SIZE = [0, 0]
7265

7366
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}'
@@ -175,28 +168,18 @@ def get_cv2_camera_picture(self, should_push):
175168
raise RuntimeError(lang._instance.get_string("error.frame"))
176169
self.FRAME_SIZE = image.shape
177170
frame_number = self.cv2_camera.get(cv2.CAP_PROP_POS_FRAMES)
178-
# Calculate the fps.
179-
yeah = time.time()
180-
delta_time = yeah - self.last_frame_time
181-
self.last_frame_time = yeah
182-
if delta_time > 0:
183-
self.bps = len(image) / delta_time
184-
self.frame_number = self.frame_number + 1
185-
self.fps = (self.fps + self.pf_fps) / 2
186-
self.newft = time.time()
187-
self.fps = 1 / (self.newft - self.prevft)
188-
self.prevft = self.newft
189-
self.fps = int(self.fps)
190-
if len(self.fl) < 60:
191-
self.fl.append(self.fps)
192-
else:
193-
self.fl.pop(0)
194-
self.fl.append(self.fps)
195-
self.fps = sum(self.fl) / len(self.fl)
196-
# self.bps = image.nbytes
171+
# Calculate FPS
172+
current_frame_time = time.time() # Should be using "time.perf_counter()", not worth ~3x cycles?
173+
delta_time = current_frame_time - self.last_frame_time
174+
self.last_frame_time = current_frame_time
175+
current_fps = 1 / delta_time if delta_time > 0 else 0
176+
# Exponential moving average (EMA). ~1100ns savings, delicious..
177+
self.fps = 0.02 * current_fps + 0.98 * self.fps
178+
self.bps = image.nbytes * self.fps
179+
197180
if should_push:
198-
self.push_image_to_queue(image, frame_number, self.fps)
199-
except Exception as e:
181+
self.push_image_to_queue(image, frame_number + 1, self.fps)
182+
except Exception:
200183
print(
201184
f'{Fore.YELLOW}[{lang._instance.get_string("log.warn")}] {lang._instance.get_string("warn.captureProblem")}{Fore.RESET}'
202185
)
@@ -207,7 +190,7 @@ def get_next_packet_bounds(self):
207190
beg = -1
208191
while beg == -1:
209192
self.buffer += self.serial_connection.read(2048)
210-
beg = self.buffer.find(ETVR_HEADER + ETVR_HEADER_FRAME)
193+
beg = self.buffer.find(ETVR_HEADER)
211194
# Discard any data before the frame header.
212195
if beg > 0:
213196
self.buffer = self.buffer[beg:]
@@ -225,7 +208,8 @@ def get_next_jpeg_frame(self):
225208

226209
def get_serial_camera_picture(self, should_push):
227210
conn = self.serial_connection
228-
if conn is None:
211+
# Stop spamming "Serial capture source problem" if connection is lost
212+
if conn is None or self.camera_status == CameraState.DISCONNECTED:
229213
return
230214
try:
231215
if conn.in_waiting:
@@ -240,34 +224,25 @@ def get_serial_camera_picture(self, should_push):
240224
f'{Fore.YELLOW}[{lang._instance.get_string("log.warn")}] {lang._instance.get_string("warn.frameDrop")}{Fore.RESET}'
241225
)
242226
return
243-
# Discard the serial buffer. This is due to the fact that it
244-
# may build up some outdated frames. A bit of a workaround here tbh.
245-
if conn.in_waiting >= 32768:
246-
print(
247-
f'{Fore.CYAN}[{lang._instance.get_string("log.info")}] {lang._instance.get_string("info.discardingSerial")} ({conn.in_waiting} bytes){Fore.RESET}'
248-
)
249-
conn.reset_input_buffer()
250-
self.buffer = b""
251-
# Calculate the fps.
252-
yeah = time.time()
253-
delta_time = yeah - self.last_frame_time
254-
self.last_frame_time = yeah
255-
if delta_time > 0:
256-
self.bps = len(jpeg) / delta_time
257-
self.fps = (self.fps + self.pf_fps) / 2
258-
self.newft = time.time()
259-
self.fps = 1 / (self.newft - self.prevft)
260-
self.prevft = self.newft
261-
self.fps = int(self.fps)
262-
if len(self.fl) < 60:
263-
self.fl.append(self.fps)
264-
else:
265-
self.fl.pop(0)
266-
self.fl.append(self.fps)
267-
self.fps = sum(self.fl) / len(self.fl)
268-
self.frame_number = self.frame_number + 1
227+
# Calculate FPS
228+
current_frame_time = time.time() # Should be using "time.perf_counter()", not worth ~3x cycles?
229+
delta_time = current_frame_time - self.last_frame_time
230+
self.last_frame_time = current_frame_time
231+
current_fps = 1 / delta_time if delta_time > 0 else 0
232+
# Exponential moving average (EMA). ~1100ns savings, delicious..
233+
self.fps = 0.02 * current_fps + 0.98 * self.fps
234+
self.bps = len(jpeg) * self.fps
235+
269236
if should_push:
270-
self.push_image_to_queue(image, self.frame_number, self.fps)
237+
self.push_image_to_queue(image, int(current_fps), self.fps)
238+
# Discard the serial buffer. This is due to the fact that it,
239+
# may build up some outdated frames. A bit of a workaround here tbh.
240+
# Do this at the end to give buffer time to refill.
241+
if conn.in_waiting >= BUFFER_SIZE:
242+
print(f'{Fore.CYAN}[{lang._instance.get_string("log.info")}] {lang._instance.get_string("info.discardingSerial")} ({conn.in_waiting} bytes){Fore.RESET}')
243+
conn.reset_input_buffer()
244+
self.buffer = b""
245+
271246
except Exception:
272247
print(
273248
f'{Fore.YELLOW}[{lang._instance.get_string("log.warn")}] {lang._instance.get_string("info.serialCapture")}{Fore.RESET}'
@@ -288,11 +263,10 @@ def start_serial_connection(self, port):
288263
if not any(p for p in com_ports if port in p):
289264
return
290265
try:
291-
conn = serial.Serial(
292-
baudrate=3000000, port=port, xonxoff=False, dsrdtr=False, rtscts=False
293-
)
266+
rate = 115200 if sys.platform == "darwin" else 3000000 # Higher baud rate not working on macOS
267+
conn = serial.Serial(baudrate=rate, port=port, xonxoff=False, dsrdtr=False, rtscts=False)
294268
# Set explicit buffer size for serial.
295-
conn.set_buffer_size(rx_size=32768, tx_size=32768)
269+
conn.set_buffer_size(rx_size=BUFFER_SIZE, tx_size=BUFFER_SIZE)
296270

297271
print(
298272
f'{Fore.CYAN}[{lang._instance.get_string("log.info")}] {lang._instance.get_string("info.ETVRConnected")} {port}{Fore.RESET}'

BabbleApp/camera_widget.py

+6-11
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ def __init__(self, widget_id: Tab, main_config: BabbleConfig, osc_queue: Queue):
7575
self.capture_queue,
7676
self.image_queue,
7777
self.cam_id,
78+
self.osc_queue,
7879
)
7980

8081
self.camera_status_queue = Queue(maxsize=2)
@@ -262,18 +263,15 @@ def __init__(self, widget_id: Tab, main_config: BabbleConfig, osc_queue: Queue):
262263
self.figure = None
263264
self.is_mouse_up = True
264265
self.in_roi_mode = False
265-
self.movavg_fps_queue = deque(maxlen=120)
266-
self.movavg_bps_queue = deque(maxlen=120)
266+
self.bps = 0
267267

268268
def _movavg_fps(self, next_fps):
269-
self.movavg_fps_queue.append(next_fps)
270-
fps = round(sum(self.movavg_fps_queue) / len(self.movavg_fps_queue))
271-
millisec = round((1 / fps if fps else 0) * 1000)
272-
return f"{fps} FPS {millisec} MS"
269+
# next_fps is already averaged
270+
return f"{round(next_fps)} FPS {round((1 / next_fps if next_fps else 0) * 1000)} ms"
273271

274272
def _movavg_bps(self, next_bps):
275-
self.movavg_bps_queue.append(next_bps)
276-
return f"{sum(self.movavg_bps_queue) / len(self.movavg_bps_queue) * 0.001 * 0.001 * 8:.3f} Mbps"
273+
self.bps = round(0.02 * next_bps + 0.98 * self.bps)
274+
return f"{self.bps * 0.001 * 0.001 * 8:.3f} Mbps"
277275

278276
def started(self):
279277
return not self.cancellation_event.is_set()
@@ -518,8 +516,5 @@ def render(self, window, event, values):
518516
imgbytes = cv2.imencode(".ppm", maybe_image)[1].tobytes()
519517
window[self.gui_tracking_image].update(data=imgbytes)
520518

521-
# Relay information to OSC
522-
if cam_info.info_type != CamInfoOrigin.FAILURE:
523-
self.osc_queue.put((self.cam_id, cam_info))
524519
except Empty:
525520
pass

BabbleApp/landmark_processor.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ def __init__(
117117

118118
def get_frame(self):
119119
return self.current_image_gray_clean
120-
120+
121121
def infer_frame(self, image):
122122
return write_image(self, image)
123123

@@ -130,12 +130,14 @@ def output_images_and_update(self, output_information: CamInfo):
130130
axis=1,
131131
)
132132
self.image_queue_outgoing.put((image_stack, output_information))
133+
if self.image_queue_outgoing.qsize() > 1:
134+
self.image_queue_outgoing.get()
135+
133136
self.previous_image = self.current_image
134137
self.previous_rotation = self.config.rotation_angle
135138
except: # If this fails it likely means that the images are not the same size for some reason.
136139
print('\033[91m[{lang._instance.get_string("log.error")}] Size of frames to display are of unequal sizes.\033[0m')
137140

138-
pass
139141
def capture_crop_rotate_image(self):
140142
# Get our current frame
141143

0 commit comments

Comments
 (0)