removed old async read_updates in favor of silent frame grabbing. Added for loop handling. Set default framerate to infinite. Added no_display example.
This commit is contained in:
@@ -107,3 +107,4 @@ venv.bak/
|
||||
|
||||
.idea/
|
||||
.pytest_cache/
|
||||
.coveragerc
|
||||
|
||||
@@ -6,7 +6,6 @@ display is a function that displays these in their own windows.
|
||||
|
||||
__version__ = "0.7.2"
|
||||
|
||||
from .window.subscriber_windows import display, breakpoint_display
|
||||
from .frame.frame_updater import read_updates
|
||||
from .window.subscriber_windows import display, breakpoint_display, read_updates
|
||||
from .frame.frame_publishing import publish_updates_zero_mq, publish_updates_ros
|
||||
from . import effects
|
||||
|
||||
@@ -9,7 +9,7 @@ np_cam simulates numpy arrays as OpenCV cameras
|
||||
"""
|
||||
|
||||
from . import subscriber_dictionary
|
||||
from .frame_updater import FrameUpdater, read_updates
|
||||
from .frame_updater import FrameUpdater
|
||||
from .get_frame_ids import get_cam_ids
|
||||
from .np_to_opencv import NpCam
|
||||
from .frame_publishing import pub_cam_thread
|
||||
|
||||
@@ -20,7 +20,7 @@ def pub_cam_loop(
|
||||
cam_id: Union[int, str, np.ndarray],
|
||||
request_size: Tuple[int, int] = (-1, -1),
|
||||
high_speed: bool = True,
|
||||
fps_limit: float = 240,
|
||||
fps_limit: float = float("inf"),
|
||||
) -> bool:
|
||||
"""
|
||||
Publish whichever camera you select to CVCams.<cam_id>.Vid.
|
||||
@@ -47,7 +47,6 @@ def pub_cam_loop(
|
||||
|
||||
subscriber_dictionary.register_cam(name)
|
||||
|
||||
# cam.set(cv2.CAP_PROP_CONVERT_RGB, 0)
|
||||
frame_counter = 0
|
||||
|
||||
sub = subscriber_dictionary.cam_cmd_sub(name)
|
||||
@@ -89,7 +88,7 @@ def pub_cam_thread(
|
||||
cam_id: Union[int, str],
|
||||
request_ize: Tuple[int, int] = (-1, -1),
|
||||
high_speed: bool = True,
|
||||
fps_limit: float = 240,
|
||||
fps_limit: float = float("inf"),
|
||||
) -> threading.Thread:
|
||||
"""Run pub_cam_loop in a new thread. Starts on creation."""
|
||||
t = threading.Thread(
|
||||
@@ -186,8 +185,8 @@ async def publish_updates_ros(
|
||||
}[dtype]
|
||||
else:
|
||||
msg_type = (
|
||||
dtype
|
||||
) # allow users to use their own custom messages in numpy arrays
|
||||
dtype # allow users to use their own custom messages in numpy arrays
|
||||
)
|
||||
return msg_type
|
||||
|
||||
publishers: Dict[str, rospy.Publisher] = {}
|
||||
|
||||
@@ -26,7 +26,7 @@ class FrameUpdater(threading.Thread):
|
||||
callbacks: Optional[Union[List[FrameCallable], FrameCallable]] = None,
|
||||
request_size: Tuple[int, int] = (-1, -1),
|
||||
high_speed: bool = True,
|
||||
fps_limit: float = 240,
|
||||
fps_limit: float = float("inf"),
|
||||
):
|
||||
"""Create the frame updater thread."""
|
||||
super(FrameUpdater, self).__init__(target=self.loop, args=())
|
||||
@@ -115,7 +115,7 @@ class FrameUpdater(threading.Thread):
|
||||
raise self.exception_raised
|
||||
|
||||
|
||||
async def read_updates(
|
||||
'''async def read_updates(
|
||||
*vids,
|
||||
callbacks: Optional[
|
||||
Union[
|
||||
@@ -174,7 +174,7 @@ async def read_updates(
|
||||
for v in vid_names:
|
||||
subscriber_dictionary.stop_cam(v)
|
||||
for v in vid_threads:
|
||||
v.join()
|
||||
v.join()'''
|
||||
|
||||
|
||||
async def read_updates_zero_mq(
|
||||
|
||||
@@ -30,6 +30,7 @@ class SubscriberWindows(object):
|
||||
window_names: Iterable[str] = ("displayarray",),
|
||||
video_sources: Iterable[Union[str, int]] = (0,),
|
||||
callbacks: Optional[List[Callable[[np.ndarray], Any]]] = None,
|
||||
silent: bool = False,
|
||||
):
|
||||
"""Create the array displaying window."""
|
||||
self.source_names: List[Union[str, int]] = []
|
||||
@@ -39,14 +40,16 @@ class SubscriberWindows(object):
|
||||
self.window_names: List[str] = []
|
||||
self.input_cams: List[str] = []
|
||||
self.exited = False
|
||||
self.silent = silent
|
||||
|
||||
if callbacks is None:
|
||||
callbacks = []
|
||||
for name in video_sources:
|
||||
self.add_source(name)
|
||||
self.callbacks = callbacks
|
||||
for name in window_names:
|
||||
self.add_window(name)
|
||||
if not self.silent:
|
||||
for name in window_names:
|
||||
self.add_window(name)
|
||||
|
||||
self.update()
|
||||
|
||||
@@ -54,6 +57,11 @@ class SubscriberWindows(object):
|
||||
self.update()
|
||||
return not self.exited
|
||||
|
||||
def __iter__(self):
|
||||
while not self.exited:
|
||||
self.update()
|
||||
yield self.frames
|
||||
|
||||
def block(self):
|
||||
"""Update the window continuously while blocking the outer program."""
|
||||
self.loop()
|
||||
@@ -109,7 +117,7 @@ class SubscriberWindows(object):
|
||||
mousey = MouseEvent(event, x, y, flags, param)
|
||||
window_commands.mouse_pub.publish(mousey)
|
||||
|
||||
def _display_frames(self, frames, win_num=0, ids=None):
|
||||
def display_frames(self, frames, win_num=0, ids=None):
|
||||
if isinstance(frames, Exception):
|
||||
raise frames
|
||||
for f in range(len(frames)):
|
||||
@@ -122,7 +130,7 @@ class SubscriberWindows(object):
|
||||
and (len(frames[f].shape) != 3 or frames[f].shape[-1] != 3)
|
||||
)
|
||||
):
|
||||
win_num = self._display_frames(frames[f], win_num, ids)
|
||||
win_num = self.display_frames(frames[f], win_num, ids)
|
||||
else:
|
||||
if len(self.window_names) <= win_num:
|
||||
self.add_window(str(win_num))
|
||||
@@ -157,7 +165,7 @@ class SubscriberWindows(object):
|
||||
self.frames[fr] = self.callbacks[-1](self.frames[fr])
|
||||
break
|
||||
|
||||
def update_window_frames(self):
|
||||
def update_frames(self):
|
||||
"""Update the windows with the newest data for all frames."""
|
||||
self.frames = []
|
||||
for i in range(len(self.input_vid_global_names)):
|
||||
@@ -172,9 +180,11 @@ class SubscriberWindows(object):
|
||||
frame = c(self.frames[-1])
|
||||
if frame is not None:
|
||||
self.frames[-1] = frame
|
||||
self.__check_too_many_channels()
|
||||
if not self.silent:
|
||||
self.__check_too_many_channels()
|
||||
self.FRAME_DICT[self.input_vid_global_names[i]] = NoData()
|
||||
self._display_frames(self.frames)
|
||||
if not self.silent:
|
||||
self.display_frames(self.frames)
|
||||
|
||||
def update(self, arr: np.ndarray = None, id: str = None):
|
||||
"""Update window frames once. Optionally add a new input and input id."""
|
||||
@@ -182,9 +192,10 @@ class SubscriberWindows(object):
|
||||
global_cv_display_callback(arr, id)
|
||||
if id not in self.input_cams:
|
||||
self.add_source(id)
|
||||
self.add_window(id)
|
||||
if not self.silent:
|
||||
self.add_window(id)
|
||||
sub_cmd = window_commands.win_cmd_sub()
|
||||
self.update_window_frames()
|
||||
self.update_frames()
|
||||
msg_cmd = sub_cmd.get()
|
||||
key = self.handle_keys(cv2.waitKey(1))
|
||||
return msg_cmd, key
|
||||
@@ -231,7 +242,7 @@ class SubscriberWindows(object):
|
||||
def _get_video_callback_dict_threads(
|
||||
*vids,
|
||||
callbacks: Optional[Dict[Any, Union[FrameCallable, List[FrameCallable]]]] = None,
|
||||
fps=240,
|
||||
fps=float("inf"),
|
||||
size=(-1, -1),
|
||||
):
|
||||
assert callbacks is not None
|
||||
@@ -264,7 +275,7 @@ def _get_video_threads(
|
||||
FrameCallable,
|
||||
]
|
||||
] = None,
|
||||
fps=240,
|
||||
fps=float("inf"),
|
||||
size=(-1, -1),
|
||||
):
|
||||
vid_threads: List[Thread] = []
|
||||
@@ -300,8 +311,9 @@ def display(
|
||||
] = None,
|
||||
window_names=None,
|
||||
blocking=False,
|
||||
fps_limit=240,
|
||||
fps_limit=float("inf"),
|
||||
size=(-1, -1),
|
||||
silent=False,
|
||||
):
|
||||
"""
|
||||
Display all the arrays, cameras, and videos passed in.
|
||||
@@ -318,11 +330,15 @@ def display(
|
||||
if window_names is None:
|
||||
window_names = ["window {}".format(i) for i in range(len(vids))]
|
||||
if blocking:
|
||||
SubscriberWindows(window_names=window_names, video_sources=vids).loop()
|
||||
SubscriberWindows(
|
||||
window_names=window_names, video_sources=vids, silent=silent
|
||||
).loop()
|
||||
for vt in vid_threads:
|
||||
vt.join()
|
||||
else:
|
||||
s = SubscriberWindows(window_names=window_names, video_sources=vids)
|
||||
s = SubscriberWindows(
|
||||
window_names=window_names, video_sources=vids, silent=silent
|
||||
)
|
||||
s.close_threads = vid_threads
|
||||
return s
|
||||
|
||||
@@ -330,3 +346,7 @@ def display(
|
||||
def breakpoint_display(*args, **kwargs):
|
||||
"""Display all the arrays, cameras, and videos passed in. Stops code execution until the window is closed."""
|
||||
return display(*args, **kwargs, blocking=True)
|
||||
|
||||
|
||||
def read_updates(*args, **kwargs):
|
||||
return display(*args, **kwargs, silent=True)
|
||||
|
||||
+2
-1
@@ -12,7 +12,8 @@
|
||||
#
|
||||
import os
|
||||
import sys
|
||||
sys.path.insert(0, os.path.abspath(f'..{os.sep}..'))
|
||||
|
||||
sys.path.insert(0, os.path.abspath(f"..{os.sep}.."))
|
||||
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
@@ -6,4 +6,11 @@ def black_and_white(arr):
|
||||
return (np.sum(arr, axis=-1) / 3).astype(np.uint8)
|
||||
|
||||
|
||||
display(0, callbacks=black_and_white, blocking=True)
|
||||
import time
|
||||
|
||||
t0 = t1 = time.time()
|
||||
for up in display(0, size=(1, 1), callbacks=black_and_white):
|
||||
if up:
|
||||
t1 = time.time()
|
||||
print(1.0 / (t1 - t0))
|
||||
t0 = t1
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
from displayarray import read_updates, end_feeds
|
||||
import time
|
||||
import cProfile
|
||||
from examples.videos import test_video
|
||||
|
||||
|
||||
def profile_reading(total_seconds=2):
|
||||
t_init = t01 = time.time()
|
||||
times = []
|
||||
started = False
|
||||
for up in read_updates(test_video, size=(1, 1)):
|
||||
if up:
|
||||
t1 = time.time()
|
||||
if started:
|
||||
times.append((t1 - t01) * 1000)
|
||||
t01 = t1
|
||||
started = True
|
||||
if started:
|
||||
t2 = time.time()
|
||||
if t2 - t_init >= total_seconds:
|
||||
if times:
|
||||
print(f"Average framerate: {1000 / (sum(times) / len(times))}fps")
|
||||
else:
|
||||
print("failure")
|
||||
break
|
||||
else:
|
||||
t_init = time.time()
|
||||
|
||||
|
||||
cProfile.run("profile_reading()")
|
||||
@@ -145,6 +145,6 @@ def test_pub_cam_thread():
|
||||
pub_cam_thread(5)
|
||||
|
||||
mock_thread.assert_called_once_with(
|
||||
target=fpub.pub_cam_loop, args=(5, (-1, -1), True, 240)
|
||||
target=fpub.pub_cam_loop, args=(5, (-1, -1), True, float("inf"))
|
||||
)
|
||||
thread_instance.start.assert_called_once()
|
||||
|
||||
@@ -14,7 +14,7 @@ def test_init_defaults():
|
||||
assert ud.callbacks == []
|
||||
assert ud.request_size == (-1, -1)
|
||||
assert ud.high_speed == True
|
||||
assert ud.fps_limit == 240
|
||||
assert ud.fps_limit == float("inf")
|
||||
|
||||
|
||||
def test_init():
|
||||
@@ -57,7 +57,7 @@ def test_loop():
|
||||
|
||||
ud.loop()
|
||||
|
||||
mock_pubcam_thread.assert_called_once_with(0, (-1, -1), True, 240)
|
||||
mock_pubcam_thread.assert_called_once_with(0, (-1, -1), True, float("inf"))
|
||||
mock_frame_sub.assert_called_once_with("0")
|
||||
handler_cmd_sub.assert_called_once_with("0")
|
||||
sub_cam.get.assert_has_calls([mock.call(blocking=True, timeout=1.0)] * 3)
|
||||
|
||||
@@ -181,7 +181,7 @@ def test_handle_mouse():
|
||||
mock_win_cmd.mouse_pub.publish.assert_called_once_with(mock_mousey)
|
||||
|
||||
|
||||
def test_update_window_frames():
|
||||
def test_update_frames():
|
||||
sub_win.SubscriberWindows.FRAME_DICT = {}
|
||||
with mock.patch.object(cv2, "namedWindow"), mock.patch.object(
|
||||
cv2, "imshow"
|
||||
@@ -191,13 +191,13 @@ def test_update_window_frames():
|
||||
frame = np.ones((100, 100))
|
||||
sw.FRAME_DICT["0"] = frame
|
||||
|
||||
sw.update_window_frames()
|
||||
sw.update_frames()
|
||||
|
||||
assert sw.frames == [frame]
|
||||
mock_imshow.assert_called_once_with("displayarray (press ESC to quit)", frame)
|
||||
|
||||
|
||||
def test_update_window_frames_callback():
|
||||
def test_update_frames_callback():
|
||||
sub_win.SubscriberWindows.FRAME_DICT = {}
|
||||
with mock.patch.object(cv2, "namedWindow"), mock.patch.object(
|
||||
cv2, "imshow"
|
||||
@@ -217,7 +217,7 @@ def test_update_window_frames_callback():
|
||||
sw.FRAME_DICT["0"] = frame
|
||||
sw.FRAME_DICT["1"] = frame
|
||||
|
||||
sw.update_window_frames()
|
||||
sw.update_frames()
|
||||
|
||||
assert sw.frames == [frame3, frame3]
|
||||
assert np.all(cb.mock_calls[0].args[0] == frame)
|
||||
@@ -230,7 +230,7 @@ def test_update_window_frames_callback():
|
||||
)
|
||||
|
||||
|
||||
def test_update_window_frames_too_many_channels():
|
||||
def test_update_frames_too_many_channels():
|
||||
sub_win.SubscriberWindows.FRAME_DICT = {}
|
||||
with mock.patch.object(cv2, "namedWindow"), mock.patch.object(
|
||||
cv2, "imshow"
|
||||
@@ -242,7 +242,7 @@ def test_update_window_frames_too_many_channels():
|
||||
frame = np.ones((100, 100, 100))
|
||||
sw.FRAME_DICT["0"] = frame
|
||||
|
||||
sw.update_window_frames()
|
||||
sw.update_frames()
|
||||
|
||||
mock_print.assert_has_calls(
|
||||
[
|
||||
@@ -263,7 +263,7 @@ def test_update_window_frames_too_many_channels():
|
||||
assert sw.frames[0].shape[-1] == 3
|
||||
|
||||
|
||||
def test_update_window_frames_nested():
|
||||
def test_update_frames_nested():
|
||||
sub_win.SubscriberWindows.FRAME_DICT = {}
|
||||
with mock.patch.object(cv2, "namedWindow"), mock.patch.object(
|
||||
cv2, "imshow"
|
||||
@@ -273,7 +273,7 @@ def test_update_window_frames_nested():
|
||||
frame = np.ones((20, 100, 100, 100))
|
||||
sw.FRAME_DICT["0"] = frame
|
||||
|
||||
sw.update_window_frames()
|
||||
sw.update_frames()
|
||||
|
||||
assert np.all(sw.frames[0] == np.ones((20, 100, 100, 3)))
|
||||
assert len(sw.frames) == 1
|
||||
@@ -319,7 +319,7 @@ def test_update_window_frames_nested():
|
||||
assert np.all(mock_imshow.mock_calls[19].args[1] == np.ones((100, 100, 3)))
|
||||
|
||||
|
||||
def test_update_window_frames_exception():
|
||||
def test_update_frames_exception():
|
||||
sub_win.SubscriberWindows.FRAME_DICT = {}
|
||||
with mock.patch.object(cv2, "namedWindow"), mock.patch.object(
|
||||
cv2, "imshow"
|
||||
@@ -330,14 +330,14 @@ def test_update_window_frames_exception():
|
||||
sw.FRAME_DICT["0"] = frame
|
||||
|
||||
with pytest.raises(RuntimeError) as e:
|
||||
sw.update_window_frames()
|
||||
sw.update_frames()
|
||||
assert e.value == frame
|
||||
|
||||
|
||||
def test_update():
|
||||
sub_win.SubscriberWindows.FRAME_DICT = {}
|
||||
with mock.patch.object(cv2, "namedWindow"), mock.patch.object(
|
||||
sub_win.SubscriberWindows, "update_window_frames"
|
||||
sub_win.SubscriberWindows, "update_frames"
|
||||
) as mock_update_win_frames, mock.patch(
|
||||
"displayarray.window.subscriber_windows.window_commands"
|
||||
) as mock_win_cmd, mock.patch.object(
|
||||
@@ -364,7 +364,7 @@ def test_update():
|
||||
def test_update_with_array():
|
||||
sub_win.SubscriberWindows.FRAME_DICT = {}
|
||||
with mock.patch.object(cv2, "namedWindow"), mock.patch.object(
|
||||
sub_win.SubscriberWindows, "update_window_frames"
|
||||
sub_win.SubscriberWindows, "update_frames"
|
||||
) as mock_update_win_frames, mock.patch(
|
||||
"displayarray.window.subscriber_windows.window_commands"
|
||||
) as mock_win_cmd, mock.patch.object(
|
||||
@@ -495,13 +495,13 @@ def test_display():
|
||||
|
||||
fup.assert_has_calls(
|
||||
[
|
||||
mock.call(0, fps_limit=240, request_size=(50, 50)),
|
||||
mock.call(1, fps_limit=240, request_size=(50, 50)),
|
||||
mock.call(0, fps_limit=float("inf"), request_size=(50, 50)),
|
||||
mock.call(1, fps_limit=float("inf"), request_size=(50, 50)),
|
||||
]
|
||||
)
|
||||
assert fup_inst.start.call_count == 2
|
||||
sws.assert_called_once_with(
|
||||
window_names=["window 0", "window 1"], video_sources=(0, 1)
|
||||
window_names=["window 0", "window 1"], video_sources=(0, 1), silent=False
|
||||
)
|
||||
assert sws_inst.close_threads == [fup_inst, fup_inst]
|
||||
assert d == sws_inst
|
||||
@@ -521,7 +521,7 @@ def test_display_blocking():
|
||||
|
||||
assert fup_inst.start.call_count == 2
|
||||
sws.assert_called_once_with(
|
||||
window_names=["window 0", "window 1"], video_sources=(0, 1)
|
||||
window_names=["window 0", "window 1"], video_sources=(0, 1), silent=False
|
||||
)
|
||||
sws_inst.loop.assert_called_once()
|
||||
assert fup_inst.join.call_count == 2
|
||||
@@ -540,8 +540,12 @@ def test_display_callbacks():
|
||||
|
||||
fup.assert_has_calls(
|
||||
[
|
||||
mock.call(0, callbacks=[cb], fps_limit=240, request_size=(-1, -1)),
|
||||
mock.call(1, callbacks=[cb], fps_limit=240, request_size=(-1, -1)),
|
||||
mock.call(
|
||||
0, callbacks=[cb], fps_limit=float("inf"), request_size=(-1, -1)
|
||||
),
|
||||
mock.call(
|
||||
1, callbacks=[cb], fps_limit=float("inf"), request_size=(-1, -1)
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
@@ -574,10 +578,17 @@ def test_display_callbacks_dict():
|
||||
|
||||
fup.assert_has_calls(
|
||||
[
|
||||
mock.call(0, callbacks=[cb1], fps_limit=240, request_size=(-1, -1)),
|
||||
mock.call(
|
||||
1, callbacks=[cb1, cb2], fps_limit=240, request_size=(-1, -1)
|
||||
0, callbacks=[cb1], fps_limit=float("inf"), request_size=(-1, -1)
|
||||
),
|
||||
mock.call(
|
||||
1,
|
||||
callbacks=[cb1, cb2],
|
||||
fps_limit=float("inf"),
|
||||
request_size=(-1, -1),
|
||||
),
|
||||
mock.call(
|
||||
2, callbacks=[cb3], fps_limit=float("inf"), request_size=(-1, -1)
|
||||
),
|
||||
mock.call(2, callbacks=[cb3], fps_limit=240, request_size=(-1, -1)),
|
||||
]
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user