diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 72eb78b..67849f1 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -14,10 +14,10 @@ jobs: with: python-version: 3.7 - uses: dschep/install-poetry-action@v1.2 - - name: Install dependencies - run: poetry install + - name: Install and build + run: pip install . - name: Build with Poetry - run: poetry build + run: python setup.py sdist bdist_wheel - name: Publish distribution 📦 to Test PyPI uses: pypa/gh-action-pypi-publish@master with: diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 5a9afd0..9eefd89 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -18,8 +18,7 @@ jobs: uses: actions/setup-python@v1 with: python-version: ${{ matrix.python-version }} - - uses: dschep/install-poetry-action@v1.2 - name: Install dependencies - run: poetry install + run: pip install . - name: Test with tox - run: poetry run tox -p auto -o + run: tox -p auto -o diff --git a/displayarray/effects/__init__.py b/displayarray/effects/__init__.py index b066ad7..901c494 100644 --- a/displayarray/effects/__init__.py +++ b/displayarray/effects/__init__.py @@ -1,3 +1,3 @@ """Effects to run on numpy arrays to make data clearer.""" -from . import crop, lens, select_channels +from . import crop, lens, select_channels, overlay, transform diff --git a/displayarray/effects/overlay.py b/displayarray/effects/overlay.py new file mode 100644 index 0000000..f82959b --- /dev/null +++ b/displayarray/effects/overlay.py @@ -0,0 +1,54 @@ +"""Overlay functions.""" + + +def overlay_transparent(background, overlay, x=None, y=None): + """ + Overlay a transparent image on top of a background image. + + :param background: background rgb image to overlay on top of + :param overlay: rgba image to overlay on top of the background + :param x: leftmost part to overlay at on the background + :param y: topmost part to overlay at on the background + """ + # https://stackoverflow.com/a/54058766/782170 + assert overlay.shape[2] == 4, "Overlay must be BGRA" + + background_width = background.shape[1] + background_height = background.shape[0] + + if (x is not None and x >= background_width) or ( + y is not None and y >= background_height + ): + return background + + h, w = overlay.shape[0], overlay.shape[1] + + if x is None: + x = int(background_width / 2 - w / 2) + if y is None: + y = int(background_height / 2 - h / 2) + + if x < 0: + w += x + overlay = overlay[:, -x:] + + if y < 0: + w += y + overlay = overlay[:, -y:] + + if x + w > background_width: + w = background_width - x + overlay = overlay[:, :w] + + if y + h > background_height: + h = background_height - y + overlay = overlay[:h] + + overlay_image = overlay[..., :3] + mask = overlay[..., 3:] / 255.0 + + background[y : y + h, x : x + w] = (1.0 - mask) * background[ + y : y + h, x : x + w + ] + mask * overlay_image + + return background diff --git a/displayarray/effects/transform.py b/displayarray/effects/transform.py new file mode 100644 index 0000000..215dc33 --- /dev/null +++ b/displayarray/effects/transform.py @@ -0,0 +1,45 @@ +"""Transform functions.""" + +import numpy as np +import cv2 + + +def transform_about_center( + arr, scale_multiplier=(1, 1), rotation_degrees=0, translation=(0, 0), skew=(0, 0) +): + """ + Transform an image about its center. + + :param arr: numpy image to be transformed + :param scale_multiplier: grow/shrink in x and y + :param rotation_degrees: degrees to rotate, from 0 to 360 + :param translation: pixels to translate the image + :param skew: mimics rotation along screen axes. In degrees. 90 degrees should give a line. + :return: transformed numpy image + """ + center_scale_xform = np.eye(3) + center_scale_xform[0, 0] = scale_multiplier[1] + center_scale_xform[1, 1] = scale_multiplier[0] + center_scale_xform[0:2, -1] = [arr.shape[1] // 2, arr.shape[0] // 2] + + rotation_xform = np.eye(3) + + theta = np.radians(rotation_degrees) + c, s = np.cos(theta), np.sin(theta) + R = np.array(((c, -s), (s, c))) + rotation_xform[0:2, 0:2] = R + skew = np.radians(skew) + skew = np.tan(skew) + rotation_xform[-1, 0:2] = [skew[1] / arr.shape[1], skew[0] / arr.shape[0]] + + translation_skew_xform = np.eye(3) + translation_skew_xform[0:2, -1] = [ + (-arr.shape[1] - translation[1]) // 2, + (-arr.shape[0] - translation[0]) // 2, + ] + + full_xform = center_scale_xform @ rotation_xform @ translation_skew_xform + xformd_arr = cv2.warpPerspective( + arr, full_xform, tuple(reversed(arr.shape[:2])), flags=0 + ) + return xformd_arr diff --git a/displayarray/frame/frame_publishing.py b/displayarray/frame/frame_publishing.py index 29934af..87890a7 100644 --- a/displayarray/frame/frame_publishing.py +++ b/displayarray/frame/frame_publishing.py @@ -11,8 +11,10 @@ import cv2 using_pyv4l2cam = False try: if sys.platform == "linux": - from PyV4L2Cam.camera import Camera as pyv4lcamera - from PyV4L2Cam.controls import ControlIDs as pyv4lcontrolids + from PyV4L2Cam.camera import Camera as pyv4lcamera # type: ignore + from PyV4L2Cam.controls import ControlIDs as pyv4lcontrolids # type: ignore + from PyV4L2Cam import convert_mjpeg, convert_rgb24 # type: ignore + from PyV4L2Cam.get_camera import get_camera_by_bus_info, get_camera_by_string # type: ignore using_pyv4l2cam = True except ImportError: @@ -32,25 +34,6 @@ from typing import Union, Tuple, Optional, Dict, Any, List, Callable FrameCallable = Callable[[np.ndarray], Optional[np.ndarray]] -def _v4l2_convert_mjpeg(mjpeg: bytes) -> Optional[np.ndarray]: - # Thanks: https://stackoverflow.com/a/21844162 - a = mjpeg.find(b"\xff\xd8") - b = mjpeg.find(b"\xff\xd9") - - if a == -1 or b == -1: - return None - else: - jpg = mjpeg[a : b + 2] - frame = cv2.imdecode(np.frombuffer(jpg, dtype=np.uint8), cv2.IMREAD_COLOR) - return frame - - -def _v4l2_convert_rgb24(rgb24: bytes, width: int, height: int) -> Optional[np.ndarray]: - nparr = np.frombuffer(rgb24, np.uint8) - np_frame = nparr.reshape((height, width, 3)) - return np_frame - - def pub_cam_loop_pyv4l2( cam_id: Union[int, str, np.ndarray], request_size: Tuple[int, int] = (-1, -1), @@ -77,13 +60,16 @@ def pub_cam_loop_pyv4l2( f"/dev/video{cam_id}", *request_size ) else: - cam = pyv4lcamera(cam_id, *request_size) # type: ignore + if "usb" in cam_id: + cam = get_camera_by_bus_info(cam_id, *request_size) + else: + cam = get_camera_by_string(cam_id, *request_size) # type: ignore else: raise TypeError( "Only strings or ints representing cameras are supported with v4l2." ) - subscriber_dictionary.register_cam(name) + subscriber_dictionary.register_cam(name, cam) sub = subscriber_dictionary.cam_cmd_sub(name) sub.return_on_no_data = "" @@ -99,9 +85,9 @@ def pub_cam_loop_pyv4l2( frame_bytes = cam.get_frame() # type: bytes if cam.pixel_format == "MJPEG": - nd_frame = _v4l2_convert_mjpeg(frame_bytes) + nd_frame = convert_mjpeg(frame_bytes) elif cam.pixel_format == "RGB24": - nd_frame = _v4l2_convert_rgb24(frame_bytes, cam.width, cam.height) + nd_frame = convert_rgb24(frame_bytes, cam.width, cam.height) else: raise NotImplementedError(f"{cam.pixel_format} format not supported.") @@ -149,7 +135,7 @@ def pub_cam_loop_opencv( "Only strings or ints representing cameras, or numpy arrays representing pictures supported." ) - subscriber_dictionary.register_cam(name) + subscriber_dictionary.register_cam(name, cam) frame_counter = 0 @@ -158,7 +144,10 @@ def pub_cam_loop_opencv( msg = "" if high_speed: - cam.set(cv2.CAP_PROP_FOURCC, cv2.CAP_OPENCV_MJPEG) + try: + cam.set(cv2.CAP_PROP_FOURCC, cv2.CAP_OPENCV_MJPEG) + except AttributeError: + warnings.warn("Please update OpenCV") cam.set(cv2.CAP_PROP_FRAME_WIDTH, request_size[0]) cam.set(cv2.CAP_PROP_FRAME_HEIGHT, request_size[1]) @@ -196,6 +185,7 @@ def pub_cam_thread( request_ize: Tuple[int, int] = (-1, -1), high_speed: bool = True, fps_limit: float = float("inf"), + force_backend="", ) -> threading.Thread: """Run pub_cam_loop in a new thread. Starts on creation.""" @@ -203,15 +193,20 @@ def pub_cam_thread( if name in uid_dict.keys(): t = uid_dict[name] else: - if ( + if "cv" in force_backend.lower(): + pub_cam_loop = pub_cam_loop_opencv + elif ( sys.platform == "linux" and using_pyv4l2cam and ( isinstance(cam_id, int) - or (isinstance(cam_id, str) and "/dev/video" in cam_id) + or ( + isinstance(cam_id, str) + and any(["/dev/video" in cam_id, "usb" in cam_id]) + ) ) - ): - pub_cam_loop = pub_cam_loop_pyv4l2 + ) or "v4l2" in force_backend.lower(): + pub_cam_loop = pub_cam_loop_pyv4l2 # type: ignore else: pub_cam_loop = pub_cam_loop_opencv diff --git a/displayarray/frame/frame_updater.py b/displayarray/frame/frame_updater.py index 4cd79a1..e09c090 100644 --- a/displayarray/frame/frame_updater.py +++ b/displayarray/frame/frame_updater.py @@ -27,6 +27,7 @@ class FrameUpdater(threading.Thread): request_size: Tuple[int, int] = (-1, -1), high_speed: bool = True, fps_limit: float = float("inf"), + force_backend="", ): """Create the frame updater thread.""" super(FrameUpdater, self).__init__(target=self.loop, args=()) @@ -42,6 +43,7 @@ class FrameUpdater(threading.Thread): self.high_speed = high_speed self.fps_limit = fps_limit self.exception_raised = None + self.force_backend = force_backend def __wait_for_cam_id(self): while str(self.cam_id) not in subscriber_dictionary.CV_CAMS_DICT: @@ -84,7 +86,11 @@ class FrameUpdater(threading.Thread): def loop(self): """Continually get frames from the video publisher, run callbacks on them, and listen to commands.""" t = pub_cam_thread( - self.video_source, self.request_size, self.high_speed, self.fps_limit + self.video_source, + self.request_size, + self.high_speed, + self.fps_limit, + self.force_backend, ) self.__wait_for_cam_id() diff --git a/displayarray/frame/subscriber_dictionary.py b/displayarray/frame/subscriber_dictionary.py index 1955579..11f7d97 100644 --- a/displayarray/frame/subscriber_dictionary.py +++ b/displayarray/frame/subscriber_dictionary.py @@ -20,22 +20,23 @@ class CamHandler(object): class Cam(object): """A camera publisher instance that will send frames, status, and commands out.""" - def __init__(self, name): + def __init__(self, name, cam_instance=None): """Create the cam.""" self.name = name self.cmd = None self.frame_pub = VariablePub() self.cmd_pub = VariablePub() self.status_pub = VariablePub() + self.cam_instance = cam_instance CV_CAM_HANDLERS_DICT: Dict[str, CamHandler] = {} CV_CAMS_DICT: Dict[str, Cam] = {} -def register_cam(cam_id): +def register_cam(cam_id, cam_instance=None): """Register camera "cam_id" to a global list so it can be picked up.""" - cam = Cam(str(cam_id)) + cam = Cam(str(cam_id), cam_instance) CV_CAMS_DICT[str(cam_id)] = cam CV_CAM_HANDLERS_DICT[str(cam_id)] = CamHandler( str(cam_id), cam.frame_pub.make_sub() diff --git a/displayarray/window/subscriber_windows.py b/displayarray/window/subscriber_windows.py index 5c47ce1..7139ed6 100644 --- a/displayarray/window/subscriber_windows.py +++ b/displayarray/window/subscriber_windows.py @@ -13,11 +13,22 @@ 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.frame.subscriber_dictionary import CV_CAMS_DICT from displayarray.input import MouseEvent from displayarray.window import window_commands from displayarray._util import WeakMethod from displayarray.effects.select_channels import SelectChannels +try: + import sys + + if sys.platform == "linux": + from PyV4L2Cam.get_camera import get_bus_info_from_camera # type: ignore + else: + get_bus_info_from_camera = None +except: + get_bus_info_from_camera = None + class SubscriberWindows(object): """Windows that subscribe to updates to cameras, videos, and arrays.""" @@ -266,12 +277,26 @@ class SubscriberWindows(object): window_commands.quit(force_all_read=False) self.__stop_all_cams() + @property + def cams(self): + """Get the camera instances. Can be used for OpenCV or V4L2 functions, depending on backend.""" + return [CV_CAMS_DICT[v].cam_instance for v in self.input_vid_global_names] + + @property + def busses(self): + """Get the busses the cameras are plugged into. Can be used as UIDs. Requires V4L2 backend.""" + if get_bus_info_from_camera is not None: + return [get_bus_info_from_camera(c) for c in self.cams] + else: + raise RuntimeError("Getting bus info not supported on this system") + def _get_video_callback_dict_threads( *vids, callbacks: Optional[Dict[Any, Union[FrameCallable, List[FrameCallable]]]] = None, fps=float("inf"), size=(-1, -1), + force_backend="", ): assert callbacks is not None vid_threads = [] @@ -289,7 +314,13 @@ def _get_video_callback_dict_threads( elif callable(callbacks[v]): v_callbacks.append(callbacks[v]) # type: ignore vid_threads.append( - FrameUpdater(v, callbacks=v_callbacks, fps_limit=fps, request_size=size) + FrameUpdater( + v, + callbacks=v_callbacks, + fps_limit=fps, + request_size=size, + force_backend=force_backend, + ) ) return vid_threads @@ -305,6 +336,7 @@ def _get_video_threads( ] = None, fps=float("inf"), size=(-1, -1), + force_backend="", ): vid_threads: List[Thread] = [] if isinstance(callbacks, Dict): @@ -314,17 +346,33 @@ def _get_video_threads( elif isinstance(callbacks, List): for v in vids: vid_threads.append( - FrameUpdater(v, callbacks=callbacks, fps_limit=fps, request_size=size) + FrameUpdater( + v, + callbacks=callbacks, + fps_limit=fps, + request_size=size, + force_backend=force_backend, + ) ) elif callable(callbacks): for v in vids: vid_threads.append( - FrameUpdater(v, callbacks=[callbacks], fps_limit=fps, request_size=size) + FrameUpdater( + v, + callbacks=[callbacks], + fps_limit=fps, + request_size=size, + force_backend=force_backend, + ) ) else: for v in vids: if v is not None: - vid_threads.append(FrameUpdater(v, fps_limit=fps, request_size=size)) + vid_threads.append( + FrameUpdater( + v, fps_limit=fps, request_size=size, force_backend=force_backend + ) + ) return vid_threads @@ -342,6 +390,7 @@ def display( fps_limit=float("inf"), size=(-1, -1), silent=False, + force_backend="", ): """ Display all the arrays, cameras, and videos passed in. @@ -351,7 +400,11 @@ def display( Window names end up becoming the title of the windows """ vid_threads = _get_video_threads( - *vids, callbacks=callbacks, fps=fps_limit, size=size + *vids, + callbacks=callbacks, + fps=fps_limit, + size=size, + force_backend=force_backend, ) for v in vid_threads: v.start() diff --git a/docs_test/.doctrees/display.doctree b/docs_test/.doctrees/display.doctree new file mode 100644 index 0000000..0eef6e2 Binary files /dev/null and b/docs_test/.doctrees/display.doctree differ diff --git a/docs_test/.doctrees/displayarray_bash.doctree b/docs_test/.doctrees/displayarray_bash.doctree new file mode 100644 index 0000000..a061d4f Binary files /dev/null and b/docs_test/.doctrees/displayarray_bash.doctree differ diff --git a/docs_test/.doctrees/effects.doctree b/docs_test/.doctrees/effects.doctree new file mode 100644 index 0000000..ee3236a Binary files /dev/null and b/docs_test/.doctrees/effects.doctree differ diff --git a/docs_test/.doctrees/frame.doctree b/docs_test/.doctrees/frame.doctree new file mode 100644 index 0000000..d956149 Binary files /dev/null and b/docs_test/.doctrees/frame.doctree differ diff --git a/docs_test/.doctrees/index.doctree b/docs_test/.doctrees/index.doctree new file mode 100644 index 0000000..109ce4b Binary files /dev/null and b/docs_test/.doctrees/index.doctree differ diff --git a/docs_test/.doctrees/input.doctree b/docs_test/.doctrees/input.doctree new file mode 100644 index 0000000..a022164 Binary files /dev/null and b/docs_test/.doctrees/input.doctree differ diff --git a/examples/callbacks/black_and_white.py b/examples/callbacks/black_and_white.py index f7e050d..bf2b5f1 100644 --- a/examples/callbacks/black_and_white.py +++ b/examples/callbacks/black_and_white.py @@ -9,7 +9,7 @@ def black_and_white(arr): import time t0 = t1 = time.time() -for up in display(0, size=(1, 1), callbacks=black_and_white): +for up in display("usb-0000:00:14.0-7"): if up: t1 = time.time() print(1.0 / (t1 - t0)) diff --git a/examples/videos/__init__.py b/examples/videos/__init__.py index 006f19a..a28a427 100644 --- a/examples/videos/__init__.py +++ b/examples/videos/__init__.py @@ -9,25 +9,25 @@ test_video_3 = str(Path.joinpath(Path(__file__).parent, "fractal test 3.mp4")) urls = { "test_video": "https://www.youtube.com/watch?v=LpWhaBVIrZw", "test_video_2": "https://www.youtube.com/watch?v=GASynpGr-c8", - "test_video_3": "https://www.youtube.com/watch?v=u_P83LcI8Oc" + "test_video_3": "https://www.youtube.com/watch?v=u_P83LcI8Oc", } def populate_videos(fps=60, res="720p", ext="mp4"): from pytube import YouTube # Note: pip install pytube3, not pytube from pathlib import Path + for n, v in globals().items(): - if 'test_video' in n: + if "test_video" in n: print(f"Checking if '{n}' is downloaded.") if Path(v).exists(): print("Video already downloaded.") else: the_path = Path(v) print("Downloading...") - YouTube(urls[n]) \ - .streams \ - .filter(fps=fps, res=res, file_extension=ext)[0] \ - .download(output_path=the_path.parent, filename=the_path.stem) + YouTube(urls[n]).streams.filter(fps=fps, res=res, file_extension=ext)[ + 0 + ].download(output_path=the_path.parent, filename=the_path.stem) if __name__ == "__main__": diff --git a/poetry.lock b/poetry.lock deleted file mode 100644 index 1914a5b..0000000 --- a/poetry.lock +++ /dev/null @@ -1,599 +0,0 @@ -[[package]] -category = "dev" -description = "A configurable sidebar-enabled Sphinx theme" -name = "alabaster" -optional = false -python-versions = "*" -version = "0.7.12" - -[[package]] -category = "main" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -name = "appdirs" -optional = false -python-versions = "*" -version = "1.4.3" - -[[package]] -category = "dev" -description = "Atomic file writes." -name = "atomicwrites" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "1.3.0" - -[[package]] -category = "main" -description = "Classes Without Boilerplate" -name = "attrs" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "19.3.0" - -[[package]] -category = "dev" -description = "Internationalization utilities" -name = "babel" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "2.7.0" - -[package.dependencies] -pytz = ">=2015.7" - -[[package]] -category = "main" -description = "The uncompromising code formatter." -name = "black" -optional = false -python-versions = ">=3.6" -version = "18.9b0" - -[package.dependencies] -appdirs = "*" -attrs = ">=17.4.0" -click = ">=6.5" -toml = ">=0.9.4" - -[[package]] -category = "dev" -description = "Python package for providing Mozilla's CA Bundle." -name = "certifi" -optional = false -python-versions = "*" -version = "2019.9.11" - -[[package]] -category = "dev" -description = "Universal encoding detector for Python 2 and 3" -name = "chardet" -optional = false -python-versions = "*" -version = "3.0.4" - -[[package]] -category = "main" -description = "Composable command line interface toolkit" -name = "click" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "7.0" - -[[package]] -category = "dev" -description = "Cross-platform colored terminal text." -marker = "sys_platform == \"win32\"" -name = "colorama" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.4.1" - -[[package]] -category = "dev" -description = "Code coverage measurement for Python" -name = "coverage" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, <4" -version = "4.5.4" - -[[package]] -category = "main" -description = "Pythonic argument parser, that will make you smile" -name = "docopt" -optional = false -python-versions = "*" -version = "0.6.2" - -[[package]] -category = "dev" -description = "Docutils -- Python Documentation Utilities" -name = "docutils" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -version = "0.15.2" - -[[package]] -category = "dev" -description = "A platform independent file lock." -name = "filelock" -optional = false -python-versions = "*" -version = "3.0.12" - -[[package]] -category = "dev" -description = "Internationalized Domain Names in Applications (IDNA)" -name = "idna" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "2.8" - -[[package]] -category = "dev" -description = "Getting image size from png/jpeg/jpeg2000/gif file" -name = "imagesize" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "1.1.0" - -[[package]] -category = "dev" -description = "Read metadata from Python packages" -marker = "python_version < \"3.8\"" -name = "importlib-metadata" -optional = false -python-versions = ">=2.7,!=3.0,!=3.1,!=3.2,!=3.3" -version = "0.23" - -[package.dependencies] -zipp = ">=0.5" - -[[package]] -category = "dev" -description = "A very fast and expressive template engine." -name = "jinja2" -optional = false -python-versions = "*" -version = "2.10.3" - -[package.dependencies] -MarkupSafe = ">=0.23" - -[[package]] -category = "main" -description = "" -name = "localpubsub" -optional = false -python-versions = "*" -version = "0.0.4" - -[[package]] -category = "dev" -description = "Safely add untrusted strings to HTML/XML markup." -name = "markupsafe" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" -version = "1.1.1" - -[[package]] -category = "dev" -description = "Rolling backport of unittest.mock for all Pythons" -name = "mock" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "3.0.5" - -[package.dependencies] -six = "*" - -[[package]] -category = "dev" -description = "More routines for operating on iterables, beyond itertools" -name = "more-itertools" -optional = false -python-versions = ">=3.4" -version = "7.2.0" - -[[package]] -category = "main" -description = "Optional static typing for Python" -name = "mypy" -optional = false -python-versions = ">=3.5" -version = "0.740" - -[package.dependencies] -mypy-extensions = ">=0.4.0,<0.5.0" -typed-ast = ">=1.4.0,<1.5.0" -typing-extensions = ">=3.7.4" - -[[package]] -category = "main" -description = "Experimental type system extensions for programs checked with the mypy typechecker." -name = "mypy-extensions" -optional = false -python-versions = "*" -version = "0.4.3" - -[[package]] -category = "main" -description = "NumPy is the fundamental package for array computing with Python." -name = "numpy" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" -version = "1.16.1" - -[[package]] -category = "main" -description = "Wrapper package for OpenCV python bindings." -name = "opencv-python" -optional = false -python-versions = "*" -version = "3.4.5.20" - -[package.dependencies] -numpy = ">=1.11.1" - -[[package]] -category = "dev" -description = "Core utilities for Python packages" -name = "packaging" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "19.2" - -[package.dependencies] -pyparsing = ">=2.0.2" -six = "*" - -[[package]] -category = "dev" -description = "plugin and hook calling mechanisms for python" -name = "pluggy" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.13.0" - -[package.dependencies] -[package.dependencies.importlib-metadata] -python = "<3.8" -version = ">=0.12" - -[[package]] -category = "dev" -description = "library with cross-python path, ini-parsing, io, code, log facilities" -name = "py" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "1.8.0" - -[[package]] -category = "main" -description = "Python docstring style checker" -name = "pydocstyle" -optional = false -python-versions = ">=3.4" -version = "4.0.1" - -[package.dependencies] -snowballstemmer = "*" - -[[package]] -category = "dev" -description = "Pygments is a syntax highlighting package written in Python." -name = "pygments" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "2.4.2" - -[[package]] -category = "dev" -description = "Python parsing module" -name = "pyparsing" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -version = "2.4.4" - -[[package]] -category = "dev" -description = "pytest: simple powerful testing with Python" -name = "pytest" -optional = false -python-versions = ">=3.5" -version = "5.2.1" - -[package.dependencies] -atomicwrites = ">=1.0" -attrs = ">=17.4.0" -colorama = "*" -more-itertools = ">=4.0.0" -packaging = "*" -pluggy = ">=0.12,<1.0" -py = ">=1.5.0" -wcwidth = "*" - -[package.dependencies.importlib-metadata] -python = "<3.8" -version = ">=0.12" - -[[package]] -category = "dev" -description = "World timezone definitions, modern and historical" -name = "pytz" -optional = false -python-versions = "*" -version = "2019.3" - -[[package]] -category = "main" -description = "Python bindings for 0MQ" -name = "pyzmq" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*" -version = "18.1.0" - -[[package]] -category = "dev" -description = "Python HTTP for Humans." -name = "requests" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "2.22.0" - -[package.dependencies] -certifi = ">=2017.4.17" -chardet = ">=3.0.2,<3.1.0" -idna = ">=2.5,<2.9" -urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26" - -[[package]] -category = "dev" -description = "Python 2 and 3 compatibility utilities" -name = "six" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*" -version = "1.13.0" - -[[package]] -category = "main" -description = "This package provides 26 stemmers for 25 languages generated from Snowball algorithms." -name = "snowballstemmer" -optional = false -python-versions = "*" -version = "2.0.0" - -[[package]] -category = "dev" -description = "Python documentation generator" -name = "sphinx" -optional = false -python-versions = ">=3.5" -version = "2.2.1" - -[package.dependencies] -Jinja2 = ">=2.3" -Pygments = ">=2.0" -alabaster = ">=0.7,<0.8" -babel = ">=1.3,<2.0 || >2.0" -colorama = ">=0.3.5" -docutils = ">=0.12" -imagesize = "*" -packaging = "*" -requests = ">=2.5.0" -setuptools = "*" -snowballstemmer = ">=1.1" -sphinxcontrib-applehelp = "*" -sphinxcontrib-devhelp = "*" -sphinxcontrib-htmlhelp = "*" -sphinxcontrib-jsmath = "*" -sphinxcontrib-qthelp = "*" -sphinxcontrib-serializinghtml = "*" - -[[package]] -category = "dev" -description = "" -name = "sphinxcontrib-applehelp" -optional = false -python-versions = "*" -version = "1.0.1" - -[[package]] -category = "dev" -description = "" -name = "sphinxcontrib-devhelp" -optional = false -python-versions = "*" -version = "1.0.1" - -[[package]] -category = "dev" -description = "" -name = "sphinxcontrib-htmlhelp" -optional = false -python-versions = "*" -version = "1.0.2" - -[[package]] -category = "dev" -description = "A sphinx extension which renders display math in HTML via JavaScript" -name = "sphinxcontrib-jsmath" -optional = false -python-versions = ">=3.5" -version = "1.0.1" - -[[package]] -category = "dev" -description = "" -name = "sphinxcontrib-qthelp" -optional = false -python-versions = "*" -version = "1.0.2" - -[[package]] -category = "dev" -description = "" -name = "sphinxcontrib-serializinghtml" -optional = false -python-versions = "*" -version = "1.1.3" - -[[package]] -category = "main" -description = "Python Library for Tom's Obvious, Minimal Language" -name = "toml" -optional = false -python-versions = "*" -version = "0.10.0" - -[[package]] -category = "dev" -description = "tox is a generic virtualenv management and test command line tool" -name = "tox" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "3.14.0" - -[package.dependencies] -filelock = ">=3.0.0,<4" -packaging = ">=14" -pluggy = ">=0.12.0,<1" -py = ">=1.4.17,<2" -six = ">=1.0.0,<2" -toml = ">=0.9.4" -virtualenv = ">=14.0.0" - -[package.dependencies.importlib-metadata] -python = "<3.8" -version = ">=0.12,<1" - -[[package]] -category = "dev" -description = "Seamless integration of tox into GitHub Actions" -name = "tox-gh-actions" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" -version = "0.3.0" - -[package.dependencies] -tox = ">=3.12.2" - -[[package]] -category = "main" -description = "a fork of Python 2 and 3 ast modules with type comment support" -name = "typed-ast" -optional = false -python-versions = "*" -version = "1.4.0" - -[[package]] -category = "dev" -description = "Type Hints for Python" -name = "typing" -optional = false -python-versions = "*" -version = "3.7.4.1" - -[[package]] -category = "main" -description = "Backported and Experimental Type Hints for Python 3.5+" -name = "typing-extensions" -optional = false -python-versions = "*" -version = "3.7.4.1" - -[[package]] -category = "dev" -description = "HTTP library with thread-safe connection pooling, file post, and more." -name = "urllib3" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, <4" -version = "1.25.6" - -[[package]] -category = "dev" -description = "Virtual Python Environment builder" -name = "virtualenv" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" -version = "16.7.7" - -[[package]] -category = "dev" -description = "Measures number of Terminal column cells of wide-character codes" -name = "wcwidth" -optional = false -python-versions = "*" -version = "0.1.7" - -[[package]] -category = "dev" -description = "Backport of pathlib-compatible object wrapper for zip files" -marker = "python_version < \"3.8\"" -name = "zipp" -optional = false -python-versions = ">=2.7" -version = "0.6.0" - -[package.dependencies] -more-itertools = "*" - -[metadata] -content-hash = "2e6c77bc331fe05281692b38131903b8bb0c893df53e30e476e9cf18a8f3c540" -python-versions = "^3.6" - -[metadata.hashes] -alabaster = ["446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359", "a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"] -appdirs = ["9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92", "d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"] -atomicwrites = ["03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4", "75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6"] -attrs = ["08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c", "f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"] -babel = ["af92e6106cb7c55286b25b38ad7695f8b4efb36a90ba483d7f7a6628c46158ab", "e86135ae101e31e2c8ec20a4e0c5220f4eed12487d5cf3f78be7e98d3a57fc28"] -black = ["817243426042db1d36617910df579a54f1afd659adb96fc5032fcf4b36209739", "e030a9a28f542debc08acceb273f228ac422798e5215ba2a791a6ddeaaca22a5"] -certifi = ["e4f3620cfea4f83eedc95b24abd9cd56f3c4b146dd0177e83a21b4eb49e21e50", "fd7c7c74727ddcf00e9acd26bba8da604ffec95bf1c2144e67aff7a8b50e6cef"] -chardet = ["84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", "fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"] -click = ["2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", "5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"] -colorama = ["05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d", "f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48"] -coverage = ["08907593569fe59baca0bf152c43f3863201efb6113ecb38ce7e97ce339805a6", "0be0f1ed45fc0c185cfd4ecc19a1d6532d72f86a2bac9de7e24541febad72650", "141f08ed3c4b1847015e2cd62ec06d35e67a3ac185c26f7635f4406b90afa9c5", "19e4df788a0581238e9390c85a7a09af39c7b539b29f25c89209e6c3e371270d", "23cc09ed395b03424d1ae30dcc292615c1372bfba7141eb85e11e50efaa6b351", "245388cda02af78276b479f299bbf3783ef0a6a6273037d7c60dc73b8d8d7755", "331cb5115673a20fb131dadd22f5bcaf7677ef758741312bee4937d71a14b2ef", "386e2e4090f0bc5df274e720105c342263423e77ee8826002dcffe0c9533dbca", "3a794ce50daee01c74a494919d5ebdc23d58873747fa0e288318728533a3e1ca", "60851187677b24c6085248f0a0b9b98d49cba7ecc7ec60ba6b9d2e5574ac1ee9", "63a9a5fc43b58735f65ed63d2cf43508f462dc49857da70b8980ad78d41d52fc", "6b62544bb68106e3f00b21c8930e83e584fdca005d4fffd29bb39fb3ffa03cb5", "6ba744056423ef8d450cf627289166da65903885272055fb4b5e113137cfa14f", "7494b0b0274c5072bddbfd5b4a6c6f18fbbe1ab1d22a41e99cd2d00c8f96ecfe", "826f32b9547c8091679ff292a82aca9c7b9650f9fda3e2ca6bf2ac905b7ce888", "93715dffbcd0678057f947f496484e906bf9509f5c1c38fc9ba3922893cda5f5", "9a334d6c83dfeadae576b4d633a71620d40d1c379129d587faa42ee3e2a85cce", "af7ed8a8aa6957aac47b4268631fa1df984643f07ef00acd374e456364b373f5", "bf0a7aed7f5521c7ca67febd57db473af4762b9622254291fbcbb8cd0ba5e33e", "bf1ef9eb901113a9805287e090452c05547578eaab1b62e4ad456fcc049a9b7e", "c0afd27bc0e307a1ffc04ca5ec010a290e49e3afbe841c5cafc5c5a80ecd81c9", "dd579709a87092c6dbee09d1b7cfa81831040705ffa12a1b248935274aee0437", "df6712284b2e44a065097846488f66840445eb987eb81b3cc6e4149e7b6982e1", "e07d9f1a23e9e93ab5c62902833bf3e4b1f65502927379148b6622686223125c", "e2ede7c1d45e65e209d6093b762e98e8318ddeff95317d07a27a2140b80cfd24", "e4ef9c164eb55123c62411f5936b5c2e521b12356037b6e1c2617cef45523d47", "eca2b7343524e7ba246cab8ff00cab47a2d6d54ada3b02772e908a45675722e2", "eee64c616adeff7db37cc37da4180a3a5b6177f5c46b187894e633f088fb5b28", "ef824cad1f980d27f26166f86856efe11eff9912c4fed97d3804820d43fa550c", "efc89291bd5a08855829a3c522df16d856455297cf35ae827a37edac45f466a7", "fa964bae817babece5aa2e8c1af841bebb6d0b9add8e637548809d040443fee0", "ff37757e068ae606659c28c3bd0d923f9d29a85de79bf25b2b34b148473b5025"] -docopt = ["49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"] -docutils = ["6c4f696463b79f1fb8ba0c594b63840ebd41f059e92b31957c46b74a4599b6d0", "9e4d7ecfc600058e07ba661411a2b7de2fd0fafa17d1a7f7361cd47b1175c827", "a2aeea129088da402665e92e0b25b04b073c04b2dce4ab65caaa38b7ce2e1a99"] -filelock = ["18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59", "929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"] -idna = ["c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", "ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"] -imagesize = ["3f349de3eb99145973fefb7dbe38554414e5c30abd0c8e4b970a7c9d09f3a1d8", "f3832918bc3c66617f92e35f5d70729187676313caa60c187eb0f28b8fe5e3b5"] -importlib-metadata = ["aa18d7378b00b40847790e7c27e11673d7fed219354109d0e7b9e5b25dc3ad26", "d5f18a79777f3aa179c145737780282e27b508fc8fd688cb17c7a813e8bd39af"] -jinja2 = ["74320bb91f31270f9551d46522e33af46a80c3d619f4a4bf42b3164d30b5911f", "9fe95f19286cfefaa917656583d020be14e7859c6b0252588391e47db34527de"] -localpubsub = ["ec8f00993a53841b41d3455ea311b310c6e716b580dfbf7eddb73b18ab0c3207"] -markupsafe = ["00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", "09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", "09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", "1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", "24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", "29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", "43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", "46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", "500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", "535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", "62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", "6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", "717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", "79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", "7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", "88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", "8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", "98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", "9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", "9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", "ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", "b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", "b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", "b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", "ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", "c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", "cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", "e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"] -mock = ["83657d894c90d5681d62155c82bda9c1187827525880eda8ff5df4ec813437c3", "d157e52d4e5b938c550f39eb2fd15610db062441a9c2747d3dbfa9298211d0f8"] -more-itertools = ["409cd48d4db7052af495b09dec721011634af3753ae1ef92d2b32f73a745f832", "92b8c4b06dac4f0611c0729b2f2ede52b2e1bac1ab48f089c7ddc12e26bb60c4"] -mypy = ["1521c186a3d200c399bd5573c828ea2db1362af7209b2adb1bb8532cea2fb36f", "31a046ab040a84a0fc38bc93694876398e62bc9f35eca8ccbf6418b7297f4c00", "3b1a411909c84b2ae9b8283b58b48541654b918e8513c20a400bb946aa9111ae", "48c8bc99380575deb39f5d3400ebb6a8a1cb5cc669bbba4d3bb30f904e0a0e7d", "540c9caa57a22d0d5d3c69047cc9dd0094d49782603eb03069821b41f9e970e9", "672e418425d957e276c291930a3921b4a6413204f53fe7c37cad7bc57b9a3391", "6ed3b9b3fdc7193ea7aca6f3c20549b377a56f28769783a8f27191903a54170f", "9371290aa2cad5ad133e4cdc43892778efd13293406f7340b9ffe99d5ec7c1d9", "ace6ac1d0f87d4072f05b5468a084a45b4eda970e4d26704f201e06d47ab2990", "b428f883d2b3fe1d052c630642cc6afddd07d5cd7873da948644508be3b9d4a7", "d5bf0e6ec8ba346a2cf35cb55bf4adfddbc6b6576fcc9e10863daa523e418dbb", "d7574e283f83c08501607586b3167728c58e8442947e027d2d4c7dcd6d82f453", "dc889c84241a857c263a2b1cd1121507db7d5b5f5e87e77147097230f374d10b", "f4748697b349f373002656bf32fede706a0e713d67bfdcf04edf39b1f61d46eb"] -mypy-extensions = ["090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d", "2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"] -numpy = ["0cdbbaa30ae69281b18dd995d3079c4e552ad6d5426977f66b9a2a95f11f552a", "2b0cca1049bd39d1879fa4d598624cafe82d35529c72de1b3d528d68031cdd95", "31d3fe5b673e99d33d70cfee2ea8fe8dccd60f265c3ed990873a88647e3dd288", "34dd4922aab246c39bf5df03ca653d6265e65971deca6784c956bf356bca6197", "384e2dfa03da7c8d54f8f934f61b6a5e4e1ebb56a65b287567629d6c14578003", "392e2ea22b41a22c0289a88053204b616181288162ba78e6823e1760309d5277", "4341a39fc085f31a583be505eabf00e17c619b469fef78dc7e8241385bfddaa4", "45080f065dcaa573ebecbfe13cdd86e8c0a68c4e999aa06bd365374ea7137706", "485cb1eb4c9962f4cd042fed9424482ec1d83fee5dc2ef3f2552ac47852cb259", "575cefd28d3e0da85b0864506ae26b06483ee4a906e308be5a7ad11083f9d757", "62784b35df7de7ca4d0d81c5b6af5983f48c5cdef32fc3635b445674e56e3266", "69c152f7c11bf3b4fc11bc4cc62eb0334371c0db6844ebace43b7c815b602805", "6ccfdcefd287f252cf1ea7a3f1656070da330c4a5658e43ad223269165cdf977", "7298fbd73c0b3eff1d53dc9b9bdb7add8797bb55eeee38c8ccd7906755ba28af", "79463d918d1bf3aeb9186e3df17ddb0baca443f41371df422f99ee94f4f2bbfe", "8bbee788d82c0ac656536de70e817af09b7694f5326b0ef08e5c1014fcb96bb3", "a863957192855c4c57f60a75a1ac06ce5362ad18506d362dd807e194b4baf3ce", "ae602ba425fb2b074e16d125cdce4f0194903da935b2e7fe284ebecca6d92e76", "b13faa258b20fa66d29011f99fdf498641ca74a0a6d9266bc27d83c70fea4a6a", "c2c39d69266621dd7464e2bb740d6eb5abc64ddc339cc97aa669f3bb4d75c103", "e9c88f173d31909d881a60f08a8494e63f1aff2a4052476b24d4f50e82c47e24", "f1a29267ac29fff0913de0f11f3a9edfcd3f39595f467026c29376fad243ebe3", "f69dde0c5a137d887676a8129373e44366055cf19d1b434e853310c7a1e68f93"] -opencv-python = ["042b141424978d2cc9b32905a22b137d39d9f61d60f9cd970d9ed35da8b508d8", "0550060919e4aa0083c552e1e9d3e6548fe946a34bbe573b94ea6e9bf9d8da4e", "154daf889e23b0c6b27449033561699378094230e3b2a29515f74b06a682e806", "1f48aa26bde4607d7e797b768ec7cac796f8b98e3cc313de4942193731daa155", "2c6c43d95e618f7c97c81553daa6cc476f83cb9b4782c131b104171d909427cc", "42d53bdb47fadc7d266c88a00120f452171bfda06f8cbb12706106c26a28322d", "45ea2d208277c044d7fb9ec0f47f44227bcabfcacb0cd4687f217cb01e7c133d", "4fb8bcc669c5be8f342b6ea7dcba00c7c4bba9e0dd68a48a5e3428e396853150", "54bfe1e32f0fcd919c1883f173787bef019aa38c6202259265b1fa15270602f4", "5ffc9d9d7922b808fed6ed4bbbc596b799d2ba93cceb2dd87a03b6489051d7e2", "70cb9b121649c5bfba3aba29b517e9ed34ae9fb9dbff5e211f979a778f230cc2", "77cc97703bbecad79484cfcd49a7e21b81ba7d481ae7a030a482347760fd6694", "90ad5029ff9a8dde58b01aa6d8903989dde9c13fc88f403d1af20a4ae8917ba2", "9fdaab937a0f9e8237c89bac8ff01279b2321e74fe0cdeaeb3710854d96819c8", "a1298bfdb7475740a825c28c9c0ecce4d80225376702c72b7fe296f7eb9f3e0b", "a16698f2d1a4c7f8c604482f252ac3b44fb680878e84e2b091b76ba6cb23d4b0", "a2141cfb98c7e6d3ab7c50b511b02d688057417ac0a89d64795394a0e91369b4", "ac3d16be4662ce179187b51ae5698f0d25b970ab9e79d7216f6587623703c61a", "b1951fece88abb0d630a8b93eae2fa6cbf085dfcd727b34ccd9151008965cc7c", "c5c95a5e8f7aaa90da39604fe2e467b914183c81c7c776e2cade0173922c48cc", "cfe04992f508389e0a21eebb3b109c560b4b55ce66eab9783baa74f4b51aef3b", "d7eae953eaf8f9a0d9c3aa54dc59ff80322f156a5293940e5ab50ff7d46f6659", "de08cda9999a6095552d26ed96fa86519c93bccd80a1cd7762feec9e4f935e43", "e4f46417e7a9b9df050b1bc2596a33cc7036a2cb0ec8d4a03bd80c7601ef87be", "e50f2897b85eb40bf6b9a51fb62ecc0b00fb18edaf376febf8cf55d29baadf58", "e91df696f8e84a306b3c95c041df2e19d752ede94006f388483d47105e15185d", "ee8de5169d1f2639e9a7f76a59eb1c5d7708051fc3f7e336bf430b1b61533922"] -packaging = ["28b924174df7a2fa32c1953825ff29c61e2f5e082343165438812f00d3a7fc47", "d9551545c6d761f3def1677baf08ab2a3ca17c56879e70fecba2fc4dde4ed108"] -pluggy = ["0db4b7601aae1d35b4a033282da476845aa19185c1e6964b25cf324b5e4ec3e6", "fa5fa1622fa6dd5c030e9cad086fa19ef6a0cf6d7a2d12318e10cb49d6d68f34"] -py = ["64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa", "dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53"] -pydocstyle = ["04c84e034ebb56eb6396c820442b8c4499ac5eb94a3bda88951ac3dc519b6058", "66aff87ffe34b1e49bff2dd03a88ce6843be2f3346b0c9814410d34987fbab59"] -pygments = ["71e430bc85c88a430f000ac1d9b331d2407f681d6f6aec95e8bcfbc3df5b0127", "881c4c157e45f30af185c1ffe8d549d48ac9127433f2c380c24b84572ad66297"] -pyparsing = ["4acadc9a2b96c19fe00932a38ca63e601180c39a189a696abce1eaab641447e1", "61b5ed888beab19ddccab3478910e2076a6b5a0295dffc43021890e136edf764"] -pytest = ["7e4800063ccfc306a53c461442526c5571e1462f61583506ce97e4da6a1d88c8", "ca563435f4941d0cb34767301c27bc65c510cb82e90b9ecf9cb52dc2c63caaa0"] -pytz = ["1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d", "b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be"] -pyzmq = ["01636e95a88d60118479041c6aaaaf5419c6485b7b1d37c9c4dd424b7b9f1121", "021dba0d1436516092c624359e5da51472b11ba8edffa334218912f7e8b65467", "0463bd941b6aead494d4035f7eebd70035293dd6caf8425993e85ad41de13fa3", "05fd51edd81eed798fccafdd49c936b6c166ffae7b32482e4d6d6a2e196af4e6", "1fadc8fbdf3d22753c36d4172169d184ee6654f8d6539e7af25029643363c490", "22efa0596cf245a78a99060fe5682c4cd00c58bb7614271129215c889062db80", "260c70b7c018905ec3659d0f04db735ac830fe27236e43b9dc0532cf7c9873ef", "2762c45e289732d4450406cedca35a9d4d71e449131ba2f491e0bf473e3d2ff2", "2fc6cada8dc53521c1189596f1898d45c5f68603194d3a6453d6db4b27f4e12e", "343b9710a61f2b167673bea1974e70b5dccfe64b5ed10626798f08c1f7227e72", "41bf96d5f554598a0632c3ec28e3026f1d6591a50f580df38eff0b8067efb9e7", "856b2cdf7a1e2cbb84928e1e8db0ea4018709b39804103d3a409e5584f553f57", "85b869abc894672de9aecdf032158ea8ad01e2f0c3b09ef60e3687fb79418096", "9055ed3f443edae7dc80f253fc54257f8455fc3062a7832c60887409e27c9f82", "93f44739db69234c013a16990e43db1aa0af3cf5a4b8b377d028ff24515fbeb3", "98fa3e75ccb22c0dc99654e3dd9ff693b956861459e8c8e8734dd6247b89eb29", "9a22c94d2e93af8bebd4fcf5fa38830f5e3b1ff0d4424e2912b07651eb1bafb4", "a7d3f4b4bbb5d7866ae727763268b5c15797cbd7b63ea17f3b0ec1067da8994b", "b645a49376547b3816433a7e2d2a99135c8e651e50497e7ecac3bd126e4bea16", "cf0765822e78cf9e45451647a346d443f66792aba906bc340f4e0ac7870c169c", "dc398e1e047efb18bfab7a8989346c6921a847feae2cad69fedf6ca12fb99e2c", "dd5995ae2e80044e33b5077fb4bc2b0c1788ac6feaf15a6b87a00c14b4bdd682", "e03fe5e07e70f245dc9013a9d48ae8cc4b10c33a1968039c5a3b64b5d01d083d", "ea09a306144dff2795e48439883349819bef2c53c0ee62a3c2fae429451843bb", "f4e37f33da282c3c319849877e34f97f0a3acec09622ec61b7333205bdd13b52", "fa4bad0d1d173dee3e8ef3c3eb6b2bb6c723fc7a661eeecc1ecb2fa99860dd45"] -requests = ["11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4", "9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"] -six = ["1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd", "30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66"] -snowballstemmer = ["209f257d7533fdb3cb73bdbd24f436239ca3b2fa67d56f6ff88e86be08cc5ef0", "df3bac3df4c2c01363f3dd2cfa78cce2840a79b9f1c2d2de9ce8d31683992f52"] -sphinx = ["31088dfb95359384b1005619827eaee3056243798c62724fd3fa4b84ee4d71bd", "52286a0b9d7caa31efee301ec4300dbdab23c3b05da1c9024b4e84896fb73d79"] -sphinxcontrib-applehelp = ["edaa0ab2b2bc74403149cb0209d6775c96de797dfd5b5e2a71981309efab3897", "fb8dee85af95e5c30c91f10e7eb3c8967308518e0f7488a2828ef7bc191d0d5d"] -sphinxcontrib-devhelp = ["6c64b077937330a9128a4da74586e8c2130262f014689b4b89e2d08ee7294a34", "9512ecb00a2b0821a146736b39f7aeb90759834b07e81e8cc23a9c70bacb9981"] -sphinxcontrib-htmlhelp = ["4670f99f8951bd78cd4ad2ab962f798f5618b17675c35c5ac3b2132a14ea8422", "d4fd39a65a625c9df86d7fa8a2d9f3cd8299a3a4b15db63b50aac9e161d8eff7"] -sphinxcontrib-jsmath = ["2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", "a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"] -sphinxcontrib-qthelp = ["513049b93031beb1f57d4daea74068a4feb77aa5630f856fcff2e50de14e9a20", "79465ce11ae5694ff165becda529a600c754f4bc459778778c7017374d4d406f"] -sphinxcontrib-serializinghtml = ["c0efb33f8052c04fd7a26c0a07f1678e8512e0faec19f4aa8f2473a8b81d5227", "db6615af393650bf1151a6cd39120c29abaf93cc60db8c48eb2dddbfdc3a9768"] -toml = ["229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c", "235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e", "f1db651f9657708513243e61e6cc67d101a39bad662eaa9b5546f789338e07a3"] -tox = ["0bc216b6a2e6afe764476b4a07edf2c1dab99ed82bb146a1130b2e828f5bff5e", "c4f6b319c20ba4913dbfe71ebfd14ff95d1853c4231493608182f66e566ecfe1"] -tox-gh-actions = ["202c23c7de04f060d956961c65bfabcb6a13f08321225a05f902470b1a3e824a", "f943d9a7009d97ed1aac1b0051b3eef97b07176bc040a2307de9461629fcdf88"] -typed-ast = ["1170afa46a3799e18b4c977777ce137bb53c7485379d9706af8a59f2ea1aa161", "18511a0b3e7922276346bcb47e2ef9f38fb90fd31cb9223eed42c85d1312344e", "262c247a82d005e43b5b7f69aff746370538e176131c32dda9cb0f324d27141e", "2b907eb046d049bcd9892e3076c7a6456c93a25bebfe554e931620c90e6a25b0", "354c16e5babd09f5cb0ee000d54cfa38401d8b8891eefa878ac772f827181a3c", "48e5b1e71f25cfdef98b013263a88d7145879fbb2d5185f2a0c79fa7ebbeae47", "4e0b70c6fc4d010f8107726af5fd37921b666f5b31d9331f0bd24ad9a088e631", "630968c5cdee51a11c05a30453f8cd65e0cc1d2ad0d9192819df9978984529f4", "66480f95b8167c9c5c5c87f32cf437d585937970f3fc24386f313a4c97b44e34", "71211d26ffd12d63a83e079ff258ac9d56a1376a25bc80b1cdcdf601b855b90b", "7954560051331d003b4e2b3eb822d9dd2e376fa4f6d98fee32f452f52dd6ebb2", "838997f4310012cf2e1ad3803bce2f3402e9ffb71ded61b5ee22617b3a7f6b6e", "95bd11af7eafc16e829af2d3df510cecfd4387f6453355188342c3e79a2ec87a", "bc6c7d3fa1325a0c6613512a093bc2a2a15aeec350451cbdf9e1d4bffe3e3233", "cc34a6f5b426748a507dd5d1de4c1978f2eb5626d51326e43280941206c209e1", "d755f03c1e4a51e9b24d899561fec4ccaf51f210d52abdf8c07ee2849b212a36", "d7c45933b1bdfaf9f36c579671fec15d25b06c8398f113dab64c18ed1adda01d", "d896919306dd0aa22d0132f62a1b78d11aaf4c9fc5b3410d3c666b818191630a", "fdc1c9bbf79510b76408840e009ed65958feba92a88833cdceecff93ae8fff66", "ffde2fbfad571af120fcbfbbc61c72469e72f550d676c3342492a9dfdefb8f12"] -typing = ["91dfe6f3f706ee8cc32d38edbbf304e9b7583fb37108fef38229617f8b3eba23", "c8cabb5ab8945cd2f54917be357d134db9cc1eb039e59d1606dc1e60cb1d9d36", "f38d83c5a7a7086543a0f649564d661859c5146a85775ab90c0d2f93ffaa9714"] -typing-extensions = ["091ecc894d5e908ac75209f10d5b4f118fbdb2eb1ede6a63544054bb1edb41f2", "910f4656f54de5993ad9304959ce9bb903f90aadc7c67a0bef07e678014e892d", "cf8b63fedea4d89bab840ecbb93e75578af28f76f66c35889bd7065f5af88575"] -urllib3 = ["3de946ffbed6e6746608990594d08faac602528ac7015ac28d33cee6a45b7398", "9a107b99a5393caf59c7aa3c1249c16e6879447533d0887f4336dde834c7be86"] -virtualenv = ["11cb4608930d5fd3afb545ecf8db83fa50e1f96fc4fca80c94b07d2c83146589", "d257bb3773e48cac60e475a19b608996c73f4d333b3ba2e4e57d5ac6134e0136"] -wcwidth = ["3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e", "f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"] -zipp = ["3718b1cbcd963c7d4c5511a8240812904164b7f381b647143a89d3b98f9bcd8e", "f06903e9f1f43b12d371004b4ac7b06ab39a44adc747266928ae6debfa7b3335"] diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index 0dfb68f..0000000 --- a/pyproject.toml +++ /dev/null @@ -1,36 +0,0 @@ -[tool.poetry] -name = 'displayarray' -version = '1.1.1' -description = 'Tool for displaying numpy arrays.' -authors = ['SimLeek '] -license = 'MIT' -readme = "README.rst" -repository = "https://github.com/simleek/displayarray" - -[tool.poetry.dependencies] -python = "^3.6" -opencv_python = "^4*" -docopt = "0.6.2" -numpy = "1.16.1" -localpubsub = "0.0.4" -pyzmq = "18.1.0" - -[tool.poetry.dev-dependencies] -pytest = "5.2.1" -typing = "3.7.4.1" -mock = "^3.0" -tox = "^3.14" -tox-gh-actions = "^0.3.0" -coverage = "^4.5" -sphinx = "^2.2" -black = {version = "^18.3-alpha.0", allows-prereleases = true} -mypy = "^0.740.0" -pydocstyle = "^4.0" - -[tool.poetry.scripts] -displayarray = "displayarray.__main__:main" - -[build-system] -requires = ['poetry'] -build-backend = "poetry.masonry.api" - diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..0326a7f --- /dev/null +++ b/setup.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- + +# DO NOT EDIT THIS FILE! +# This file has been autogenerated by dephell <3 +# https://github.com/dephell/dephell + +try: + from setuptools import setup +except ImportError: + from distutils.core import setup + + +import os.path + +readme = "" +here = os.path.abspath(os.path.dirname(__file__)) +readme_path = os.path.join(here, "README.rst") +if os.path.exists(readme_path): + with open(readme_path, "rb") as stream: + readme = stream.read().decode("utf8") + + +setup( + long_description=readme, + name="displayarray", + version="1.2.0", + description="Tool for displaying numpy arrays.", + python_requires="==3.*,>=3.6.0", + project_urls={"repository": "https://github.com/simleek/displayarray"}, + author="SimLeek", + author_email="simulator.leek@gmail.com", + license="MIT", + entry_points={"console_scripts": ["displayarray = displayarray.__main__:main"]}, + packages=[ + "displayarray", + "displayarray.effects", + "displayarray.frame", + "displayarray.window", + ], + package_dir={"": "."}, + package_data={}, + install_requires=[ + "docopt==0.6.2", + "localpubsub==0.0.4", + "numpy==1.16.1", + "opencv-python==4.*,>=4.0.0", + "pyzmq==18.1.0", + ], + extras_require={ + "linux": ["PyV4L2Cam @ git+https://github.com/SimLeek/PyV4L2Cam"], + "dev": [ + "black==18.*,>=18.3.0.a0", + "coverage==4.*,>=4.5.0", + "mock==3.*,>=3.0.0", + "mypy==0.*,>=0.740.0", + "pydocstyle==4.*,>=4.0.0", + "pytest==5.2.1", + "sphinx==2.*,>=2.2.0", + "tox==3.*,>=3.14.0", + "tox-gh-actions==0.*,>=0.3.0", + "typing==3.7.4.1", + ], + }, +) diff --git a/tests/frame/test_frame_publishing.py b/tests/frame/test_frame_publishing.py index 2f73124..c01c2a4 100644 --- a/tests/frame/test_frame_publishing.py +++ b/tests/frame/test_frame_publishing.py @@ -42,7 +42,7 @@ def test_pub_cam_int(): cam_pub.assert_has_calls([mock.call(img)] * 4) - reg_cam.assert_called_once_with("0") + reg_cam.assert_called_once_with("0", cap) cam_cmd_sub.assert_called_once_with("0") cap.set.assert_has_calls( diff --git a/tests/frame/test_frame_updater.py b/tests/frame/test_frame_updater.py index 1fe291e..85e39bd 100644 --- a/tests/frame/test_frame_updater.py +++ b/tests/frame/test_frame_updater.py @@ -57,7 +57,7 @@ def test_loop(): ud.loop() - mock_pubcam_thread.assert_called_once_with(0, (-1, -1), True, float("inf")) + mock_pubcam_thread.assert_called_once_with(0, (-1, -1), True, float("inf"), "") mock_frame_sub.assert_called_once_with("0") handler_cmd_sub.assert_called_once_with("0") sub_cam.get.assert_has_calls([mock.call(blocking=True, timeout=1.0)] * 3) diff --git a/tests/window/test_subscriber_windows.py b/tests/window/test_subscriber_windows.py index 827da4f..fd98030 100644 --- a/tests/window/test_subscriber_windows.py +++ b/tests/window/test_subscriber_windows.py @@ -495,8 +495,12 @@ def test_display(): fup.assert_has_calls( [ - mock.call(0, fps_limit=float("inf"), request_size=(50, 50)), - mock.call(1, fps_limit=float("inf"), request_size=(50, 50)), + mock.call( + 0, force_backend="", fps_limit=float("inf"), request_size=(50, 50) + ), + mock.call( + 1, force_backend="", fps_limit=float("inf"), request_size=(50, 50) + ), ] ) assert fup_inst.start.call_count == 2 @@ -541,10 +545,18 @@ def test_display_callbacks(): fup.assert_has_calls( [ mock.call( - 0, callbacks=[cb], fps_limit=float("inf"), request_size=(-1, -1) + 0, + callbacks=[cb], + force_backend="", + fps_limit=float("inf"), + request_size=(-1, -1), ), mock.call( - 1, callbacks=[cb], fps_limit=float("inf"), request_size=(-1, -1) + 1, + callbacks=[cb], + force_backend="", + fps_limit=float("inf"), + request_size=(-1, -1), ), ] ) @@ -557,8 +569,20 @@ def test_display_callbacks(): fup.assert_has_calls( [ - mock.call(0, callbacks=[cb, cb2], fps_limit=60, request_size=(-1, -1)), - mock.call(1, callbacks=[cb, cb2], fps_limit=60, request_size=(-1, -1)), + mock.call( + 0, + callbacks=[cb, cb2], + force_backend="", + fps_limit=60, + request_size=(-1, -1), + ), + mock.call( + 1, + callbacks=[cb, cb2], + force_backend="", + fps_limit=60, + request_size=(-1, -1), + ), ] ) @@ -579,16 +603,25 @@ def test_display_callbacks_dict(): fup.assert_has_calls( [ mock.call( - 0, callbacks=[cb1], fps_limit=float("inf"), request_size=(-1, -1) - ), - mock.call( - 1, - callbacks=[cb1, cb2], + 0, + callbacks=[cb1], + force_backend="", fps_limit=float("inf"), request_size=(-1, -1), ), mock.call( - 2, callbacks=[cb3], fps_limit=float("inf"), request_size=(-1, -1) + 1, + callbacks=[cb1, cb2], + force_backend="", + fps_limit=float("inf"), + request_size=(-1, -1), + ), + mock.call( + 2, + callbacks=[cb3], + force_backend="", + fps_limit=float("inf"), + request_size=(-1, -1), ), ] ) diff --git a/tox.ini b/tox.ini index bf25cbd..3f8c29f 100644 --- a/tox.ini +++ b/tox.ini @@ -10,39 +10,39 @@ python = 3.7: py37, mypy, test-docs, black, pydocstyle [testenv] -whitelist_externals = poetry +whitelist_externals = coverage description = run the tests with pytest under {basepython} -commands = poetry run coverage run --source=displayarray -m pytest tests - poetry run coverage report - poetry run coverage erase +commands = coverage run --source=displayarray -m pytest tests + coverage report + coverage erase [testenv:docgen] -whitelist_externals = poetry +whitelist_externals = sphinx-build description = generating documentation -commands = poetry run sphinx-build -b dirhtml docs/docsrc docs +commands = sphinx-build -b dirhtml docs/docsrc docs [testenv:test-docs] -whitelist_externals = poetry +whitelist_externals = sphinx-build cmd description = generating documentation -commands = poetry run sphinx-build -b dirhtml docs/docsrc docs_test -n -T - #rm -rf docs_test - cmd /c RMDIR /Q/S docs_test +commands = sphinx-build -b dirhtml docs/docsrc docs_test -n -T + rm -rf docs_test + #cmd /c RMDIR /Q/S docs_test [testenv:mypy] -whitelist_externals = poetry +whitelist_externals = mypy description = enforce typing -commands = poetry run mypy displayarray +commands = mypy displayarray [testenv:black] -whitelist_externals = poetry +whitelist_externals = black description = enforce code style -commands = poetry run black displayarray --check +commands = black displayarray --check [pydocstyle] ignore = D105, D212, D203, D202 [testenv:pydocstyle] -whitelist_externals = poetry +whitelist_externals = pydocstyle description = enforce documentation style -commands = poetry run pydocstyle displayarray +commands = pydocstyle displayarray