Updated docs to satisfy tox

This commit is contained in:
simleek
2019-11-10 10:18:32 -07:00
parent 8ba0083449
commit 09db2856cc
18 changed files with 191 additions and 25 deletions
+2
View File
@@ -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
+3 -1
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
"""Effects to run on numpy arrays to make data clearer."""
+22 -3
View File
@@ -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):
+82 -8
View File
@@ -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)
+21 -1
View File
@@ -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:
+5 -2
View File
@@ -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
+8 -2
View File
@@ -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
+2
View File
@@ -1,3 +1,5 @@
"""Get camera IDs."""
import cv2
from typing import List
+3
View File
@@ -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
View File
@@ -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):
+6 -2
View File
@@ -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()
+2
View File
@@ -1,3 +1,5 @@
"""Commands to control the array displaying windows."""
from localpubsub import VariablePub, VariableSub
key_pub = VariablePub()
Binary file not shown.
+3
View File
@@ -39,6 +39,9 @@ whitelist_externals = poetry
description = enforce code style
commands = poetry run black displayarray --check
[pydocstyle]
ignore = D105, D212, D203, D202
[testenv:pydocstyle]
whitelist_externals = poetry
description = enforce documentation style