Updated docs to satisfy tox
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
"""
|
||||
DisplayArray.
|
||||
|
||||
Display NumPy arrays.
|
||||
|
||||
Usage:
|
||||
@@ -25,6 +26,7 @@ import asyncio
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
"""Process command line arguments."""
|
||||
arguments = docopt(__doc__, argv=argv)
|
||||
if arguments["--version"]:
|
||||
from displayarray import __version__
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"""Generate unique IDs for videos."""
|
||||
|
||||
from collections.abc import Hashable
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"""Functions needed to deal with OpenCV."""
|
||||
|
||||
import weakref
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"""Standard callbacks to use on incoming frames."""
|
||||
|
||||
from displayarray.window import window_commands
|
||||
import numpy as np
|
||||
|
||||
@@ -35,7 +37,7 @@ class function_display_callback(object): # NOSONAR
|
||||
"""
|
||||
|
||||
def __init__(self, display_function, finish_function=None):
|
||||
|
||||
"""Run display_function on frames."""
|
||||
self.looping = True
|
||||
self.first_call = True
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
"""Effects to run on numpy arrays to make data clearer."""
|
||||
|
||||
@@ -1,11 +1,26 @@
|
||||
"""Crop any n-dimensional array."""
|
||||
|
||||
import numpy as np
|
||||
from ..input import mouse_loop
|
||||
from displayarray.input import mouse_loop
|
||||
|
||||
|
||||
class Crop(object):
|
||||
"""A callback class that will return the input array cropped to the output size. N-dimensional."""
|
||||
"""
|
||||
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.]],
|
||||
<BLANKLINE>
|
||||
[[1., 1.],
|
||||
[1., 1.]]])
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, output_size=(64, 64, 3), center=None):
|
||||
"""Create the cropper."""
|
||||
self._output_size = None
|
||||
self._center = None
|
||||
self.odd_center = None
|
||||
@@ -17,20 +32,24 @@ class Crop(object):
|
||||
|
||||
@property
|
||||
def output_size(self):
|
||||
"""Get the output size after cropping."""
|
||||
return self._output_size
|
||||
|
||||
@output_size.setter
|
||||
def output_size(self, set):
|
||||
"""Set what the output size will be after cropping."""
|
||||
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."""
|
||||
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)
|
||||
@@ -87,7 +106,7 @@ class Crop(object):
|
||||
return out_array.astype(arr.dtype)
|
||||
|
||||
def enable_mouse_control(self):
|
||||
"""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."""
|
||||
|
||||
@mouse_loop
|
||||
def m_loop(me):
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
"""Create lens effects. Currently only 2D+color arrays are supported."""
|
||||
|
||||
import numpy as np
|
||||
from ..input import mouse_loop
|
||||
from displayarray.input import mouse_loop
|
||||
import cv2
|
||||
|
||||
|
||||
class ControllableLens(object):
|
||||
class _ControllableLens(object):
|
||||
def __init__(self, use_bleed=False, zoom=1, center=None):
|
||||
self.center = center
|
||||
self.zoom = zoom
|
||||
@@ -22,10 +24,66 @@ class ControllableLens(object):
|
||||
) / 2
|
||||
|
||||
|
||||
class Barrel(ControllableLens):
|
||||
def __init__(
|
||||
self, use_bleed=False, barrel_power=1, pincushion_power=1, zoom=1, center=None
|
||||
):
|
||||
class Barrel(_ControllableLens):
|
||||
"""
|
||||
Create a barrel distortion.
|
||||
|
||||
>>> 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. ]],
|
||||
<BLANKLINE>
|
||||
[[0. , 0. ],
|
||||
[0.33333333, 0.33333333],
|
||||
[0.66666667, 0.66666667],
|
||||
[1. , 1. ]],
|
||||
<BLANKLINE>
|
||||
[[0. , 0. ],
|
||||
[0.33333333, 0.33333333],
|
||||
[0.66666667, 0.66666667],
|
||||
[1. , 1. ]],
|
||||
<BLANKLINE>
|
||||
[[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]],
|
||||
<BLANKLINE>
|
||||
[[0.33333333, 0.33333333],
|
||||
[0.33333333, 0.33333333],
|
||||
[0.66666667, 0.66666667],
|
||||
[0.66666667, 0.66666667]],
|
||||
<BLANKLINE>
|
||||
[[0.33333333, 0.33333333],
|
||||
[0.33333333, 0.33333333],
|
||||
[0.66666667, 0.66666667],
|
||||
[0.66666667, 0.66666667]],
|
||||
<BLANKLINE>
|
||||
[[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<x<1 increases peripheral.
|
||||
:param center: Center to apply the distortion at on the source image.
|
||||
:param use_bleed: Fill in black regions with previos frame values. Shouldn't be neccesary in most cases.
|
||||
"""
|
||||
|
||||
def __init__(self, zoom=1, barrel_power=1, center=None, use_bleed=False):
|
||||
"""Create the distorter."""
|
||||
super().__init__(use_bleed, zoom, center)
|
||||
self.center = center
|
||||
self.zoom = zoom
|
||||
@@ -36,7 +94,9 @@ class Barrel(ControllableLens):
|
||||
|
||||
def enable_mouse_control(self):
|
||||
"""
|
||||
Move the mouse to center the image, scroll to increase/decrease barrel, ctrl+scroll to increase/decrease zoom
|
||||
Enable mouse control.
|
||||
|
||||
Move the mouse to center the image, scroll to increase/decrease barrel, ctrl+scroll to increase/decrease zoom.
|
||||
"""
|
||||
|
||||
@mouse_loop
|
||||
@@ -58,6 +118,7 @@ class Barrel(ControllableLens):
|
||||
return self
|
||||
|
||||
def __call__(self, arr):
|
||||
"""Run the distortion on an array."""
|
||||
zoom_out = 1.0 / self.zoom
|
||||
self.check_setup_bleed(arr)
|
||||
|
||||
@@ -102,10 +163,13 @@ class Barrel(ControllableLens):
|
||||
return arr
|
||||
|
||||
|
||||
class Mustache(ControllableLens):
|
||||
class Mustache(_ControllableLens):
|
||||
"""Create a mustache distortion."""
|
||||
|
||||
def __init__(
|
||||
self, use_bleed=False, barrel_power=1, pincushion_power=1, zoom=1, center=None
|
||||
):
|
||||
"""Create the distorter."""
|
||||
super().__init__(use_bleed, zoom, center)
|
||||
self.center = center
|
||||
self.zoom = zoom
|
||||
@@ -116,6 +180,15 @@ class Mustache(ControllableLens):
|
||||
self.mouse_control = None
|
||||
|
||||
def enable_mouse_control(self):
|
||||
"""
|
||||
Enable mouse control.
|
||||
|
||||
Move the mouse to center the image.
|
||||
Scroll to increase/decrease barrel.
|
||||
Ctrl+scroll to increase/decrease zoom.
|
||||
Shift+Scroll to increase/decrease pincushion.
|
||||
"""
|
||||
|
||||
@mouse_loop
|
||||
def m_loop(me):
|
||||
self.center[:] = [me.y, me.x]
|
||||
@@ -139,6 +212,7 @@ class Mustache(ControllableLens):
|
||||
self.mouse_control = m_loop
|
||||
|
||||
def __call__(self, arr):
|
||||
"""Run the distortion on an array."""
|
||||
zoom_out = 1.0 / self.zoom
|
||||
self.check_setup_bleed(arr)
|
||||
|
||||
|
||||
@@ -1,10 +1,21 @@
|
||||
"""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
|
||||
|
||||
|
||||
class SelectChannels(object):
|
||||
def __init__(self, selected_channels=None):
|
||||
"""
|
||||
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
|
||||
@@ -13,6 +24,7 @@ class SelectChannels(object):
|
||||
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
|
||||
@@ -21,6 +33,14 @@ class SelectChannels(object):
|
||||
return out_arr
|
||||
|
||||
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:
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import sys
|
||||
"""Publish frames so any function within this program can find them."""
|
||||
|
||||
import threading
|
||||
import time
|
||||
import asyncio
|
||||
@@ -8,7 +9,7 @@ import numpy as np
|
||||
|
||||
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
|
||||
|
||||
@@ -113,6 +114,7 @@ async def publish_updates_zero_mq(
|
||||
copy=True,
|
||||
track=False
|
||||
):
|
||||
"""Publish frames to ZeroMQ when they're updated."""
|
||||
import zmq
|
||||
from displayarray import read_updates
|
||||
|
||||
@@ -160,6 +162,7 @@ async def publish_updates_ros(
|
||||
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
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"""Get and handle updated frames."""
|
||||
|
||||
import threading
|
||||
import asyncio
|
||||
from typing import Union, Tuple, Any, Callable, List, Optional, Dict
|
||||
@@ -5,7 +7,7 @@ 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
|
||||
@@ -26,6 +28,7 @@ class FrameUpdater(threading.Thread):
|
||||
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
|
||||
@@ -126,7 +129,8 @@ async def read_updates(
|
||||
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
|
||||
@@ -179,6 +183,7 @@ async def read_updates_zero_mq(
|
||||
blocking: bool = False,
|
||||
end_callback: Callable[[Any], bool] = lambda x: False,
|
||||
):
|
||||
"""Read updated frames from ZeroMQ."""
|
||||
import zmq
|
||||
|
||||
ctx = zmq.Context()
|
||||
@@ -216,6 +221,7 @@ async def read_updates_ros(
|
||||
poll_rate_hz=None,
|
||||
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
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"""Get camera IDs."""
|
||||
|
||||
import cv2
|
||||
|
||||
from typing import List
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"""Allow OpenCV to handle numpy arrays as input."""
|
||||
|
||||
import numpy as np
|
||||
import cv2
|
||||
|
||||
@@ -6,6 +8,7 @@ class NpCam(object):
|
||||
"""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
|
||||
|
||||
@@ -9,6 +9,7 @@ class CamHandler(object):
|
||||
"""A camera handler instance that will send commands to and receive data from a camera."""
|
||||
|
||||
def __init__(self, name, sub):
|
||||
"""Create the cam handler."""
|
||||
self.name = name
|
||||
self.cmd = None
|
||||
self.sub: VariableSub = sub
|
||||
@@ -20,6 +21,7 @@ class Cam(object):
|
||||
"""A camera publisher instance that will send frames, status, and commands out."""
|
||||
|
||||
def __init__(self, name):
|
||||
"""Create the cam."""
|
||||
self.name = name
|
||||
self.cmd = None
|
||||
self.frame_pub = VariablePub()
|
||||
|
||||
+25
-6
@@ -1,3 +1,5 @@
|
||||
"""Decorators for creating input loops that OpenCV handles."""
|
||||
|
||||
from displayarray.window import window_commands
|
||||
import threading
|
||||
import time
|
||||
@@ -9,6 +11,7 @@ class MouseEvent(object):
|
||||
"""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
|
||||
@@ -62,10 +65,19 @@ class _mouse_loop_thread(object): # NOSONAR
|
||||
|
||||
|
||||
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))
|
||||
>>> @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):
|
||||
@@ -111,10 +123,17 @@ class _key_loop_thread(object): # NOSONAR
|
||||
|
||||
|
||||
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))
|
||||
>>> @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):
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"""OpenCV windows that will display the arrays."""
|
||||
|
||||
import warnings
|
||||
from threading import Thread
|
||||
from typing import List, Union, Callable, Any, Dict, Iterable, Optional
|
||||
@@ -7,13 +9,13 @@ import numpy as np
|
||||
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
|
||||
|
||||
|
||||
@@ -29,6 +31,7 @@ class SubscriberWindows(object):
|
||||
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] = []
|
||||
@@ -52,6 +55,7 @@ class SubscriberWindows(object):
|
||||
return not self.exited
|
||||
|
||||
def block(self):
|
||||
"""Update the window continuously while blocking the outer program."""
|
||||
self.loop()
|
||||
for ct in self.close_threads:
|
||||
ct.join()
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
"""Commands to control the array displaying windows."""
|
||||
|
||||
from localpubsub import VariablePub, VariableSub
|
||||
|
||||
key_pub = VariablePub()
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user