Skip to content

Commit 9017457

Browse files
Merge pull request Project-Babble#59 from SnuffSocket/ema-branch
Feat: New FPS/BPS calc
2 parents 1cf649f + cb5f2ac commit 9017457

File tree

2 files changed

+47
-76
lines changed

2 files changed

+47
-76
lines changed

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

+5-8
Original file line numberDiff line numberDiff line change
@@ -263,18 +263,15 @@ def __init__(self, widget_id: Tab, main_config: BabbleConfig, osc_queue: Queue):
263263
self.figure = None
264264
self.is_mouse_up = True
265265
self.in_roi_mode = False
266-
self.movavg_fps_queue = deque(maxlen=120)
267-
self.movavg_bps_queue = deque(maxlen=120)
266+
self.bps = 0
268267

269268
def _movavg_fps(self, next_fps):
270-
self.movavg_fps_queue.append(next_fps)
271-
fps = round(sum(self.movavg_fps_queue) / len(self.movavg_fps_queue))
272-
millisec = round((1 / fps if fps else 0) * 1000)
273-
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"
274271

275272
def _movavg_bps(self, next_bps):
276-
self.movavg_bps_queue.append(next_bps)
277-
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"
278275

279276
def started(self):
280277
return not self.cancellation_event.is_set()

0 commit comments

Comments
 (0)