diff --git a/displayarray/__init__.py b/displayarray/__init__.py index 0d09492..90a7bca 100644 --- a/displayarray/__init__.py +++ b/displayarray/__init__.py @@ -4,8 +4,9 @@ Display any array, webcam, or video file. display is a function that displays these in their own windows. """ -__version__ = "0.7.1" +__version__ = "0.7.2" from .window.subscriber_windows import display, breakpoint_display from .frame.frame_updater import read_updates from .frame.frame_publishing import publish_updates_zero_mq, publish_updates_ros +from . import effects diff --git a/displayarray/callbacks.py b/displayarray/callbacks.py index 0e2586c..e276829 100644 --- a/displayarray/callbacks.py +++ b/displayarray/callbacks.py @@ -24,13 +24,15 @@ class function_display_callback(object): # NOSONAR """ Used for running arbitrary functions on pixels. - >>> import random - >>> from displayarray.frame import FrameUpdater - >>> img = np.zeros((300, 300, 3)) - >>> def fun(array, coords, finished): - ... r,g,b = random.random()/20.0, random.random()/20.0, random.random()/20.0 - ... array[coords[0:2]] = (array[coords[0:2]] + [r,g,b])%1.0 - >>> FrameUpdater(video_source=img, callbacks=function_display_callback(fun)).display() + .. code-block:: python + + >>> import random + >>> from displayarray.frame import FrameUpdater + >>> img = np.zeros((300, 300, 3)) + >>> def fun(array, coords, finished): + ... r,g,b = random.random()/20.0, random.random()/20.0, random.random()/20.0 + ... array[coords[0:2]] = (array[coords[0:2]] + [r,g,b])%1.0 + >>> FrameUpdater(video_source=img, callbacks=function_display_callback(fun)).display() :param display_function: a function to run on the input image. :param finish_function: a function to run on the input image when the other function finishes. diff --git a/displayarray/effects/__init__.py b/displayarray/effects/__init__.py index 2885141..b066ad7 100644 --- a/displayarray/effects/__init__.py +++ b/displayarray/effects/__init__.py @@ -1 +1,3 @@ """Effects to run on numpy arrays to make data clearer.""" + +from . import crop, lens, select_channels diff --git a/displayarray/effects/crop.py b/displayarray/effects/crop.py index ae18e32..347a731 100644 --- a/displayarray/effects/crop.py +++ b/displayarray/effects/crop.py @@ -1,28 +1,21 @@ """Crop any n-dimensional array.""" import numpy as np -from displayarray.input import mouse_loop +from ..input import mouse_loop class Crop(object): - """ - A callback class that will return the input array cropped to the output size. N-dimensional. - - >>> crop_it = Crop((2,2,2)) - >>> arr = np.ones((4,4,4)) - >>> crop_it(arr) - array([[[1., 1.], - [1., 1.]], - - [[1., 1.], - [1., 1.]]]) - - """ + """A callback class that will return the input array cropped to the output size. N-dimensional.""" def __init__(self, output_size=(64, 64, 3), center=None): - """Create the cropper.""" + """ + Create the cropping callback class. + + :param output_size: Specified the size the input should be cropped to. Can be redefined later. + :param center: Specifies the center on the input array to take the crop out of. + """ self._output_size = None - self._center = None + self._center = np.asarray([o // 2 for o in output_size]) self.odd_center = None self.mouse_control = None self.input_size = None @@ -32,27 +25,27 @@ class Crop(object): @property def output_size(self): - """Get the output size after cropping.""" + """Get the output size.""" return self._output_size @output_size.setter def output_size(self, set): - """Set what the output size will be after cropping.""" + """Set the output size.""" self._output_size = set if self._output_size is not None: self._output_size = np.asarray(set) @property def center(self): - """Get center crop position on the input.""" + """Get the center.""" return self._center @center.setter def center(self, set): - """Set center crop position on the input.""" - self._center = set - if self._center is not None: - self._center = np.asarray(set) + """Set the center. Guarded so that colors need not be set.""" + if set is not None: + for x in range(len(set)): + self._center[x] = set[x] def __call__(self, arr): """Crop the input array to the specified output size. output is centered on self.center point on input.""" diff --git a/displayarray/effects/lens.py b/displayarray/effects/lens.py index 5dc8278..754522b 100644 --- a/displayarray/effects/lens.py +++ b/displayarray/effects/lens.py @@ -1,145 +1,156 @@ """Create lens effects. Currently only 2D+color arrays are supported.""" import numpy as np -from displayarray.input import mouse_loop +from ..input import mouse_loop import cv2 +try: + import torch +except ImportError: + torch = None # type: ignore + + +class ControllableLens(object): + """A lens callback that can be controlled by the program or the user.""" -class _ControllableLens(object): def __init__(self, use_bleed=False, zoom=1, center=None): - self.center = center - self.zoom = zoom + """Create the lens callback.""" + self._center = center + self._zoom = zoom self.use_bleed = use_bleed self.bleed = None self.mouse_control = None - def check_setup_bleed(self, arr): + def _check_setup_bleed(self, arr): if not isinstance(self.bleed, np.ndarray) and self.use_bleed: self.bleed = np.zeros_like(arr) def run_bleed(self, arr, x, y): + """Spread color outwards, like food coloring in water.""" arr[y, ...] = (arr[(y + 1) % len(y), ...] + arr[(y - 1) % len(y), ...]) / 2 arr[:, x, ...] = ( arr[:, (x + 1) % len(x), ...] + arr[:, (x - 1) % len(x), ...] ) / 2 -class Barrel(_ControllableLens): - """ - Create a barrel distortion. +class Barrel(ControllableLens): + """A barrel lens distortion callback.""" - >>> distort_it = Barrel(zoom=1, barrel_power=1.5) - >>> x = np.linspace(0, 1, 4) - >>> y = np.linspace(0, 1, 4) - >>> c = np.linspace(0, 1, 2) - >>> arrx, arry, arrc = np.meshgrid(x,y,c) - >>> arrx - array([[[0. , 0. ], - [0.33333333, 0.33333333], - [0.66666667, 0.66666667], - [1. , 1. ]], - - [[0. , 0. ], - [0.33333333, 0.33333333], - [0.66666667, 0.66666667], - [1. , 1. ]], - - [[0. , 0. ], - [0.33333333, 0.33333333], - [0.66666667, 0.66666667], - [1. , 1. ]], - - [[0. , 0. ], - [0.33333333, 0.33333333], - [0.66666667, 0.66666667], - [1. , 1. ]]]) - - >>> distort_it(arrx) - array([[[0.33333333, 0.33333333], - [0.33333333, 0.33333333], - [0.66666667, 0.66666667], - [0.66666667, 0.66666667]], - - [[0.33333333, 0.33333333], - [0.33333333, 0.33333333], - [0.66666667, 0.66666667], - [0.66666667, 0.66666667]], - - [[0.33333333, 0.33333333], - [0.33333333, 0.33333333], - [0.66666667, 0.66666667], - [0.66666667, 0.66666667]], - - [[0.33333333, 0.33333333], - [0.33333333, 0.33333333], - [0.66666667, 0.66666667], - [0.66666667, 0.66666667]]]) - - :param zoom: How far to zoom into the array - :param barrel_power: How much to distort. - 1 = no distortion. >1 increases size of center. 0 0: - self.zoom *= 1.1 - else: - self.zoom /= 1.1 + if self.input_size is not None: + if crop_size is not None: + self.center = [ + me.y * self.input_size[0] / crop_size[0], + me.x * self.input_size[1] / crop_size[1], + ] else: - if me.flags > 0: - self.barrel_power *= 1.1 + self.center = [me.y, me.x] + if me.event == cv2.EVENT_MOUSEWHEEL: + if me.flags & cv2.EVENT_FLAG_CTRLKEY: + if me.flags > 0: + self.zoom *= 1.1 + else: + self.zoom /= 1.1 else: - self.barrel_power /= 1.1 + if me.flags > 0: + self.barrel_power *= 1.1 + else: + self.barrel_power /= 1.1 + print(self.barrel_power) self.mouse_control = m_loop return self def __call__(self, arr): - """Run the distortion on an array.""" + """Run the lens distortion algorithm on the input.""" zoom_out = 1.0 / self.zoom - self.check_setup_bleed(arr) + self._check_setup_bleed(arr) + + self.input_size = arr.shape y = np.arange(arr.shape[0]) x = np.arange(arr.shape[1]) - if self.center is None: - self.center = [len(y) / 2.0, len(x) / 2.0] + if self._center is None: + self._center = [len(y) / 2.0, len(x) / 2.0] y2_ = (y - (len(y) / 2.0)) * zoom_out / arr.shape[0] x2_ = (x - (len(x) / 2.0)) * zoom_out / arr.shape[1] p2 = np.array(np.meshgrid(x2_, y2_)) - cy = self.center[0] / arr.shape[0] - cx = self.center[1] / arr.shape[1] - - barrel_power = self.barrel_power + cy = self._center[0] / arr.shape[0] + cx = self._center[1] / arr.shape[1] theta = np.arctan2(p2[1], p2[0]) radius = np.linalg.norm(p2, axis=0, ord=2) - radius = pow(radius, barrel_power) + radius = pow(radius, self.barrel_power) x_new = 0.5 * (radius * np.cos(theta) + cx * 2) x_new = np.clip(x_new * len(x), 0, len(x) - 1) @@ -163,13 +174,54 @@ class Barrel(_ControllableLens): return arr -class Mustache(_ControllableLens): - """Create a mustache distortion.""" +class BarrelPyTorch(Barrel): + """A barrel distortion callback class accelerated by PyTorch.""" + + def __call__(self, arr): + """Run a pytorch accelerated lens distortion algorithm on the input.""" + zoom_out = 1.0 / self.zoom + self.input_size = arr.shape + y = torch.arange(arr.shape[1]).type(torch.FloatTensor).cuda() + x = torch.arange(arr.shape[0]).type(torch.FloatTensor).cuda() + if self._center is None: + self._center = [y.shape[0] / 2.0, x.shape[0] / 2.0] + + y2_ = (y - (y.shape[0] / 2.0)) * zoom_out / arr.shape[1] + x2_ = (x - (x.shape[0] / 2.0)) * zoom_out / arr.shape[0] + p2 = torch.stack(torch.meshgrid(x2_, y2_)) + + cy = self._center[1] / arr.shape[1] + cx = self._center[0] / arr.shape[0] + + theta = torch.atan2(p2[1], p2[0]) + + radius = torch.norm(p2, dim=0) + + radius = torch.pow(radius, self.barrel_power) + + x_new = 0.5 * (radius * torch.cos(theta) + cx * 2) + x_new = torch.clamp(x_new * x.shape[0], 0, x.shape[0] - 1) + + y_new = 0.5 * (radius * torch.sin(theta) + cy * 2) + y_new = torch.clamp(y_new * y.shape[0], 0, y.shape[0] - 1) + + p = torch.stack(torch.meshgrid([x, y])).type(torch.IntTensor) + + p_new = torch.stack((x_new, y_new)) + p_new = p_new.type(torch.IntTensor) + + arr[p[0], p[1], :] = arr[p_new[0], p_new[1], :] + + return arr + + +class Mustache(ControllableLens): + """A mustache distortion callback.""" def __init__( self, use_bleed=False, barrel_power=1, pincushion_power=1, zoom=1, center=None ): - """Create the distorter.""" + """Create the mustache distortion callback.""" super().__init__(use_bleed, zoom, center) self.center = center self.zoom = zoom @@ -181,12 +233,11 @@ class Mustache(_ControllableLens): def enable_mouse_control(self): """ - Enable mouse control. + Enable the default mouse loop to control the mustache distortion. - Move the mouse to center the image. - Scroll to increase/decrease barrel. - Ctrl+scroll to increase/decrease zoom. - Shift+Scroll to increase/decrease pincushion. + ctrl+scroll wheel: zoom in and out + shift+scroll wheel: adjust pincushion power + scroll wheel: adjust barrel power """ @mouse_loop @@ -212,9 +263,9 @@ class Mustache(_ControllableLens): self.mouse_control = m_loop def __call__(self, arr): - """Run the distortion on an array.""" + """Run the mustache distortion algorithm on the input numpy array.""" zoom_out = 1.0 / self.zoom - self.check_setup_bleed(arr) + self._check_setup_bleed(arr) y = np.arange(arr.shape[0]) x = np.arange(arr.shape[1]) diff --git a/displayarray/frame/frame_updater.py b/displayarray/frame/frame_updater.py index b149ac8..de14e01 100644 --- a/displayarray/frame/frame_updater.py +++ b/displayarray/frame/frame_updater.py @@ -48,7 +48,7 @@ class FrameUpdater(threading.Thread): continue def __apply_callbacks_to_frame(self, frame): - if frame is not None: + if frame is not None and not isinstance(frame, NoData): try: for c in self.callbacks: frame_c = c(frame) @@ -133,10 +133,13 @@ async def read_updates( Read back all updates from the requested videos. Example usage: - >>> from examples.videos import test_video - >>> f = 0 - >>> for f, r in enumerate(read_updates(test_video, end_callback=lambda :f==2)): - ... print(f"Frame:{f}. Array:{r}") + + .. code-block:: python + + >>> from examples.videos import test_video + >>> f = 0 + >>> for f, r in enumerate(read_updates(test_video, end_callback=lambda :f==2)): + ... print(f"Frame:{f}. Array:{r}") """ from displayarray.window import SubscriberWindows diff --git a/displayarray/frame/np_to_opencv.py b/displayarray/frame/np_to_opencv.py index a9a482c..a4912a3 100644 --- a/displayarray/frame/np_to_opencv.py +++ b/displayarray/frame/np_to_opencv.py @@ -12,9 +12,14 @@ class NpCam(object): assert isinstance(img, np.ndarray) self.__img = img self.__is_opened = True - - self.__width = self.__img.shape[1] - self.__height = self.__img.shape[0] + if len(img.shape) > 0: + self.__height = self.__img.shape[0] + if len(self.__img.shape) > 1: + self.__width = self.__img.shape[1] + else: + self.__width = self.__height + else: + self.__width = self.__height = 1 self.__ratio = float(self.__width) / self.__height self.__wait_for_ratio = False diff --git a/displayarray/input.py b/displayarray/input.py index ea814cb..3695c0a 100644 --- a/displayarray/input.py +++ b/displayarray/input.py @@ -68,9 +68,11 @@ class mouse_loop(object): # NOSONAR """ Run a function on mouse information that is received by the window, continuously in a new thread. - >>> @mouse_loop - ... def fun(mouse_event): - ... print("x:{}, y:{}".format(mouse_event.x, mouse_event.y)) + .. code-block:: python + + >>> @mouse_loop + ... def fun(mouse_event): + ... print("x:{}, y:{}".format(mouse_event.x, mouse_event.y)) """ def __init__(self, f): @@ -126,9 +128,11 @@ class key_loop(object): # NOSONAR """ Run a function on mouse information that is received by the window, continuously in a new thread. - >>> @key_loop - ... def fun(key): - ... print("key pressed:{}".format(key)) + .. code-block:: python + + >>> @key_loop + ... def fun(key): + ... print("key pressed:{}".format(key)) """ def __init__(self, f: Callable[[str], None]): diff --git a/displayarray/window/subscriber_windows.py b/displayarray/window/subscriber_windows.py index b294803..d054b2d 100644 --- a/displayarray/window/subscriber_windows.py +++ b/displayarray/window/subscriber_windows.py @@ -173,6 +173,7 @@ class SubscriberWindows(object): if frame is not None: self.frames[-1] = frame self.__check_too_many_channels() + self.FRAME_DICT[self.input_vid_global_names[i]] = NoData() self._display_frames(self.frames) def update(self, arr: np.ndarray = None, id: str = None): diff --git a/docs/.doctrees/crop.doctree b/docs/.doctrees/crop.doctree index 5201c06..08fcb90 100644 Binary files a/docs/.doctrees/crop.doctree and b/docs/.doctrees/crop.doctree differ diff --git a/docs/.doctrees/display.doctree b/docs/.doctrees/display.doctree new file mode 100644 index 0000000..023d9ae Binary files /dev/null and b/docs/.doctrees/display.doctree differ diff --git a/docs/.doctrees/displayarray.doctree b/docs/.doctrees/displayarray.doctree index 75b407d..bef47b0 100644 Binary files a/docs/.doctrees/displayarray.doctree and b/docs/.doctrees/displayarray.doctree differ diff --git a/docs/.doctrees/displayarray_bash.doctree b/docs/.doctrees/displayarray_bash.doctree index 90826f1..85c97d7 100644 Binary files a/docs/.doctrees/displayarray_bash.doctree and b/docs/.doctrees/displayarray_bash.doctree differ diff --git a/docs/.doctrees/effects.doctree b/docs/.doctrees/effects.doctree new file mode 100644 index 0000000..8dec84d Binary files /dev/null and b/docs/.doctrees/effects.doctree differ diff --git a/docs/.doctrees/environment.pickle b/docs/.doctrees/environment.pickle index ff830a0..55e2066 100644 Binary files a/docs/.doctrees/environment.pickle and b/docs/.doctrees/environment.pickle differ diff --git a/docs/.doctrees/frame.doctree b/docs/.doctrees/frame.doctree index 0304d29..d1f789d 100644 Binary files a/docs/.doctrees/frame.doctree and b/docs/.doctrees/frame.doctree differ diff --git a/docs/.doctrees/index.doctree b/docs/.doctrees/index.doctree index 4945bdb..932995e 100644 Binary files a/docs/.doctrees/index.doctree and b/docs/.doctrees/index.doctree differ diff --git a/docs/.doctrees/input.doctree b/docs/.doctrees/input.doctree index 6dd9e4f..b252b8a 100644 Binary files a/docs/.doctrees/input.doctree and b/docs/.doctrees/input.doctree differ diff --git a/docs/.doctrees/lens.doctree b/docs/.doctrees/lens.doctree index 755bca8..13537ab 100644 Binary files a/docs/.doctrees/lens.doctree and b/docs/.doctrees/lens.doctree differ diff --git a/docs/.doctrees/select_channels.doctree b/docs/.doctrees/select_channels.doctree index c10cdc1..2b5ff63 100644 Binary files a/docs/.doctrees/select_channels.doctree and b/docs/.doctrees/select_channels.doctree differ diff --git a/docs/.doctrees/window.doctree b/docs/.doctrees/window.doctree index 3722321..376aba8 100644 Binary files a/docs/.doctrees/window.doctree and b/docs/.doctrees/window.doctree differ diff --git a/docs/_modules/displayarray/__main__/index.html b/docs/_modules/displayarray/__main__/index.html new file mode 100644 index 0000000..0dd93d9 --- /dev/null +++ b/docs/_modules/displayarray/__main__/index.html @@ -0,0 +1,185 @@ + + + + + + + displayarray.__main__ — DisplayArray documentation + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +

Source code for displayarray.__main__

+"""
+DisplayArray.
+
+Display NumPy arrays.
+
+Usage:
+  displayarray (-w <webcam-number> | -v <video-filename> | -t <topic-name>[,dtype])... [-m <msg-backend>]
+  displayarray -h
+  displayarray --version
+
+
+Options:
+  -h, --help                                           Show this help text.
+  --version                                            Show version number.
+  -w <webcam-number>, --webcam=<webcam-number>         Display video from a webcam.
+  -v <video-filename>, --video=<video-filename>        Display frames from a video file.
+  -t <topic-name>, --topic=<topic-name>                Display frames from a topic using the chosen message broker.
+  -m <msg-backend>, --message-backend <msg-backend>    Choose message broker backend. [Default: ROS]
+                                                       Currently supported: ROS, ZeroMQ
+  --ros                                                Use ROS as the backend message broker.
+  --zeromq                                             Use ZeroMQ as the backend message broker.
+"""
+
+from docopt import docopt
+import asyncio
+
+
+
[docs]def main(argv=None): + """Process command line arguments.""" + arguments = docopt(__doc__, argv=argv) + if arguments["--version"]: + from displayarray import __version__ + + print(f"DisplayArray V{__version__}") + return + from displayarray import display + + vids = [int(w) for w in arguments["--webcam"]] + arguments["--video"] + v_disps = None + if vids: + v_disps = display(*vids, blocking=False) + from displayarray.frame.frame_updater import read_updates_ros, read_updates_zero_mq + + topics = arguments["--topic"] + topics_split = [t.split(",") for t in topics] + d = display() + + async def msg_recv(): + nonlocal d + while d: + if arguments["--message-backend"] == "ROS": + async for v_name, frame in read_updates_ros( + [t for t, d in topics_split], [d for t, d in topics_split] + ): + d.update(arr=frame, id=v_name) + if arguments["--message-backend"] == "ZeroMQ": + async for v_name, frame in read_updates_zero_mq( + *[bytes(t, encoding="ascii") for t in topics] + ): + d.update(arr=frame, id=v_name) + + async def update_vids(): + while v_disps: + if v_disps: + v_disps.update() + await asyncio.sleep(0) + + async def runner(): + await asyncio.wait([msg_recv(), update_vids()]) + + loop = asyncio.get_event_loop() + loop.run_until_complete(runner()) + loop.close()
+ + +if __name__ == "__main__": + main() +
+ +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/docs/_modules/displayarray/effects/crop/index.html b/docs/_modules/displayarray/effects/crop/index.html index e569173..3a71165 100644 --- a/docs/_modules/displayarray/effects/crop/index.html +++ b/docs/_modules/displayarray/effects/crop/index.html @@ -31,16 +31,24 @@

Source code for displayarray.effects.crop

-import numpy as np
+"""Crop any n-dimensional array."""
+
+import numpy as np
 from ..input import mouse_loop
 
 
-
[docs]class Crop(object): +
[docs]class Crop(object): """A callback class that will return the input array cropped to the output size. N-dimensional.""" def __init__(self, output_size=(64, 64, 3), center=None): + """ + Create the cropping callback class. + + :param output_size: Specified the size the input should be cropped to. Can be redefined later. + :param center: Specifies the center on the input array to take the crop out of. + """ self._output_size = None - self._center = None + self._center = np.asarray([o // 2 for o in output_size]) self.odd_center = None self.mouse_control = None self.input_size = None @@ -50,23 +58,27 @@ @property def output_size(self): + """Get the output size.""" return self._output_size @output_size.setter def output_size(self, set): + """Set the output size.""" self._output_size = set if self._output_size is not None: self._output_size = np.asarray(set) @property def center(self): + """Get the center.""" return self._center @center.setter def center(self, set): - self._center = set - if self._center is not None: - self._center = np.asarray(set) + """Set the center. Guarded so that colors need not be set.""" + if set is not None: + for x in range(len(set)): + self._center[x] = set[x] def __call__(self, arr): """Crop the input array to the specified output size. output is centered on self.center point on input.""" @@ -119,8 +131,8 @@ out_array[put_slices] = arr[get_slices] return out_array.astype(arr.dtype) -
[docs] def enable_mouse_control(self): - """Move the mouse to move where the crop is from on the original image""" +
[docs] def enable_mouse_control(self): + """Move the mouse to move where the crop is from on the original image.""" @mouse_loop def m_loop(me): @@ -154,13 +166,10 @@

Navigation

Python API

Bash API

    diff --git a/docs/_modules/displayarray/effects/lens/index.html b/docs/_modules/displayarray/effects/lens/index.html index 315887a..8465230 100644 --- a/docs/_modules/displayarray/effects/lens/index.html +++ b/docs/_modules/displayarray/effects/lens/index.html @@ -31,87 +31,159 @@

    Source code for displayarray.effects.lens

    -import numpy as np
    +"""Create lens effects. Currently only 2D+color arrays are supported."""
    +
    +import numpy as np
     from ..input import mouse_loop
     import cv2
     
    +try:
    +    import torch
    +except ImportError:
    +    torch = None  # type: ignore
    +
    +
    +
    [docs]class ControllableLens(object): + """A lens callback that can be controlled by the program or the user.""" -class ControllableLens(object): def __init__(self, use_bleed=False, zoom=1, center=None): - self.center = center - self.zoom = zoom + """Create the lens callback.""" + self._center = center + self._zoom = zoom self.use_bleed = use_bleed self.bleed = None self.mouse_control = None - def check_setup_bleed(self, arr): + def _check_setup_bleed(self, arr): if not isinstance(self.bleed, np.ndarray) and self.use_bleed: self.bleed = np.zeros_like(arr) - def run_bleed(self, arr, x, y): +
    [docs] def run_bleed(self, arr, x, y): + """Spread color outwards, like food coloring in water.""" arr[y, ...] = (arr[(y + 1) % len(y), ...] + arr[(y - 1) % len(y), ...]) / 2 arr[:, x, ...] = ( arr[:, (x + 1) % len(x), ...] + arr[:, (x - 1) % len(x), ...] - ) / 2 + ) / 2
    -
    [docs]class Barrel(ControllableLens): +
    [docs]class Barrel(ControllableLens): + """A barrel lens distortion callback.""" + def __init__( self, use_bleed=False, barrel_power=1, pincushion_power=1, zoom=1, center=None ): + """Create the barrel lens distortion callback.""" super().__init__(use_bleed, zoom, center) - self.center = center - self.zoom = zoom + self._center = center + self._zoom = zoom self.use_bleed = use_bleed self.bleed = None - self.barrel_power = barrel_power + self._barrel_power = barrel_power self.mouse_control = None + self.input_size = None -
    [docs] def enable_mouse_control(self): + @property + def center(self): + """Guarded get center. Limits to within input.""" + if self.input_size is not None: + self._center[:] = [ + min(max(0, s), self.input_size[i]) for i, s in enumerate(self._center) + ] + return self._center + + @center.setter + def center(self, setpoint): + """Guarded set center. Limits to within input.""" + if self.input_size is not None: + setpoint = [ + min(max(0, s), self.input_size[i]) for i, s in enumerate(setpoint) + ] + self._center[:] = setpoint + + @property + def zoom(self): + """Guarded zoom. Avoids divide by zero conditions.""" + if self._zoom == 0: + return 1e-15 + else: + return self._zoom + + @property + def barrel_power(self): + """Guarded barrel power. Avoids divide by zero conditions.""" + if self._barrel_power == 0: + return 1e-15 + else: + return self._barrel_power + + @barrel_power.setter + def barrel_power(self, setpoint): + """Set the barrel power.""" + self._barrel_power = setpoint + + @zoom.setter # type: ignore + def zoom(self, setpoint): + """Set the zoom power.""" + self._zoom = setpoint + +
    [docs] def enable_mouse_control(self, crop_size=None): """ - Move the mouse to center the image, scroll to increase/decrease barrel, ctrl+scroll to increase/decrease zoom + Enable the default mouse controls. + + Move the mouse to center the image + scroll to increase/decrease barrel + ctrl+scroll to increase/decrease zoom """ @mouse_loop def m_loop(me): - self.center[:] = [me.y, me.x] - if me.event == cv2.EVENT_MOUSEWHEEL: - if me.flags & cv2.EVENT_FLAG_CTRLKEY: - if me.flags > 0: - self.zoom *= 1.1 - else: - self.zoom /= 1.1 + if self.input_size is not None: + if crop_size is not None: + self.center = [ + me.y * self.input_size[0] / crop_size[0], + me.x * self.input_size[1] / crop_size[1], + ] else: - if me.flags > 0: - self.barrel_power *= 1.1 + self.center = [me.y, me.x] + if me.event == cv2.EVENT_MOUSEWHEEL: + if me.flags & cv2.EVENT_FLAG_CTRLKEY: + if me.flags > 0: + self.zoom *= 1.1 + else: + self.zoom /= 1.1 else: - self.barrel_power /= 1.1 + if me.flags > 0: + self.barrel_power *= 1.1 + else: + self.barrel_power /= 1.1 + print(self.barrel_power) self.mouse_control = m_loop return self
    def __call__(self, arr): + """Run the lens distortion algorithm on the input.""" zoom_out = 1.0 / self.zoom - self.check_setup_bleed(arr) + self._check_setup_bleed(arr) + + self.input_size = arr.shape y = np.arange(arr.shape[0]) x = np.arange(arr.shape[1]) - if self.center is None: - self.center = [len(y) / 2.0, len(x) / 2.0] + if self._center is None: + self._center = [len(y) / 2.0, len(x) / 2.0] y2_ = (y - (len(y) / 2.0)) * zoom_out / arr.shape[0] x2_ = (x - (len(x) / 2.0)) * zoom_out / arr.shape[1] p2 = np.array(np.meshgrid(x2_, y2_)) - cy = self.center[0] / arr.shape[0] - cx = self.center[1] / arr.shape[1] - - barrel_power = self.barrel_power + cy = self._center[0] / arr.shape[0] + cx = self._center[1] / arr.shape[1] theta = np.arctan2(p2[1], p2[0]) radius = np.linalg.norm(p2, axis=0, ord=2) - radius = pow(radius, barrel_power) + radius = pow(radius, self.barrel_power) x_new = 0.5 * (radius * np.cos(theta) + cx * 2) x_new = np.clip(x_new * len(x), 0, len(x) - 1) @@ -135,10 +207,54 @@ return arr
    -
    [docs]class Mustache(ControllableLens): +
    [docs]class BarrelPyTorch(Barrel): + """A barrel distortion callback class accelerated by PyTorch.""" + + def __call__(self, arr): + """Run a pytorch accelerated lens distortion algorithm on the input.""" + zoom_out = 1.0 / self.zoom + self.input_size = arr.shape + y = torch.arange(arr.shape[1]).type(torch.FloatTensor).cuda() + x = torch.arange(arr.shape[0]).type(torch.FloatTensor).cuda() + if self._center is None: + self._center = [y.shape[0] / 2.0, x.shape[0] / 2.0] + + y2_ = (y - (y.shape[0] / 2.0)) * zoom_out / arr.shape[1] + x2_ = (x - (x.shape[0] / 2.0)) * zoom_out / arr.shape[0] + p2 = torch.stack(torch.meshgrid(x2_, y2_)) + + cy = self._center[1] / arr.shape[1] + cx = self._center[0] / arr.shape[0] + + theta = torch.atan2(p2[1], p2[0]) + + radius = torch.norm(p2, dim=0) + + radius = torch.pow(radius, self.barrel_power) + + x_new = 0.5 * (radius * torch.cos(theta) + cx * 2) + x_new = torch.clamp(x_new * x.shape[0], 0, x.shape[0] - 1) + + y_new = 0.5 * (radius * torch.sin(theta) + cy * 2) + y_new = torch.clamp(y_new * y.shape[0], 0, y.shape[0] - 1) + + p = torch.stack(torch.meshgrid([x, y])).type(torch.IntTensor) + + p_new = torch.stack((x_new, y_new)) + p_new = p_new.type(torch.IntTensor) + + arr[p[0], p[1], :] = arr[p_new[0], p_new[1], :] + + return arr
    + + +
    [docs]class Mustache(ControllableLens): + """A mustache distortion callback.""" + def __init__( self, use_bleed=False, barrel_power=1, pincushion_power=1, zoom=1, center=None ): + """Create the mustache distortion callback.""" super().__init__(use_bleed, zoom, center) self.center = center self.zoom = zoom @@ -148,7 +264,15 @@ self.pincushion_power = pincushion_power self.mouse_control = None - def enable_mouse_control(self): +
    [docs] def enable_mouse_control(self): + """ + Enable the default mouse loop to control the mustache distortion. + + ctrl+scroll wheel: zoom in and out + shift+scroll wheel: adjust pincushion power + scroll wheel: adjust barrel power + """ + @mouse_loop def m_loop(me): self.center[:] = [me.y, me.x] @@ -169,11 +293,12 @@ else: self.barrel_power /= 1.1 - self.mouse_control = m_loop + self.mouse_control = m_loop
    def __call__(self, arr): + """Run the mustache distortion algorithm on the input numpy array.""" zoom_out = 1.0 / self.zoom - self.check_setup_bleed(arr) + self._check_setup_bleed(arr) y = np.arange(arr.shape[0]) x = np.arange(arr.shape[1]) @@ -234,13 +359,10 @@

    Navigation

    Python API

    Bash API

      diff --git a/docs/_modules/displayarray/effects/select_channels/index.html b/docs/_modules/displayarray/effects/select_channels/index.html index 2c46bb2..ed1ce40 100644 --- a/docs/_modules/displayarray/effects/select_channels/index.html +++ b/docs/_modules/displayarray/effects/select_channels/index.html @@ -31,13 +31,24 @@

      Source code for displayarray.effects.select_channels

      -import numpy as np
      +"""Reduce many color images to the three colors that your eyeballs can see."""
      +
      +import numpy as np
       from ..input import mouse_loop
       import cv2
       
      +from typing import Iterable
       
      -
      [docs]class SelectChannels(object): - def __init__(self, selected_channels=None): + +
      [docs]class SelectChannels(object): + """ + Select channels to display from an array with too many colors. + + :param selected_channels: the list of channels to display. + """ + + def __init__(self, selected_channels: Iterable[int] = None): + """Select which channels from the input array to display in the output.""" if selected_channels is None: selected_channels = [0, 0, 0] self.selected_channels = selected_channels @@ -46,6 +57,7 @@ self.num_input_channels = None def __call__(self, arr): + """Run the channel selector.""" self.num_input_channels = arr.shape[-1] out_arr = [ arr[..., min(max(0, x), arr.shape[-1] - 1)] for x in self.selected_channels @@ -53,7 +65,15 @@ out_arr = np.stack(out_arr, axis=-1) return out_arr - def enable_mouse_control(self): +
      [docs] def enable_mouse_control(self): + """ + Enable mouse control. + + Alt+Scroll to increase/decrease channel 2. + Shift+Scroll to increase/decrease channel 1. + Ctrl+scroll to increase/decrease channel 0. + """ + @mouse_loop def m_loop(me): if me.event == cv2.EVENT_MOUSEWHEEL: @@ -91,7 +111,7 @@ if self.mouse_print_channels: print(f"Channel 2 now maps to {self.selected_channels[2]}.") - self.mouse_control = m_loop
      + self.mouse_control = m_loop
      @@ -112,13 +132,10 @@

      Navigation

      Python API

      Bash API

        diff --git a/docs/_modules/displayarray/frame/frame_publishing/index.html b/docs/_modules/displayarray/frame/frame_publishing/index.html index 8c273e4..5945ed5 100644 --- a/docs/_modules/displayarray/frame/frame_publishing/index.html +++ b/docs/_modules/displayarray/frame/frame_publishing/index.html @@ -31,7 +31,8 @@

        Source code for displayarray.frame.frame_publishing

        -import sys
        +"""Publish frames so any function within this program can find them."""
        +
         import threading
         import time
         import asyncio
        @@ -41,7 +42,7 @@
         
         from displayarray.frame import subscriber_dictionary
         from .np_to_opencv import NpCam
        -from displayarray.uid import uid_for_source
        +from displayarray._uid import uid_for_source
         
         from typing import Union, Tuple, Optional, Dict, Any, List, Callable
         
        @@ -146,6 +147,7 @@
             copy=True,
             track=False
         ):
        +    """Publish frames to ZeroMQ when they're updated."""
             import zmq
             from displayarray import read_updates
         
        @@ -193,6 +195,7 @@
             rate_hz=None,
             dtype=None
         ):
        +    """Publish frames to ROS when they're updated."""
             import rospy
             from rospy.numpy_msg import numpy_msg
             import std_msgs.msg
        @@ -220,7 +223,7 @@
                     )  # allow users to use their own custom messages in numpy arrays
                 return msg_type
         
        -    publishers = {}
        +    publishers: Dict[str, rospy.Publisher] = {}
             rospy.init_node(node_name, anonymous=True)
             try:
                 for v in read_updates(vids, callbacks, fps_limit, size, end_callback, blocking):
        @@ -268,13 +271,10 @@
         

        Navigation

        Python API

        Bash API

          diff --git a/docs/_modules/displayarray/frame/frame_updater/index.html b/docs/_modules/displayarray/frame/frame_updater/index.html index 3673043..34940d7 100644 --- a/docs/_modules/displayarray/frame/frame_updater/index.html +++ b/docs/_modules/displayarray/frame/frame_updater/index.html @@ -31,14 +31,16 @@

          Source code for displayarray.frame.frame_updater

          -import threading
          +"""Get and handle updated frames."""
          +
          +import threading
           import asyncio
           from typing import Union, Tuple, Any, Callable, List, Optional, Dict
           
           import numpy as np
           
           from displayarray.callbacks import global_cv_display_callback
          -from displayarray.uid import uid_for_source
          +from displayarray._uid import uid_for_source
           from displayarray.frame import subscriber_dictionary
           from displayarray.frame.frame_publishing import pub_cam_thread
           from displayarray.window import window_commands
          @@ -59,6 +61,7 @@
                   high_speed: bool = True,
                   fps_limit: float = 240,
               ):
          +        """Create the frame updater thread."""
                   super(FrameUpdater, self).__init__(target=self.loop, args=())
                   self.cam_id = uid_for_source(video_source)
                   self.video_source = video_source
          @@ -78,7 +81,7 @@
                       continue
           
               def __apply_callbacks_to_frame(self, frame):
          -        if frame is not None:
          +        if frame is not None and not isinstance(frame, NoData):
                       try:
                           for c in self.callbacks:
                               frame_c = c(frame)
          @@ -148,20 +151,28 @@
           
          [docs]async def read_updates( *vids, callbacks: Optional[ - Union[Dict[Any, FrameCallable], List[FrameCallable], FrameCallable] + Union[ + Dict[Any, Union[FrameCallable, List[FrameCallable]]], + List[FrameCallable], + FrameCallable, + ] ] = None, fps_limit=float("inf"), size=(-1, -1), end_callback: Callable[[], bool] = lambda: False, blocking=True, ): - """Reads back all updates from the requested videos. + """ + Read back all updates from the requested videos. Example usage: - >>> from examples.videos import test_video - >>> f = 0 - >>> for f, r in enumerate(read_updates(test_video, end_callback=lambda :f==2)): - ... print(f"Frame:{f}. Array:{r}") + + .. code-block:: python + + >>> from examples.videos import test_video + >>> f = 0 + >>> for f, r in enumerate(read_updates(test_video, end_callback=lambda :f==2)): + ... print(f"Frame:{f}. Array:{r}") """ from displayarray.window import SubscriberWindows @@ -201,13 +212,14 @@ async def read_updates_zero_mq( *topic_names, - address="tcp://127.0.0.1:5600", - flags=0, - copy=True, - track=False, - blocking=False, - end_callback: Callable[[Any], bool] = lambda: False, + address: str = "tcp://127.0.0.1:5600", + flags: int = 0, + copy: bool = True, + track: bool = False, + blocking: bool = False, + end_callback: Callable[[Any], bool] = lambda x: False, ): + """Read updated frames from ZeroMQ.""" import zmq ctx = zmq.Context() @@ -243,8 +255,9 @@ dtypes=None, listener_node_name=None, poll_rate_hz=None, - end_callback: Callable[[Any], bool] = lambda: False, + end_callback: Callable[[Any], bool] = lambda x: False, ): + """Read updated frames from ROS.""" import rospy from rospy.numpy_msg import numpy_msg from rospy.client import _WFM @@ -322,7 +335,8 @@ pass finally: if s is not None: - s.unregister() + for _, sub in s.items(): + sub.unregister() if rospy.core.is_shutdown(): raise rospy.exceptions.ROSInterruptException("rospy shutdown")
          @@ -345,13 +359,10 @@

          Navigation

          Python API

          Bash API

            diff --git a/docs/_modules/displayarray/frame/get_frame_ids/index.html b/docs/_modules/displayarray/frame/get_frame_ids/index.html index d1257b9..88c03ca 100644 --- a/docs/_modules/displayarray/frame/get_frame_ids/index.html +++ b/docs/_modules/displayarray/frame/get_frame_ids/index.html @@ -31,7 +31,9 @@

            Source code for displayarray.frame.get_frame_ids

            -import cv2
            +"""Get camera IDs."""
            +
            +import cv2
             
             from typing import List
             
            @@ -67,13 +69,10 @@
             

            Navigation

            Python API

            Bash API

              diff --git a/docs/_modules/displayarray/frame/np_to_opencv/index.html b/docs/_modules/displayarray/frame/np_to_opencv/index.html index fbef36c..3ad2aea 100644 --- a/docs/_modules/displayarray/frame/np_to_opencv/index.html +++ b/docs/_modules/displayarray/frame/np_to_opencv/index.html @@ -31,7 +31,9 @@

              Source code for displayarray.frame.np_to_opencv

              -import numpy as np
              +"""Allow OpenCV to handle numpy arrays as input."""
              +
              +import numpy as np
               import cv2
               
               
              @@ -39,12 +41,18 @@
                   """Add OpenCV camera controls to a numpy array."""
               
                   def __init__(self, img):
              +        """Create a fake camera for OpenCV based on the initial array."""
                       assert isinstance(img, np.ndarray)
                       self.__img = img
                       self.__is_opened = True
              -
              -        self.__width = self.__img.shape[1]
              -        self.__height = self.__img.shape[0]
              +        if len(img.shape) > 0:
              +            self.__height = self.__img.shape[0]
              +            if len(self.__img.shape) > 1:
              +                self.__width = self.__img.shape[1]
              +            else:
              +                self.__width = self.__height
              +        else:
              +            self.__width = self.__height = 1
                       self.__ratio = float(self.__width) / self.__height
               
                       self.__wait_for_ratio = False
              @@ -105,13 +113,10 @@
               

              Navigation

              Python API

              Bash API

                diff --git a/docs/_modules/displayarray/input/index.html b/docs/_modules/displayarray/input/index.html index d5d6e6a..c6529da 100644 --- a/docs/_modules/displayarray/input/index.html +++ b/docs/_modules/displayarray/input/index.html @@ -31,7 +31,9 @@

                Source code for displayarray.input

                -from displayarray.window import window_commands
                +"""Decorators for creating input loops that OpenCV handles."""
                +
                +from displayarray.window import window_commands
                 import threading
                 import time
                 
                @@ -42,6 +44,7 @@
                     """Holds all the OpenCV mouse event information."""
                 
                     def __init__(self, event, x, y, flags, param):
                +        """Create an OpenCV mouse event."""
                         self.event = event
                         self.x = x
                         self.y = y
                @@ -95,10 +98,21 @@
                 
                 
                 
                [docs]class mouse_loop(object): # NOSONAR - """Run a function on mouse information that is received by the window, continuously in a new thread.""" + """ + Run a function on mouse information that is received by the window, continuously in a new thread. - def __init__(self, f, run_when_no_events=False): - self.t = threading.Thread(target=_mouse_loop_thread(f, run_when_no_events)) + .. code-block:: python + + >>> @mouse_loop + ... def fun(mouse_event): + ... print("x:{}, y:{}".format(mouse_event.x, mouse_event.y)) + """ + + def __init__(self, f): + """Start a new mouse thread for the decorated function.""" + self.t = threading.Thread( + target=_mouse_loop_thread(f, run_when_no_events=False) + ) self.t.start() def __call__(self, *args, **kwargs): @@ -144,10 +158,19 @@
                [docs]class key_loop(object): # NOSONAR - """Run a function on mouse information that is received by the window, continuously in a new thread.""" + """ + Run a function on mouse information that is received by the window, continuously in a new thread. - def __init__(self, f: Callable[[str], None], run_when_no_events=False): - self.t = threading.Thread(target=_key_loop_thread(f, run_when_no_events)) + .. code-block:: python + + >>> @key_loop + ... def fun(key): + ... print("key pressed:{}".format(key)) + """ + + def __init__(self, f: Callable[[str], None]): + """Start a new key thread for the decorated function.""" + self.t = threading.Thread(target=_key_loop_thread(f, run_when_no_events=False)) self.t.start() def __call__(self, *args, **kwargs): @@ -173,13 +196,10 @@

                Navigation

                Python API

                Bash API

                  diff --git a/docs/_modules/displayarray/window/subscriber_windows/index.html b/docs/_modules/displayarray/window/subscriber_windows/index.html index 0a059c9..8e5f4a9 100644 --- a/docs/_modules/displayarray/window/subscriber_windows/index.html +++ b/docs/_modules/displayarray/window/subscriber_windows/index.html @@ -31,7 +31,9 @@

                  Source code for displayarray.window.subscriber_windows

                  -import warnings
                  +"""OpenCV windows that will display the arrays."""
                  +
                  +import warnings
                   from threading import Thread
                   from typing import List, Union, Callable, Any, Dict, Iterable, Optional
                   
                  @@ -40,17 +42,17 @@
                   from localpubsub import NoData
                   
                   from displayarray.callbacks import global_cv_display_callback
                  -from displayarray.uid import uid_for_source
                  +from displayarray._uid import uid_for_source
                   from displayarray.frame import subscriber_dictionary
                   from displayarray.frame.frame_updater import FrameCallable
                   from displayarray.frame.frame_updater import FrameUpdater
                   from displayarray.input import MouseEvent
                   from displayarray.window import window_commands
                  -from displayarray.util import WeakMethod
                  +from displayarray._util import WeakMethod
                   from displayarray.effects.select_channels import SelectChannels
                   
                   
                  -
                  [docs]class SubscriberWindows(object): +
                  [docs]class SubscriberWindows(object): """Windows that subscribe to updates to cameras, videos, and arrays.""" FRAME_DICT: Dict[str, np.ndarray] = {} @@ -62,6 +64,7 @@ video_sources: Iterable[Union[str, int]] = (0,), callbacks: Optional[List[Callable[[np.ndarray], Any]]] = None, ): + """Create the array displaying window.""" self.source_names: List[Union[str, int]] = [] self.close_threads: Optional[List[Thread]] = [] self.frames: List[np.ndarray] = [] @@ -84,12 +87,13 @@ self.update() return not self.exited - def block(self): +
                  [docs] def block(self): + """Update the window continuously while blocking the outer program.""" self.loop() for ct in self.close_threads: - ct.join() + ct.join()
                  -
                  [docs] def add_source(self, name): +
                  [docs] def add_source(self, name): """Add another source for this class to display.""" uid = uid_for_source(name) self.source_names.append(uid) @@ -97,7 +101,7 @@ self.input_cams.append(name) return self
                  -
                  [docs] def add_window(self, name): +
                  [docs] def add_window(self, name): """Add another window for this class to display sources with. The name will be the title.""" self.window_names.append(name) cv2.namedWindow(name + " (press ESC to quit)") @@ -105,7 +109,7 @@ cv2.setMouseCallback(name + " (press ESC to quit)", m) return self
                  -
                  [docs] def add_callback(self, callback): +
                  [docs] def add_callback(self, callback): """Add a callback for this class to apply to videos.""" self.callbacks.append(callback) return self
                  @@ -114,9 +118,7 @@ for c in self.source_names: subscriber_dictionary.stop_cam(c) -
                  [docs] def handle_keys( - self, key_input # type: int - ): +
                  [docs] def handle_keys(self, key_input: int): """Capture key input for the escape function and passing to key control subscriber threads.""" if key_input in self.ESC_KEY_CODES: for name in self.window_names: @@ -135,7 +137,7 @@ ) )
                  -
                  [docs] def handle_mouse(self, event, x, y, flags, param): +
                  [docs] def handle_mouse(self, event, x, y, flags, param): """Capture mouse input for mouse control subscriber threads.""" mousey = MouseEvent(event, x, y, flags, param) window_commands.mouse_pub.publish(mousey)
                  @@ -188,7 +190,7 @@ self.frames[fr] = self.callbacks[-1](self.frames[fr]) break -
                  [docs] def update_window_frames(self): +
                  [docs] def update_window_frames(self): """Update the windows with the newest data for all frames.""" self.frames = [] for i in range(len(self.input_vid_global_names)): @@ -204,9 +206,10 @@ if frame is not None: self.frames[-1] = frame self.__check_too_many_channels() + self.FRAME_DICT[self.input_vid_global_names[i]] = NoData() self._display_frames(self.frames)
                  -
                  [docs] def update(self, arr: np.ndarray = None, id: str = None): +
                  [docs] def update(self, arr: np.ndarray = None, id: str = None): """Update window frames once. Optionally add a new input and input id.""" if arr is not None and id is not None: global_cv_display_callback(arr, id) @@ -219,7 +222,7 @@ key = self.handle_keys(cv2.waitKey(1)) return msg_cmd, key
                  -
                  [docs] def wait_for_init(self): +
                  [docs] def wait_for_init(self): """Update window frames in a loop until they're actually updated. Useful for waiting for cameras to init.""" msg_cmd = "" key = "" @@ -227,7 +230,7 @@ msg_cmd, key = self.update() return self
                  -
                  [docs] def end(self): +
                  [docs] def end(self): """Close all threads. Should be used with non-blocking mode.""" window_commands.quit(force_all_read=False) self.__stop_all_cams() @@ -246,7 +249,7 @@ def __delete__(self, instance): self.end() -
                  [docs] def loop(self): +
                  [docs] def loop(self): """Continually update window frame. OpenCV only allows this in the main thread.""" sub_cmd = window_commands.win_cmd_sub() msg_cmd = "" @@ -259,7 +262,10 @@ def _get_video_callback_dict_threads( - *vids, callbacks: Optional[Dict[Any, FrameCallable]] = None, fps=240, size=(-1, -1) + *vids, + callbacks: Optional[Dict[Any, Union[FrameCallable, List[FrameCallable]]]] = None, + fps=240, + size=(-1, -1), ): assert callbacks is not None vid_threads = [] @@ -268,14 +274,14 @@ v_callbacks: List[Callable[[np.ndarray], Any]] = [] if v_name in callbacks: if isinstance(callbacks[v_name], List): - v_callbacks.extend(callbacks[v_name]) + v_callbacks.extend(callbacks[v_name]) # type: ignore elif callable(callbacks[v_name]): - v_callbacks.append(callbacks[v_name]) + v_callbacks.append(callbacks[v_name]) # type: ignore if v in callbacks: if isinstance(callbacks[v], List): - v_callbacks.extend(callbacks[v]) + v_callbacks.extend(callbacks[v]) # type: ignore elif callable(callbacks[v]): - v_callbacks.append(callbacks[v]) + v_callbacks.append(callbacks[v]) # type: ignore vid_threads.append( FrameUpdater(v, callbacks=v_callbacks, fps_limit=fps, request_size=size) ) @@ -285,7 +291,11 @@ def _get_video_threads( *vids, callbacks: Optional[ - Union[Dict[Any, FrameCallable], List[FrameCallable], FrameCallable] + Union[ + Dict[Any, Union[FrameCallable, List[FrameCallable]]], + List[FrameCallable], + FrameCallable, + ] ] = None, fps=240, size=(-1, -1), @@ -312,10 +322,14 @@ return vid_threads -
                  [docs]def display( +
                  [docs]def display( *vids, callbacks: Optional[ - Union[Dict[Any, FrameCallable], List[FrameCallable], FrameCallable] + Union[ + Dict[Any, Union[FrameCallable, List[FrameCallable]]], + List[FrameCallable], + FrameCallable, + ] ] = None, window_names=None, blocking=False, @@ -346,7 +360,7 @@ return s
                  -
                  [docs]def breakpoint_display(*args, **kwargs): +
                  [docs]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)
                  @@ -369,13 +383,10 @@

                  Navigation

                  Python API

                  Bash API

                    diff --git a/docs/_modules/index.html b/docs/_modules/index.html index 9c9b24c..bfd20e6 100644 --- a/docs/_modules/index.html +++ b/docs/_modules/index.html @@ -31,7 +31,8 @@

                    All modules for which code is available

                    -
                    • displayarray.effects.crop
                    • +
                      • displayarray.__main__
                      • +
                      • displayarray.effects.crop
                      • displayarray.effects.lens
                      • displayarray.effects.select_channels
                      • displayarray.frame.frame_publishing
                      • @@ -60,13 +61,10 @@

                        Navigation

                        Python API

                        Bash API

                          diff --git a/docs/docsrc/displayarray.rst b/docs/_sources/display.rst.txt similarity index 59% rename from docs/docsrc/displayarray.rst rename to docs/_sources/display.rst.txt index b54dc88..1a324d2 100644 --- a/docs/docsrc/displayarray.rst +++ b/docs/_sources/display.rst.txt @@ -1,12 +1,11 @@ -displayarray +displayarray.display =================================== .. automodule:: displayarray -Display -------- .. autofunction:: display .. autofunction:: breakpoint_display -Frames ------- -.. autofunction:: read_updates \ No newline at end of file +Windows +------- +.. autoclass:: displayarray.window.SubscriberWindows + :members: \ No newline at end of file diff --git a/docs/_sources/displayarray.rst.txt b/docs/_sources/displayarray.rst.txt index b54dc88..7406c76 100644 --- a/docs/_sources/displayarray.rst.txt +++ b/docs/_sources/displayarray.rst.txt @@ -9,4 +9,13 @@ Display Frames ------ -.. autofunction:: read_updates \ No newline at end of file +.. autofunction:: read_updates + +Effects +------- +.. autoclass:: displayarray.effects.crop.Crop + :members: +.. autoclass:: displayarray.effects.lens.Barrel + :members: +.. autoclass:: displayarray.effects.select_channels.SelectChannels + :members: diff --git a/docs/_sources/effects.rst.txt b/docs/_sources/effects.rst.txt new file mode 100644 index 0000000..4e81234 --- /dev/null +++ b/docs/_sources/effects.rst.txt @@ -0,0 +1,30 @@ +displayarray.effects +==================== + +Crop +---- +.. currentmodule:: displayarray.effects.crop + +.. autoclass:: Crop + :members: + +Lens +---- +.. currentmodule:: displayarray.effects.lens + +.. automodule:: displayarray.effects.lens + :members: + +.. autoclass:: Barrel + :members: + +.. autoclass:: Mustache + :members: + +Select Channels +--------------- +.. currentmodule:: displayarray.effects.select_channels + +.. autoclass:: SelectChannels + :members: + diff --git a/docs/_sources/frame.rst.txt b/docs/_sources/frame.rst.txt index 3d9e51e..8d8412f 100644 --- a/docs/_sources/frame.rst.txt +++ b/docs/_sources/frame.rst.txt @@ -1,6 +1,14 @@ displayarray.frame =================================== +Read Updates +------------ +.. currentmodule:: displayarray + +.. autofunction:: read_updates + +Frame Passing +------------- .. currentmodule:: displayarray.frame .. automodule:: displayarray.frame diff --git a/docs/_sources/index.rst.txt b/docs/_sources/index.rst.txt index 9bed90f..8b5c696 100644 --- a/docs/_sources/index.rst.txt +++ b/docs/_sources/index.rst.txt @@ -14,13 +14,10 @@ DisplayArray displays arrays. :maxdepth: 3 :caption: Python API - displayarray - displayarray.effects.crop - displayarray.effects.lens - displayarray.effects.select_channels - displayarray.frame - displayarray.window - displayarray.input + display + frame + effects + input .. toctree:: :maxdepth: 2 diff --git a/docs/crop/index.html b/docs/crop/index.html index 8d62983..0761e6f 100644 --- a/docs/crop/index.html +++ b/docs/crop/index.html @@ -38,10 +38,22 @@
                          class Crop(output_size=(64, 64, 3), center=None)[source]

                          A callback class that will return the input array cropped to the output size. N-dimensional.

                          +
                          +
                          +property center
                          +

                          Get the center.

                          +
                          +
                          enable_mouse_control()[source]
                          -

                          Move the mouse to move where the crop is from on the original image

                          +

                          Move the mouse to move where the crop is from on the original image.

                          +
                          + +
                          +
                          +property output_size
                          +

                          Get the output size.

                          diff --git a/docs/display/index.html b/docs/display/index.html new file mode 100644 index 0000000..1cc6500 --- /dev/null +++ b/docs/display/index.html @@ -0,0 +1,212 @@ + + + + + + + displayarray.display — DisplayArray documentation + + + + + + + + + + + + + + + + + + + + +
                          +
                          +
                          + + +
                          + +
                          +

                          displayarray.display

                          +

                          Display any array, webcam, or video file.

                          +

                          display is a function that displays these in their own windows.

                          +
                          +
                          +display(*vids, callbacks: Union[Dict[Any, Union[Callable[[numpy.ndarray], Optional[numpy.ndarray]], List[Callable[[numpy.ndarray], Optional[numpy.ndarray]]]]], List[Callable[[numpy.ndarray], Optional[numpy.ndarray]]], Callable[[numpy.ndarray], Optional[numpy.ndarray]], None] = None, window_names=None, blocking=False, fps_limit=240, size=(-1, -1))[source]
                          +

                          Display all the arrays, cameras, and videos passed in.

                          +
                          +
                          callbacks can be a dictionary linking functions to videos, or a list of function or functions operating on the video

                          data before displaying.

                          +
                          +
                          +

                          Window names end up becoming the title of the windows

                          +
                          + +
                          +
                          +breakpoint_display(*args, **kwargs)[source]
                          +

                          Display all the arrays, cameras, and videos passed in. Stops code execution until the window is closed.

                          +
                          + +
                          +

                          Windows

                          +
                          +
                          +class SubscriberWindows(window_names: Iterable[str] = ('displayarray',), video_sources: Iterable[Union[str, int]] = (0,), callbacks: Optional[List[Callable[[numpy.ndarray], Any]]] = None)[source]
                          +

                          Windows that subscribe to updates to cameras, videos, and arrays.

                          +
                          +
                          +add_callback(callback)[source]
                          +

                          Add a callback for this class to apply to videos.

                          +
                          + +
                          +
                          +add_source(name)[source]
                          +

                          Add another source for this class to display.

                          +
                          + +
                          +
                          +add_window(name)[source]
                          +

                          Add another window for this class to display sources with. The name will be the title.

                          +
                          + +
                          +
                          +block()[source]
                          +

                          Update the window continuously while blocking the outer program.

                          +
                          + +
                          +
                          +end()[source]
                          +

                          Close all threads. Should be used with non-blocking mode.

                          +
                          + +
                          +
                          +handle_keys(key_input: int)[source]
                          +

                          Capture key input for the escape function and passing to key control subscriber threads.

                          +
                          + +
                          +
                          +handle_mouse(event, x, y, flags, param)[source]
                          +

                          Capture mouse input for mouse control subscriber threads.

                          +
                          + +
                          +
                          +loop()[source]
                          +

                          Continually update window frame. OpenCV only allows this in the main thread.

                          +
                          + +
                          +
                          +update(arr: numpy.ndarray = None, id: str = None)[source]
                          +

                          Update window frames once. Optionally add a new input and input id.

                          +
                          + +
                          +
                          +update_window_frames()[source]
                          +

                          Update the windows with the newest data for all frames.

                          +
                          + +
                          +
                          +wait_for_init()[source]
                          +

                          Update window frames in a loop until they’re actually updated. Useful for waiting for cameras to init.

                          +
                          + +
                          + +
                          +
                          + + +
                          + +
                          +
                          + +
                          +
                          + + + + + + + \ No newline at end of file diff --git a/docs/displayarray/index.html b/docs/displayarray/index.html index 05308a7..e2bc6a4 100644 --- a/docs/displayarray/index.html +++ b/docs/displayarray/index.html @@ -14,7 +14,6 @@ - @@ -40,7 +39,7 @@

                          Display

                          -display(*vids, callbacks: Union[Dict[Any, Callable[[numpy.ndarray], Optional[numpy.ndarray]]], List[Callable[[numpy.ndarray], Optional[numpy.ndarray]]], Callable[[numpy.ndarray], Optional[numpy.ndarray]], None] = None, window_names=None, blocking=False, fps_limit=240, size=(-1, -1))[source]
                          +display(*vids, callbacks: Union[Dict[Any, Union[Callable[[numpy.ndarray], Optional[numpy.ndarray]], List[Callable[[numpy.ndarray], Optional[numpy.ndarray]]]]], List[Callable[[numpy.ndarray], Optional[numpy.ndarray]]], Callable[[numpy.ndarray], Optional[numpy.ndarray]], None] = None, window_names=None, blocking=False, fps_limit=240, size=(-1, -1))[source]

                          Display all the arrays, cameras, and videos passed in.

                          callbacks can be a dictionary linking functions to videos, or a list of function or functions operating on the video

                          data before displaying.

                          @@ -60,8 +59,8 @@

                          Frames

                          -read_updates(*vids, callbacks: Union[Dict[Any, Callable[[numpy.ndarray], Optional[numpy.ndarray]]], List[Callable[[numpy.ndarray], Optional[numpy.ndarray]]], Callable[[numpy.ndarray], Optional[numpy.ndarray]], None] = None, fps_limit=inf, size=(-1, -1), end_callback: Callable[[], bool] = <function <lambda>>, blocking=True)[source]
                          -

                          Reads back all updates from the requested videos.

                          +read_updates(*vids, callbacks: Union[Dict[Any, Union[Callable[[numpy.ndarray], Optional[numpy.ndarray]], List[Callable[[numpy.ndarray], Optional[numpy.ndarray]]]]], List[Callable[[numpy.ndarray], Optional[numpy.ndarray]]], Callable[[numpy.ndarray], Optional[numpy.ndarray]], None] = None, fps_limit=inf, size=(-1, -1), end_callback: Callable[[], bool] = <function <lambda>>, blocking=True)[source] +

                          Read back all updates from the requested videos.

                          Example usage: >>> from examples.videos import test_video >>> f = 0 @@ -69,6 +68,86 @@ … print(f”Frame:{f}. Array:{r}”)

                          +
                    +
                    +

                    Effects

                    +
                    +
                    +class Crop(output_size=(64, 64, 3), center=None)[source]
                    +

                    A callback class that will return the input array cropped to the output size. N-dimensional.

                    +
                    +
                    +property center
                    +

                    Get the center.

                    +
                    + +
                    +
                    +enable_mouse_control()[source]
                    +

                    Move the mouse to move where the crop is from on the original image.

                    +
                    + +
                    +
                    +property output_size
                    +

                    Get the output size.

                    +
                    + +
                    + +
                    +
                    +class Barrel(use_bleed=False, barrel_power=1, pincushion_power=1, zoom=1, center=None)[source]
                    +

                    A barrel lens distortion callback.

                    +
                    +
                    +property barrel_power
                    +

                    Guarded barrel power. Avoids divide by zero conditions.

                    +
                    + +
                    +
                    +property center
                    +

                    Guarded get center. Limits to within input.

                    +
                    + +
                    +
                    +enable_mouse_control(crop_size=None)[source]
                    +

                    Enable the default mouse controls.

                    +

                    Move the mouse to center the image +scroll to increase/decrease barrel +ctrl+scroll to increase/decrease zoom

                    +
                    + +
                    +
                    +property zoom
                    +

                    Guarded zoom. Avoids divide by zero conditions.

                    +
                    + +
                    + +
                    +
                    +class SelectChannels(selected_channels: Iterable[int] = None)[source]
                    +

                    Select channels to display from an array with too many colors.

                    +
                    +
                    Parameters
                    +

                    selected_channels – the list of channels to display.

                    +
                    +
                    +
                    +
                    +enable_mouse_control()[source]
                    +

                    Enable mouse control.

                    +

                    Alt+Scroll to increase/decrease channel 2. +Shift+Scroll to increase/decrease channel 1. +Ctrl+scroll to increase/decrease channel 0.

                    +
                    + +
                    +
                  @@ -94,9 +173,9 @@
                • displayarray
                • -
                • displayarray.effects.crop
                • displayarray.effects.lens
                • displayarray.effects.select_channels
                • displayarray.frame
                • @@ -113,7 +192,6 @@
                diff --git a/docs/displayarray_bash/index.html b/docs/displayarray_bash/index.html index de18b08..d8feef0 100644 --- a/docs/displayarray_bash/index.html +++ b/docs/displayarray_bash/index.html @@ -33,8 +33,8 @@

                displayarray cli

                -

                DisplayArray. -Display NumPy arrays.

                +

                DisplayArray.

                +

                Display NumPy arrays.

                Usage:

                displayarray (-w <webcam-number> | -v <video-filename> | -t <topic-name>[,dtype])… [-m <msg-backend>] displayarray -h @@ -69,6 +69,12 @@ Currently supported: ROS, ZeroMQ

                +
                +
                +main(argv=None)[source]
                +

                Process command line arguments.

                +
                +
                diff --git a/docs/docsrc/conf.py b/docs/docsrc/conf.py index e505c11..11561d4 100644 --- a/docs/docsrc/conf.py +++ b/docs/docsrc/conf.py @@ -10,16 +10,16 @@ # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # -# import os -# import sys -# sys.path.insert(0, os.path.abspath('.')) +import os +import sys +sys.path.insert(0, os.path.abspath(f'..{os.sep}..')) # -- Project information ----------------------------------------------------- -project = 'DisplayArray' -copyright = '2019, Simulator Leek' -author = 'Simulator Leek' +project = "DisplayArray" +copyright = "2019, Simulator Leek" +author = "Simulator Leek" # -- General configuration --------------------------------------------------- @@ -27,15 +27,15 @@ author = 'Simulator Leek' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = ['sphinx.ext.todo', 'sphinx.ext.viewcode', 'sphinx.ext.autodoc'] +extensions = ["sphinx.ext.todo", "sphinx.ext.viewcode", "sphinx.ext.autodoc"] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] add_module_names = False @@ -45,9 +45,9 @@ add_module_names = False # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'alabaster' +html_theme = "alabaster" # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] \ No newline at end of file +html_static_path = ["_static"] diff --git a/docs/docsrc/crop.rst b/docs/docsrc/crop.rst deleted file mode 100644 index 3f5b9fd..0000000 --- a/docs/docsrc/crop.rst +++ /dev/null @@ -1,7 +0,0 @@ -displayarray.effects.crop -=================================== - -.. currentmodule:: displayarray.effects.crop - -.. autoclass:: Crop - :members: \ No newline at end of file diff --git a/docs/docsrc/display.rst b/docs/docsrc/display.rst new file mode 100644 index 0000000..1a324d2 --- /dev/null +++ b/docs/docsrc/display.rst @@ -0,0 +1,11 @@ +displayarray.display +=================================== +.. automodule:: displayarray + +.. autofunction:: display +.. autofunction:: breakpoint_display + +Windows +------- +.. autoclass:: displayarray.window.SubscriberWindows + :members: \ No newline at end of file diff --git a/docs/docsrc/effects.rst b/docs/docsrc/effects.rst new file mode 100644 index 0000000..4e81234 --- /dev/null +++ b/docs/docsrc/effects.rst @@ -0,0 +1,30 @@ +displayarray.effects +==================== + +Crop +---- +.. currentmodule:: displayarray.effects.crop + +.. autoclass:: Crop + :members: + +Lens +---- +.. currentmodule:: displayarray.effects.lens + +.. automodule:: displayarray.effects.lens + :members: + +.. autoclass:: Barrel + :members: + +.. autoclass:: Mustache + :members: + +Select Channels +--------------- +.. currentmodule:: displayarray.effects.select_channels + +.. autoclass:: SelectChannels + :members: + diff --git a/docs/docsrc/frame.rst b/docs/docsrc/frame.rst index 3d9e51e..8d8412f 100644 --- a/docs/docsrc/frame.rst +++ b/docs/docsrc/frame.rst @@ -1,6 +1,14 @@ displayarray.frame =================================== +Read Updates +------------ +.. currentmodule:: displayarray + +.. autofunction:: read_updates + +Frame Passing +------------- .. currentmodule:: displayarray.frame .. automodule:: displayarray.frame diff --git a/docs/docsrc/index.rst b/docs/docsrc/index.rst index 9bed90f..8b5c696 100644 --- a/docs/docsrc/index.rst +++ b/docs/docsrc/index.rst @@ -14,13 +14,10 @@ DisplayArray displays arrays. :maxdepth: 3 :caption: Python API - displayarray - displayarray.effects.crop - displayarray.effects.lens - displayarray.effects.select_channels - displayarray.frame - displayarray.window - displayarray.input + display + frame + effects + input .. toctree:: :maxdepth: 2 diff --git a/docs/docsrc/lens.rst b/docs/docsrc/lens.rst deleted file mode 100644 index e59939b..0000000 --- a/docs/docsrc/lens.rst +++ /dev/null @@ -1,13 +0,0 @@ -displayarray.effects.lens -============================ - -.. currentmodule:: displayarray.effects.lens - -.. automodule:: displayarray.effects.lens - :members: - -.. autoclass:: Barrel - :members: - -.. autoclass:: Mustache - :members: \ No newline at end of file diff --git a/docs/docsrc/select_channels.rst b/docs/docsrc/select_channels.rst deleted file mode 100644 index 4188e74..0000000 --- a/docs/docsrc/select_channels.rst +++ /dev/null @@ -1,7 +0,0 @@ -displayarray.effects.select_channels -====================================== - -.. currentmodule:: displayarray.effects.select_channels - -.. autoclass:: SelectChannels - :members: \ No newline at end of file diff --git a/docs/docsrc/window.rst b/docs/docsrc/window.rst deleted file mode 100644 index bcbfbf3..0000000 --- a/docs/docsrc/window.rst +++ /dev/null @@ -1,7 +0,0 @@ -displayarray.window -=================================== - -.. currentmodule:: displayarray.window - -.. autoclass:: SubscriberWindows - :members: diff --git a/docs/effects/index.html b/docs/effects/index.html new file mode 100644 index 0000000..9ba7bb3 --- /dev/null +++ b/docs/effects/index.html @@ -0,0 +1,288 @@ + + + + + + + displayarray.effects — DisplayArray documentation + + + + + + + + + + + + + + + + + + + + +
                +
                +
                + + +
                + +
                +

                displayarray.effects

                +
                +

                Crop

                +
                +
                +class Crop(output_size=(64, 64, 3), center=None)[source]
                +

                A callback class that will return the input array cropped to the output size. N-dimensional.

                +
                +
                +property center
                +

                Get the center.

                +
                + +
                +
                +enable_mouse_control()[source]
                +

                Move the mouse to move where the crop is from on the original image.

                +
                + +
                +
                +property output_size
                +

                Get the output size.

                +
                + +
                + +
                +
                +

                Lens

                +

                Create lens effects. Currently only 2D+color arrays are supported.

                +
                +
                +class Barrel(use_bleed=False, barrel_power=1, pincushion_power=1, zoom=1, center=None)[source]
                +

                A barrel lens distortion callback.

                +
                +
                +property barrel_power
                +

                Guarded barrel power. Avoids divide by zero conditions.

                +
                + +
                +
                +property center
                +

                Guarded get center. Limits to within input.

                +
                + +
                +
                +enable_mouse_control(crop_size=None)[source]
                +

                Enable the default mouse controls.

                +

                Move the mouse to center the image +scroll to increase/decrease barrel +ctrl+scroll to increase/decrease zoom

                +
                + +
                +
                +property zoom
                +

                Guarded zoom. Avoids divide by zero conditions.

                +
                + +
                + +
                +
                +class BarrelPyTorch(use_bleed=False, barrel_power=1, pincushion_power=1, zoom=1, center=None)[source]
                +

                A barrel distortion callback class accelerated by PyTorch.

                +
                + +
                +
                +class ControllableLens(use_bleed=False, zoom=1, center=None)[source]
                +

                A lens callback that can be controlled by the program or the user.

                +
                +
                +run_bleed(arr, x, y)[source]
                +

                Spread color outwards, like food coloring in water.

                +
                + +
                + +
                +
                +class Mustache(use_bleed=False, barrel_power=1, pincushion_power=1, zoom=1, center=None)[source]
                +

                A mustache distortion callback.

                +
                +
                +enable_mouse_control()[source]
                +

                Enable the default mouse loop to control the mustache distortion.

                +

                ctrl+scroll wheel: zoom in and out +shift+scroll wheel: adjust pincushion power +scroll wheel: adjust barrel power

                +
                + +
                + +
                +
                +class Barrel(use_bleed=False, barrel_power=1, pincushion_power=1, zoom=1, center=None)[source]
                +

                A barrel lens distortion callback.

                +
                +
                +property barrel_power
                +

                Guarded barrel power. Avoids divide by zero conditions.

                +
                + +
                +
                +property center
                +

                Guarded get center. Limits to within input.

                +
                + +
                +
                +enable_mouse_control(crop_size=None)[source]
                +

                Enable the default mouse controls.

                +

                Move the mouse to center the image +scroll to increase/decrease barrel +ctrl+scroll to increase/decrease zoom

                +
                + +
                +
                +property zoom
                +

                Guarded zoom. Avoids divide by zero conditions.

                +
                + +
                + +
                +
                +class Mustache(use_bleed=False, barrel_power=1, pincushion_power=1, zoom=1, center=None)[source]
                +

                A mustache distortion callback.

                +
                +
                +enable_mouse_control()[source]
                +

                Enable the default mouse loop to control the mustache distortion.

                +

                ctrl+scroll wheel: zoom in and out +shift+scroll wheel: adjust pincushion power +scroll wheel: adjust barrel power

                +
                + +
                + +
                +
                +

                Select Channels

                +
                +
                +class SelectChannels(selected_channels: Iterable[int] = None)[source]
                +

                Select channels to display from an array with too many colors.

                +
                +
                Parameters
                +

                selected_channels – the list of channels to display.

                +
                +
                +
                +
                +enable_mouse_control()[source]
                +

                Enable mouse control.

                +

                Alt+Scroll to increase/decrease channel 2. +Shift+Scroll to increase/decrease channel 1. +Ctrl+scroll to increase/decrease channel 0.

                +
                + +
                + +
                +
                + + +
                + +
                +
                + +
                +
                + + + + + + + \ No newline at end of file diff --git a/docs/frame/index.html b/docs/frame/index.html index 186f0cf..ea9a7b7 100644 --- a/docs/frame/index.html +++ b/docs/frame/index.html @@ -14,8 +14,8 @@ - - + + @@ -32,8 +32,26 @@
                -
                -

                displayarray.frame

                +
                +

                displayarray.frame

                +
                +

                Read Updates

                +
                +
                +read_updates(*vids, callbacks: Union[Dict[Any, Union[Callable[[numpy.ndarray], Optional[numpy.ndarray]], List[Callable[[numpy.ndarray], Optional[numpy.ndarray]]]]], List[Callable[[numpy.ndarray], Optional[numpy.ndarray]]], Callable[[numpy.ndarray], Optional[numpy.ndarray]], None] = None, fps_limit=inf, size=(-1, -1), end_callback: Callable[[], bool] = <function <lambda>>, blocking=True)[source]
                +

                Read back all updates from the requested videos.

                +

                Example usage:

                +
                >>> from examples.videos import test_video
                +>>> f = 0
                +>>> for f, r in enumerate(read_updates(test_video, end_callback=lambda :f==2)):
                +...   print(f"Frame:{f}. Array:{r}")
                +
                +
                +
                + +
                +
                +

                Frame Passing

                Handles publishing arrays, videos, and cameras.

                CamCtrl handles sending and receiving commands to specific camera (or array/video) publishers VideoHandlerThread updates the frames for the global displayer, since OpenCV can only update on the main thread @@ -66,13 +84,15 @@ np_cam simulates numpy arrays as OpenCV cameras

                -read_updates(*vids, callbacks: Union[Dict[Any, Callable[[numpy.ndarray], Optional[numpy.ndarray]]], List[Callable[[numpy.ndarray], Optional[numpy.ndarray]]], Callable[[numpy.ndarray], Optional[numpy.ndarray]], None] = None, fps_limit=inf, size=(-1, -1), end_callback: Callable[[], bool] = <function <lambda>>, blocking=True)[source]
                -

                Reads back all updates from the requested videos.

                -

                Example usage: ->>> from examples.videos import test_video ->>> f = 0 ->>> for f, r in enumerate(read_updates(test_video, end_callback=lambda :f==2)): -… print(f”Frame:{f}. Array:{r}”)

                +read_updates(*vids, callbacks: Union[Dict[Any, Union[Callable[[numpy.ndarray], Optional[numpy.ndarray]], List[Callable[[numpy.ndarray], Optional[numpy.ndarray]]]]], List[Callable[[numpy.ndarray], Optional[numpy.ndarray]]], Callable[[numpy.ndarray], Optional[numpy.ndarray]], None] = None, fps_limit=inf, size=(-1, -1), end_callback: Callable[[], bool] = <function <lambda>>, blocking=True)[source] +

                Read back all updates from the requested videos.

                +

                Example usage:

                +
                >>> from examples.videos import test_video
                +>>> f = 0
                +>>> for f, r in enumerate(read_updates(test_video, end_callback=lambda :f==2)):
                +...   print(f"Frame:{f}. Array:{r}")
                +
                +
                @@ -123,6 +143,7 @@ np_cam simulates numpy arrays as OpenCV cameras

                Run pub_cam_loop in a new thread. Starts on creation.

                +
                @@ -144,13 +165,14 @@ np_cam simulates numpy arrays as OpenCV cameras

                Navigation

                Python API

                Bash API

                diff --git a/docs/genindex/index.html b/docs/genindex/index.html index 979c7e6..288e791 100644 --- a/docs/genindex/index.html +++ b/docs/genindex/index.html @@ -48,23 +48,25 @@ | L | M | N + | O | P | R | S | U | W + | Z

                A

                @@ -72,11 +74,17 @@

                B

                @@ -84,7 +92,17 @@

                C

                +
                @@ -95,16 +113,16 @@
              • display() (FrameUpdater method)
              • -
              • displayarray (module) +
              • displayarray (module)
              @@ -190,13 +212,15 @@

              M

              @@ -209,6 +233,14 @@
            +

            O

            + + +
            +

            P

              @@ -222,7 +254,7 @@
            @@ -238,13 +272,13 @@

            S

            @@ -252,11 +286,11 @@

            U

            @@ -264,7 +298,15 @@

            W

            +
            + +

            Z

            + +
            @@ -289,13 +331,10 @@

            Navigation

            Python API

            Bash API

            @@ -79,13 +89,10 @@

            Navigation

            Python API

              -
            • displayarray
            • -
            • displayarray.effects.crop
            • -
            • displayarray.effects.lens
            • -
            • displayarray.effects.select_channels
            • -
            • displayarray.frame
            • -
            • displayarray.window
            • -
            • displayarray.input
                +
              • display
              • +
              • frame
              • +
              • effects
              • +
              • input @@ -100,7 +107,7 @@

                Related Topics

                diff --git a/docs/lens/index.html b/docs/lens/index.html index 9ba4162..88a8165 100644 --- a/docs/lens/index.html +++ b/docs/lens/index.html @@ -34,13 +34,54 @@

                displayarray.effects.lens

                +

                Create lens effects. Currently only 2D+color arrays are supported.

                class Barrel(use_bleed=False, barrel_power=1, pincushion_power=1, zoom=1, center=None)[source]
                -
                +

                A barrel lens distortion callback.

                +
                +
                +property barrel_power
                +

                Guarded barrel power. Avoids divide by zero conditions.

                +
                + +
                +
                +property center
                +

                Guarded get center. Limits to within input.

                +
                + +
                -enable_mouse_control()[source]
                -

                Move the mouse to center the image, scroll to increase/decrease barrel, ctrl+scroll to increase/decrease zoom

                +enable_mouse_control(crop_size=None)[source] +

                Enable the default mouse controls.

                +

                Move the mouse to center the image +scroll to increase/decrease barrel +ctrl+scroll to increase/decrease zoom

                +
                + +
                +
                +property zoom
                +

                Guarded zoom. Avoids divide by zero conditions.

                +
                + +
                + +
                +
                +class BarrelPyTorch(use_bleed=False, barrel_power=1, pincushion_power=1, zoom=1, center=None)[source]
                +

                A barrel distortion callback class accelerated by PyTorch.

                +
                + +
                +
                +class ControllableLens(use_bleed=False, zoom=1, center=None)[source]
                +

                A lens callback that can be controlled by the program or the user.

                +
                +
                +run_bleed(arr, x, y)[source]
                +

                Spread color outwards, like food coloring in water.

                @@ -48,7 +89,65 @@
                class Mustache(use_bleed=False, barrel_power=1, pincushion_power=1, zoom=1, center=None)[source]
                -
                +

                A mustache distortion callback.

                +
                +
                +enable_mouse_control()[source]
                +

                Enable the default mouse loop to control the mustache distortion.

                +

                ctrl+scroll wheel: zoom in and out +shift+scroll wheel: adjust pincushion power +scroll wheel: adjust barrel power

                +
                + +
                + +
                +
                +class Barrel(use_bleed=False, barrel_power=1, pincushion_power=1, zoom=1, center=None)[source]
                +

                A barrel lens distortion callback.

                +
                +
                +property barrel_power
                +

                Guarded barrel power. Avoids divide by zero conditions.

                +
                + +
                +
                +property center
                +

                Guarded get center. Limits to within input.

                +
                + +
                +
                +enable_mouse_control(crop_size=None)[source]
                +

                Enable the default mouse controls.

                +

                Move the mouse to center the image +scroll to increase/decrease barrel +ctrl+scroll to increase/decrease zoom

                +
                + +
                +
                +property zoom
                +

                Guarded zoom. Avoids divide by zero conditions.

                +
                + +
                + +
                +
                +class Mustache(use_bleed=False, barrel_power=1, pincushion_power=1, zoom=1, center=None)[source]
                +

                A mustache distortion callback.

                +
                +
                +enable_mouse_control()[source]
                +

                Enable the default mouse loop to control the mustache distortion.

                +

                ctrl+scroll wheel: zoom in and out +shift+scroll wheel: adjust pincushion power +scroll wheel: adjust barrel power

                +
                + +
                diff --git a/docs/objects.inv b/docs/objects.inv index 2236204..d06227a 100644 Binary files a/docs/objects.inv and b/docs/objects.inv differ diff --git a/docs/py-modindex/index.html b/docs/py-modindex/index.html index 1cc4fe6..5c1fbdc 100644 --- a/docs/py-modindex/index.html +++ b/docs/py-modindex/index.html @@ -48,7 +48,7 @@ - displayarray + displayarray @@ -58,7 +58,7 @@     - displayarray.effects.lens + displayarray.effects.lens @@ -86,13 +86,10 @@

                Navigation

                Python API

                Bash API

                  diff --git a/docs/search/index.html b/docs/search/index.html index 4ff96c6..2c9735c 100644 --- a/docs/search/index.html +++ b/docs/search/index.html @@ -77,13 +77,10 @@

                  Navigation

                  Python API

                  Bash API

                    diff --git a/docs/searchindex.js b/docs/searchindex.js index 93f7251..edd8c42 100644 --- a/docs/searchindex.js +++ b/docs/searchindex.js @@ -1 +1 @@ -Search.setIndex({docnames:["crop","displayarray","displayarray_bash","frame","index","input","lens","select_channels","window"],envversion:{"sphinx.domains.c":1,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":1,"sphinx.domains.javascript":1,"sphinx.domains.math":2,"sphinx.domains.python":1,"sphinx.domains.rst":1,"sphinx.domains.std":1,"sphinx.ext.todo":2,"sphinx.ext.viewcode":1,sphinx:56},filenames:["crop.rst","displayarray.rst","displayarray_bash.rst","frame.rst","index.rst","input.rst","lens.rst","select_channels.rst","window.rst"],objects:{"":{displayarray:[1,0,0,"-"]},"displayarray.effects":{lens:[6,0,0,"-"]},"displayarray.effects.crop":{Crop:[0,2,1,""]},"displayarray.effects.crop.Crop":{enable_mouse_control:[0,3,1,""]},"displayarray.effects.lens":{Barrel:[6,2,1,""],Mustache:[6,2,1,""]},"displayarray.effects.lens.Barrel":{enable_mouse_control:[6,3,1,""]},"displayarray.effects.select_channels":{SelectChannels:[7,2,1,""]},"displayarray.frame":{FrameUpdater:[3,2,1,""],NpCam:[3,2,1,""],get_cam_ids:[3,1,1,""],pub_cam_thread:[3,1,1,""],read_updates:[3,1,1,""]},"displayarray.frame.FrameUpdater":{display:[3,3,1,""],loop:[3,3,1,""]},"displayarray.frame.NpCam":{get:[3,3,1,""],isOpened:[3,3,1,""],read:[3,3,1,""],release:[3,3,1,""],set:[3,3,1,""]},"displayarray.input":{MouseEvent:[5,2,1,""],key_loop:[5,2,1,""],mouse_loop:[5,2,1,""]},"displayarray.window":{SubscriberWindows:[8,2,1,""]},"displayarray.window.SubscriberWindows":{add_callback:[8,3,1,""],add_source:[8,3,1,""],add_window:[8,3,1,""],end:[8,3,1,""],handle_keys:[8,3,1,""],handle_mouse:[8,3,1,""],loop:[8,3,1,""],update:[8,3,1,""],update_window_frames:[8,3,1,""],wait_for_init:[8,3,1,""]},displayarray:{__main__:[2,0,0,"-"],breakpoint_display:[1,1,1,""],display:[1,1,1,""],frame:[3,0,0,"-"],read_updates:[1,1,1,""]}},objnames:{"0":["py","module","Python module"],"1":["py","function","Python function"],"2":["py","class","Python class"],"3":["py","method","Python method"]},objtypes:{"0":"py:module","1":"py:function","2":"py:class","3":"py:method"},terms:{"class":[0,3,5,6,7,8],"default":[2,3],"float":3,"function":[1,3,5,8],"import":[1,3],"int":[3,8],"new":[3,5,8],"return":0,"static":3,"true":[1,3],For:3,ROS:2,The:8,Use:2,Useful:8,actual:8,adapt:4,add:[3,8],add_callback:8,add_sourc:8,add_window:8,all:[1,3,5,8],allow:8,ani:[1,3,8],anoth:8,api:4,appli:8,arg:[1,3],arr:8,arrai:[0,1,2,3,4,8],back:[1,3],backend:2,barrel:6,barrel_pow:6,bash:4,becom:1,befor:[1,3],block:[1,3,8],bool:[1,3],breakpoint_displai:1,broker:2,call:3,callabl:[1,3,5,8],callback:[0,1,3,8],cam_id:3,camctrl:3,camera:[1,3,8],can:[1,3,4],cap_prop_frame_count:3,cap_prop_frame_height:3,cap_prop_frame_width:3,captur:8,center:[0,6],choos:2,chosen:2,close:[1,8],code:1,com:4,command:3,complet:4,contain:4,continu:[3,5,8],control:[3,8],creation:3,crop:4,ctrl:6,current:[2,3],data:[1,8],decreas:6,detect:3,dict:[1,3],dictionari:1,did:3,dimension:0,direct:4,displai:[2,3,4,8],display:3,dtype:2,effect:4,enable_mouse_control:[0,6],end:[1,3,8],end_callback:[1,3],enumer:[1,3],escap:8,event:[5,8],exampl:[1,3],execut:1,fake:3,fals:[1,5,6],file:[1,2,4],filenam:2,finish:3,fix:3,flag:[5,8],format:3,fps_limit:[1,3],frame:[2,4,8],frameupdat:3,from:[0,1,2,3],get:3,get_cam_id:3,github:4,github_url:4,global:3,hack:3,handl:3,handle_kei:8,handle_mous:8,help:2,high_spe:3,hold:5,http:4,ids:3,imag:[0,6],img:3,increas:6,index:4,inf:[1,3],inform:5,init:8,input:[0,4,8],isopen:3,iter:8,kei:[4,8],key_input:8,key_loop:5,know:3,kwarg:[1,3],lambda:[1,3],least:4,len:4,let:3,like:4,link:1,list:[1,3,8],listen:3,loop:[3,8],main:[3,8],messag:2,mode:8,modul:4,mous:[0,4,6,8],mouse_loop:5,mouseev:5,move:[0,6],msg:2,multipl:3,mustach:6,name:[1,2,8],ndarrai:[1,3,8],newest:8,non:8,none:[0,1,3,5,6,7,8],nov:4,np_cam:3,npcam:3,number:2,numpi:[1,2,3,8],onc:8,onli:[3,8],open:3,opencv:[3,5,8],oper:[1,3],option:[1,2,3,8],origin:0,output:0,output_s:0,outsid:3,own:1,page:4,param:[5,8],paramet:3,pass:[1,8],pincushion_pow:6,pleas:3,print:[1,3],pub_cam_loop:3,pub_cam_thread:3,publish:3,python:4,quickstart:4,read:[1,3],read_upd:[1,3],receiv:[3,5],releas:3,request:[1,3],request_:3,request_s:3,root:4,ros:2,run:[3,5],run_when_no_ev:5,scale:3,screen:3,scroll:6,search:4,select_channel:4,selectchannel:7,selected_channel:7,send:3,set:3,should:[4,8],show:2,simleek:4,simul:3,sinc:3,size:[0,1,3],someth:3,sourc:[0,1,3,5,6,7,8],specif:3,sphinx:4,standard:3,start:3,stop:1,str:[3,5,8],subscrib:8,subscriberwindow:8,support:2,tell:3,test_video:[1,3],text:2,thei:8,them:3,thi:[2,3,4,8],thread:[3,5,8],thu:4,titl:[1,8],toctre:4,topic:2,tupl:3,union:[1,3,8],until:[1,3,8],updat:[1,3,8],update_window_fram:8,usag:[1,2,3],use:3,use_ble:6,used:8,using:2,version:2,vid:[1,3],video:[1,2,3,8],video_sourc:[3,8],videohandlerthread:3,wait:8,wait_for_init:8,webcam:[1,2],where:0,window:[1,4,5],window_nam:[1,8],work:3,you:4,your:4,zeromq:2,zoom:6},titles:["displayarray.effects.crop","displayarray","displayarray cli","displayarray.frame","DisplayArray Documentation","displayarray.input","displayarray.effects.lens","displayarray.effects.select_channels","displayarray.window"],titleterms:{cli:2,crop:0,displai:1,displayarrai:[0,1,2,3,4,5,6,7,8],document:4,effect:[0,6,7],frame:[1,3],indic:4,input:5,kei:5,len:6,mous:5,select_channel:7,tabl:4,window:8}}) \ No newline at end of file +Search.setIndex({docnames:["display","displayarray_bash","effects","frame","index","input"],envversion:{"sphinx.domains.c":1,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":1,"sphinx.domains.javascript":1,"sphinx.domains.math":2,"sphinx.domains.python":1,"sphinx.domains.rst":1,"sphinx.domains.std":1,"sphinx.ext.todo":2,"sphinx.ext.viewcode":1,sphinx:56},filenames:["display.rst","displayarray_bash.rst","effects.rst","frame.rst","index.rst","input.rst"],objects:{"":{displayarray:[0,0,0,"-"]},"displayarray.__main__":{main:[1,1,1,""]},"displayarray.effects":{lens:[2,0,0,"-"]},"displayarray.effects.crop":{Crop:[2,2,1,""]},"displayarray.effects.crop.Crop":{center:[2,3,1,""],enable_mouse_control:[2,3,1,""],output_size:[2,3,1,""]},"displayarray.effects.lens":{Barrel:[2,2,1,""],BarrelPyTorch:[2,2,1,""],ControllableLens:[2,2,1,""],Mustache:[2,2,1,""]},"displayarray.effects.lens.Barrel":{barrel_power:[2,3,1,""],center:[2,3,1,""],enable_mouse_control:[2,3,1,""],zoom:[2,3,1,""]},"displayarray.effects.lens.ControllableLens":{run_bleed:[2,3,1,""]},"displayarray.effects.lens.Mustache":{enable_mouse_control:[2,3,1,""]},"displayarray.effects.select_channels":{SelectChannels:[2,2,1,""]},"displayarray.effects.select_channels.SelectChannels":{enable_mouse_control:[2,3,1,""]},"displayarray.frame":{FrameUpdater:[3,2,1,""],NpCam:[3,2,1,""],get_cam_ids:[3,1,1,""],pub_cam_thread:[3,1,1,""],read_updates:[3,1,1,""]},"displayarray.frame.FrameUpdater":{display:[3,3,1,""],loop:[3,3,1,""]},"displayarray.frame.NpCam":{get:[3,3,1,""],isOpened:[3,3,1,""],read:[3,3,1,""],release:[3,3,1,""],set:[3,3,1,""]},"displayarray.input":{MouseEvent:[5,2,1,""],key_loop:[5,2,1,""],mouse_loop:[5,2,1,""]},"displayarray.window":{SubscriberWindows:[0,2,1,""]},"displayarray.window.SubscriberWindows":{add_callback:[0,3,1,""],add_source:[0,3,1,""],add_window:[0,3,1,""],block:[0,3,1,""],end:[0,3,1,""],handle_keys:[0,3,1,""],handle_mouse:[0,3,1,""],loop:[0,3,1,""],update:[0,3,1,""],update_window_frames:[0,3,1,""],wait_for_init:[0,3,1,""]},displayarray:{__main__:[1,0,0,"-"],breakpoint_display:[0,1,1,""],display:[0,1,1,""],frame:[3,0,0,"-"],read_updates:[3,1,1,""]}},objnames:{"0":["py","module","Python module"],"1":["py","function","Python function"],"2":["py","class","Python class"],"3":["py","method","Python method"]},objtypes:{"0":"py:module","1":"py:function","2":"py:class","3":"py:method"},terms:{"class":[0,2,3,5],"default":[1,2,3],"float":3,"function":[0,3,5],"import":3,"int":[0,2,3],"new":[0,3,5],"return":2,"static":3,"true":3,"while":0,For:3,ROS:1,The:0,Use:1,Useful:0,acceler:2,actual:0,adapt:4,add:[0,3],add_callback:0,add_sourc:0,add_window:0,adjust:2,alia:[],all:[0,3,5],allow:0,alt:2,ani:[0,3],anoth:0,api:4,appli:0,arg:[0,3],argument:1,argv:1,arr:[0,2],arrai:[0,1,2,3,4],avoid:2,back:3,backend:1,barrel:2,barrel_pow:2,barrelpytorch:2,bash:4,becom:0,befor:[0,3],block:[0,3],bool:3,breakpoint_displai:0,broker:1,call:3,callabl:[0,3,5],callback:[0,2,3],cam_id:3,camctrl:3,camera:[0,3],can:[0,2,3,4],cap_prop_frame_count:3,cap_prop_frame_height:3,cap_prop_frame_width:3,captur:0,center:2,channel:4,choos:1,chosen:1,close:0,code:0,color:2,com:4,command:[1,3],complet:4,condit:2,contain:4,continu:[0,3,5],control:[0,2,3],controllablelen:2,creat:2,creation:3,crop:4,crop_siz:2,ctrl:2,current:[1,2,3],data:0,decreas:2,def:5,detect:3,dict:[0,3],dictionari:0,did:3,dimension:2,direct:4,displai:[1,2,3,4],display:3,distort:2,divid:2,doctest:[],dtype:1,effect:4,enabl:2,enable_mouse_control:2,end:[0,3],end_callback:3,enumer:3,escap:0,event:[0,5],exampl:3,execut:0,eyebal:[],fake:3,fals:[0,2],file:[0,1,4],filenam:1,finish:3,fix:3,flag:[0,5],food:2,format:[3,5],fps_limit:[0,3],frame:[0,1,4],frameupdat:3,from:[1,2,3],fun:5,get:[2,3],get_cam_id:3,github:4,github_url:4,global:3,guard:2,hack:3,handl:3,handle_kei:0,handle_mous:0,help:1,high_spe:3,hold:5,http:4,ids:3,imag:2,img:3,increas:2,index:4,inf:3,inform:5,init:0,input:[0,2,4],isopen:3,iter:[0,2],kei:[0,4],key_input:0,key_loop:5,know:3,kwarg:[0,3],lambda:3,least:4,len:4,let:3,like:[2,4],limit:2,line:1,link:0,list:[0,2,3],listen:3,loop:[0,2,3],main:[0,1,3],mani:2,messag:1,mode:0,modul:4,mous:[0,2,4],mouse_ev:5,mouse_loop:5,mouseev:5,move:2,msg:1,multipl:3,mustach:2,name:[0,1],ndarrai:[0,3],newest:0,non:0,none:[0,1,2,3,5],nov:4,np_cam:3,npcam:3,number:1,numpi:[0,1,3],onc:0,onli:[0,2,3],open:3,opencv:[0,3,5],oper:[0,3],option:[0,1,3],origin:2,out:2,outer:0,output:2,output_s:2,outsid:3,outward:2,own:0,page:4,param:[0,5],paramet:[2,3],pass:[0,4],pincushion:2,pincushion_pow:2,pleas:3,power:2,press:5,print:[3,5],process:1,program:[0,2],properti:2,pub_cam_loop:3,pub_cam_thread:3,publish:3,python:4,pytorch:2,quickstart:4,read:4,read_upd:3,receiv:[3,5],reduc:[],releas:3,request:3,request_:3,request_s:3,root:4,ros:1,run:[3,5],run_ble:2,scale:3,screen:3,scroll:2,search:4,see:[],select:4,select_channel:[],selectchannel:2,selected_channel:2,send:3,set:3,shift:2,should:[0,4],show:1,simleek:4,simul:3,sinc:3,size:[0,2,3],someth:3,sourc:[0,1,2,3,5],specif:3,sphinx:4,spread:2,standard:3,start:3,stop:0,str:[0,3,5],subscrib:0,subscriberwindow:0,support:[1,2],tell:3,test_video:3,text:1,thei:0,them:3,thi:[0,1,3,4],thread:[0,3,5],three:[],thu:4,titl:0,toctre:4,too:2,topic:1,tupl:3,union:[0,3],until:[0,3],updat:[0,4],update_window_fram:0,usag:[1,3],use:3,use_ble:2,used:0,user:2,using:1,version:1,vid:[0,3],video:[0,1,3],video_sourc:[0,3],videohandlerthread:3,wait:0,wait_for_init:0,water:2,webcam:[0,1],wheel:2,where:2,window:[4,5],window_nam:0,within:2,work:3,you:4,your:4,zero:2,zeromq:1,zoom:2},titles:["displayarray.display","displayarray cli","displayarray.effects","displayarray.frame","DisplayArray Documentation","displayarray.input"],titleterms:{channel:2,cli:1,crop:2,displai:0,displayarrai:[0,1,2,3,4,5],document:4,effect:2,frame:3,indic:4,input:5,kei:5,len:2,mous:5,pass:3,read:3,select:2,select_channel:[],tabl:4,updat:3,window:0}}) \ No newline at end of file diff --git a/docs/select_channels/index.html b/docs/select_channels/index.html index c3e57c9..11367ed 100644 --- a/docs/select_channels/index.html +++ b/docs/select_channels/index.html @@ -36,8 +36,23 @@

                    displayarray.effects.select_channels

                    -class SelectChannels(selected_channels=None)[source]
                    -
                    +class SelectChannels(selected_channels: Iterable[int] = None)[source] +

                    Select channels to display from an array with too many colors.

                    +
                    +
                    Parameters
                    +

                    selected_channels – the list of channels to display.

                    +
                    +
                    +
                    +
                    +enable_mouse_control()[source]
                    +

                    Enable mouse control.

                    +

                    Alt+Scroll to increase/decrease channel 2. +Shift+Scroll to increase/decrease channel 1. +Ctrl+scroll to increase/decrease channel 0.

                    +
                    + +
            diff --git a/docs/window/index.html b/docs/window/index.html index 293643d..fcede89 100644 --- a/docs/window/index.html +++ b/docs/window/index.html @@ -56,6 +56,12 @@

            Add another window for this class to display sources with. The name will be the title.

            +
            +
            +block()[source]
            +

            Update the window continuously while blocking the outer program.

            +
            +
            end()[source]
            @@ -64,7 +70,7 @@
            -handle_keys(key_input)[source]
            +handle_keys(key_input: int)[source]

            Capture key input for the escape function and passing to key control subscriber threads.

            diff --git a/examples/documentation/fractal.py b/examples/documentation/fractal.py index e61a200..9c15b84 100644 --- a/examples/documentation/fractal.py +++ b/examples/documentation/fractal.py @@ -10,17 +10,19 @@ def mandel( source: https://thesamovar.wordpress.com/2009/03/22/fast-fractals-with-python-and-numpy/ - >>> img = mandel() - >>> center = (0, -1.78) - >>> length = 3.2 - >>> d = display(img) - >>> while d: - ... length*=.9 - ... y_min = center[1]-length/2.0 - ... y_max = center[1]+length/2.0 - ... x_min = center[0]-length/2.0 - ... x_max = center[0]+length/2.0 - ... img[...] = mandel(y_min=y_min, y_max=y_max, x_min=x_min, x_max=x_max) + .. code-block:: python + + >>> img = mandel() + >>> center = (0, -1.78) + >>> length = 3.2 + >>> d = display(img) + >>> while d: + ... length*=.9 + ... y_min = center[1]-length/2.0 + ... y_max = center[1]+length/2.0 + ... x_min = center[0]-length/2.0 + ... x_max = center[0]+length/2.0 + ... img[...] = mandel(y_min=y_min, y_max=y_max, x_min=x_min, x_max=x_max) """ ix, iy = np.mgrid[0:height, 0:width] diff --git a/examples/effects/lens_crop.py b/examples/effects/lens_crop.py index 7db1f2e..556adfc 100644 --- a/examples/effects/lens_crop.py +++ b/examples/effects/lens_crop.py @@ -6,10 +6,11 @@ from examples.videos import test_video d = ( display(test_video) - .add_callback(crop.Crop()) - .add_callback(lens.Barrel().enable_mouse_control()) + .add_callback(lens.BarrelPyTorch().enable_mouse_control(crop_size=(256, 256))) + .add_callback(crop.Crop(output_size=(256, 256, 3))) .wait_for_init() ) while d: - print(d.frames[0].shape) + if len(d.frames) > 0: + pass diff --git a/examples/effects/manual_control.py b/examples/effects/manual_control.py new file mode 100644 index 0000000..758ce86 --- /dev/null +++ b/examples/effects/manual_control.py @@ -0,0 +1,36 @@ +from displayarray.effects import crop, lens +from displayarray import display +from examples.videos import test_video +import math as m + +# Move the mouse to center the image, scroll to increase/decrease barrel, ctrl+scroll to increase/decrease zoom + +pre_crop_callback = crop.Crop(output_size=(480, 640, 3)).enable_mouse_control() +lens_callback = lens.BarrelPyTorch() +post_crop_callback = crop.Crop(output_size=(256, 256, 3)).enable_mouse_control() + +d = ( + display(0, size=(99999, 99999)) + .add_callback(pre_crop_callback) + .add_callback(lens_callback) + .add_callback(post_crop_callback) + .wait_for_init() +) + +i = 0 +while d: + if len(d.frames) > 0: + i += 1 + frame = d.frames[0] + center_sin = [(m.sin(m.pi * (i / 70.0))), (m.cos(m.pi * (i / 120.0)))] + pre_crop_callback.center = [ + center_sin[0] * 720 / 2 + 720 / 2, + center_sin[1] * 1280 / 2 + 1280 / 2, + ] + lens_callback.center = [ + center_sin[0] * 480 / 2 + 480 / 2, + center_sin[1] * 640 / 2 + 640 / 2, + ] + post_crop_callback.center = [480 / 2, 640 / 2] + lens_callback.zoom = m.sin(m.pi * ((i + 25) / 50.0)) + 1.01 + lens_callback.barrel_power = m.sin((m.pi * (i + 33) / 25)) + 1.5 diff --git a/mypy.ini b/mypy.ini index 7c4eb3e..b5896ba 100644 --- a/mypy.ini +++ b/mypy.ini @@ -20,4 +20,7 @@ ignore_missing_imports = True ignore_missing_imports = True [mypy-docopt.*] +ignore_missing_imports = True + +[mypy-torch.*] ignore_missing_imports = True \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index db49003..f32c23d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = 'displayarray' -version = '0.7.1' +version = '0.7.2' description = 'Tool for displaying numpy arrays.' authors = ['SimLeek '] license = 'MIT' diff --git a/tests/effects/test_crop.py b/tests/effects/test_crop.py index 108dcde..b3529ee 100644 --- a/tests/effects/test_crop.py +++ b/tests/effects/test_crop.py @@ -8,7 +8,7 @@ def test_init_defaults(): c = crop.Crop() assert np.all(c.output_size == (64, 64, 3)) - assert c.center is None + assert all(c.center == [32, 32, 1]) assert c.odd_center is None assert c.input_size is None @@ -16,7 +16,7 @@ def test_init_defaults(): def test_init(): c = crop.Crop((32, 32, 3), (16, 16, 1)) - c(np.ndarray((64,64,3))) + c(np.ndarray((64, 64, 3))) assert np.all(c.output_size == (32, 32, 3)) assert np.all(c.center == (16, 16, 1))