diff --git a/.bumpversion.cfg b/.bumpversion.cfg deleted file mode 100644 index 4bcffbd..0000000 --- a/.bumpversion.cfg +++ /dev/null @@ -1,8 +0,0 @@ -[bumpversion] -current_version = 0.0.1 -commit = True -tag = True - -[bumpversion:file:setup.cfg] - -[bumpversion:file:src/surface_tracker/__init__.py] diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..6a34e66 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,7 @@ +[run] +omit = + # leading `*/` for pytest-dev/pytest-cov#456 + */.tox/* + +[report] +show_missing = True diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..b8aeea1 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +root = true + +[*] +charset = utf-8 +indent_style = tab +indent_size = 4 +insert_final_newline = true +end_of_line = lf + +[*.py] +indent_style = space +max_line_length = 88 + +[*.{yml,yaml}] +indent_style = space +indent_size = 2 diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..52378bc --- /dev/null +++ b/.flake8 @@ -0,0 +1,16 @@ +[flake8] +max-line-length = 88 + +max-complexity = 18 + +extend-ignore = + # Black creates whitespace before colon + E203 + W503 + F541 + +extend-select = B,C,E,F,W,T4,B9 +exclude = + .venv + .pyenv + __pycache__ diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..89ff339 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,8 @@ +version: 2 +updates: + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "daily" + allow: + - dependency-type: "all" diff --git a/.github/workflows/build-test-deploy.yml b/.github/workflows/build-test-deploy.yml deleted file mode 100644 index 3e2b894..0000000 --- a/.github/workflows/build-test-deploy.yml +++ /dev/null @@ -1,69 +0,0 @@ -name: Build, test, and deploy - -on: - push: - tags: - - "**" - pull_request: - -jobs: - build: - name: Build source and wheel distribution - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v1 - with: - python-version: 3.7 - - name: Build source package - run: | - pip install build - python -m build . - - name: Upload source package - uses: actions/upload-artifact@v2 - with: - name: distribution - path: dist/ - - test: - name: Test source and wheel distributions - runs-on: ubuntu-latest - needs: [build] - strategy: - matrix: - python-version: [3.6, 3.7, 3.8, 3.9] - - steps: - - uses: actions/checkout@v1 - - uses: actions/download-artifact@v2 - with: - name: distribution - path: dist/ - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install tox tox-gh-actions - - name: Test sdist with tox - run: tox --installpkg dist/*.tar.gz - - name: Test wheel with tox - run: tox --installpkg dist/*.whl - - deploy: - runs-on: ubuntu-latest - needs: [test] - if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/') - steps: - - uses: actions/checkout@v2 - - uses: actions/download-artifact@v2 - with: - name: distribution - path: dist/ - - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@master - with: - user: __token__ - password: ${{ secrets.PYPI_TOKEN }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..a63f166 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,50 @@ +name: tests + +on: [push, pull_request] + +jobs: + test: + strategy: + matrix: + python: + - 3.6 + - 3.7 + - 3.8 + - 3.9 + - "3.10" + platform: + - ubuntu-latest + - macos-latest + - windows-latest + runs-on: ${{ matrix.platform }} + steps: + - uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python }} + - name: Install tox + run: | + python -m pip install tox + - name: Run tests + run: tox + + release: + needs: test + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: "3.10" + - name: Install tox + run: | + python -m pip install tox + - name: Release + run: tox -e release + env: + TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 5756659..1ba3d18 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,159 @@ +version.py + +# Created by https://www.toptal.com/developers/gitignore/api/python,visualstudiocode,macos,windows,linux,pycharm +# Edit at https://www.toptal.com/developers/gitignore?templates=python,visualstudiocode,macos,windows,linux,pycharm + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### PyCharm ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### PyCharm Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +### Python ### # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] @@ -50,6 +206,7 @@ coverage.xml *.py,cover .hypothesis/ .pytest_cache/ +cover/ # Translations *.mo @@ -72,6 +229,7 @@ instance/ docs/_build/ # PyBuilder +.pybuilder/ target/ # Jupyter Notebook @@ -175,6 +333,30 @@ Icon Network Trash Folder Temporary Items .apdisk +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +# Support for Project snippet scope +!.vscode/*.code-snippets ### Windows ### # Windows thumbnail cache files @@ -202,4 +384,4 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk -# End of https://www.toptal.com/developers/gitignore/api/macos,windows,linux +# End of https://www.toptal.com/developers/gitignore/api/python,visualstudiocode,macos,windows,linux,pycharm diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e5303c7..3615c2f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,10 +19,16 @@ repos: args: ["--py36-plus"] exclude: ^bin/ + - repo: https://github.com/pycqa/flake8 + rev: 4.0.1 + hooks: + - id: flake8 + - repo: https://github.com/PyCQA/isort rev: 5.10.1 hooks: - id: isort + args: [--profile, black] - repo: https://github.com/psf/black rev: 22.3.0 diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 0000000..cc69854 --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,6 @@ +version: 2 +python: + install: + - path: . + extra_requirements: + - docs diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index e69de29..0000000 diff --git a/CHANGES.rst b/CHANGES.rst new file mode 100644 index 0000000..c83bc66 --- /dev/null +++ b/CHANGES.rst @@ -0,0 +1,13 @@ +1.0.0b1 +####### +- Adoption of the `Pupil Labs Python Module Skeleton`_ +- API-breaking changes + - Moved ``surface_tracker`` to ``pupil_labs.surface_tracker`` + +.. _`Pupil Labs Python Module Skeleton`: + https://github.com/pupil-labs/python-module-skeleton + +0.0.1 +##### + +- Initial release diff --git a/README.md b/README.md deleted file mode 100644 index b9151d2..0000000 --- a/README.md +++ /dev/null @@ -1 +0,0 @@ -# surface-tracker diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..7c48fb1 --- /dev/null +++ b/README.rst @@ -0,0 +1,50 @@ +.. image:: https://img.shields.io/pypi/v/surface-tracker.svg + :target: `PyPI link`_ + +.. image:: https://img.shields.io/pypi/pyversions/surface-tracker.svg + :target: `PyPI link`_ + +.. _PyPI link: https://pypi.org/project/surface-tracker + +.. image:: https://github.com/pupil-labs/surface-tracker/workflows/tests/badge.svg + :target: https://github.com/pupil-labs/surface-tracker/actions?query=workflow%3A%22tests%22 + :alt: tests + +.. image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/psf/black + :alt: Code style: Black + +.. image:: https://readthedocs.org/projects/pupil-labs-surface-tracker/badge/?version=latest + :target: https://pupil-labs-surface-tracker.readthedocs.io/en/latest/?badge=latest + +.. image:: https://img.shields.io/badge/skeleton-2021-informational + :target: https://blog.jaraco.com/skeleton + +Pupil Labs Surface Tracker +========================== + +This Python module provides the core functionality of the `Pupil Cloud Marker Mapper`_. + +.. _Pupil Cloud Marker Mapper: + https://docs.pupil-labs.com/invisible/explainers/enrichments/#marker-mapper + +Installation +------------ + +We recommend installing the pre-packaged binary wheels from PyPI: + +.. code-block:: console + + pip install surface-tracker + +To run the example, you will need to install some dependencies. You can get them by +running: + +.. code-block:: console + + pip install "surface-tracker[example]" + + +For more information see the `documentation`_. + +.. _documentation: https://pupil-labes-surface-tracker.readthedocs.io/ diff --git a/docs/api.rst b/docs/api.rst new file mode 100644 index 0000000..23f898b --- /dev/null +++ b/docs/api.rst @@ -0,0 +1,7 @@ +API Reference +************* + +.. automodule:: pupil_labs.surface_tracker + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..124fd67 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 + +extensions = ['sphinx.ext.autodoc', 'jaraco.packaging.sphinx', 'rst.linker'] + +master_doc = "index" + +link_files = { + '../CHANGES.rst': dict( + using=dict(GH='https://github.com'), + replace=[ + dict( + pattern=r'(Issue #|\B#)(?P\d+)', + url='{package_url}/issues/{issue}', + ), + dict( + pattern=r'(?m:^((?Pv?\d+(\.\d+){1,2}))\n[-=]+\n)', + with_scm='{text}\n{rev[timestamp]:%d %b %Y}\n', + ), + dict( + pattern=r'PEP[- ](?P\d+)', + url='https://www.python.org/dev/peps/pep-{pep_number:0>4}/', + ), + ], + ) +} + +# Be strict about any broken references: +nitpicky = True + +# Include Python intersphinx mapping to prevent failures +# jaraco/skeleton#51 +extensions += ['sphinx.ext.intersphinx'] +intersphinx_mapping = { + 'python': ('https://docs.python.org/3', None), + 'numpy': ('https://numpy.org/doc/stable', None), +} + +html_theme = "furo" diff --git a/docs/history.rst b/docs/history.rst new file mode 100644 index 0000000..8e21750 --- /dev/null +++ b/docs/history.rst @@ -0,0 +1,8 @@ +:tocdepth: 2 + +.. _changes: + +History +******* + +.. include:: ../CHANGES (links).rst diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..adcd754 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,16 @@ +.. include:: ../README.rst + :start-line: 22 + :end-line: 46 + +Table of Content +================ + +.. toctree:: + :maxdepth: 1 + + api + history + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 0000000..976ba02 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,2 @@ +[mypy] +ignore_missing_imports = True diff --git a/pyproject.toml b/pyproject.toml index cb63cde..a15babc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,12 +1,19 @@ [build-system] -requires = [ - "setuptools>=42", - "wheel" -] - +requires = ["setuptools>=56", "wheel", "setuptools_scm[toml]>=3.4.1"] build-backend = "setuptools.build_meta" [tool.pytest.ini_options] testpaths = [ "tests", ] + +[tool.black] +skip-string-normalization = true + +[tool.setuptools_scm] + +[pytest.enabler.mypy] +addopts = "--mypy" + +[pytest.enabler.cov] +addopts = "--cov" diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..845aec0 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,5 @@ +[pytest] +norecursedirs=dist build .tox .eggs +addopts=--doctest-modules +doctest_optionflags=ALLOW_UNICODE ELLIPSIS +filterwarnings= diff --git a/setup.cfg b/setup.cfg index 665dd3b..2dcbe16 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,12 +1,11 @@ [metadata] name = surface_tracker -version = 0.0.1 description = Surface tracker -long_description = file: README.md -long_description_content_type = text/markdown +long_description = file: README.rst +long_description_content_type = text/x-rst url = https://github.com/pupil-labs/surface-tracker author = Pupil Labs GmbH -author_email = pypi@pupil-labs.com +author_email = info@pupil-labs.com license = LGPL-3.0 license_file = LICENSE classifiers = @@ -21,15 +20,17 @@ classifiers = Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 - Programming Language :: Python :: Implementation :: CPython project_urls = - Changelog=https://github.com/pupil-labs/surface-tracker/blob/master/CHANGELOG.md - Documentation=https://docs.pupil-labs.com/cloud/enrichments/#marker-mapper + Documentation=https://pupil-labs-surface-tracker.readthedocs.io/ + Changelog=https://pupil-labs-surface-tracker.readthedocs.io/en/latest/history.html + Pupil Cloud Enrichment=https://docs.pupil-labs.com/cloud/enrichments/#marker-mapper [options] -packages = find: +packages = find_namespace: install_requires = numpy + opencv-python + importlib-metadata;python_version<"3.8" python_requires = >=3.6 include_package_data = True package_dir = @@ -38,21 +39,27 @@ zip_safe = False [options.packages.find] where = src +exclude = + build* + dist* + docs* + tests* -[tox:tox] -envlist = py36, py37, py38, py39 -skip_missing_interpreters = true -isolated_build = true - -[testenv] -changedir = tests -deps = - pytest +[options.extras_require] +docs = + furo + jaraco.packaging>=8.2 + rst.linker>=1.9 + sphinx<4.4 # 4.4 does not detect TypeVars correctly +example = + Pillow + matplotlib + msgpack + pupil-apriltags +testing = opencv-python-headless - -[gh-actions] -python = - 3.6: py36 - 3.7: py37 - 3.8: py38 - 3.9: py39 + pytest>=6 + pytest-checkdocs>=2.4 + pytest-cov + pytest-enabler>=1.0.1 + pytest-mypy diff --git a/setup.py b/setup.py index 4a52a95..dbe9716 100644 --- a/setup.py +++ b/setup.py @@ -1,24 +1,5 @@ -""" -(*)~--------------------------------------------------------------------------- -Pupil - eye tracking platform -Copyright (C) 2012-2020 Pupil Labs -Distributed under the terms of the GNU -Lesser General Public License (LGPL v3.0). -See LICENSE for license details. ----------------------------------------------------------------------------~(*) -""" -from setuptools import setup +#!/usr/bin/env python +import setuptools -setup( - extras_require={ - "dev": ["pre-commit", "tox"], - "deploy": ["build", "twine", "bump2version"], - "example": [ - "opencv-python", - "pupil-apriltags", - "matplotlib", - "Pillow", - "msgpack", - ], - } -) +if __name__ == "__main__": + setuptools.setup() diff --git a/src/pupil_labs/surface_tracker/__init__.py b/src/pupil_labs/surface_tracker/__init__.py new file mode 100644 index 0000000..636db74 --- /dev/null +++ b/src/pupil_labs/surface_tracker/__init__.py @@ -0,0 +1,41 @@ +from . import utils +from .camera import Camera +from .coordinate_space import CoordinateSpace +from .corner import CornerId +from .heatmap import SurfaceHeatmap +from .image_crop import SurfaceImageCrop +from .location import SurfaceLocation +from .marker import Marker, MarkerId +from .orientation import SurfaceOrientation +from .surface import Surface, SurfaceId +from .tracker import SurfaceTracker +from .visual_anchors import SurfaceVisualAnchors + +try: + from importlib.metadata import PackageNotFoundError, version +except ImportError: + from importlib_metadata import PackageNotFoundError, version + +try: + __version__ = version("pupil_labs.surface_tracker") +except PackageNotFoundError: + # package is not installed + __version__ = "unknown" + +__all__ = [ + "__version__", + "utils", + "Camera", + "CoordinateSpace", + "CornerId", + "SurfaceHeatmap", + "SurfaceImageCrop", + "SurfaceLocation", + "Marker", + "MarkerId", + "SurfaceOrientation", + "Surface", + "SurfaceId", + "SurfaceTracker", + "SurfaceVisualAnchors", +] diff --git a/src/surface_tracker/camera.py b/src/pupil_labs/surface_tracker/camera.py similarity index 69% rename from src/surface_tracker/camera.py rename to src/pupil_labs/surface_tracker/camera.py index 42d4440..645cc6d 100644 --- a/src/surface_tracker/camera.py +++ b/src/pupil_labs/surface_tracker/camera.py @@ -1,13 +1,3 @@ -""" -(*)~--------------------------------------------------------------------------- -Pupil - eye tracking platform -Copyright (C) 2012-2020 Pupil Labs -Distributed under the terms of the GNU -Lesser General Public License (LGPL v3.0). -See LICENSE for license details. ----------------------------------------------------------------------------~(*) -""" - import cv2 import numpy as np diff --git a/src/pupil_labs/surface_tracker/coordinate_space.py b/src/pupil_labs/surface_tracker/coordinate_space.py new file mode 100644 index 0000000..ec1abdf --- /dev/null +++ b/src/pupil_labs/surface_tracker/coordinate_space.py @@ -0,0 +1,8 @@ +import enum + + +class CoordinateSpace(enum.Enum): + IMAGE_DISTORTED = "image-distorted" + IMAGE_UNDISTORTED = "image-undistorted" + SURFACE_DISTORTED = "surface-distorted" + SURFACE_UNDISTORTED = "surface-undisitorted" diff --git a/src/surface_tracker/corner.py b/src/pupil_labs/surface_tracker/corner.py similarity index 78% rename from src/surface_tracker/corner.py rename to src/pupil_labs/surface_tracker/corner.py index ecbee4e..6865dd6 100644 --- a/src/surface_tracker/corner.py +++ b/src/pupil_labs/surface_tracker/corner.py @@ -1,12 +1,3 @@ -""" -(*)~--------------------------------------------------------------------------- -Pupil - eye tracking platform -Copyright (C) 2012-2020 Pupil Labs -Distributed under the terms of the GNU -Lesser General Public License (LGPL v3.0). -See LICENSE for license details. ----------------------------------------------------------------------------~(*) -""" import enum import typing as T diff --git a/src/surface_tracker/heatmap.py b/src/pupil_labs/surface_tracker/heatmap.py similarity index 92% rename from src/surface_tracker/heatmap.py rename to src/pupil_labs/surface_tracker/heatmap.py index 8ba11d2..130e4f6 100644 --- a/src/surface_tracker/heatmap.py +++ b/src/pupil_labs/surface_tracker/heatmap.py @@ -1,12 +1,3 @@ -""" -(*)~--------------------------------------------------------------------------- -Pupil - eye tracking platform -Copyright (C) 2012-2020 Pupil Labs -Distributed under the terms of the GNU -Lesser General Public License (LGPL v3.0). -See LICENSE for license details. ----------------------------------------------------------------------------~(*) -""" import enum import typing as T diff --git a/src/surface_tracker/image_crop.py b/src/pupil_labs/surface_tracker/image_crop.py similarity index 84% rename from src/surface_tracker/image_crop.py rename to src/pupil_labs/surface_tracker/image_crop.py index 21ffbfd..94a1e74 100644 --- a/src/surface_tracker/image_crop.py +++ b/src/pupil_labs/surface_tracker/image_crop.py @@ -1,12 +1,3 @@ -""" -(*)~--------------------------------------------------------------------------- -Pupil - eye tracking platform -Copyright (C) 2012-2020 Pupil Labs -Distributed under the terms of the GNU -Lesser General Public License (LGPL v3.0). -See LICENSE for license details. ----------------------------------------------------------------------------~(*) -""" import typing as T import cv2 @@ -40,12 +31,12 @@ def _create_image_crop( points=surface_corners_in_surface_space ) - surface_corners_in_image_space_distorted = camera.distort_and_project( - points=surface_corners_in_image_space - ) + # surface_corners_in_image_space_distorted = camera.distort_and_project( + # points=surface_corners_in_image_space + # ) crop_size = SurfaceImageCrop.__calculate_crop_size( - *surface_corners_in_image_space_distorted, width=width, height=height + *surface_corners_in_image_space, width=width, height=height ) crop_w, crop_h = crop_size @@ -58,12 +49,12 @@ def _create_image_crop( crop_corners_in_image_space = np.array( crop_corners_in_image_space, dtype=np.float32 ) - surface_corners_in_image_space_distorted = np.array( - surface_corners_in_image_space_distorted, dtype=np.float32 + surface_corners_in_image_space = np.array( + surface_corners_in_image_space, dtype=np.float32 ) perspective_transform = cv2.getPerspectiveTransform( - surface_corners_in_image_space_distorted, crop_corners_in_image_space + surface_corners_in_image_space, crop_corners_in_image_space ) return SurfaceImageCrop( diff --git a/src/surface_tracker/location.py b/src/pupil_labs/surface_tracker/location.py similarity index 92% rename from src/surface_tracker/location.py rename to src/pupil_labs/surface_tracker/location.py index 3a84249..301b0e7 100644 --- a/src/surface_tracker/location.py +++ b/src/pupil_labs/surface_tracker/location.py @@ -1,12 +1,3 @@ -""" -(*)~--------------------------------------------------------------------------- -Pupil - eye tracking platform -Copyright (C) 2012-2020 Pupil Labs -Distributed under the terms of the GNU -Lesser General Public License (LGPL v3.0). -See LICENSE for license details. ----------------------------------------------------------------------------~(*) -""" import abc import logging import typing as T @@ -31,18 +22,20 @@ def __init_subclass__(cls): version = cls.version if version is None: raise ValueError( - f'SurfaceLocation subclass {cls.__name__} must overwrite class property "version"' + f'SurfaceLocation subclass {cls.__name__} must overwrite class ' + 'property "version"' ) if version in storage: raise ValueError( - f"SurfaceLocation subclass {cls.__name__} defines an already registered version {version}" + f"SurfaceLocation subclass {cls.__name__} defines an already " + "registered version {version}" ) storage[version] = cls return super().__init_subclass__() # ## Abstract members - version = None # type: ClassVar[int] + version: T.ClassVar[int] = None @property @abc.abstractmethod @@ -146,8 +139,12 @@ def _create_location_from_markers( return _SurfaceLocation_v2( surface_uid=surface.uid, number_of_markers_detected=len(matching_marker_uids), - transform_matrix_from_image_to_surface_undistorted=transform_matrix_from_image_to_surface_undistorted, - transform_matrix_from_surface_to_image_undistorted=transform_matrix_from_surface_to_image_undistorted, + transform_matrix_from_image_to_surface_undistorted=( + transform_matrix_from_image_to_surface_undistorted + ), + transform_matrix_from_surface_to_image_undistorted=( + transform_matrix_from_surface_to_image_undistorted + ), ) # ## Mapping @@ -280,8 +277,8 @@ def __map_points( # Perspective transform shape = points.shape - points.shape = (-1, 1, 2) - if custom_transformation: + points = points.reshape(-1, 1, 2) + if custom_transformation and points.shape[0] == 4: points = _perspective_transform(points, transform_matrix) else: points = cv2.perspectiveTransform(points, transform_matrix) @@ -388,7 +385,7 @@ def _find_homographies(points_A, points_B): class _SurfaceLocation_v2(SurfaceLocation): - version = 2 # type: ClassVar[int] + version = 2 # type: T.ClassVar[int] @property def surface_uid(self) -> SurfaceId: @@ -435,8 +432,12 @@ def as_dict(self) -> dict: "version": self.version, "surface_uid": str(self.surface_uid), "number_of_markers_detected": self.number_of_markers_detected, - "transform_matrix_from_image_to_surface_undistorted": self.transform_matrix_from_image_to_surface_undistorted.tolist(), - "transform_matrix_from_surface_to_image_undistorted": self.transform_matrix_from_surface_to_image_undistorted.tolist(), + "transform_matrix_from_image_to_surface_undistorted": ( + self.transform_matrix_from_image_to_surface_undistorted.tolist() + ), + "transform_matrix_from_surface_to_image_undistorted": ( + self.transform_matrix_from_surface_to_image_undistorted.tolist() + ), } @staticmethod @@ -444,9 +445,10 @@ def from_dict(value: dict) -> "SurfaceLocation": try: actual_version = value["version"] expected_version = _SurfaceLocation_v2.version - assert ( - expected_version == actual_version - ), f"SurfaceLocation version missmatch; expected {expected_version}, but got {actual_version}" + assert expected_version == actual_version, ( + f"SurfaceLocation version missmatch; expected {expected_version}, but " + f"got {actual_version}" + ) return _SurfaceLocation_v2( surface_uid=SurfaceId(value["surface_uid"]), number_of_markers_detected=int(value["number_of_markers_detected"]), diff --git a/src/surface_tracker/marker.py b/src/pupil_labs/surface_tracker/marker.py similarity index 89% rename from src/surface_tracker/marker.py rename to src/pupil_labs/surface_tracker/marker.py index 82891c8..16f86e3 100644 --- a/src/surface_tracker/marker.py +++ b/src/pupil_labs/surface_tracker/marker.py @@ -1,12 +1,3 @@ -""" -(*)~--------------------------------------------------------------------------- -Pupil - eye tracking platform -Copyright (C) 2012-2020 Pupil Labs -Distributed under the terms of the GNU -Lesser General Public License (LGPL v3.0). -See LICENSE for license details. ----------------------------------------------------------------------------~(*) -""" import abc import typing as T @@ -14,6 +5,7 @@ from .corner import CornerId MarkerId = T.NewType("MarkerId", str) +"""Type annotation for marker ids""" class Marker(abc.ABC): @@ -58,7 +50,8 @@ def from_vertices( actual_len = len(undistorted_image_space_vertices) if expected_len != actual_len: raise ValueError( - f'Expected "vertices" to have a lenght of {expected_len}, but got {actual_len}' + f'Expected "vertices" to have a lenght of {expected_len}, but got ' + f'{actual_len}' ) vertices_by_corner_id = dict(zip(corners, undistorted_image_space_vertices)) diff --git a/src/surface_tracker/orientation.py b/src/pupil_labs/surface_tracker/orientation.py similarity index 91% rename from src/surface_tracker/orientation.py rename to src/pupil_labs/surface_tracker/orientation.py index 7cf5429..04cb51e 100644 --- a/src/surface_tracker/orientation.py +++ b/src/pupil_labs/surface_tracker/orientation.py @@ -1,12 +1,3 @@ -""" -(*)~--------------------------------------------------------------------------- -Pupil - eye tracking platform -Copyright (C) 2012-2020 Pupil Labs -Distributed under the terms of the GNU -Lesser General Public License (LGPL v3.0). -See LICENSE for license details. ----------------------------------------------------------------------------~(*) -""" from .corner import CornerId diff --git a/src/surface_tracker/surface.py b/src/pupil_labs/surface_tracker/surface.py similarity index 93% rename from src/surface_tracker/surface.py rename to src/pupil_labs/surface_tracker/surface.py index d5e8e13..fb3b020 100644 --- a/src/surface_tracker/surface.py +++ b/src/pupil_labs/surface_tracker/surface.py @@ -1,12 +1,3 @@ -""" -(*)~--------------------------------------------------------------------------- -Pupil - eye tracking platform -Copyright (C) 2012-2020 Pupil Labs -Distributed under the terms of the GNU -Lesser General Public License (LGPL v3.0). -See LICENSE for license details. ----------------------------------------------------------------------------~(*) -""" import abc import collections import logging @@ -25,6 +16,7 @@ SurfaceId = T.NewType("SurfaceId", str) +"""Type annotation for surface ids""" class Surface(abc.ABC): @@ -36,18 +28,20 @@ def __init_subclass__(cls): version = cls.version if version is None: raise ValueError( - f'Surface subclass {cls.__name__} must overwrite class property "version"' + f'Surface subclass {cls.__name__} must overwrite class property ' + '"version"' ) if version in storage: raise ValueError( - f"Surface subclass {cls.__name__} defines an already registered version {version}" + f"Surface subclass {cls.__name__} defines an already registered " + f"version {version}" ) storage[version] = cls return super().__init_subclass__() # ## Abstract members - version = None # type: ClassVar[int] + version: T.ClassVar[int] = None @property @abc.abstractmethod @@ -259,8 +253,8 @@ def _bounding_quadrangle(vertices: np.ndarray): # According to OpenCV implementation, cv2.convexHull only accepts arrays with # 32bit floats (CV_32F) or 32bit signed ints (CV_32S). - # See: https://github.com/opencv/opencv/blob/3.4/modules/imgproc/src/convhull.cpp#L137 - # See: https://github.com/pupil-labs/pupil/issues/1544 + # See: https://github.com/pupil-labs/pupil/issues/1544 and + # https://github.com/opencv/opencv/blob/3.4/modules/imgproc/src/convhull.cpp#L137 vertices = np.asarray(vertices, dtype=np.float32) hull_points = cv2.convexHull(vertices, clockwise=False) @@ -320,8 +314,13 @@ def _GetAnglesPolyline(polyline, closed=False): # print 'cb:',cb # float dot = (ab.x * cb.x + ab.y * cb.y) dot product - # dot = np.dot(ab,cb.T) # this is a full matrix mulitplication we only need the diagonal \ - # dot = dot.diagonal() # because all we look for are the dotproducts of corresponding vectors (ab[n] and cb[n]) + + # this is a full matrix mulitplication we only need the diagonal \: + # dot = np.dot(ab,cb.T) + + # because all we look for are the dotproducts of corresponding vectors (ab[n] and + # cb[n]): + # dot = dot.diagonal() dot = np.sum( ab * cb, axis=1 ) # or just do the dot product of the correspoing vectors in the first place! @@ -386,9 +385,10 @@ def from_dict(value: dict) -> "Surface": try: actual_version = value["version"] expected_version = _Surface_V2.version - assert ( - expected_version == actual_version - ), f"Surface version missmatch; expected {expected_version}, but got {actual_version}" + assert expected_version == actual_version, ( + f"Surface version missmatch; expected {expected_version}, but got " + f"{actual_version}" + ) registered_markers_undistorted = value["registered_markers_undistorted"] registered_markers_undistorted = { diff --git a/src/surface_tracker/tracker.py b/src/pupil_labs/surface_tracker/tracker.py similarity index 96% rename from src/surface_tracker/tracker.py rename to src/pupil_labs/surface_tracker/tracker.py index aee7ac6..b968e2f 100644 --- a/src/surface_tracker/tracker.py +++ b/src/pupil_labs/surface_tracker/tracker.py @@ -1,14 +1,6 @@ -""" -(*)~--------------------------------------------------------------------------- -Pupil - eye tracking platform -Copyright (C) 2012-2020 Pupil Labs -Distributed under the terms of the GNU -Lesser General Public License (LGPL v3.0). -See LICENSE for license details. ----------------------------------------------------------------------------~(*) -""" import logging import typing as T +import weakref import numpy as np @@ -72,7 +64,9 @@ def surface_points_in_image_space( location: SurfaceLocation, points: T.List[T.Tuple[float, float]], ) -> T.List[T.Tuple[int, int]]: - """Transform a list of points in surface space into a list of points in image space.""" + """Transform a list of points in surface space into a list of points in image + space. + """ # Validate the surface definition and the surface location arguments self.__argument_validator.validate_surface_and_location( @@ -320,9 +314,6 @@ def locate_surface_image_crop_with_heatmap( # #### Private Helpers -import weakref - - class _SurfaceTrackerWeakLocationStore: def __init__(self): self.__storage = {} @@ -370,7 +361,8 @@ def validate_surface_and_location( if not isinstance(location, SurfaceLocation): raise ValueError( - f'Expected an instance of SurfaceLocation, but got "{location.__class__}"' + f'Expected an instance of SurfaceLocation, but got ' + f'"{location.__class__}"' ) if surface.uid != location.surface_uid: @@ -378,5 +370,6 @@ def validate_surface_and_location( if (not ignore_location_staleness) and location.is_stale: raise ValueError( - f"Stale location: the surface definition has changed; location must be recomputed" + f"Stale location: the surface definition has changed; location must be " + "recomputed" ) diff --git a/src/surface_tracker/utils.py b/src/pupil_labs/surface_tracker/utils.py similarity index 61% rename from src/surface_tracker/utils.py rename to src/pupil_labs/surface_tracker/utils.py index 79a7729..faabdce 100644 --- a/src/surface_tracker/utils.py +++ b/src/pupil_labs/surface_tracker/utils.py @@ -1,14 +1,3 @@ -""" -(*)~--------------------------------------------------------------------------- -Pupil - eye tracking platform -Copyright (C) 2012-2020 Pupil Labs -Distributed under the terms of the GNU -Lesser General Public License (LGPL v3.0). -See LICENSE for license details. ----------------------------------------------------------------------------~(*) -""" - - def left_rotation(a: list, k: int): """Rotate list to the left e.g.: [1, 2, 3, 4] -> [2, 3, 4, 1] diff --git a/src/surface_tracker/visual_anchors.py b/src/pupil_labs/surface_tracker/visual_anchors.py similarity index 89% rename from src/surface_tracker/visual_anchors.py rename to src/pupil_labs/surface_tracker/visual_anchors.py index 67bcd4f..f5c816c 100644 --- a/src/surface_tracker/visual_anchors.py +++ b/src/pupil_labs/surface_tracker/visual_anchors.py @@ -1,12 +1,3 @@ -""" -(*)~--------------------------------------------------------------------------- -Pupil - eye tracking platform -Copyright (C) 2012-2020 Pupil Labs -Distributed under the terms of the GNU -Lesser General Public License (LGPL v3.0). -See LICENSE for license details. ----------------------------------------------------------------------------~(*) -""" import typing as T import numpy as np diff --git a/src/surface_tracker/__init__.py b/src/surface_tracker/__init__.py deleted file mode 100644 index 9b7ad6a..0000000 --- a/src/surface_tracker/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -(*)~--------------------------------------------------------------------------- -Pupil - eye tracking platform -Copyright (C) 2012-2020 Pupil Labs -Distributed under the terms of the GNU -Lesser General Public License (LGPL v3.0). -See LICENSE for license details. ----------------------------------------------------------------------------~(*) -""" -from . import utils -from .camera import Camera -from .coordinate_space import CoordinateSpace -from .corner import CornerId -from .heatmap import SurfaceHeatmap -from .image_crop import SurfaceImageCrop -from .location import SurfaceLocation -from .marker import Marker, MarkerId -from .orientation import SurfaceOrientation -from .surface import Surface, SurfaceId -from .tracker import SurfaceTracker -from .visual_anchors import SurfaceVisualAnchors - -__version__ = "0.0.1" diff --git a/src/surface_tracker/coordinate_space.py b/src/surface_tracker/coordinate_space.py deleted file mode 100644 index cbb61cd..0000000 --- a/src/surface_tracker/coordinate_space.py +++ /dev/null @@ -1,17 +0,0 @@ -""" -(*)~--------------------------------------------------------------------------- -Pupil - eye tracking platform -Copyright (C) 2012-2020 Pupil Labs -Distributed under the terms of the GNU -Lesser General Public License (LGPL v3.0). -See LICENSE for license details. ----------------------------------------------------------------------------~(*) -""" -import enum - - -class CoordinateSpace(enum.Enum): - IMAGE_DISTORTED = "image-distorted" - IMAGE_UNDISTORTED = "image-undistorted" - SURFACE_DISTORTED = "surface-distorted" - SURFACE_UNDISTORTED = "surface-undisitorted" diff --git a/tests/unit/test_corner.py b/tests/unit/test_corner.py index 42060a7..223017d 100644 --- a/tests/unit/test_corner.py +++ b/tests/unit/test_corner.py @@ -1,15 +1,4 @@ -""" -(*)~--------------------------------------------------------------------------- -Pupil - eye tracking platform -Copyright (C) 2012-2020 Pupil Labs -Distributed under the terms of the GNU -Lesser General Public License (LGPL v3.0). -See LICENSE for license details. ----------------------------------------------------------------------------~(*) -""" -import pytest - -from surface_tracker.corner import CornerId +from pupil_labs.surface_tracker.corner import CornerId def test_corner_id_all_corners(): diff --git a/tests/unit/test_package.py b/tests/unit/test_package.py index 44f79d4..5b83321 100644 --- a/tests/unit/test_package.py +++ b/tests/unit/test_package.py @@ -1,14 +1,4 @@ -""" -(*)~--------------------------------------------------------------------------- -Pupil - eye tracking platform -Copyright (C) 2012-2020 Pupil Labs -Distributed under the terms of the GNU -Lesser General Public License (LGPL v3.0). -See LICENSE for license details. ----------------------------------------------------------------------------~(*) -""" -import pytest +def test_package_metadata() -> None: + import pupil_labs.surface_tracker as this_project - -def test_import(): - import surface_tracker + assert hasattr(this_project, "__version__") diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index e29e85e..7988839 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -1,15 +1,4 @@ -""" -(*)~--------------------------------------------------------------------------- -Pupil - eye tracking platform -Copyright (C) 2012-2020 Pupil Labs -Distributed under the terms of the GNU -Lesser General Public License (LGPL v3.0). -See LICENSE for license details. ----------------------------------------------------------------------------~(*) -""" -import pytest - -from surface_tracker.utils import left_rotation, right_rotation +from pupil_labs.surface_tracker.utils import left_rotation, right_rotation def test_rotation(): diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..5bf4e06 --- /dev/null +++ b/tox.ini @@ -0,0 +1,39 @@ +[tox] +envlist = python +minversion = 3.2 +# https://github.com/jaraco/skeleton/issues/6 +tox_pip_extensions_ext_venv_update = true +toxworkdir={env:TOX_WORK_DIR:.tox} + + +[testenv] +deps = pytest +commands = + pytest tests {posargs} +usedevelop = True +extras = testing + +[testenv:docs] +extras = + docs + testing +changedir = docs +commands = + python -m sphinx -W --keep-going . {toxinidir}/build/html + +[testenv:release] +skip_install = True +deps = + build + twine>=3 + jaraco.develop>=7.1 +passenv = + TWINE_PASSWORD + GITHUB_TOKEN +setenv = + TWINE_USERNAME = {env:TWINE_USERNAME:__token__} +commands = + python -c "import shutil; shutil.rmtree('dist', ignore_errors=True)" + python -m build + python -m twine upload dist/* + python -m jaraco.develop.create-github-release diff --git a/update_license_header.py b/update_license_header.py deleted file mode 100644 index db18482..0000000 --- a/update_license_header.py +++ /dev/null @@ -1,99 +0,0 @@ -""" -(*)~--------------------------------------------------------------------------- -Pupil - eye tracking platform -Copyright (C) 2012-2020 Pupil Labs -Distributed under the terms of the GNU -Lesser General Public License (LGPL v3.0). -See LICENSE for license details. ----------------------------------------------------------------------------~(*) -""" -import fnmatch -import os -import re - -license_txt = """\ -(*)~--------------------------------------------------------------------------- -Pupil - eye tracking platform -Copyright (C) 2012-2020 Pupil Labs -Distributed under the terms of the GNU -Lesser General Public License (LGPL v3.0). -See LICENSE for license details. ----------------------------------------------------------------------------~(*)\ -""" - - -pattern = re.compile( - "(\"{3}|'{3}|[/][*])\n\\([*]\\)~(.+?)~\\([*]\\)\n(\"{3}|'{3}|[*][/])", - re.DOTALL | re.MULTILINE, -) - -# choose files types to include -# choose directories to exclude from search -includes = ["*.py", "*.pxd", "*.pyx", "*.pxi"] -excludes = [ - ".gitignore", -] - -# transform glob patterns to regular expressions -includes = r"|".join([fnmatch.translate(x) for x in includes]) -excludes = r"|".join([fnmatch.translate(x) for x in excludes]) or r"$." - - -def get_files(start_dir, includes, excludes): - # use os.walk to recursively dig down into the Pupil directory - match_files = [] - for root, dirs, files in os.walk(start_dir): - if not re.search(excludes, root): - files = [ - f - for f in files - if re.search(includes, f) and not re.search(excludes, f) - ] - files = [os.path.join(root, f) for f in files] - match_files += files - else: - print("Excluding '%s'" % root) - - return match_files - - -def write_header(file_name, license_txt): - # find and replace license header - # or add new header if not existing - c_comment = ["/*\n", "\n*/\n"] - py_comment = ['"""\n', '\n"""\n'] - file_type = os.path.splitext(file_name)[-1] - - if file_type in (".py", ".pxd", ".pyx", ".pxi"): - license_txt = py_comment[0] + license_txt + py_comment[1] - elif file_type in (".c", ".cpp", ".hpp", ".h"): - license_txt = c_comment[0] + license_txt + c_comment[1] - else: - raise Exception("Dont know how to deal with this filetype") - - with open(file_name) as original: - data = original.read() - - with open(file_name, "w") as modified: - if re.findall(pattern, data): - # if header already exists, then update, but dont add the last newline. - modified.write(re.sub(pattern, license_txt[:-1], data)) - modified.close() - else: - # else write the license header - modified.write(license_txt + data) - modified.close() - - -if __name__ == "__main__": - - # find out the cwd and change to the top level Pupil folder - cwd = os.getcwd() - pupil_dir = cwd - - # Add a license/docstring header to selected files - match_files = get_files(pupil_dir, includes, excludes) - print(len(match_files)) - - for f in match_files: - write_header(f, license_txt)