From 404249c03aab3c9098ec9283b9ec94f18b02b9b6 Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Sat, 5 Dec 2020 14:04:14 +1300 Subject: [PATCH 01/51] Remove "Python" 2 support. --- .github/workflows/continuous-integration.yml | 19 +-------- TODO.rst | 2 +- colour_demosaicing/__init__.py | 2 - colour_demosaicing/bayer/__init__.py | 2 - .../bayer/demosaicing/__init__.py | 2 - .../bayer/demosaicing/bilinear.py | 2 - .../bayer/demosaicing/malvar2004.py | 2 - .../bayer/demosaicing/menon2007.py | 2 - .../bayer/demosaicing/tests/test_bilinear.py | 2 - .../demosaicing/tests/test_malvar2004.py | 2 - .../bayer/demosaicing/tests/test_menon2007.py | 2 - colour_demosaicing/bayer/masks.py | 2 - colour_demosaicing/bayer/mosaicing.py | 2 - colour_demosaicing/bayer/tests/test_masks.py | 2 - .../bayer/tests/test_mosaicing.py | 2 - pyproject.toml | 9 +---- requirements.txt | 13 +++--- setup.py | 5 +-- tasks.py | 40 ++++++++----------- utilities/export_todo.py | 2 - utilities/unicode_to_ascii.py | 12 ++---- 21 files changed, 33 insertions(+), 95 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 88cca6b..bc7d8c0 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: os: [macOS-latest, ubuntu-18.04, windows-latest] - python-version: [2.7, 3.6, 3.7, 3.8] + python-version: [3.6, 3.7, 3.8] fail-fast: false runs-on: ${{ matrix.os }} steps: @@ -38,22 +38,6 @@ jobs: python get-poetry.py --version 1.0.10 echo "$HOME/.poetry/bin" >> $GITHUB_PATH shell: bash - - name: Update pyproject.toml file (Python 2.7) - if: matrix.python-version == '2.7' - run: | - sed -i.bak 's/python = "~2.7 || ^3.6"/python = "~2.7"/g' pyproject.toml - sed -i.bak '/colour-science = "\^0\.3\.16"/ a qtconsole = "4.7.7"' pyproject.toml - shell: bash - - name: Update pyproject.toml file (Python 3.x) - if: matrix.python-version != '2.7' - run: | - sed -i.bak 's/python = "~2.7 || ^3.6"/python = "^3.6"/g' pyproject.toml - shell: bash - - name: Update pyproject.toml file (Windows, Python 2.7) - if: matrix.os == 'windows-latest' && matrix.python-version == '2.7' - run: | - sed -i.bak '/colour-science = "\^0\.3\.16"/ a pywin32 = "228"' pyproject.toml - shell: bash - name: Install Package Dependencies run: | poetry install @@ -68,7 +52,6 @@ jobs: poetry run python -W ignore -m nose -q -v --with-doctest --doctest-options=+ELLIPSIS --with-coverage --cover-package=$CI_PACKAGE $CI_PACKAGE shell: bash - name: Upload Coverage to coveralls.io - if: matrix.python-version == '3.6' || matrix.python-version == '3.7' run: | if [ -z "$COVERALLS_REPO_TOKEN" ]; then echo \"COVERALLS_REPO_TOKEN\" secret is undefined!; else poetry run coveralls; fi shell: bash diff --git a/TODO.rst b/TODO.rst index 5f5ab17..385210f 100644 --- a/TODO.rst +++ b/TODO.rst @@ -6,7 +6,7 @@ TODO - colour_demosaicing/__init__.py - - Line 66 : # TODO: Remove legacy printing support when deemed appropriate. + - Line 64 : # TODO: Remove legacy printing support when deemed appropriate. About ----- diff --git a/colour_demosaicing/__init__.py b/colour_demosaicing/__init__.py index 01fb626..4a96115 100644 --- a/colour_demosaicing/__init__.py +++ b/colour_demosaicing/__init__.py @@ -10,8 +10,6 @@ - bayer: *Bayer* CFA mosaicing and demosaicing computations. """ -from __future__ import absolute_import - import numpy as np import os import subprocess # nosec diff --git a/colour_demosaicing/bayer/__init__.py b/colour_demosaicing/bayer/__init__.py index 44e5382..90b56af 100644 --- a/colour_demosaicing/bayer/__init__.py +++ b/colour_demosaicing/bayer/__init__.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import absolute_import - from .masks import masks_CFA_Bayer from .mosaicing import mosaicing_CFA_Bayer from .demosaicing import * # noqa diff --git a/colour_demosaicing/bayer/demosaicing/__init__.py b/colour_demosaicing/bayer/demosaicing/__init__.py index 528854e..31798ce 100644 --- a/colour_demosaicing/bayer/demosaicing/__init__.py +++ b/colour_demosaicing/bayer/demosaicing/__init__.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import absolute_import - from .bilinear import demosaicing_CFA_Bayer_bilinear from .malvar2004 import demosaicing_CFA_Bayer_Malvar2004 from .menon2007 import (demosaicing_CFA_Bayer_DDFAPD, diff --git a/colour_demosaicing/bayer/demosaicing/bilinear.py b/colour_demosaicing/bayer/demosaicing/bilinear.py index de37226..c2ba6f4 100644 --- a/colour_demosaicing/bayer/demosaicing/bilinear.py +++ b/colour_demosaicing/bayer/demosaicing/bilinear.py @@ -12,8 +12,6 @@ Electron Physics (Vol. 162, pp. 173-265). doi:10.1016/S1076-5670(10)62005-8 """ -from __future__ import division, unicode_literals - from scipy.ndimage.filters import convolve from colour.utilities import as_float_array, tstack diff --git a/colour_demosaicing/bayer/demosaicing/malvar2004.py b/colour_demosaicing/bayer/demosaicing/malvar2004.py index 1a924ba..411fac0 100644 --- a/colour_demosaicing/bayer/demosaicing/malvar2004.py +++ b/colour_demosaicing/bayer/demosaicing/malvar2004.py @@ -14,8 +14,6 @@ http://research.microsoft.com/apps/pubs/default.aspx?id=102068 """ -from __future__ import division, unicode_literals - import numpy as np from scipy.ndimage.filters import convolve diff --git a/colour_demosaicing/bayer/demosaicing/menon2007.py b/colour_demosaicing/bayer/demosaicing/menon2007.py index 4146928..2d0ed9c 100644 --- a/colour_demosaicing/bayer/demosaicing/menon2007.py +++ b/colour_demosaicing/bayer/demosaicing/menon2007.py @@ -13,8 +13,6 @@ doi:10.1109/TIP.2006.884928 """ -from __future__ import division, unicode_literals - import numpy as np from scipy.ndimage.filters import convolve, convolve1d diff --git a/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py b/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py index f9b55ad..79dcba7 100644 --- a/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py +++ b/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py @@ -5,8 +5,6 @@ module. """ -from __future__ import division, unicode_literals - import numpy as np import os import unittest diff --git a/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py b/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py index b8356b3..dda5aa2 100644 --- a/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py +++ b/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py @@ -5,8 +5,6 @@ module. """ -from __future__ import division, unicode_literals - import numpy as np import os import unittest diff --git a/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py b/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py index 7c73935..83757ee 100644 --- a/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py +++ b/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py @@ -5,8 +5,6 @@ module. """ -from __future__ import division, unicode_literals - import numpy as np import os import unittest diff --git a/colour_demosaicing/bayer/masks.py b/colour_demosaicing/bayer/masks.py index 9148877..7415f2b 100644 --- a/colour_demosaicing/bayer/masks.py +++ b/colour_demosaicing/bayer/masks.py @@ -6,8 +6,6 @@ *Bayer* CFA (Colour Filter Array) masks generation. """ -from __future__ import division, unicode_literals - import numpy as np __author__ = 'Colour Developers' diff --git a/colour_demosaicing/bayer/mosaicing.py b/colour_demosaicing/bayer/mosaicing.py index 9e0cee5..91b790a 100644 --- a/colour_demosaicing/bayer/mosaicing.py +++ b/colour_demosaicing/bayer/mosaicing.py @@ -6,8 +6,6 @@ *Bayer* CFA (Colour Filter Array) data generation. """ -from __future__ import division, unicode_literals - from colour.utilities import as_float_array, tsplit from colour_demosaicing.bayer import masks_CFA_Bayer diff --git a/colour_demosaicing/bayer/tests/test_masks.py b/colour_demosaicing/bayer/tests/test_masks.py index 33836d8..46efe67 100644 --- a/colour_demosaicing/bayer/tests/test_masks.py +++ b/colour_demosaicing/bayer/tests/test_masks.py @@ -4,8 +4,6 @@ Defines unit tests for :mod:`colour_demosaicing.bayer.masks` module. """ -from __future__ import division, unicode_literals - import numpy as np import os import unittest diff --git a/colour_demosaicing/bayer/tests/test_mosaicing.py b/colour_demosaicing/bayer/tests/test_mosaicing.py index 7b83899..20c580c 100644 --- a/colour_demosaicing/bayer/tests/test_mosaicing.py +++ b/colour_demosaicing/bayer/tests/test_mosaicing.py @@ -4,8 +4,6 @@ Defines unit tests for :mod:`colour_demosaicing.bayer.mosaicing` module. """ -from __future__ import division, unicode_literals - import numpy as np import os import unittest diff --git a/pyproject.toml b/pyproject.toml index cc3a568..aa6930f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,7 +38,6 @@ classifiers = [ "License :: OSI Approved", "Natural Language :: English", "Operating System :: OS Independent", - "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Topic :: Scientific/Engineering", "Topic :: Software Development" @@ -49,10 +48,9 @@ exclude = [ ] [tool.poetry.dependencies] -python = "~2.7 || ^3.6" +python = "^3.6" colour-science = "^0.3.16" -"backports.functools_lru_cache" = { version = "*", optional = true } biblib-simple = { version = "*", optional = true } # Development dependency. coverage = { version = "*", optional = true } # Development dependency. coveralls = { version = "*", optional = true } # Development dependency. @@ -61,7 +59,6 @@ invoke = { version = "*", optional = true } # Development dependency. jupyter = { version = "*", optional = true } # Development dependency. matplotlib = { version = "*", optional = true } mock = { version = "*", optional = true } # Development dependency. -nbformat = { version = "^4", optional = true } # Fixed development dependency for Python 2.7 support only. nose = { version = "*", optional = true } # Development dependency. numpy = { version = "*", optional = true } pre-commit = { version = "*", optional = true } # Development dependency. @@ -82,7 +79,6 @@ flake8 = "*" invoke = "*" jupyter = "*" mock = "*" -nbformat = "^4" nose = "*" pre-commit = "*" pytest = "*" @@ -103,7 +99,6 @@ development = [ "invoke", "jupyter", "mock", - "nbformat", "nose", "pre-commit", "pytest", @@ -115,7 +110,7 @@ development = [ "twine", "yapf" ] -plotting = [ "backports.functools_lru_cache", "matplotlib" ] +plotting = [ "matplotlib" ] read-the-docs = [ "mock", "numpy", "sphinxcontrib-bibtex" ] [build-system] diff --git a/requirements.txt b/requirements.txt index 12844bc..0ca32d3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,11 +1,11 @@ alabaster==0.7.12 appdirs==1.4.4 -appnope==0.1.0 +appnope==0.1.2 argon2-cffi==20.1.0 +async-generator==1.10 attrs==20.3.0 Babel==2.9.0 backcall==0.2.0 -backports.functools-lru-cache==1.6.1 biblib-simple==0.1.1 bleach==3.2.1 certifi==2020.11.8 @@ -42,6 +42,7 @@ jupyter==1.0.0 jupyter-client==6.1.7 jupyter-console==6.2.0 jupyter-core==4.7.0 +jupyterlab-pygments==0.1.2 keyring==21.5.0 kiwisolver==1.3.1 latexcodec==2.0.1 @@ -50,14 +51,16 @@ matplotlib==3.3.3 mccabe==0.6.1 mistune==0.8.4 mock==4.0.2 -nbconvert==5.6.1 -nbformat==4.4.0 +nbclient==0.5.1 +nbconvert==6.0.7 +nbformat==5.0.8 +nest-asyncio==1.4.3 nodeenv==1.5.0 nose==1.3.7 notebook==6.1.5 numpy==1.19.4 oset==0.1.3 -packaging==20.4 +packaging==20.7 pandocfilters==1.4.3 parso==0.7.1 pexpect==4.8.0 diff --git a/setup.py b/setup.py index ce1df34..c6bb811 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,6 @@ 'invoke', 'jupyter', 'mock', - 'nbformat>=4,<5', 'nose', 'pre-commit', 'pytest', @@ -37,7 +36,7 @@ 'toml', 'twine', 'yapf==0.23'], - 'plotting': ['backports.functools_lru_cache', 'matplotlib'], + 'plotting': ['matplotlib'], 'read-the-docs': ['mock', 'numpy', 'sphinxcontrib-bibtex']} setup( @@ -54,5 +53,5 @@ package_data=package_data, install_requires=install_requires, extras_require=extras_require, - python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*', + python_requires='>=3.6,<4.0', ) diff --git a/tasks.py b/tasks.py index 604495b..7faa7cc 100644 --- a/tasks.py +++ b/tasks.py @@ -4,13 +4,7 @@ ============== """ -from __future__ import unicode_literals - -import sys -try: - import biblib.bib -except ImportError: - pass +import biblib.bib import fnmatch import os import re @@ -111,25 +105,23 @@ def formatting(ctx, yapf=True, asciify=True, bibtex=True): with ctx.cd('utilities'): ctx.run('./unicode_to_ascii.py') - if bibtex and sys.version_info[:2] >= (3, 2): - message_box('Cleaning up "BibTeX" file...') - bibtex_path = BIBLIOGRAPHY_NAME - with open(bibtex_path) as bibtex_file: - bibtex = biblib.bib.Parser().parse( - bibtex_file.read()).get_entries() + message_box('Cleaning up "BibTeX" file...') + bibtex_path = BIBLIOGRAPHY_NAME + with open(bibtex_path) as bibtex_file: + bibtex = biblib.bib.Parser().parse(bibtex_file.read()).get_entries() + + for entry in sorted(bibtex.values(), key=lambda x: x.key): + try: + del entry['file'] + except KeyError: + pass + for key, value in entry.items(): + entry[key] = re.sub('(? Date: Sun, 6 Dec 2020 17:37:20 +1300 Subject: [PATCH 02/51] Add "funding.yml" file. --- .github/funding.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .github/funding.yml diff --git a/.github/funding.yml b/.github/funding.yml new file mode 100644 index 0000000..2614032 --- /dev/null +++ b/.github/funding.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: # Replace with a single Patreon username +open_collective: colour-science # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] From c2224531566b7d862e18110818bf3e9b50bcd03c Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Fri, 1 Jan 2021 14:31:46 +1300 Subject: [PATCH 03/51] Update copyright statements. --- CONTRIBUTORS.rst | 2 +- LICENSE | 2 +- README.rst | 2 +- TODO.rst | 2 +- colour_demosaicing/__init__.py | 2 +- colour_demosaicing/bayer/demosaicing/bilinear.py | 2 +- colour_demosaicing/bayer/demosaicing/malvar2004.py | 2 +- colour_demosaicing/bayer/demosaicing/menon2007.py | 2 +- colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py | 2 +- colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py | 2 +- colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py | 2 +- colour_demosaicing/bayer/masks.py | 2 +- colour_demosaicing/bayer/mosaicing.py | 2 +- colour_demosaicing/bayer/tests/test_masks.py | 2 +- colour_demosaicing/bayer/tests/test_mosaicing.py | 2 +- docs/index.rst | 2 +- tasks.py | 2 +- utilities/export_todo.py | 4 ++-- utilities/unicode_to_ascii.py | 2 +- 19 files changed, 20 insertions(+), 20 deletions(-) diff --git a/CONTRIBUTORS.rst b/CONTRIBUTORS.rst index e239d5f..d0a6a1c 100644 --- a/CONTRIBUTORS.rst +++ b/CONTRIBUTORS.rst @@ -30,6 +30,6 @@ About ----- | **Colour - Demosaicing** by Colour Developers -| Copyright © 2015-2020 – Colour Developers – `colour-developers@colour-science.org `__ +| Copyright © 2015-2021 – Colour Developers – `colour-developers@colour-science.org `__ | This software is released under terms of New BSD License: https://opensource.org/licenses/BSD-3-Clause | `https://github.com/colour-science/colour-demosaicing `__ diff --git a/LICENSE b/LICENSE index 09d34c5..3a083c8 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2015-2020, Colour Developers +Copyright (c) 2015-2021, Colour Developers All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/README.rst b/README.rst index 00c3297..e18a182 100644 --- a/README.rst +++ b/README.rst @@ -114,6 +114,6 @@ About ----- | **Colour - Demosaicing** by Colour Developers -| Copyright © 2015-2020 – Colour Developers – `colour-developers@colour-science.org `__ +| Copyright © 2015-2021 – Colour Developers – `colour-developers@colour-science.org `__ | This software is released under terms of New BSD License: https://opensource.org/licenses/BSD-3-Clause | `https://github.com/colour-science/colour-demosaicing `__ diff --git a/TODO.rst b/TODO.rst index 385210f..b27fcab 100644 --- a/TODO.rst +++ b/TODO.rst @@ -12,6 +12,6 @@ About ----- | **Colour - Demosaicing** by Colour Developers -| Copyright © 2015-2020 – Colour Developers – `colour-developers@colour-science.org `__ +| Copyright © 2015-2021 – Colour Developers – `colour-developers@colour-science.org `__ | This software is released under terms of New BSD License: https://opensource.org/licenses/BSD-3-Clause | `https://github.com/colour-science/colour-demosaicing `__ diff --git a/colour_demosaicing/__init__.py b/colour_demosaicing/__init__.py index 4a96115..20e2aea 100644 --- a/colour_demosaicing/__init__.py +++ b/colour_demosaicing/__init__.py @@ -22,7 +22,7 @@ masks_CFA_Bayer, mosaicing_CFA_Bayer) __author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2015-2020 - Colour Developers' +__copyright__ = 'Copyright (C) 2015-2021 - Colour Developers' __license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' __maintainer__ = 'Colour Developers' __email__ = 'colour-developers@colour-science.org' diff --git a/colour_demosaicing/bayer/demosaicing/bilinear.py b/colour_demosaicing/bayer/demosaicing/bilinear.py index c2ba6f4..ea024fe 100644 --- a/colour_demosaicing/bayer/demosaicing/bilinear.py +++ b/colour_demosaicing/bayer/demosaicing/bilinear.py @@ -19,7 +19,7 @@ from colour_demosaicing.bayer import masks_CFA_Bayer __author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2015-2020 - Colour Developers' +__copyright__ = 'Copyright (C) 2015-2021 - Colour Developers' __license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' __maintainer__ = 'Colour Developers' __email__ = 'colour-developers@colour-science.org' diff --git a/colour_demosaicing/bayer/demosaicing/malvar2004.py b/colour_demosaicing/bayer/demosaicing/malvar2004.py index 411fac0..c9c8ec0 100644 --- a/colour_demosaicing/bayer/demosaicing/malvar2004.py +++ b/colour_demosaicing/bayer/demosaicing/malvar2004.py @@ -22,7 +22,7 @@ from colour_demosaicing.bayer import masks_CFA_Bayer __author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2015-2020 - Colour Developers' +__copyright__ = 'Copyright (C) 2015-2021 - Colour Developers' __license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' __maintainer__ = 'Colour Developers' __email__ = 'colour-developers@colour-science.org' diff --git a/colour_demosaicing/bayer/demosaicing/menon2007.py b/colour_demosaicing/bayer/demosaicing/menon2007.py index 2d0ed9c..53620a5 100644 --- a/colour_demosaicing/bayer/demosaicing/menon2007.py +++ b/colour_demosaicing/bayer/demosaicing/menon2007.py @@ -21,7 +21,7 @@ from colour_demosaicing.bayer import masks_CFA_Bayer __author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2015-2020 - Colour Developers' +__copyright__ = 'Copyright (C) 2015-2021 - Colour Developers' __license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' __maintainer__ = 'Colour Developers' __email__ = 'colour-developers@colour-science.org' diff --git a/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py b/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py index 79dcba7..867cf12 100644 --- a/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py +++ b/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py @@ -15,7 +15,7 @@ from colour_demosaicing.bayer import demosaicing_CFA_Bayer_bilinear __author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2015-2020 - Colour Developers' +__copyright__ = 'Copyright (C) 2015-2021 - Colour Developers' __license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' __maintainer__ = 'Colour Developers' __email__ = 'colour-developers@colour-science.org' diff --git a/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py b/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py index dda5aa2..dbb440d 100644 --- a/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py +++ b/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py @@ -15,7 +15,7 @@ from colour_demosaicing.bayer import demosaicing_CFA_Bayer_Malvar2004 __author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2015-2020 - Colour Developers' +__copyright__ = 'Copyright (C) 2015-2021 - Colour Developers' __license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' __maintainer__ = 'Colour Developers' __email__ = 'colour-developers@colour-science.org' diff --git a/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py b/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py index 83757ee..26c4958 100644 --- a/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py +++ b/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py @@ -15,7 +15,7 @@ from colour_demosaicing.bayer import demosaicing_CFA_Bayer_Menon2007 __author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2015-2020 - Colour Developers' +__copyright__ = 'Copyright (C) 2015-2021 - Colour Developers' __license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' __maintainer__ = 'Colour Developers' __email__ = 'colour-developers@colour-science.org' diff --git a/colour_demosaicing/bayer/masks.py b/colour_demosaicing/bayer/masks.py index 7415f2b..9985c64 100644 --- a/colour_demosaicing/bayer/masks.py +++ b/colour_demosaicing/bayer/masks.py @@ -9,7 +9,7 @@ import numpy as np __author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2015-2020 - Colour Developers' +__copyright__ = 'Copyright (C) 2015-2021 - Colour Developers' __license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' __maintainer__ = 'Colour Developers' __email__ = 'colour-developers@colour-science.org' diff --git a/colour_demosaicing/bayer/mosaicing.py b/colour_demosaicing/bayer/mosaicing.py index 91b790a..59b7881 100644 --- a/colour_demosaicing/bayer/mosaicing.py +++ b/colour_demosaicing/bayer/mosaicing.py @@ -11,7 +11,7 @@ from colour_demosaicing.bayer import masks_CFA_Bayer __author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2015-2020 - Colour Developers' +__copyright__ = 'Copyright (C) 2015-2021 - Colour Developers' __license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' __maintainer__ = 'Colour Developers' __email__ = 'colour-developers@colour-science.org' diff --git a/colour_demosaicing/bayer/tests/test_masks.py b/colour_demosaicing/bayer/tests/test_masks.py index 46efe67..7ffda03 100644 --- a/colour_demosaicing/bayer/tests/test_masks.py +++ b/colour_demosaicing/bayer/tests/test_masks.py @@ -15,7 +15,7 @@ from colour_demosaicing.bayer import masks_CFA_Bayer __author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2015-2020 - Colour Developers' +__copyright__ = 'Copyright (C) 2015-2021 - Colour Developers' __license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' __maintainer__ = 'Colour Developers' __email__ = 'colour-developers@colour-science.org' diff --git a/colour_demosaicing/bayer/tests/test_mosaicing.py b/colour_demosaicing/bayer/tests/test_mosaicing.py index 20c580c..a983446 100644 --- a/colour_demosaicing/bayer/tests/test_mosaicing.py +++ b/colour_demosaicing/bayer/tests/test_mosaicing.py @@ -14,7 +14,7 @@ from colour_demosaicing.bayer import mosaicing_CFA_Bayer __author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2015-2020 - Colour Developers' +__copyright__ = 'Copyright (C) 2015-2021 - Colour Developers' __license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' __maintainer__ = 'Colour Developers' __email__ = 'colour-developers@colour-science.org' diff --git a/docs/index.rst b/docs/index.rst index ab48907..41bd92e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -109,6 +109,6 @@ About ----- | **Colour - Demosaicing** by Colour Developers -| Copyright © 2015-2020 – Colour Developers – `colour-developers@colour-science.org `__ +| Copyright © 2015-2021 – Colour Developers – `colour-developers@colour-science.org `__ | This software is released under terms of New BSD License: https://opensource.org/licenses/BSD-3-Clause | `https://github.com/colour-science/colour-demosaicing `__ diff --git a/tasks.py b/tasks.py index 7faa7cc..9abde4a 100644 --- a/tasks.py +++ b/tasks.py @@ -15,7 +15,7 @@ from colour.utilities import message_box __author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2015-2020 - Colour Developers' +__copyright__ = 'Copyright (C) 2015-2021 - Colour Developers' __license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' __maintainer__ = 'Colour Developers' __email__ = 'colour-developers@colour-science.org' diff --git a/utilities/export_todo.py b/utilities/export_todo.py index b951112..5635ad0 100755 --- a/utilities/export_todo.py +++ b/utilities/export_todo.py @@ -9,7 +9,7 @@ import os from collections import OrderedDict -__copyright__ = 'Copyright (C) 2015-2020 - Colour Developers' +__copyright__ = 'Copyright (C) 2015-2021 - Colour Developers' __license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' __maintainer__ = 'Colour Developers' __email__ = 'colour-developers@colour-science.org' @@ -30,7 +30,7 @@ ----- | **Colour - Demosaicing** by Colour Developers -| Copyright © 2015-2020 – Colour Developers – \ +| Copyright © 2015-2021 – Colour Developers – \ `colour-developers@colour-science.org `__ | This software is released under terms of New BSD License: \ https://opensource.org/licenses/BSD-3-Clause diff --git a/utilities/unicode_to_ascii.py b/utilities/unicode_to_ascii.py index 3a63a7e..f59b410 100755 --- a/utilities/unicode_to_ascii.py +++ b/utilities/unicode_to_ascii.py @@ -9,7 +9,7 @@ import os import unicodedata -__copyright__ = 'Copyright (C) 2015-2020 - Colour Developers' +__copyright__ = 'Copyright (C) 2015-2021 - Colour Developers' __license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' __maintainer__ = 'Colour Developers' __email__ = 'colour-developers@colour-science.org' From 1528fb2fae6f676d204de26ee6086b5644a6ce18 Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Wed, 13 Jan 2021 19:34:25 +1300 Subject: [PATCH 04/51] Update "pyproject.toml" file. --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index aa6930f..6ba3ad2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -66,7 +66,7 @@ pytest = { version = "*", optional = true } # Development dependency. restructuredtext-lint = { version = "*", optional = true } # Development dependency. sphinx = { version = "<=3.1.2", optional = true } # Development dependency. sphinx_rtd_theme = { version = "*", optional = true } # Development dependency. -sphinxcontrib-bibtex = { version = "*", optional = true } # Development dependency. +sphinxcontrib-bibtex = { version = "<2.0.0", optional = true } # Development dependency. toml = { version = "*", optional = true } # Development dependency. twine = { version = "*", optional = true } # Development dependency. yapf = { version = "0.23", optional = true } # Development dependency. @@ -85,7 +85,7 @@ pytest = "*" restructuredtext-lint = "*" sphinx = "<=3.1.2" sphinx_rtd_theme = "*" -sphinxcontrib-bibtex = "*" +sphinxcontrib-bibtex = "<2.0.0" toml = "*" twine = "*" yapf = "0.23" From 097c97a9e7f913b7e05577e6a2f447c771f242cd Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Wed, 13 Jan 2021 19:35:24 +1300 Subject: [PATCH 05/51] Update "requirements.txt" file. --- requirements.txt | 57 ++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/requirements.txt b/requirements.txt index 0ca32d3..c410a0a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,14 +8,14 @@ Babel==2.9.0 backcall==0.2.0 biblib-simple==0.1.1 bleach==3.2.1 -certifi==2020.11.8 +certifi==2020.12.5 cffi==1.14.4 cfgv==3.0.0 -chardet==3.0.4 +chardet==4.0.0 colorama==0.4.4 colour-science==0.3.16 -coverage==5.3 -coveralls==2.2.0 +coverage==5.3.1 +coveralls==3.0.0 cycler==0.10.0 decorator==4.4.2 defusedxml==0.6.0 @@ -25,71 +25,72 @@ docutils==0.16 entrypoints==0.3 filelock==3.0.12 flake8==3.8.4 -identify==1.5.10 +identify==1.5.12 idna==2.10 imageio==2.9.0 imagesize==1.2.0 iniconfig==1.1.1 -invoke==1.4.1 -ipykernel==5.3.4 +invoke==1.5.0 +ipykernel==5.4.3 ipython==7.16.1 ipython-genutils==0.2.0 -ipywidgets==7.5.1 -jedi==0.17.2 +ipywidgets==7.6.3 +jedi==0.18.0 Jinja2==2.11.2 jsonschema==3.2.0 jupyter==1.0.0 -jupyter-client==6.1.7 +jupyter-client==6.1.11 jupyter-console==6.2.0 jupyter-core==4.7.0 jupyterlab-pygments==0.1.2 -keyring==21.5.0 +jupyterlab-widgets==1.0.0 +keyring==21.8.0 kiwisolver==1.3.1 latexcodec==2.0.1 MarkupSafe==1.1.1 matplotlib==3.3.3 mccabe==0.6.1 mistune==0.8.4 -mock==4.0.2 +mock==4.0.3 nbclient==0.5.1 nbconvert==6.0.7 nbformat==5.0.8 nest-asyncio==1.4.3 nodeenv==1.5.0 nose==1.3.7 -notebook==6.1.5 -numpy==1.19.4 +notebook==6.1.6 +numpy==1.19.5 oset==0.1.3 -packaging==20.7 +packaging==20.8 pandocfilters==1.4.3 -parso==0.7.1 +parso==0.8.1 pexpect==4.8.0 pickleshare==0.7.5 -Pillow==8.0.1 +Pillow==8.1.0 pkginfo==1.6.1 pluggy==0.13.1 pre-commit==2.1.1 prometheus-client==0.9.0 prompt-toolkit==3.0.3 -ptyprocess==0.6.0 -py==1.9.0 +ptyprocess==0.7.0 +py==1.10.0 pybtex==0.23.0 pybtex-docutils==0.2.2 pycodestyle==2.6.0 pycparser==2.20 pyflakes==2.2.0 -Pygments==2.7.2 +Pygments==2.7.4 pyparsing==2.4.7 pyrsistent==0.17.3 -pytest==6.1.2 +pytest==6.2.1 python-dateutil==2.8.1 -pytz==2020.4 +pytz==2020.5 PyYAML==5.3.1 pyzmq==20.0.0 qtconsole==5.0.1 QtPy==1.9.0 readme-renderer==28.0 -requests==2.25.0 +requests==2.25.1 requests-toolbelt==0.9.1 restructuredtext-lint==1.3.2 rfc3986==1.4.0 @@ -98,7 +99,7 @@ Send2Trash==1.5.0 six==1.15.0 snowballstemmer==2.0.0 Sphinx==3.1.2 -sphinx-rtd-theme==0.5.0 +sphinx-rtd-theme==0.5.1 sphinxcontrib-applehelp==1.0.2 sphinxcontrib-bibtex==1.0.0 sphinxcontrib-devhelp==1.0.2 @@ -106,15 +107,15 @@ sphinxcontrib-htmlhelp==1.0.3 sphinxcontrib-jsmath==1.0.1 sphinxcontrib-qthelp==1.0.3 sphinxcontrib-serializinghtml==1.1.4 -terminado==0.9.1 +terminado==0.9.2 testpath==0.4.4 toml==0.10.2 tornado==6.1 -tqdm==4.54.0 +tqdm==4.56.0 traitlets==4.3.3 -twine==3.2.0 +twine==3.3.0 urllib3==1.26.2 -virtualenv==20.2.1 +virtualenv==20.3.0 wcwidth==0.2.5 webencodings==0.5.1 widgetsnbextension==3.5.1 From c6f1fb5275d8ee1f134320e71a38e16ce6ad4b9e Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Sun, 17 Jan 2021 13:09:28 +1300 Subject: [PATCH 06/51] Update "README.rst" file. --- README.rst | 12 ++++++++++++ docs/index.rst | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/README.rst b/README.rst index e18a182..83361b2 100644 --- a/README.rst +++ b/README.rst @@ -110,6 +110,18 @@ Code of Conduct The *Code of Conduct*, adapted from the `Contributor Covenant 1.4 `__, is available on the `Code of Conduct `__ page. +Contact & Social +---------------- + +The *Colour Developers* can be reached via different means: + +- `Email `__ +- `Discourse `__ +- `Facebook `__ +- `Github Discussions `__ +- `Gitter `__ +- `Twitter `__ + About ----- diff --git a/docs/index.rst b/docs/index.rst index 41bd92e..2f5b264 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -105,6 +105,18 @@ Code of Conduct The *Code of Conduct*, adapted from the `Contributor Covenant 1.4 `__, is available on the `Code of Conduct `__ page. +Contact & Social +---------------- + +The *Colour Developers* can be reached via different means: + +- `Email `__ +- `Discourse `__ +- `Facebook `__ +- `Github Discussions `__ +- `Gitter `__ +- `Twitter `__ + About ----- From 257b87f40360819a3fea6f7b785c8bcfb7354762 Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Sun, 14 Feb 2021 12:45:23 +1300 Subject: [PATCH 07/51] Add "--traverse-namespace" to "nose" invocation. --- .github/workflows/continuous-integration.yml | 2 +- tasks.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index bc7d8c0..3a90f4f 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -49,7 +49,7 @@ jobs: shell: bash - name: Test with nosetests run: | - poetry run python -W ignore -m nose -q -v --with-doctest --doctest-options=+ELLIPSIS --with-coverage --cover-package=$CI_PACKAGE $CI_PACKAGE + poetry run python -W ignore -m nose -q -v --with-doctest --doctest-options=+ELLIPSIS --with-coverage --traverse-namespace --cover-package=$CI_PACKAGE $CI_PACKAGE shell: bash - name: Upload Coverage to coveralls.io run: | diff --git a/tasks.py b/tasks.py index 9abde4a..c41e142 100644 --- a/tasks.py +++ b/tasks.py @@ -145,8 +145,9 @@ def tests(ctx, nose=True): if nose: message_box('Running "Nosetests"...') ctx.run( - 'nosetests --with-doctest --with-coverage --cover-package={0} {0}'. - format(PYTHON_PACKAGE_NAME), + 'nosetests --with-doctest --with-coverage ' + '--traverse-namespace --cover-package={0} {0}'.format( + PYTHON_PACKAGE_NAME), env={'MPLBACKEND': 'AGG'}) else: message_box('Running "Pytest"...') From f3f0ef3c1936ef79334395dc2a44640dd915041c Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Sun, 7 Mar 2021 22:13:42 +1300 Subject: [PATCH 08/51] Update "tasks.py" file. --- tasks.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tasks.py b/tasks.py index c41e142..9b250bd 100644 --- a/tasks.py +++ b/tasks.py @@ -67,6 +67,7 @@ def clean(ctx, docs=True, bytecode=False): patterns.append('docs/generated') if bytecode: + patterns.append('**/__pycache__') patterns.append('**/*.pyc') for pattern in patterns: @@ -301,8 +302,8 @@ def requirements(ctx): """ message_box('Exporting "requirements.txt" file...') - ctx.run('poetry run pip freeze | ' - 'egrep -v "github.com/colour-science|enum34" ' + ctx.run('poetry run pip list --format=freeze | ' + 'egrep -v "github.com/colour-science" ' '> requirements.txt') From 7967c3182d5ba6679051186ca6e35cc90f648879 Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Sun, 30 May 2021 11:24:24 +1200 Subject: [PATCH 09/51] Update various docstrings. --- colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py | 4 ++-- colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py | 4 ++-- colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py | 4 ++-- colour_demosaicing/bayer/tests/test_masks.py | 2 +- colour_demosaicing/bayer/tests/test_mosaicing.py | 3 ++- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py b/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py index 867cf12..2bc47ce 100644 --- a/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py +++ b/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py @@ -1,8 +1,8 @@ # !/usr/bin/env python # -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour_demosaicing.bayer.demosaicing.bilinear` -module. +Defines the unit tests for the +:mod:`colour_demosaicing.bayer.demosaicing.bilinear` module. """ import numpy as np diff --git a/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py b/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py index dbb440d..074290f 100644 --- a/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py +++ b/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py @@ -1,8 +1,8 @@ # !/usr/bin/env python # -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour_demosaicing.bayer.demosaicing.malvar2004` -module. +Defines the unit tests for the +:mod:`colour_demosaicing.bayer.demosaicing.malvar2004` module. """ import numpy as np diff --git a/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py b/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py index 26c4958..4827eb1 100644 --- a/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py +++ b/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py @@ -1,8 +1,8 @@ # !/usr/bin/env python # -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour_demosaicing.bayer.demosaicing.menon2007` -module. +Defines the unit tests for the +:mod:`colour_demosaicing.bayer.demosaicing.menon2007` module. """ import numpy as np diff --git a/colour_demosaicing/bayer/tests/test_masks.py b/colour_demosaicing/bayer/tests/test_masks.py index 7ffda03..fb0a8cb 100644 --- a/colour_demosaicing/bayer/tests/test_masks.py +++ b/colour_demosaicing/bayer/tests/test_masks.py @@ -1,7 +1,7 @@ # !/usr/bin/env python # -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour_demosaicing.bayer.masks` module. +Defines the unit tests for the :mod:`colour_demosaicing.bayer.masks` module. """ import numpy as np diff --git a/colour_demosaicing/bayer/tests/test_mosaicing.py b/colour_demosaicing/bayer/tests/test_mosaicing.py index a983446..956a10b 100644 --- a/colour_demosaicing/bayer/tests/test_mosaicing.py +++ b/colour_demosaicing/bayer/tests/test_mosaicing.py @@ -1,7 +1,8 @@ # !/usr/bin/env python # -*- coding: utf-8 -*- """ -Defines unit tests for :mod:`colour_demosaicing.bayer.mosaicing` module. +Defines the unit tests for the :mod:`colour_demosaicing.bayer.mosaicing` +module. """ import numpy as np From 9c2d5f6ad77db86ae17fd73548015fe67d05b818 Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Fri, 2 Jul 2021 22:00:51 +1200 Subject: [PATCH 10/51] Update "tasks.py" file. --- tasks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks.py b/tasks.py index 9b250bd..fa6d3fb 100644 --- a/tasks.py +++ b/tasks.py @@ -303,7 +303,7 @@ def requirements(ctx): message_box('Exporting "requirements.txt" file...') ctx.run('poetry run pip list --format=freeze | ' - 'egrep -v "github.com/colour-science" ' + 'egrep -v "colour-demosaicing=" ' '> requirements.txt') From b5881e19a14593ccceed2e9dd7b919119f7a6d02 Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Fri, 2 Jul 2021 22:10:44 +1200 Subject: [PATCH 11/51] Update "conf.py" file. --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 68b690b..9549ee7 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -372,5 +372,5 @@ def _autodoc_process_docstring(app, what, name, obj, options, lines): def setup(app): - app.add_stylesheet('custom.css') + app.add_css_file('custom.css') app.connect('autodoc-process-docstring', _autodoc_process_docstring) From 720155da3f565a3ea9e635214d67d495bc8dd6bf Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Tue, 14 Sep 2021 20:33:18 +1200 Subject: [PATCH 12/51] Update "continuous-integration.yml" file. --- .github/workflows/continuous-integration.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 3a90f4f..12e6f6a 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -40,6 +40,7 @@ jobs: shell: bash - name: Install Package Dependencies run: | + poetry run python -m pip install --upgrade pip poetry install poetry run python -c "import imageio;imageio.plugins.freeimage.download()" shell: bash From 678ce33c78354af2530f5ea5c21c54f480418558 Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Sat, 9 Oct 2021 11:53:39 +1300 Subject: [PATCH 13/51] Update "README.rst" file. --- README.rst | 13 ++++++------- docs/index.rst | 13 ++++++------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/README.rst b/README.rst index 83361b2..5a80cb7 100644 --- a/README.rst +++ b/README.rst @@ -39,9 +39,9 @@ Features The following CFA (Colour Filter Array) demosaicing algorithms are implemented: -- Bilinear -- Malvar (2004) -- DDFAPD - Menon (2007) +- Bilinear +- Malvar (2004) +- DDFAPD - Menon (2007) Installation ------------ @@ -58,9 +58,8 @@ Primary Dependencies **Colour - Demosaicing** requires various dependencies in order to run: -- `python>=2.7 `__ or - `python>=3.5 `__ -- `colour-science `__ +- `python>=3.5 `__ +- `colour-science `__ Pypi ^^^^ @@ -69,7 +68,7 @@ Once the dependencies are satisfied, **Colour - Demosaicing** can be installed f the `Python Package Index `__ by issuing this command in a shell:: - pip install --user colour-demosaicing + pip install --user colour-demosaicing The overall development dependencies are installed as follows:: diff --git a/docs/index.rst b/docs/index.rst index 2f5b264..99f2b81 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -20,9 +20,9 @@ Features The following CFA (Colour Filter Array) demosaicing algorithms are implemented: -- Bilinear -- Malvar (2004) -- DDFAPD - Menon (2007) +- Bilinear +- Malvar (2004) +- DDFAPD - Menon (2007) Installation ------------ @@ -39,9 +39,8 @@ Primary Dependencies **Colour - Demosaicing** requires various dependencies in order to run: -- `python>=2.7 `__ or - `python>=3.5 `__ -- `colour-science `__ +- `python>=3.5 `__ +- `colour-science `__ Pypi ^^^^ @@ -50,7 +49,7 @@ Once the dependencies are satisfied, **Colour - Demosaicing** can be installed f the `Python Package Index `__ by issuing this command in a shell:: - pip install --user colour-demosaicing + pip install --user colour-demosaicing The tests suite dependencies are installed as follows:: From a98b24b904cd8c8d922fa0defbc5a36f2ba96a96 Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Sun, 17 Oct 2021 16:49:39 +1300 Subject: [PATCH 14/51] Drop "Python" 3.6 support, use "Ubuntu" 20.04 and add "Python" 3.9 for CI. --- .github/workflows/continuous-integration.yml | 4 ++-- pyproject.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 12e6f6a..a428fac 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -7,8 +7,8 @@ jobs: name: ${{ matrix.os }} - Python ${{ matrix.python-version }} strategy: matrix: - os: [macOS-latest, ubuntu-18.04, windows-latest] - python-version: [3.6, 3.7, 3.8] + os: [macOS-latest, ubuntu-20.04, windows-latest] + python-version: [3.7, 3.8, 3.9] fail-fast: false runs-on: ${{ matrix.os }} steps: diff --git a/pyproject.toml b/pyproject.toml index 6ba3ad2..7f256be 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,7 +48,7 @@ exclude = [ ] [tool.poetry.dependencies] -python = "^3.6" +python = "^3.7" colour-science = "^0.3.16" biblib-simple = { version = "*", optional = true } # Development dependency. From 31baa6d2d95f262c160dc77b5ef4600caa192acd Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Thu, 4 Nov 2021 18:42:02 +1300 Subject: [PATCH 15/51] Test for optimised python execution. --- .github/workflows/continuous-integration.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index a428fac..dbe36c4 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -48,6 +48,10 @@ jobs: run: | poetry run flake8 $CI_PACKAGE --count --show-source --statistics shell: bash + - name: Test Optimised Python Execution + run: | + poetry run python -OO -c "import $CI_PACKAGE" + shell: bash - name: Test with nosetests run: | poetry run python -W ignore -m nose -q -v --with-doctest --doctest-options=+ELLIPSIS --with-coverage --traverse-namespace --cover-package=$CI_PACKAGE $CI_PACKAGE From 894393f1ee7ceff2b3991f7e84358242943e3ad1 Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Sun, 7 Nov 2021 17:52:34 +1300 Subject: [PATCH 16/51] Implement support for documentation continuous integration. --- .../continuous-integration-documentation.yml | 46 +++++++++++++++++++ ...yml => continuous-integration-package.yml} | 4 +- .readthedocs.yaml | 20 ++++++++ .readthedocs.yml | 8 ---- docs/bibliography.rst | 1 - docs/conf.py | 3 ++ pyproject.toml | 5 +- 7 files changed, 72 insertions(+), 15 deletions(-) create mode 100644 .github/workflows/continuous-integration-documentation.yml rename .github/workflows/{continuous-integration.yml => continuous-integration-package.yml} (96%) create mode 100644 .readthedocs.yaml delete mode 100644 .readthedocs.yml diff --git a/.github/workflows/continuous-integration-documentation.yml b/.github/workflows/continuous-integration-documentation.yml new file mode 100644 index 0000000..9299c7f --- /dev/null +++ b/.github/workflows/continuous-integration-documentation.yml @@ -0,0 +1,46 @@ +name: Continuous Integration - Documentation + +on: [push, pull_request] + +jobs: + continuous-integration-documentation: + name: ${{ matrix.os }} - Python ${{ matrix.python-version }} + strategy: + matrix: + os: [ubuntu-20.04] + python-version: [3.8] + fail-fast: false + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v1 + - name: Environment Variables + run: | + echo "CI_PYTHON_VERSION=${{ matrix.python-version }}" >> $GITHUB_ENV + echo "CI_PACKAGE=colour" >> $GITHUB_ENV + echo "CI_SHA=${{ github.sha }}" >> $GITHUB_ENV + echo "MPLBACKEND=AGG" >> $GITHUB_ENV + echo "COLOUR_SCIENCE__DOCUMENTATION_BUILD=True" >> $GITHUB_ENV + shell: bash + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + - name: Install Dependencies + run: | + sudo apt-get --yes install latexmk texlive-full + - name: Install Poetry + run: | + curl -L https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py -o get-poetry.py + python get-poetry.py + echo "$HOME/.poetry/bin" >> $GITHUB_PATH + shell: bash + - name: Install Package Dependencies + run: | + poetry run python -m pip install --upgrade pip + poetry install --extras "read-the-docs" + poetry run python -c "import imageio;imageio.plugins.freeimage.download()" + shell: bash + - name: Build Documentation + run: | + poetry run invoke docs + shell: bash diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration-package.yml similarity index 96% rename from .github/workflows/continuous-integration.yml rename to .github/workflows/continuous-integration-package.yml index dbe36c4..0c66386 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration-package.yml @@ -1,9 +1,9 @@ -name: Continuous Integration +name: Continuous Integration - Package on: [push, pull_request] jobs: - continuous-integration: + continuous-integration-package: name: ${{ matrix.os }} - Python ${{ matrix.python-version }} strategy: matrix: diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..3f9c1e4 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,20 @@ +version: 2 + +build: + os: ubuntu-20.04 + tools: + python: "3.8" + +sphinx: + configuration: docs/conf.py + +formats: + - htmlzip + - pdf + +python: + install: + - method: pip + path: . + extra_requirements: + - read-the-docs \ No newline at end of file diff --git a/.readthedocs.yml b/.readthedocs.yml deleted file mode 100644 index 6e5818f..0000000 --- a/.readthedocs.yml +++ /dev/null @@ -1,8 +0,0 @@ -build: - image: latest - -python: - version: 3.7 - pip_install: true - extra_requirements: - - read-the-docs diff --git a/docs/bibliography.rst b/docs/bibliography.rst index b8ea67e..9862a8c 100644 --- a/docs/bibliography.rst +++ b/docs/bibliography.rst @@ -3,4 +3,3 @@ Bibliography .. bibliography:: bibliography.bib :all: - :encoding: utf8 diff --git a/docs/conf.py b/docs/conf.py index 9549ee7..60f2b75 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -46,6 +46,9 @@ 'sphinxcontrib.bibtex' ] +bibtex_bibfiles = ['bibliography.bib'] +bibtex_encoding = 'utf8' + # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] diff --git a/pyproject.toml b/pyproject.toml index 7f256be..52f5314 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,7 +58,6 @@ flake8 = { version = "*", optional = true } # Development dependency. invoke = { version = "*", optional = true } # Development dependency. jupyter = { version = "*", optional = true } # Development dependency. matplotlib = { version = "*", optional = true } -mock = { version = "*", optional = true } # Development dependency. nose = { version = "*", optional = true } # Development dependency. numpy = { version = "*", optional = true } pre-commit = { version = "*", optional = true } # Development dependency. @@ -78,7 +77,6 @@ coveralls = "*" flake8 = "*" invoke = "*" jupyter = "*" -mock = "*" nose = "*" pre-commit = "*" pytest = "*" @@ -98,7 +96,6 @@ development = [ "flake8", "invoke", "jupyter", - "mock", "nose", "pre-commit", "pytest", @@ -111,7 +108,7 @@ development = [ "yapf" ] plotting = [ "matplotlib" ] -read-the-docs = [ "mock", "numpy", "sphinxcontrib-bibtex" ] +read-the-docs = [ "matplotlib", "numpy", "sphinxcontrib-bibtex" ] [build-system] requires = [ "poetry>=0.12" ] From 1b1102b0839d774654237c8644109efafccc78e2 Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Sun, 7 Nov 2021 22:24:57 +1300 Subject: [PATCH 17/51] Update various docstrings. --- colour_demosaicing/bayer/demosaicing/bilinear.py | 2 +- colour_demosaicing/bayer/demosaicing/malvar2004.py | 2 +- colour_demosaicing/bayer/demosaicing/menon2007.py | 2 +- colour_demosaicing/bayer/masks.py | 2 +- colour_demosaicing/bayer/mosaicing.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/colour_demosaicing/bayer/demosaicing/bilinear.py b/colour_demosaicing/bayer/demosaicing/bilinear.py index ea024fe..679b91e 100644 --- a/colour_demosaicing/bayer/demosaicing/bilinear.py +++ b/colour_demosaicing/bayer/demosaicing/bilinear.py @@ -37,7 +37,7 @@ def demosaicing_CFA_Bayer_bilinear(CFA, pattern='RGGB'): ---------- CFA : array_like *Bayer* CFA. - pattern : unicode, optional + pattern : str, optional **{'RGGB', 'BGGR', 'GRBG', 'GBRG'}**, Arrangement of the colour filters on the pixel array. diff --git a/colour_demosaicing/bayer/demosaicing/malvar2004.py b/colour_demosaicing/bayer/demosaicing/malvar2004.py index c9c8ec0..5145714 100644 --- a/colour_demosaicing/bayer/demosaicing/malvar2004.py +++ b/colour_demosaicing/bayer/demosaicing/malvar2004.py @@ -40,7 +40,7 @@ def demosaicing_CFA_Bayer_Malvar2004(CFA, pattern='RGGB'): ---------- CFA : array_like *Bayer* CFA. - pattern : unicode, optional + pattern : str, optional **{'RGGB', 'BGGR', 'GRBG', 'GBRG'}**, Arrangement of the colour filters on the pixel array. diff --git a/colour_demosaicing/bayer/demosaicing/menon2007.py b/colour_demosaicing/bayer/demosaicing/menon2007.py index 53620a5..064b979 100644 --- a/colour_demosaicing/bayer/demosaicing/menon2007.py +++ b/colour_demosaicing/bayer/demosaicing/menon2007.py @@ -58,7 +58,7 @@ def demosaicing_CFA_Bayer_Menon2007(CFA, pattern='RGGB', refining_step=True): ---------- CFA : array_like *Bayer* CFA. - pattern : unicode, optional + pattern : str, optional **{'RGGB', 'BGGR', 'GRBG', 'GBRG'}**, Arrangement of the colour filters on the pixel array. refining_step : bool diff --git a/colour_demosaicing/bayer/masks.py b/colour_demosaicing/bayer/masks.py index 9985c64..309c3a9 100644 --- a/colour_demosaicing/bayer/masks.py +++ b/colour_demosaicing/bayer/masks.py @@ -26,7 +26,7 @@ def masks_CFA_Bayer(shape, pattern='RGGB'): ---------- shape : array_like Dimensions of the *Bayer* CFA. - pattern : unicode, optional + pattern : str, optional **{'RGGB', 'BGGR', 'GRBG', 'GBRG'}**, Arrangement of the colour filters on the pixel array. diff --git a/colour_demosaicing/bayer/mosaicing.py b/colour_demosaicing/bayer/mosaicing.py index 59b7881..6e1f594 100644 --- a/colour_demosaicing/bayer/mosaicing.py +++ b/colour_demosaicing/bayer/mosaicing.py @@ -28,7 +28,7 @@ def mosaicing_CFA_Bayer(RGB, pattern='RGGB'): ---------- RGB : array_like *RGB* colourspace array. - pattern : unicode, optional + pattern : str, optional **{'RGGB', 'BGGR', 'GRBG', 'GBRG'}**, Arrangement of the colour filters on the pixel array. From 5e592bd2cc2c755e46c98e515ebf4381180f973f Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Tue, 9 Nov 2021 22:00:38 +1300 Subject: [PATCH 18/51] Format "__all__" attributes. --- colour_demosaicing/__init__.py | 9 ++++++--- colour_demosaicing/bayer/__init__.py | 8 ++++++-- colour_demosaicing/bayer/demosaicing/__init__.py | 13 ++++++++++--- colour_demosaicing/bayer/demosaicing/bilinear.py | 4 +++- colour_demosaicing/bayer/demosaicing/malvar2004.py | 4 +++- colour_demosaicing/bayer/demosaicing/menon2007.py | 5 +++-- .../bayer/demosaicing/tests/test_bilinear.py | 5 ++++- .../bayer/demosaicing/tests/test_malvar2004.py | 5 ++++- .../bayer/demosaicing/tests/test_menon2007.py | 5 ++++- colour_demosaicing/bayer/masks.py | 4 +++- colour_demosaicing/bayer/mosaicing.py | 4 +++- colour_demosaicing/bayer/tests/test_masks.py | 5 ++++- colour_demosaicing/bayer/tests/test_mosaicing.py | 5 ++++- 13 files changed, 57 insertions(+), 19 deletions(-) diff --git a/colour_demosaicing/__init__.py b/colour_demosaicing/__init__.py index 20e2aea..a25e14d 100644 --- a/colour_demosaicing/__init__.py +++ b/colour_demosaicing/__init__.py @@ -29,9 +29,12 @@ __status__ = 'Production' __all__ = [ - 'demosaicing_CFA_Bayer_bilinear', 'demosaicing_CFA_Bayer_DDFAPD', - 'demosaicing_CFA_Bayer_Malvar2004', 'demosaicing_CFA_Bayer_Menon2007', - 'masks_CFA_Bayer', 'mosaicing_CFA_Bayer' + 'demosaicing_CFA_Bayer_bilinear', + 'demosaicing_CFA_Bayer_DDFAPD', + 'demosaicing_CFA_Bayer_Malvar2004', + 'demosaicing_CFA_Bayer_Menon2007', + 'masks_CFA_Bayer', + 'mosaicing_CFA_Bayer', ] RESOURCES_DIRECTORY = os.path.join(os.path.dirname(__file__), 'resources') diff --git a/colour_demosaicing/bayer/__init__.py b/colour_demosaicing/bayer/__init__.py index 90b56af..cc6932a 100644 --- a/colour_demosaicing/bayer/__init__.py +++ b/colour_demosaicing/bayer/__init__.py @@ -6,6 +6,10 @@ from . import demosaicing __all__ = [] -__all__ += ['masks_CFA_Bayer'] -__all__ += ['mosaicing_CFA_Bayer'] +__all__ += [ + 'masks_CFA_Bayer', +] +__all__ += [ + 'mosaicing_CFA_Bayer', +] __all__ += demosaicing.__all__ diff --git a/colour_demosaicing/bayer/demosaicing/__init__.py b/colour_demosaicing/bayer/demosaicing/__init__.py index 31798ce..aafd954 100644 --- a/colour_demosaicing/bayer/demosaicing/__init__.py +++ b/colour_demosaicing/bayer/demosaicing/__init__.py @@ -6,6 +6,13 @@ demosaicing_CFA_Bayer_Menon2007) __all__ = [] -__all__ += ['demosaicing_CFA_Bayer_bilinear'] -__all__ += ['demosaicing_CFA_Bayer_Malvar2004'] -__all__ += ['demosaicing_CFA_Bayer_DDFAPD', 'demosaicing_CFA_Bayer_Menon2007'] +__all__ += [ + 'demosaicing_CFA_Bayer_bilinear', +] +__all__ += [ + 'demosaicing_CFA_Bayer_Malvar2004', +] +__all__ += [ + 'demosaicing_CFA_Bayer_DDFAPD', + 'demosaicing_CFA_Bayer_Menon2007', +] diff --git a/colour_demosaicing/bayer/demosaicing/bilinear.py b/colour_demosaicing/bayer/demosaicing/bilinear.py index 679b91e..97b1182 100644 --- a/colour_demosaicing/bayer/demosaicing/bilinear.py +++ b/colour_demosaicing/bayer/demosaicing/bilinear.py @@ -25,7 +25,9 @@ __email__ = 'colour-developers@colour-science.org' __status__ = 'Production' -__all__ = ['demosaicing_CFA_Bayer_bilinear'] +__all__ = [ + 'demosaicing_CFA_Bayer_bilinear', +] def demosaicing_CFA_Bayer_bilinear(CFA, pattern='RGGB'): diff --git a/colour_demosaicing/bayer/demosaicing/malvar2004.py b/colour_demosaicing/bayer/demosaicing/malvar2004.py index 5145714..09aa1d2 100644 --- a/colour_demosaicing/bayer/demosaicing/malvar2004.py +++ b/colour_demosaicing/bayer/demosaicing/malvar2004.py @@ -28,7 +28,9 @@ __email__ = 'colour-developers@colour-science.org' __status__ = 'Production' -__all__ = ['demosaicing_CFA_Bayer_Malvar2004'] +__all__ = [ + 'demosaicing_CFA_Bayer_Malvar2004', +] def demosaicing_CFA_Bayer_Malvar2004(CFA, pattern='RGGB'): diff --git a/colour_demosaicing/bayer/demosaicing/menon2007.py b/colour_demosaicing/bayer/demosaicing/menon2007.py index 064b979..95c0f94 100644 --- a/colour_demosaicing/bayer/demosaicing/menon2007.py +++ b/colour_demosaicing/bayer/demosaicing/menon2007.py @@ -28,8 +28,9 @@ __status__ = 'Production' __all__ = [ - 'demosaicing_CFA_Bayer_Menon2007', 'demosaicing_CFA_Bayer_DDFAPD', - 'refining_step_Menon2007' + 'demosaicing_CFA_Bayer_Menon2007', + 'demosaicing_CFA_Bayer_DDFAPD', + 'refining_step_Menon2007', ] diff --git a/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py b/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py index 2bc47ce..b5f6702 100644 --- a/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py +++ b/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py @@ -21,7 +21,10 @@ __email__ = 'colour-developers@colour-science.org' __status__ = 'Production' -__all__ = ['BAYER_DIRECTORY', 'TestDemosaicing_CFA_Bayer_bilinear'] +__all__ = [ + 'BAYER_DIRECTORY', + 'TestDemosaicing_CFA_Bayer_bilinear', +] BAYER_DIRECTORY = os.path.join(TESTS_RESOURCES_DIRECTORY, 'colour_demosaicing', 'bayer') diff --git a/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py b/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py index 074290f..10301ec 100644 --- a/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py +++ b/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py @@ -21,7 +21,10 @@ __email__ = 'colour-developers@colour-science.org' __status__ = 'Production' -__all__ = ['BAYER_DIRECTORY', 'TestDemosaicing_CFA_Bayer_Malvar2004'] +__all__ = [ + 'BAYER_DIRECTORY', + 'TestDemosaicing_CFA_Bayer_Malvar2004', +] BAYER_DIRECTORY = os.path.join(TESTS_RESOURCES_DIRECTORY, 'colour_demosaicing', 'bayer') diff --git a/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py b/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py index 4827eb1..3029fda 100644 --- a/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py +++ b/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py @@ -21,7 +21,10 @@ __email__ = 'colour-developers@colour-science.org' __status__ = 'Production' -__all__ = ['BAYER_DIRECTORY', 'TestDemosaicing_CFA_Bayer_Menon2007'] +__all__ = [ + 'BAYER_DIRECTORY', + 'TestDemosaicing_CFA_Bayer_Menon2007', +] BAYER_DIRECTORY = os.path.join(TESTS_RESOURCES_DIRECTORY, 'colour_demosaicing', 'bayer') diff --git a/colour_demosaicing/bayer/masks.py b/colour_demosaicing/bayer/masks.py index 309c3a9..922e829 100644 --- a/colour_demosaicing/bayer/masks.py +++ b/colour_demosaicing/bayer/masks.py @@ -15,7 +15,9 @@ __email__ = 'colour-developers@colour-science.org' __status__ = 'Production' -__all__ = ['masks_CFA_Bayer'] +__all__ = [ + 'masks_CFA_Bayer', +] def masks_CFA_Bayer(shape, pattern='RGGB'): diff --git a/colour_demosaicing/bayer/mosaicing.py b/colour_demosaicing/bayer/mosaicing.py index 6e1f594..b85f6e4 100644 --- a/colour_demosaicing/bayer/mosaicing.py +++ b/colour_demosaicing/bayer/mosaicing.py @@ -17,7 +17,9 @@ __email__ = 'colour-developers@colour-science.org' __status__ = 'Production' -__all__ = ['mosaicing_CFA_Bayer'] +__all__ = [ + 'mosaicing_CFA_Bayer', +] def mosaicing_CFA_Bayer(RGB, pattern='RGGB'): diff --git a/colour_demosaicing/bayer/tests/test_masks.py b/colour_demosaicing/bayer/tests/test_masks.py index fb0a8cb..d6fbc49 100644 --- a/colour_demosaicing/bayer/tests/test_masks.py +++ b/colour_demosaicing/bayer/tests/test_masks.py @@ -21,7 +21,10 @@ __email__ = 'colour-developers@colour-science.org' __status__ = 'Production' -__all__ = ['BAYER_DIRECTORY', 'TestMasks_CFA_Bayer'] +__all__ = [ + 'BAYER_DIRECTORY', + 'TestMasks_CFA_Bayer', +] BAYER_DIRECTORY = os.path.join(TESTS_RESOURCES_DIRECTORY, 'colour_demosaicing', 'bayer') diff --git a/colour_demosaicing/bayer/tests/test_mosaicing.py b/colour_demosaicing/bayer/tests/test_mosaicing.py index 956a10b..5b26c36 100644 --- a/colour_demosaicing/bayer/tests/test_mosaicing.py +++ b/colour_demosaicing/bayer/tests/test_mosaicing.py @@ -21,7 +21,10 @@ __email__ = 'colour-developers@colour-science.org' __status__ = 'Production' -__all__ = ['BAYER_DIRECTORY', 'TestMosaicing_CFA_Bayer'] +__all__ = [ + 'BAYER_DIRECTORY', + 'TestMosaicing_CFA_Bayer', +] BAYER_DIRECTORY = os.path.join(TESTS_RESOURCES_DIRECTORY, 'colour_demosaicing', 'bayer') From 8238b643407ffaeded46678ece4612580215be81 Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Tue, 9 Nov 2021 22:01:10 +1300 Subject: [PATCH 19/51] Update "requirements.txt" file. --- requirements.txt | 173 ++++++++++++++++++++++++----------------------- 1 file changed, 90 insertions(+), 83 deletions(-) diff --git a/requirements.txt b/requirements.txt index c410a0a..f165aa4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,122 +1,129 @@ alabaster==0.7.12 -appdirs==1.4.4 appnope==0.1.2 -argon2-cffi==20.1.0 -async-generator==1.10 -attrs==20.3.0 -Babel==2.9.0 +argon2-cffi==21.1.0 +attrs==21.2.0 +Babel==2.9.1 backcall==0.2.0 +backports.entry-points-selectable==1.1.1 biblib-simple==0.1.1 -bleach==3.2.1 -certifi==2020.12.5 -cffi==1.14.4 -cfgv==3.0.0 -chardet==4.0.0 +bleach==4.1.0 +certifi==2021.10.8 +cffi==1.15.0 +cfgv==3.3.1 +charset-normalizer==2.0.7 colorama==0.4.4 colour-science==0.3.16 -coverage==5.3.1 -coveralls==3.0.0 +coverage==6.1.1 +coveralls==3.3.0 cycler==0.10.0 -decorator==4.4.2 -defusedxml==0.6.0 -distlib==0.3.1 +debugpy==1.5.1 +decorator==5.1.0 +defusedxml==0.7.1 +distlib==0.3.3 docopt==0.6.2 -docutils==0.16 +docutils==0.17.1 entrypoints==0.3 -filelock==3.0.12 -flake8==3.8.4 -identify==1.5.12 -idna==2.10 -imageio==2.9.0 +filelock==3.3.2 +flake8==4.0.1 +identify==2.3.5 +idna==3.3 +imageio==2.10.3 imagesize==1.2.0 +importlib-metadata==4.2.0 +importlib-resources==5.4.0 iniconfig==1.1.1 -invoke==1.5.0 -ipykernel==5.4.3 -ipython==7.16.1 +invoke==1.6.0 +ipykernel==6.5.0 +ipython==7.29.0 ipython-genutils==0.2.0 -ipywidgets==7.6.3 +ipywidgets==7.6.5 jedi==0.18.0 -Jinja2==2.11.2 -jsonschema==3.2.0 +Jinja2==3.0.2 +jsonschema==4.2.1 jupyter==1.0.0 -jupyter-client==6.1.11 -jupyter-console==6.2.0 -jupyter-core==4.7.0 +jupyter-client==7.0.6 +jupyter-console==6.4.0 +jupyter-core==4.9.1 jupyterlab-pygments==0.1.2 -jupyterlab-widgets==1.0.0 -keyring==21.8.0 +jupyterlab-widgets==1.0.2 +keyring==23.2.1 kiwisolver==1.3.1 latexcodec==2.0.1 -MarkupSafe==1.1.1 +MarkupSafe==2.0.1 matplotlib==3.3.3 +matplotlib-inline==0.1.3 mccabe==0.6.1 mistune==0.8.4 -mock==4.0.3 -nbclient==0.5.1 -nbconvert==6.0.7 -nbformat==5.0.8 -nest-asyncio==1.4.3 -nodeenv==1.5.0 +nbclient==0.5.5 +nbconvert==6.2.0 +nbformat==5.1.3 +nest-asyncio==1.5.1 +nodeenv==1.6.0 nose==1.3.7 -notebook==6.1.6 -numpy==1.19.5 +notebook==6.4.5 +numpy==1.21.1 oset==0.1.3 -packaging==20.8 -pandocfilters==1.4.3 -parso==0.8.1 +packaging==21.2 +pandocfilters==1.5.0 +parso==0.8.2 pexpect==4.8.0 pickleshare==0.7.5 -Pillow==8.1.0 -pkginfo==1.6.1 -pluggy==0.13.1 -pre-commit==2.1.1 -prometheus-client==0.9.0 -prompt-toolkit==3.0.3 +Pillow==8.4.0 +pip==20.2.2 +pkginfo==1.7.1 +platformdirs==2.4.0 +pluggy==1.0.0 +pre-commit==2.15.0 +prometheus-client==0.12.0 +prompt-toolkit==3.0.22 ptyprocess==0.7.0 -py==1.10.0 -pybtex==0.23.0 -pybtex-docutils==0.2.2 -pycodestyle==2.6.0 -pycparser==2.20 -pyflakes==2.2.0 -Pygments==2.7.4 +py==1.11.0 +pybtex==0.24.0 +pybtex-docutils==1.0.1 +pycodestyle==2.8.0 +pycparser==2.21 +pyflakes==2.4.0 +Pygments==2.10.0 pyparsing==2.4.7 -pyrsistent==0.17.3 -pytest==6.2.1 -python-dateutil==2.8.1 -pytz==2020.5 -PyYAML==5.3.1 -pyzmq==20.0.0 -qtconsole==5.0.1 -QtPy==1.9.0 -readme-renderer==28.0 -requests==2.25.1 +pyrsistent==0.18.0 +pytest==6.2.5 +python-dateutil==2.8.2 +pytz==2021.3 +PyYAML==6.0 +pyzmq==22.3.0 +qtconsole==5.2.0 +QtPy==1.11.2 +readme-renderer==30.0 +requests==2.26.0 requests-toolbelt==0.9.1 restructuredtext-lint==1.3.2 -rfc3986==1.4.0 -scipy==1.5.4 -Send2Trash==1.5.0 -six==1.15.0 -snowballstemmer==2.0.0 +rfc3986==1.5.0 +scipy==1.6.1 +Send2Trash==1.8.0 +setuptools==49.6.0 +six==1.16.0 +snowballstemmer==2.1.0 Sphinx==3.1.2 -sphinx-rtd-theme==0.5.1 +sphinx-rtd-theme==1.0.0 sphinxcontrib-applehelp==1.0.2 sphinxcontrib-bibtex==1.0.0 sphinxcontrib-devhelp==1.0.2 -sphinxcontrib-htmlhelp==1.0.3 +sphinxcontrib-htmlhelp==2.0.0 sphinxcontrib-jsmath==1.0.1 sphinxcontrib-qthelp==1.0.3 -sphinxcontrib-serializinghtml==1.1.4 -terminado==0.9.2 -testpath==0.4.4 +sphinxcontrib-serializinghtml==1.1.5 +terminado==0.12.1 +testpath==0.5.0 toml==0.10.2 tornado==6.1 -tqdm==4.56.0 -traitlets==4.3.3 -twine==3.3.0 -urllib3==1.26.2 -virtualenv==20.3.0 +tqdm==4.62.3 +traitlets==5.1.1 +twine==3.5.0 +urllib3==1.26.7 +virtualenv==20.10.0 wcwidth==0.2.5 webencodings==0.5.1 -widgetsnbextension==3.5.1 +wheel==0.35.1 +widgetsnbextension==3.5.2 yapf==0.23.0 +zipp==3.6.0 From c5d30de5ce4550c8e8f0ef18f651cd3433b0c2c1 Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Tue, 9 Nov 2021 22:45:16 +1300 Subject: [PATCH 20/51] Format imports. --- colour_demosaicing/__init__.py | 10 +++++++--- colour_demosaicing/bayer/demosaicing/__init__.py | 6 ++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/colour_demosaicing/__init__.py b/colour_demosaicing/__init__.py index a25e14d..79666f0 100644 --- a/colour_demosaicing/__init__.py +++ b/colour_demosaicing/__init__.py @@ -17,9 +17,13 @@ import colour from .bayer import ( - demosaicing_CFA_Bayer_bilinear, demosaicing_CFA_Bayer_DDFAPD, - demosaicing_CFA_Bayer_Malvar2004, demosaicing_CFA_Bayer_Menon2007, - masks_CFA_Bayer, mosaicing_CFA_Bayer) + demosaicing_CFA_Bayer_bilinear, + demosaicing_CFA_Bayer_DDFAPD, + demosaicing_CFA_Bayer_Malvar2004, + demosaicing_CFA_Bayer_Menon2007, + masks_CFA_Bayer, + mosaicing_CFA_Bayer, +) __author__ = 'Colour Developers' __copyright__ = 'Copyright (C) 2015-2021 - Colour Developers' diff --git a/colour_demosaicing/bayer/demosaicing/__init__.py b/colour_demosaicing/bayer/demosaicing/__init__.py index aafd954..e01604e 100644 --- a/colour_demosaicing/bayer/demosaicing/__init__.py +++ b/colour_demosaicing/bayer/demosaicing/__init__.py @@ -2,8 +2,10 @@ from .bilinear import demosaicing_CFA_Bayer_bilinear from .malvar2004 import demosaicing_CFA_Bayer_Malvar2004 -from .menon2007 import (demosaicing_CFA_Bayer_DDFAPD, - demosaicing_CFA_Bayer_Menon2007) +from .menon2007 import ( + demosaicing_CFA_Bayer_DDFAPD, + demosaicing_CFA_Bayer_Menon2007, +) __all__ = [] __all__ += [ From 8476a7360b7d71f13196a5f3d27182d92604d03b Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Sun, 12 Dec 2021 21:21:49 +1300 Subject: [PATCH 21/51] Update documentation CI build. --- .github/workflows/continuous-integration-documentation.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/continuous-integration-documentation.yml b/.github/workflows/continuous-integration-documentation.yml index 9299c7f..bd9f051 100644 --- a/.github/workflows/continuous-integration-documentation.yml +++ b/.github/workflows/continuous-integration-documentation.yml @@ -27,6 +27,7 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install Dependencies run: | + sudo apt-get update sudo apt-get --yes install latexmk texlive-full - name: Install Poetry run: | From db00ada2846b034992c1caf6602f9d9e96a5615b Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Sat, 22 Jan 2022 10:46:18 +1300 Subject: [PATCH 22/51] Implement support for typing annotations. --- ...inuous-integration-quality-unit-tests.yml} | 2 +- ...nuous-integration-static-type-checking.yml | 32 ++ .gitignore | 1 + .pre-commit-config.yaml | 2 +- docs/conf.py | 67 +++- pyproject.toml | 10 + tasks.py | 304 ++++++++++-------- utilities/export_todo.py | 20 +- utilities/unicode_to_ascii.py | 14 +- 9 files changed, 284 insertions(+), 168 deletions(-) rename .github/workflows/{continuous-integration-package.yml => continuous-integration-quality-unit-tests.yml} (97%) create mode 100644 .github/workflows/continuous-integration-static-type-checking.yml diff --git a/.github/workflows/continuous-integration-package.yml b/.github/workflows/continuous-integration-quality-unit-tests.yml similarity index 97% rename from .github/workflows/continuous-integration-package.yml rename to .github/workflows/continuous-integration-quality-unit-tests.yml index 0c66386..f160aca 100644 --- a/.github/workflows/continuous-integration-package.yml +++ b/.github/workflows/continuous-integration-quality-unit-tests.yml @@ -1,4 +1,4 @@ -name: Continuous Integration - Package +name: Continuous Integration - Quality & Unit Tests on: [push, pull_request] diff --git a/.github/workflows/continuous-integration-static-type-checking.yml b/.github/workflows/continuous-integration-static-type-checking.yml new file mode 100644 index 0000000..d7c7329 --- /dev/null +++ b/.github/workflows/continuous-integration-static-type-checking.yml @@ -0,0 +1,32 @@ +name: Continuous Integration - Static Type Checking + +on: [push, pull_request] + +jobs: + continuous-integration-package: + name: ${{ matrix.os }} - Python ${{ matrix.python-version }} + strategy: + matrix: + os: [macOS-latest] + python-version: [3.9] + fail-fast: false + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v1 + - name: Environment Variables + run: | + echo "CI_PACKAGE=colour" >> $GITHUB_ENV + shell: bash + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + - name: Install Dependencies (macOS) + run: | + brew install gnu-sed graphviz + ln -s /usr/local/bin/gsed /usr/local/bin/sed + shell: bash + - name: Static Type Checking + run: | + pip install -r requirements.txt + mypy --install-types --non-interactive --show-error-codes --warn-unused-ignores --warn-redundant-casts -p $CI_PACKAGE diff --git a/.gitignore b/.gitignore index 1048eec..9a04916 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ *.pyc .DS_Store .coverage +.dmypy.json .idea .ipynb_checkpoints/ __pycache__ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f759724..02bc48e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://gitlab.com/pycqa/flake8 - rev: 3.7.8 + rev: 4.0.1 hooks: - id: flake8 exclude: examples|setup\.py diff --git a/docs/conf.py b/docs/conf.py index 60f2b75..6731452 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -19,13 +19,6 @@ basename = re.sub('_(\\w)', lambda x: x.group(1).upper(), package.__name__.title()) -autodoc_member_order = 'bysource' -autodoc_mock_imports = ['colour', 'scipy', 'scipy.ndimage.filters'] - -autosummary_generate = True - -napoleon_custom_sections = ['Attributes', 'Methods'] - # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. @@ -40,15 +33,59 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', 'sphinx.ext.autosummary', 'sphinx.ext.coverage', - 'sphinx.ext.ifconfig', 'sphinx.ext.intersphinx', 'sphinx.ext.mathjax', - 'sphinx.ext.napoleon', 'sphinx.ext.todo', 'sphinx.ext.viewcode', - 'sphinxcontrib.bibtex' + 'sphinx.ext.autodoc', + 'sphinx.ext.autosummary', + 'sphinx.ext.coverage', + 'sphinx.ext.ifconfig', + 'sphinx.ext.inheritance_diagram', + 'sphinx.ext.intersphinx', + 'sphinx.ext.mathjax', + 'sphinx.ext.napoleon', + 'sphinx.ext.todo', + 'sphinx.ext.viewcode', + 'sphinxcontrib.bibtex', ] +autodoc_member_order = 'bysource' +autodoc_mock_imports = [ + 'colour', + 'scipy', + 'scipy.ndimage.filters', +] +autodoc_typehints = 'both' +autodoc_type_aliases = { + 'ArrayLike': 'ArrayLike', + 'Boolean': 'Boolean', + 'BooleanOrArrayLike': 'BooleanOrArrayLike', + 'BooleanOrNDArray': 'BooleanOrNDArray', + 'DType': 'DType', + 'DTypeBoolean': 'DTypeBoolean', + 'DTypeComplex': 'DTypeComplex', + 'DTypeFloating': 'DTypeFloating', + 'DTypeInteger': 'DTypeInteger', + 'DTypeNumber': 'DTypeNumber', + 'Floating': 'Floating', + 'FloatingOrArrayLike': 'FloatingOrArrayLike', + 'FloatingOrNDArray': 'FloatingOrNDArray', + 'Integer': 'Integer', + 'IntegerOrArrayLike': 'IntegerOrArrayLike', + 'IntegerOrNDArray': 'IntegerOrNDArray', + 'NestedSequence': 'NestedSequence', + 'Number': 'Number', + 'NumberOrArrayLike': 'NumberOrArrayLike', + 'NumberOrNDArray': 'NumberOrNDArray', + 'StrOrArrayLike': 'StrOrArrayLike', + 'StrOrNDArray': 'StrOrNDArray', +} +autodoc_preserve_defaults = True + +autosummary_generate = True + bibtex_bibfiles = ['bibliography.bib'] bibtex_encoding = 'utf8' +napoleon_custom_sections = ['Attributes', 'Methods'] + # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -362,7 +399,13 @@ autoclass_content = 'both' -intersphinx_mapping = {'python': ('https://docs.python.org/3.7', None)} +intersphinx_mapping = { + 'python': ('https://docs.python.org/3.7', None), + 'matplotlib': ('http://matplotlib.org/stable', None), + 'numpy': ('http://docs.scipy.org/doc/numpy', None), + 'pandas': ('http://pandas.pydata.org/pandas-docs/dev', None), + 'scipy': ('http://docs.scipy.org/doc/scipy/reference', None) +} def _autodoc_process_docstring(app, what, name, obj, options, lines): diff --git a/pyproject.toml b/pyproject.toml index 52f5314..e6c7ac8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,6 +58,7 @@ flake8 = { version = "*", optional = true } # Development dependency. invoke = { version = "*", optional = true } # Development dependency. jupyter = { version = "*", optional = true } # Development dependency. matplotlib = { version = "*", optional = true } +mypy = { version = "*", optional = true } # Development dependency. nose = { version = "*", optional = true } # Development dependency. numpy = { version = "*", optional = true } pre-commit = { version = "*", optional = true } # Development dependency. @@ -68,6 +69,7 @@ sphinx_rtd_theme = { version = "*", optional = true } # Development dependency. sphinxcontrib-bibtex = { version = "<2.0.0", optional = true } # Development dependency. toml = { version = "*", optional = true } # Development dependency. twine = { version = "*", optional = true } # Development dependency. +typing-extensions = { version = "*", optional = true } # Development dependency. yapf = { version = "0.23", optional = true } # Development dependency. [tool.poetry.dev-dependencies] @@ -77,6 +79,7 @@ coveralls = "*" flake8 = "*" invoke = "*" jupyter = "*" +mypy = "*" nose = "*" pre-commit = "*" pytest = "*" @@ -86,6 +89,7 @@ sphinx_rtd_theme = "*" sphinxcontrib-bibtex = "<2.0.0" toml = "*" twine = "*" +typing-extensions = "*" yapf = "0.23" [tool.poetry.extras] @@ -96,6 +100,7 @@ development = [ "flake8", "invoke", "jupyter", + "mypy", "nose", "pre-commit", "pytest", @@ -105,11 +110,16 @@ development = [ "sphinxcontrib-bibtex", "toml", "twine", + "typing-extensions", "yapf" ] plotting = [ "matplotlib" ] read-the-docs = [ "matplotlib", "numpy", "sphinxcontrib-bibtex" ] +[tool.mypy] +plugins = "numpy.typing.mypy_plugin" +ignore_missing_imports = true + [build-system] requires = [ "poetry>=0.12" ] build-backend = "poetry.masonry.api" diff --git a/tasks.py b/tasks.py index fa6d3fb..6ceef7a 100644 --- a/tasks.py +++ b/tasks.py @@ -4,12 +4,16 @@ ============== """ +from __future__ import annotations + import biblib.bib import fnmatch import os import re import uuid -from invoke import task +from invoke import Context, task + +from colour.hints import Boolean import colour_demosaicing from colour.utilities import message_box @@ -22,42 +26,87 @@ __status__ = 'Production' __all__ = [ - 'APPLICATION_NAME', 'APPLICATION_VERSION', 'PYTHON_PACKAGE_NAME', - 'PYPI_PACKAGE_NAME', 'BIBLIOGRAPHY_NAME', 'clean', 'formatting', 'tests', - 'quality', 'examples', 'preflight', 'docs', 'todo', 'requirements', - 'build', 'virtualise', 'tag', 'release', 'sha256' + 'APPLICATION_NAME', + 'APPLICATION_VERSION', + 'PYTHON_PACKAGE_NAME', + 'PYPI_PACKAGE_NAME', + 'BIBLIOGRAPHY_NAME', + 'clean', + 'formatting', + 'tests', + 'quality', + 'examples', + 'preflight', + 'docs', + 'todo', + 'requirements', + 'build', + 'virtualise', + 'tag', + 'release', + 'sha256', ] -APPLICATION_NAME = colour_demosaicing.__application_name__ +APPLICATION_NAME: str = colour_demosaicing.__application_name__ + +APPLICATION_VERSION: str = colour_demosaicing.__version__ + +PYTHON_PACKAGE_NAME: str = colour_demosaicing.__name__ + +PYPI_PACKAGE_NAME: str = 'colour-demosaicing' + +BIBLIOGRAPHY_NAME: str = 'BIBLIOGRAPHY.bib' + + +def _patch_invoke_annotations_support(): + """ + See https://github.com/pyinvoke/invoke/issues/357 + """ + + import invoke + from unittest.mock import patch + from inspect import getfullargspec, ArgSpec + + def patched_inspect_getargspec(function): + spec = getfullargspec(function) + return ArgSpec(*spec[0:4]) -APPLICATION_VERSION = colour_demosaicing.__version__ + org_task_argspec = invoke.tasks.Task.argspec -PYTHON_PACKAGE_NAME = colour_demosaicing.__name__ + def patched_task_argspec(*args, **kwargs): + with patch( + target="inspect.getargspec", new=patched_inspect_getargspec): + return org_task_argspec(*args, **kwargs) -PYPI_PACKAGE_NAME = 'colour-demosaicing' + invoke.tasks.Task.argspec = patched_task_argspec -BIBLIOGRAPHY_NAME = 'BIBLIOGRAPHY.bib' + +_patch_invoke_annotations_support() @task -def clean(ctx, docs=True, bytecode=False): +def clean(ctx: Context, + docs: Boolean = True, + bytecode: Boolean = False, + mypy: Boolean = True, + pytest: Boolean = True): """ Cleans the project. Parameters ---------- - ctx : invoke.context.Context + ctx Context. - docs : bool, optional + docs Whether to clean the *docs* directory. - bytecode : bool, optional + bytecode Whether to clean the bytecode files, e.g. *.pyc* files. - - Returns - ------- - bool - Task success. + mypy + Whether to clean the *Mypy* cache directory. + pytest + Whether to clean the *Pytest* cache directory. """ + message_box('Cleaning project...') patterns = ['build', '*.egg-info', 'dist'] @@ -70,31 +119,35 @@ def clean(ctx, docs=True, bytecode=False): patterns.append('**/__pycache__') patterns.append('**/*.pyc') + if mypy: + patterns.append('.mypy_cache') + + if pytest: + patterns.append('.pytest_cache') + for pattern in patterns: ctx.run("rm -rf {}".format(pattern)) @task -def formatting(ctx, yapf=True, asciify=True, bibtex=True): +def formatting(ctx: Context, + yapf: Boolean = True, + asciify: Boolean = True, + bibtex: Boolean = True): """ Formats the codebase with *Yapf*, converts unicode characters to ASCII and cleanup the "BibTeX" file. Parameters ---------- - ctx : invoke.context.Context + ctx Context. - yapf : bool, optional + yapf Whether to format the codebase with *Yapf*. - asciify : bool, optional + asciify Whether to convert unicode characters to ASCII. - bibtex : bool, optional + bibtex Whether to cleanup the *BibTeX* file. - - Returns - ------- - bool - Task success. """ if yapf: @@ -106,41 +159,39 @@ def formatting(ctx, yapf=True, asciify=True, bibtex=True): with ctx.cd('utilities'): ctx.run('./unicode_to_ascii.py') - message_box('Cleaning up "BibTeX" file...') - bibtex_path = BIBLIOGRAPHY_NAME - with open(bibtex_path) as bibtex_file: - bibtex = biblib.bib.Parser().parse(bibtex_file.read()).get_entries() + if bibtex: + message_box('Cleaning up "BibTeX" file...') + bibtex_path = BIBLIOGRAPHY_NAME + with open(bibtex_path) as bibtex_file: + entries = biblib.bib.Parser().parse( + bibtex_file.read()).get_entries() - for entry in sorted(bibtex.values(), key=lambda x: x.key): - try: - del entry['file'] - except KeyError: - pass - for key, value in entry.items(): - entry[key] = re.sub('(? OrderedDict: """ Extracts the TODO items from given directory. Parameters ---------- - root_directory : unicode + root_directory Directory to extract the TODO items from. Returns ------- - OrderedDict + :class:`collections.OrderedDict` TODO items. """ @@ -90,15 +96,15 @@ def extract_todo_items(root_directory): return todo_items -def export_todo_items(todo_items, file_path): +def export_todo_items(todo_items: OrderedDict, file_path: str): """ Exports TODO items to given file. Parameters ---------- - todo_items : OrderedDict + todo_items TODO items. - file_path : unicode + file_path File to write the TODO items to. """ diff --git a/utilities/unicode_to_ascii.py b/utilities/unicode_to_ascii.py index f59b410..0d0789d 100755 --- a/utilities/unicode_to_ascii.py +++ b/utilities/unicode_to_ascii.py @@ -5,9 +5,12 @@ ======================== """ +from __future__ import annotations + import codecs import os import unicodedata +from typing import Dict __copyright__ = 'Copyright (C) 2015-2021 - Colour Developers' __license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' @@ -15,9 +18,12 @@ __email__ = 'colour-developers@colour-science.org' __status__ = 'Production' -__all__ = ['SUBSTITUTIONS', 'unicode_to_ascii'] +__all__ = [ + 'SUBSTITUTIONS', + 'unicode_to_ascii', +] -SUBSTITUTIONS = { +SUBSTITUTIONS: Dict = { '–': '-', '“': '"', '”': '"', @@ -27,14 +33,14 @@ } -def unicode_to_ascii(root_directory): +def unicode_to_ascii(root_directory: str): """ Recursively converts from unicode to ASCII *.py*, *.bib* and *.rst* files in given directory. Parameters ---------- - root_directory : unicode + root_directory Directory to convert the files from unicode to ASCII. """ From 2195dc02adcd4e8e862b5c03897adede46a48447 Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Wed, 2 Feb 2022 22:09:48 +1300 Subject: [PATCH 23/51] Add issue and pull request templates. --- .github/ISSUE_TEMPLATE/bug-report.yml | 43 +++++++++++++++++++ .../documentation-improvement.yml | 40 +++++++++++++++++ .github/ISSUE_TEMPLATE/feature-request.yml | 18 ++++++++ .github/ISSUE_TEMPLATE/question.yml | 17 ++++++++ .github/PULL_REQUEST_TEMPLATE.md | 31 +++++++++++++ 5 files changed, 149 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug-report.yml create mode 100644 .github/ISSUE_TEMPLATE/documentation-improvement.yml create mode 100644 .github/ISSUE_TEMPLATE/feature-request.yml create mode 100644 .github/ISSUE_TEMPLATE/question.yml create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml new file mode 100644 index 0000000..a315630 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -0,0 +1,43 @@ +name: Bug Report +description: Report an issue or a bug. +title: "[BUG]: << Please use a comprehensive title... >>" +labels: [ Defect ] + +body: + - type: markdown + attributes: + value: > + Thank you for taking the time to file a bug report. Before continuing, please take some time to check the existing [issues](https://github.com/colour-science/colour-demosaicing/issues). + The issue could already be fixed in the [develop](https://github.com/colour-science/colour-demosaicing) branch. If you have an installation problem, the [installation guide](https://www.colour-science.org/installation-guide/) describes the recommended process. + + - type: textarea + attributes: + label: "Description" + description: > + Please describe the issue in a few short sentences. + validations: + required: true + + - type: textarea + attributes: + label: "Code for Reproduction" + description: > + If possible, please provide a minimum self-contained example reproducing the issue. + placeholder: | + << Your code here... >> + render: python + + - type: textarea + attributes: + label: "Exception Message" + description: > + If any, please paste the *full* exception message. + placeholder: | + << Full traceback starting from `Traceback (most recent call last):`... >> + render: shell + + - type: textarea + attributes: + label: "Environment Information" + description: If possible, please paste the output from `import colour; colour.utilities.describe_environment()`. + render: shell diff --git a/.github/ISSUE_TEMPLATE/documentation-improvement.yml b/.github/ISSUE_TEMPLATE/documentation-improvement.yml new file mode 100644 index 0000000..7105a63 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation-improvement.yml @@ -0,0 +1,40 @@ +name: Documentation Improvement +description: Report a documentation improvement. +title: "[DOCUMENTATION]: << Please use a comprehensive title... >>" +labels: [ Documentation ] + +body: + - type: markdown + attributes: + value: > + Thank you for taking the time to file a documentation improvement report. Before continuing, please take some time to check the existing [issues](https://github.com/colour-science/colour-demosaicing/issues). + + - type: input + attributes: + label: Documentation Link + description: > + Please link to any documentation or examples that you are referencing. Suggested improvements should be based on the [development version of the documentation](https://colour.readthedocs.io/en/develop/). + placeholder: > + << https://colour.readthedocs.io/en/develop/... >> + validations: + required: true + + - type: textarea + attributes: + label: Description + description: > + Please describe what is missing, unclear or incorrect. + validations: + required: true + + - type: textarea + attributes: + label: Suggested Improvement + description: > + Please describe how the documentation could be improved. + + - type: textarea + attributes: + label: "Environment Information" + description: If possible, please paste the output from `import colour; colour.utilities.describe_environment()`. + render: shell diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml new file mode 100644 index 0000000..c86bc6d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -0,0 +1,18 @@ +name: Feature Request +description: Suggest a new feature to implement. +title: "[FEATURE]: << Please use a comprehensive title... >>" +labels: [ Feature ] + +body: + - type: markdown + attributes: + value: > + Thank you for taking the time to file a feature request. Before continuing, please take some time to check the existing [issues](https://github.com/colour-science/colour-demosaicing/issues) and also the [draft release notes](https://gist.github.com/KelSolaar/4a6ebe9ec3d389f0934b154fec8df51d). + + - type: textarea + attributes: + label: "Description" + description: > + Please describe the new feature in a few short sentences. + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/question.yml b/.github/ISSUE_TEMPLATE/question.yml new file mode 100644 index 0000000..9d9c125 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/question.yml @@ -0,0 +1,17 @@ +name: Question +description: Ask a question. +title: "[DISCUSSION]: << Please use a comprehensive title... >>" +labels: [ Discussion ] + +body: + - type: markdown + attributes: + value: Thank you for taking the time to ask a question or discuss. Before continuing, we would be glad if you were to start this discussion in the dedicated [discussions](https://github.com/colour-science/colour-demosaicing/discussions) area. + + - type: textarea + attributes: + label: "Question" + description: > + If you are still here, please consider using the dedicated [discussions](https://github.com/colour-science/colour-demosaicing/discussions) area. + placeholder: > + << The discussions area is this way: https://github.com/colour-science/colour-demosaicing/discussions... >> diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..79473e4 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,31 @@ + + +# Summary + +# Preflight + + + +**Code Style and Quality** + +- [ ] Unit tests have been implemented and passed. +- [ ] Mypy static checking has been run and passed. +- [ ] Pre-commit hooks have been run and passed. +- [ ] New transformations have been added to the *Automatic Colour Conversion Graph*. +- [ ] New transformations have been exported to the relevant namespaces, e.g. `colour`, `colour.models`. + + + + +**Documentation** + +- [ ] New features are documented along with examples if relevant. +- [ ] The documentation is [Sphinx](https://www.sphinx-doc.org/en/master/) and [numpydoc](https://numpydoc.readthedocs.io/en/latest/format.html). + + From 2fcacd6b907f98f116c38b36b5fe4dbb729c3f13 Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Wed, 2 Feb 2022 22:12:43 +1300 Subject: [PATCH 24/51] Update "PULL_REQUEST_TEMPLATE.md" file. --- .github/PULL_REQUEST_TEMPLATE.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 79473e4..fafc925 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -15,11 +15,9 @@ is available to guide the process: https://www.colour-science.org/contributing/. - [ ] Unit tests have been implemented and passed. - [ ] Mypy static checking has been run and passed. - [ ] Pre-commit hooks have been run and passed. -- [ ] New transformations have been added to the *Automatic Colour Conversion Graph*. -- [ ] New transformations have been exported to the relevant namespaces, e.g. `colour`, `colour.models`. - + **Documentation** From 2c4f024c6ad0580f540f413e516689c929b94085 Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Wed, 2 Feb 2022 22:28:29 +1300 Subject: [PATCH 25/51] Use correct documentation url in issue templates. --- .github/ISSUE_TEMPLATE/documentation-improvement.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/documentation-improvement.yml b/.github/ISSUE_TEMPLATE/documentation-improvement.yml index 7105a63..50bc0ba 100644 --- a/.github/ISSUE_TEMPLATE/documentation-improvement.yml +++ b/.github/ISSUE_TEMPLATE/documentation-improvement.yml @@ -13,9 +13,9 @@ body: attributes: label: Documentation Link description: > - Please link to any documentation or examples that you are referencing. Suggested improvements should be based on the [development version of the documentation](https://colour.readthedocs.io/en/develop/). + Please link to any documentation or examples that you are referencing. Suggested improvements should be based on the [development version of the documentation](https://colour-demosaicing.readthedocs.io/en/develop/). placeholder: > - << https://colour.readthedocs.io/en/develop/... >> + << https://colour-demosaicing.readthedocs.io/en/develop/... >> validations: required: true From 6bc358d7129afc0a94b4b2fa27c2930aee5195c4 Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Sun, 6 Feb 2022 15:36:32 +1300 Subject: [PATCH 26/51] Update "PULL_REQUEST_TEMPLATE.md" file. --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index fafc925..24e0e77 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -22,7 +22,7 @@ is available to guide the process: https://www.colour-science.org/contributing/. **Documentation** - [ ] New features are documented along with examples if relevant. -- [ ] The documentation is [Sphinx](https://www.sphinx-doc.org/en/master/) and [numpydoc](https://numpydoc.readthedocs.io/en/latest/format.html). +- [ ] The documentation is [Sphinx](https://www.sphinx-doc.org/en/master/) and [numpydoc](https://numpydoc.readthedocs.io/en/latest/format.html) compliant. + # Preflight From 3dea45e05830afa8682a99e609a87bb13e3cd183 Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Tue, 8 Feb 2022 19:24:03 +1300 Subject: [PATCH 28/51] Implement typing support in "colour_demosaicing" package. --- colour_demosaicing/__init__.py | 21 ++++--- .../bayer/demosaicing/bilinear.py | 35 ++++++----- .../bayer/demosaicing/malvar2004.py | 56 ++++++++++-------- .../bayer/demosaicing/menon2007.py | 59 ++++++++++--------- .../bayer/demosaicing/tests/test_bilinear.py | 6 +- .../demosaicing/tests/test_malvar2004.py | 6 +- .../bayer/demosaicing/tests/test_menon2007.py | 6 +- colour_demosaicing/bayer/masks.py | 21 +++++-- colour_demosaicing/bayer/mosaicing.py | 15 +++-- colour_demosaicing/bayer/tests/test_masks.py | 6 +- .../bayer/tests/test_mosaicing.py | 6 +- 11 files changed, 142 insertions(+), 95 deletions(-) diff --git a/colour_demosaicing/__init__.py b/colour_demosaicing/__init__.py index 79666f0..22875e9 100644 --- a/colour_demosaicing/__init__.py +++ b/colour_demosaicing/__init__.py @@ -10,6 +10,8 @@ - bayer: *Bayer* CFA mosaicing and demosaicing computations. """ +from __future__ import annotations + import numpy as np import os import subprocess # nosec @@ -41,11 +43,11 @@ 'mosaicing_CFA_Bayer', ] -RESOURCES_DIRECTORY = os.path.join(os.path.dirname(__file__), 'resources') -EXAMPLES_RESOURCES_DIRECTORY = os.path.join( +RESOURCES_DIRECTORY: str = os.path.join(os.path.dirname(__file__), 'resources') +EXAMPLES_RESOURCES_DIRECTORY: str = os.path.join( RESOURCES_DIRECTORY, 'colour-demosaicing-examples-datasets') -TESTS_RESOURCES_DIRECTORY = os.path.join(RESOURCES_DIRECTORY, - 'colour-demosaicing-tests-datasets') +TESTS_RESOURCES_DIRECTORY: str = os.path.join( + RESOURCES_DIRECTORY, 'colour-demosaicing-tests-datasets') __application_name__ = 'Colour - Demosaicing' @@ -58,16 +60,17 @@ __change_version__)) # yapf: disable try: - version = subprocess.check_output( # nosec + _version: str = subprocess.check_output( # nosec ['git', 'describe'], cwd=os.path.dirname(__file__), - stderr=subprocess.STDOUT).strip() - version = version.decode('utf-8') + stderr=subprocess.STDOUT).strip().decode('utf-8') except Exception: - version = __version__ + _version: str = __version__ # type: ignore[no-redef] colour.utilities.ANCILLARY_COLOUR_SCIENCE_PACKAGES['colour-demosaicing'] = ( - version) + _version) + +del _version # TODO: Remove legacy printing support when deemed appropriate. try: diff --git a/colour_demosaicing/bayer/demosaicing/bilinear.py b/colour_demosaicing/bayer/demosaicing/bilinear.py index 97b1182..35bf3d3 100644 --- a/colour_demosaicing/bayer/demosaicing/bilinear.py +++ b/colour_demosaicing/bayer/demosaicing/bilinear.py @@ -12,8 +12,11 @@ Electron Physics (Vol. 162, pp. 173-265). doi:10.1016/S1076-5670(10)62005-8 """ +from __future__ import annotations + from scipy.ndimage.filters import convolve +from colour.hints import ArrayLike, Literal, NDArray, Union from colour.utilities import as_float_array, tstack from colour_demosaicing.bayer import masks_CFA_Bayer @@ -30,22 +33,24 @@ ] -def demosaicing_CFA_Bayer_bilinear(CFA, pattern='RGGB'): +def demosaicing_CFA_Bayer_bilinear( + CFA: ArrayLike, + pattern: Union[Literal['RGGB', 'BGGR', 'GRBG', 'GBRG'], str] = 'RGGB' +) -> NDArray: """ Returns the demosaiced *RGB* colourspace array from given *Bayer* CFA using bilinear interpolation. Parameters ---------- - CFA : array_like + CFA *Bayer* CFA. - pattern : str, optional - **{'RGGB', 'BGGR', 'GRBG', 'GBRG'}**, + pattern Arrangement of the colour filters on the pixel array. Returns ------- - ndarray + :class:`numpy.ndarray` *RGB* colourspace array. Notes @@ -95,15 +100,17 @@ def demosaicing_CFA_Bayer_bilinear(CFA, pattern='RGGB'): CFA = as_float_array(CFA) R_m, G_m, B_m = masks_CFA_Bayer(CFA.shape, pattern) - H_G = as_float_array( - [[0, 1, 0], - [1, 4, 1], - [0, 1, 0]]) / 4 # yapf: disable - - H_RB = as_float_array( - [[1, 2, 1], - [2, 4, 2], - [1, 2, 1]]) / 4 # yapf: disable + H_G = as_float_array([ + [0, 1, 0], + [1, 4, 1], + [0, 1, 0], + ]) / 4 + + H_RB = as_float_array([ + [1, 2, 1], + [2, 4, 2], + [1, 2, 1], + ]) / 4 R = convolve(CFA * R_m, H_RB) G = convolve(CFA * G_m, H_G) diff --git a/colour_demosaicing/bayer/demosaicing/malvar2004.py b/colour_demosaicing/bayer/demosaicing/malvar2004.py index 09aa1d2..270bbdf 100644 --- a/colour_demosaicing/bayer/demosaicing/malvar2004.py +++ b/colour_demosaicing/bayer/demosaicing/malvar2004.py @@ -14,9 +14,12 @@ http://research.microsoft.com/apps/pubs/default.aspx?id=102068 """ +from __future__ import annotations + import numpy as np from scipy.ndimage.filters import convolve +from colour.hints import ArrayLike, Literal, NDArray, Union from colour.utilities import as_float_array, tstack from colour_demosaicing.bayer import masks_CFA_Bayer @@ -33,22 +36,24 @@ ] -def demosaicing_CFA_Bayer_Malvar2004(CFA, pattern='RGGB'): +def demosaicing_CFA_Bayer_Malvar2004( + CFA: ArrayLike, + pattern: Union[Literal['RGGB', 'BGGR', 'GRBG', 'GBRG'], str] = 'RGGB' +) -> NDArray: """ Returns the demosaiced *RGB* colourspace array from given *Bayer* CFA using *Malvar (2004)* demosaicing algorithm. Parameters ---------- - CFA : array_like + CFA *Bayer* CFA. - pattern : str, optional - **{'RGGB', 'BGGR', 'GRBG', 'GBRG'}**, + pattern Arrangement of the colour filters on the pixel array. Returns ------- - ndarray + :class:`numpy.ndarray` *RGB* colourspace array. Notes @@ -97,28 +102,31 @@ def demosaicing_CFA_Bayer_Malvar2004(CFA, pattern='RGGB'): CFA = as_float_array(CFA) R_m, G_m, B_m = masks_CFA_Bayer(CFA.shape, pattern) - GR_GB = as_float_array( - [[0, 0, -1, 0, 0], - [0, 0, 2, 0, 0], - [-1, 2, 4, 2, -1], - [0, 0, 2, 0, 0], - [0, 0, -1, 0, 0]]) / 8 # yapf: disable - - Rg_RB_Bg_BR = as_float_array( - [[0, 0, 0.5, 0, 0], - [0, -1, 0, -1, 0], - [-1, 4, 5, 4, - 1], - [0, -1, 0, -1, 0], - [0, 0, 0.5, 0, 0]]) / 8 # yapf: disable + GR_GB = as_float_array([ + [0.0, 0.0, -1.0, 0.0, 0.0], + [0.0, 0.0, 2.0, 0.0, 0.0], + [-1.0, 2.0, 4.0, 2.0, -1.0], + [0.0, 0.0, 2.0, 0.0, 0.0], + [0.0, 0.0, -1.0, 0.0, 0.0], + ]) / 8 + + Rg_RB_Bg_BR = as_float_array([ + [0.0, 0.0, 0.5, 0.0, 0.0], + [0.0, -1.0, 0.0, -1.0, 0.0], + [-1.0, 4.0, 5.0, 4.0, -1.0], + [0.0, -1.0, 0.0, -1.0, 0.0], + [0.0, 0.0, 0.5, 0.0, 0.0], + ]) / 8 Rg_BR_Bg_RB = np.transpose(Rg_RB_Bg_BR) - Rb_BB_Br_RR = as_float_array( - [[0, 0, -1.5, 0, 0], - [0, 2, 0, 2, 0], - [-1.5, 0, 6, 0, -1.5], - [0, 2, 0, 2, 0], - [0, 0, -1.5, 0, 0]]) / 8 # yapf: disable + Rb_BB_Br_RR = as_float_array([ + [0.0, 0.0, -1.5, 0.0, 0.0], + [0.0, 2.0, 0.0, 2.0, 0.0], + [-1.5, 0.0, 6.0, 0.0, -1.5], + [0.0, 2.0, 0.0, 2.0, 0.0], + [0.0, 0.0, -1.5, 0.0, 0.0], + ]) / 8 R = CFA * R_m G = CFA * G_m diff --git a/colour_demosaicing/bayer/demosaicing/menon2007.py b/colour_demosaicing/bayer/demosaicing/menon2007.py index 95c0f94..e2dae2f 100644 --- a/colour_demosaicing/bayer/demosaicing/menon2007.py +++ b/colour_demosaicing/bayer/demosaicing/menon2007.py @@ -13,9 +13,12 @@ doi:10.1109/TIP.2006.884928 """ +from __future__ import annotations + import numpy as np from scipy.ndimage.filters import convolve, convolve1d +from colour.hints import ArrayLike, Boolean, Literal, NDArray, Union from colour.utilities import as_float_array, tsplit, tstack from colour_demosaicing.bayer import masks_CFA_Bayer @@ -34,7 +37,7 @@ ] -def _cnv_h(x, y): +def _cnv_h(x: ArrayLike, y: ArrayLike) -> NDArray: """ Helper function for horizontal convolution. """ @@ -42,7 +45,7 @@ def _cnv_h(x, y): return convolve1d(x, y, mode='mirror') -def _cnv_v(x, y): +def _cnv_v(x: ArrayLike, y: ArrayLike) -> NDArray: """ Helper function for vertical convolution. """ @@ -50,24 +53,26 @@ def _cnv_v(x, y): return convolve1d(x, y, mode='mirror', axis=0) -def demosaicing_CFA_Bayer_Menon2007(CFA, pattern='RGGB', refining_step=True): +def demosaicing_CFA_Bayer_Menon2007( + CFA: ArrayLike, + pattern: Union[Literal['RGGB', 'BGGR', 'GRBG', 'GBRG'], str] = 'RGGB', + refining_step: Boolean = True): """ Returns the demosaiced *RGB* colourspace array from given *Bayer* CFA using DDFAPD - *Menon (2007)* demosaicing algorithm. Parameters ---------- - CFA : array_like + CFA *Bayer* CFA. - pattern : str, optional - **{'RGGB', 'BGGR', 'GRBG', 'GBRG'}**, + pattern Arrangement of the colour filters on the pixel array. - refining_step : bool + refining_step Perform refining step. Returns ------- - ndarray + :class:`numpy.ndarray` *RGB* colourspace array. Notes @@ -116,8 +121,8 @@ def demosaicing_CFA_Bayer_Menon2007(CFA, pattern='RGGB', refining_step=True): CFA = as_float_array(CFA) R_m, G_m, B_m = masks_CFA_Bayer(CFA.shape, pattern) - h_0 = np.array([0, 0.5, 0, 0.5, 0]) - h_1 = np.array([-0.25, 0, 0.5, 0, -0.25]) + h_0 = as_float_array([0.0, 0.5, 0.0, 0.5, 0.0]) + h_1 = as_float_array([-0.25, 0.0, 0.5, 0.0, -0.25]) R = CFA * R_m G = CFA * G_m @@ -132,19 +137,18 @@ def demosaicing_CFA_Bayer_Menon2007(CFA, pattern='RGGB', refining_step=True): C_V = np.where(R_m == 1, R - G_V, 0) C_V = np.where(B_m == 1, B - G_V, C_V) - D_H = np.abs(C_H - np.pad(C_H, ((0, 0), - (0, 2)), mode=str('reflect'))[:, 2:]) - D_V = np.abs(C_V - np.pad(C_V, ((0, 2), - (0, 0)), mode=str('reflect'))[2:, :]) + D_H = np.abs(C_H - np.pad(C_H, ((0, 0), (0, 2)), mode="reflect")[:, 2:]) + D_V = np.abs(C_V - np.pad(C_V, ((0, 2), (0, 0)), mode="reflect")[2:, :]) del h_0, h_1, CFA, C_V, C_H - k = np.array( - [[0, 0, 1, 0, 1], - [0, 0, 0, 1, 0], - [0, 0, 3, 0, 3], - [0, 0, 0, 1, 0], - [0, 0, 1, 0, 1]]) # yapf: disable + k = as_float_array([ + [0.0, 0.0, 1.0, 0.0, 1.0], + [0.0, 0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 3.0, 0.0, 3.0], + [0.0, 0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 1.0, 0.0, 1.0], + ]) d_H = convolve(D_H, k, mode='constant') d_V = convolve(D_V, np.transpose(k), mode='constant') @@ -162,7 +166,7 @@ def demosaicing_CFA_Bayer_Menon2007(CFA, pattern='RGGB', refining_step=True): # Blue rows. B_r = np.transpose(np.any(B_m == 1, axis=1)[np.newaxis]) * np.ones(B.shape) - k_b = np.array([0.5, 0, 0.5]) + k_b = as_float_array([0.5, 0, 0.5]) R = np.where( np.logical_and(G_m == 1, R_r == 1), @@ -223,22 +227,23 @@ def demosaicing_CFA_Bayer_Menon2007(CFA, pattern='RGGB', refining_step=True): demosaicing_CFA_Bayer_DDFAPD = demosaicing_CFA_Bayer_Menon2007 -def refining_step_Menon2007(RGB, RGB_m, M): +def refining_step_Menon2007(RGB: ArrayLike, RGB_m: ArrayLike, + M: ArrayLike) -> NDArray: """ Performs the refining step on given *RGB* colourspace array. Parameters ---------- - RGB : array_like + RGB *RGB* colourspace array. - RGB_m : array_like + RGB_m *Bayer* CFA red, green and blue masks. - M : array_like + M Estimation for the best directional reconstruction. Returns ------- - ndarray + :class:`numpy.ndarray` Refined *RGB* colourspace array. Examples @@ -317,7 +322,7 @@ def refining_step_Menon2007(RGB, RGB_m, M): R_G = R - G B_G = B - G - k_b = np.array([0.5, 0, 0.5]) + k_b = as_float_array([0.5, 0.0, 0.5]) R_G_m = np.where( np.logical_and(G_m == 1, B_r == 1), diff --git a/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py b/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py index b5f6702..d9fee96 100644 --- a/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py +++ b/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py @@ -5,6 +5,8 @@ :mod:`colour_demosaicing.bayer.demosaicing.bilinear` module. """ +from __future__ import annotations + import numpy as np import os import unittest @@ -26,8 +28,8 @@ 'TestDemosaicing_CFA_Bayer_bilinear', ] -BAYER_DIRECTORY = os.path.join(TESTS_RESOURCES_DIRECTORY, 'colour_demosaicing', - 'bayer') +BAYER_DIRECTORY: str = os.path.join(TESTS_RESOURCES_DIRECTORY, + 'colour_demosaicing', 'bayer') class TestDemosaicing_CFA_Bayer_bilinear(unittest.TestCase): diff --git a/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py b/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py index 10301ec..4bc2a58 100644 --- a/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py +++ b/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py @@ -5,6 +5,8 @@ :mod:`colour_demosaicing.bayer.demosaicing.malvar2004` module. """ +from __future__ import annotations + import numpy as np import os import unittest @@ -26,8 +28,8 @@ 'TestDemosaicing_CFA_Bayer_Malvar2004', ] -BAYER_DIRECTORY = os.path.join(TESTS_RESOURCES_DIRECTORY, 'colour_demosaicing', - 'bayer') +BAYER_DIRECTORY: str = os.path.join(TESTS_RESOURCES_DIRECTORY, + 'colour_demosaicing', 'bayer') class TestDemosaicing_CFA_Bayer_Malvar2004(unittest.TestCase): diff --git a/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py b/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py index 3029fda..8e99080 100644 --- a/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py +++ b/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py @@ -5,6 +5,8 @@ :mod:`colour_demosaicing.bayer.demosaicing.menon2007` module. """ +from __future__ import annotations + import numpy as np import os import unittest @@ -26,8 +28,8 @@ 'TestDemosaicing_CFA_Bayer_Menon2007', ] -BAYER_DIRECTORY = os.path.join(TESTS_RESOURCES_DIRECTORY, 'colour_demosaicing', - 'bayer') +BAYER_DIRECTORY: str = os.path.join(TESTS_RESOURCES_DIRECTORY, + 'colour_demosaicing', 'bayer') class TestDemosaicing_CFA_Bayer_Menon2007(unittest.TestCase): diff --git a/colour_demosaicing/bayer/masks.py b/colour_demosaicing/bayer/masks.py index 922e829..50d937b 100644 --- a/colour_demosaicing/bayer/masks.py +++ b/colour_demosaicing/bayer/masks.py @@ -6,8 +6,13 @@ *Bayer* CFA (Colour Filter Array) masks generation. """ +from __future__ import annotations + import numpy as np +from colour.hints import Literal, NDArray, Tuple, Union +from colour.utilities import validate_method + __author__ = 'Colour Developers' __copyright__ = 'Copyright (C) 2015-2021 - Colour Developers' __license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' @@ -20,21 +25,23 @@ ] -def masks_CFA_Bayer(shape, pattern='RGGB'): +def masks_CFA_Bayer( + shape: Union[int, Tuple[int, ...]], + pattern: Union[Literal['RGGB', 'BGGR', 'GRBG', 'GBRG'], str] = 'RGGB' +) -> Tuple[NDArray, ...]: """ Returns the *Bayer* CFA red, green and blue masks for given pattern. Parameters ---------- - shape : array_like + shape Dimensions of the *Bayer* CFA. - pattern : str, optional - **{'RGGB', 'BGGR', 'GRBG', 'GBRG'}**, + pattern Arrangement of the colour filters on the pixel array. Returns ------- - tuple + :class:`tuple` *Bayer* CFA red, green and blue masks. Examples @@ -63,7 +70,9 @@ def masks_CFA_Bayer(shape, pattern='RGGB'): [ True, False, True]], dtype=bool)) """ - pattern = pattern.upper() + pattern = validate_method( + pattern, ['RGGB', 'BGGR', 'GRBG', 'GBRG'], + '"{0}" CFA pattern is invalid, it must be one of {1}!').upper() channels = dict((channel, np.zeros(shape)) for channel in 'RGB') for channel, (y, x) in zip(pattern, [(0, 0), (0, 1), (1, 0), (1, 1)]): diff --git a/colour_demosaicing/bayer/mosaicing.py b/colour_demosaicing/bayer/mosaicing.py index b85f6e4..8887cf2 100644 --- a/colour_demosaicing/bayer/mosaicing.py +++ b/colour_demosaicing/bayer/mosaicing.py @@ -6,6 +6,9 @@ *Bayer* CFA (Colour Filter Array) data generation. """ +from __future__ import annotations + +from colour.hints import ArrayLike, Literal, NDArray, Union from colour.utilities import as_float_array, tsplit from colour_demosaicing.bayer import masks_CFA_Bayer @@ -22,21 +25,23 @@ ] -def mosaicing_CFA_Bayer(RGB, pattern='RGGB'): +def mosaicing_CFA_Bayer( + RGB: ArrayLike, + pattern: Union[Literal['RGGB', 'BGGR', 'GRBG', 'GBRG'], str] = 'RGGB' +) -> NDArray: """ Returns the *Bayer* CFA mosaic for a given *RGB* colourspace array. Parameters ---------- - RGB : array_like + RGB *RGB* colourspace array. - pattern : str, optional - **{'RGGB', 'BGGR', 'GRBG', 'GBRG'}**, + pattern Arrangement of the colour filters on the pixel array. Returns ------- - ndarray + :class:`numpy.ndarray` *Bayer* CFA mosaic. Examples diff --git a/colour_demosaicing/bayer/tests/test_masks.py b/colour_demosaicing/bayer/tests/test_masks.py index d6fbc49..e968356 100644 --- a/colour_demosaicing/bayer/tests/test_masks.py +++ b/colour_demosaicing/bayer/tests/test_masks.py @@ -4,6 +4,8 @@ Defines the unit tests for the :mod:`colour_demosaicing.bayer.masks` module. """ +from __future__ import annotations + import numpy as np import os import unittest @@ -26,8 +28,8 @@ 'TestMasks_CFA_Bayer', ] -BAYER_DIRECTORY = os.path.join(TESTS_RESOURCES_DIRECTORY, 'colour_demosaicing', - 'bayer') +BAYER_DIRECTORY: str = os.path.join(TESTS_RESOURCES_DIRECTORY, + 'colour_demosaicing', 'bayer') class TestMasks_CFA_Bayer(unittest.TestCase): diff --git a/colour_demosaicing/bayer/tests/test_mosaicing.py b/colour_demosaicing/bayer/tests/test_mosaicing.py index 5b26c36..a34438b 100644 --- a/colour_demosaicing/bayer/tests/test_mosaicing.py +++ b/colour_demosaicing/bayer/tests/test_mosaicing.py @@ -5,6 +5,8 @@ module. """ +from __future__ import annotations + import numpy as np import os import unittest @@ -26,8 +28,8 @@ 'TestMosaicing_CFA_Bayer', ] -BAYER_DIRECTORY = os.path.join(TESTS_RESOURCES_DIRECTORY, 'colour_demosaicing', - 'bayer') +BAYER_DIRECTORY: str = os.path.join(TESTS_RESOURCES_DIRECTORY, + 'colour_demosaicing', 'bayer') class TestMosaicing_CFA_Bayer(unittest.TestCase): From 3051ea550392636c5255b1b89868ce22b7782b21 Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Sun, 16 Jan 2022 19:55:09 +1300 Subject: [PATCH 29/51] Add "py.typed" file. --- colour_demosaicing/py.typed | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 colour_demosaicing/py.typed diff --git a/colour_demosaicing/py.typed b/colour_demosaicing/py.typed new file mode 100644 index 0000000..e69de29 From 0fec1a18d64ed0d7c90fbabe9042ff3e5d38a56a Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Sun, 30 Jan 2022 15:21:09 +1300 Subject: [PATCH 30/51] Upgrade development process. --- .flake8 | 3 + ...tinuous-integration-quality-unit-tests.yml | 8 +- .pre-commit-config.yaml | 20 +- .style.yapf | 180 -------- colour_demosaicing/__init__.py | 68 +-- colour_demosaicing/bayer/__init__.py | 6 +- .../bayer/demosaicing/__init__.py | 10 +- .../bayer/demosaicing/bilinear.py | 51 ++- .../bayer/demosaicing/malvar2004.py | 78 ++-- .../bayer/demosaicing/menon2007.py | 55 +-- .../bayer/demosaicing/tests/__init__.py | 1 - .../bayer/demosaicing/tests/test_bilinear.py | 36 +- .../demosaicing/tests/test_malvar2004.py | 39 +- .../bayer/demosaicing/tests/test_menon2007.py | 47 +- colour_demosaicing/bayer/masks.py | 29 +- colour_demosaicing/bayer/mosaicing.py | 19 +- colour_demosaicing/bayer/tests/__init__.py | 1 - colour_demosaicing/bayer/tests/test_masks.py | 31 +- .../bayer/tests/test_mosaicing.py | 34 +- docs/conf.py | 178 ++++---- pyproject.toml | 59 ++- tasks.py | 400 +++++++++--------- utilities/export_todo.py | 53 +-- utilities/unicode_to_ascii.py | 52 +-- 24 files changed, 690 insertions(+), 768 deletions(-) create mode 100644 .flake8 delete mode 100644 .style.yapf diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..8dd399a --- /dev/null +++ b/.flake8 @@ -0,0 +1,3 @@ +[flake8] +max-line-length = 88 +extend-ignore = E203 diff --git a/.github/workflows/continuous-integration-quality-unit-tests.yml b/.github/workflows/continuous-integration-quality-unit-tests.yml index f160aca..5a1c72e 100644 --- a/.github/workflows/continuous-integration-quality-unit-tests.yml +++ b/.github/workflows/continuous-integration-quality-unit-tests.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: os: [macOS-latest, ubuntu-20.04, windows-latest] - python-version: [3.7, 3.8, 3.9] + python-version: [3.8, 3.9, '3.10'] fail-fast: false runs-on: ${{ matrix.os }} steps: @@ -35,7 +35,7 @@ jobs: - name: Install Poetry run: | curl -L https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py -o get-poetry.py - python get-poetry.py --version 1.0.10 + python get-poetry.py echo "$HOME/.poetry/bin" >> $GITHUB_PATH shell: bash - name: Install Package Dependencies @@ -52,9 +52,9 @@ jobs: run: | poetry run python -OO -c "import $CI_PACKAGE" shell: bash - - name: Test with nosetests + - name: Test with Pytest run: | - poetry run python -W ignore -m nose -q -v --with-doctest --doctest-options=+ELLIPSIS --with-coverage --traverse-namespace --cover-package=$CI_PACKAGE $CI_PACKAGE + poetry run python -W ignore -m py.test --disable-warnings --doctest-modules --ignore=$CI_PACKAGE/examples --cov=$CI_PACKAGE $CI_PACKAGE shell: bash - name: Upload Coverage to coveralls.io run: | diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 02bc48e..340cfa3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,11 +1,19 @@ repos: +- repo: https://github.com/asottile/pyupgrade + rev: v2.31.0 + hooks: + - id: pyupgrade + args: [--py38-plus] +- repo: https://github.com/ikamensh/flynt/ + rev: '0.76' + hooks: + - id: flynt +- repo: https://github.com/psf/black + rev: stable + hooks: + - id: black + language_version: python3.8 - repo: https://gitlab.com/pycqa/flake8 rev: 4.0.1 hooks: - id: flake8 - exclude: examples|setup\.py -- repo: https://github.com/pre-commit/mirrors-yapf - rev: v0.23.0 - hooks: - - id: yapf - exclude: setup\.py diff --git a/.style.yapf b/.style.yapf deleted file mode 100644 index 45e56e9..0000000 --- a/.style.yapf +++ /dev/null @@ -1,180 +0,0 @@ -[style] -# Align closing bracket with visual indentation. -align_closing_bracket_with_visual_indent=True - -# Allow dictionary keys to exist on multiple lines. For example: -# -# x = { -# ('this is the first element of a tuple', -# 'this is the second element of a tuple'): -# value, -# } -allow_multiline_dictionary_keys=False - -# Allow lambdas to be formatted on more than one line. -allow_multiline_lambdas=False - -# Insert a blank line before a class-level docstring. -blank_line_before_class_docstring=False - -# Insert a blank line before a 'def' or 'class' immediately nested -# within another 'def' or 'class'. For example: -# -# class Foo: -# # <------ this blank line -# def method(): -# ... -blank_line_before_nested_class_or_def=False - -# Do not split consecutive brackets. Only relevant when -# dedent_closing_brackets is set. For example: -# -# call_func_that_takes_a_dict( -# { -# 'key1': 'value1', -# 'key2': 'value2', -# } -# ) -# -# would reformat to: -# -# call_func_that_takes_a_dict({ -# 'key1': 'value1', -# 'key2': 'value2', -# }) -coalesce_brackets=False - -# The column limit. -column_limit=79 - -# Indent width used for line continuations. -continuation_indent_width=4 - -# Put closing brackets on a separate line, dedented, if the bracketed -# expression can't fit in a single line. Applies to all kinds of brackets, -# including function definitions and calls. For example: -# -# config = { -# 'key1': 'value1', -# 'key2': 'value2', -# } # <--- this bracket is dedented and on a separate line -# -# time_series = self.remote_client.query_entity_counters( -# entity='dev3246.region1', -# key='dns.query_latency_tcp', -# transform=Transformation.AVERAGE(window=timedelta(seconds=60)), -# start_ts=now()-timedelta(days=3), -# end_ts=now(), -# ) # <--- this bracket is dedented and on a separate line -dedent_closing_brackets=False - -# Place each dictionary entry onto its own line. -each_dict_entry_on_separate_line=True - -# The regex for an i18n comment. The presence of this comment stops -# reformatting of that line, because the comments are required to be -# next to the string they translate. -i18n_comment= - -# The i18n function call names. The presence of this function stops -# reformattting on that line, because the string it has cannot be moved -# away from the i18n comment. -i18n_function_call= - -# Indent the dictionary value if it cannot fit on the same line as the -# dictionary key. For example: -# -# config = { -# 'key1': -# 'value1', -# 'key2': value1 + -# value2, -# } -indent_dictionary_value=True - -# The number of columns to use for indentation. -indent_width=4 - -# Join short lines into one line. E.g., single line 'if' statements. -join_multiple_lines=True - -# Use spaces around default or named assigns. -spaces_around_default_or_named_assign=False - -# Use spaces around the power operator. -spaces_around_power_operator=True - -# The number of spaces required before a trailing comment. -spaces_before_comment=2 - -# Insert a space between the ending comma and closing bracket of a list, -# etc. -space_between_ending_comma_and_closing_bracket=True - -# Split before arguments if the argument list is terminated by a -# comma. -split_arguments_when_comma_terminated=False - -# Set to True to prefer splitting before '&', '|' or '^' rather than -# after. -split_before_bitwise_operator=False - -# Split before a dictionary or set generator (comp_for). For example, note -# the split before the 'for': -# -# foo = { -# variable: 'Hello world, have a nice day!' -# for variable in bar if variable != 42 -# } -split_before_dict_set_generator=True - -# If an argument / parameter list is going to be split, then split before -# the first argument. -split_before_first_argument=False - -# Set to True to prefer splitting before 'and' or 'or' rather than -# after. -split_before_logical_operator=False - -# Split named assignments onto individual lines. -split_before_named_assigns=True - -# The penalty for splitting right after the opening bracket. -split_penalty_after_opening_bracket=30 - -# The penalty for splitting the line after a unary operator. -split_penalty_after_unary_operator=10000 - -# The penalty for splitting right before an if expression. -split_penalty_before_if_expr=0 - -# The penalty of splitting the line around the '&', '|', and '^' -# operators. -split_penalty_bitwise_operator=300 - -# The penalty for characters over the column limit. -split_penalty_excess_character=4500 - -# The penalty incurred by adding a line split to the unwrapped line. The -# more line splits added the higher the penalty. -split_penalty_for_added_line_split=30 - -# The penalty of splitting a list of "import as" names. For example: -# -# from a_very_long_or_indented_module_name_yada_yad import (long_argument_1, -# long_argument_2, -# long_argument_3) -# -# would reformat to something like: -# -# from a_very_long_or_indented_module_name_yada_yad import ( -# long_argument_1, long_argument_2, long_argument_3) -split_penalty_import_names=0 - -# The penalty of splitting the line around the 'and' and 'or' -# operators. -split_penalty_logical_operator=300 - -# Use the Tab character for indentation. -use_tabs=False - diff --git a/colour_demosaicing/__init__.py b/colour_demosaicing/__init__.py index 22875e9..8376566 100644 --- a/colour_demosaicing/__init__.py +++ b/colour_demosaicing/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Colour - Demosaicing ==================== @@ -27,53 +26,60 @@ mosaicing_CFA_Bayer, ) -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2015-2021 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +__author__ = "Colour Developers" +__copyright__ = "Copyright (C) 2015-2021 - Colour Developers" +__license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" +__maintainer__ = "Colour Developers" +__email__ = "colour-developers@colour-science.org" +__status__ = "Production" __all__ = [ - 'demosaicing_CFA_Bayer_bilinear', - 'demosaicing_CFA_Bayer_DDFAPD', - 'demosaicing_CFA_Bayer_Malvar2004', - 'demosaicing_CFA_Bayer_Menon2007', - 'masks_CFA_Bayer', - 'mosaicing_CFA_Bayer', + "demosaicing_CFA_Bayer_bilinear", + "demosaicing_CFA_Bayer_DDFAPD", + "demosaicing_CFA_Bayer_Malvar2004", + "demosaicing_CFA_Bayer_Menon2007", + "masks_CFA_Bayer", + "mosaicing_CFA_Bayer", ] -RESOURCES_DIRECTORY: str = os.path.join(os.path.dirname(__file__), 'resources') +RESOURCES_DIRECTORY: str = os.path.join(os.path.dirname(__file__), "resources") EXAMPLES_RESOURCES_DIRECTORY: str = os.path.join( - RESOURCES_DIRECTORY, 'colour-demosaicing-examples-datasets') + RESOURCES_DIRECTORY, "colour-demosaicing-examples-datasets" +) TESTS_RESOURCES_DIRECTORY: str = os.path.join( - RESOURCES_DIRECTORY, 'colour-demosaicing-tests-datasets') + RESOURCES_DIRECTORY, "colour-demosaicing-tests-datasets" +) -__application_name__ = 'Colour - Demosaicing' +__application_name__ = "Colour - Demosaicing" -__major_version__ = '0' -__minor_version__ = '1' -__change_version__ = '6' -__version__ = '.'.join( - (__major_version__, - __minor_version__, - __change_version__)) # yapf: disable +__major_version__ = "0" +__minor_version__ = "1" +__change_version__ = "6" +__version__ = ".".join( + (__major_version__, __minor_version__, __change_version__) +) try: - _version: str = subprocess.check_output( # nosec - ['git', 'describe'], - cwd=os.path.dirname(__file__), - stderr=subprocess.STDOUT).strip().decode('utf-8') + _version: str = ( + subprocess.check_output( # nosec + ["git", "describe"], + cwd=os.path.dirname(__file__), + stderr=subprocess.STDOUT, + ) + .strip() + .decode("utf-8") + ) except Exception: _version: str = __version__ # type: ignore[no-redef] -colour.utilities.ANCILLARY_COLOUR_SCIENCE_PACKAGES['colour-demosaicing'] = ( - _version) +colour.utilities.ANCILLARY_COLOUR_SCIENCE_PACKAGES[ + "colour-demosaicing" +] = _version del _version # TODO: Remove legacy printing support when deemed appropriate. try: - np.set_printoptions(legacy='1.13') + np.set_printoptions(legacy="1.13") except TypeError: pass diff --git a/colour_demosaicing/bayer/__init__.py b/colour_demosaicing/bayer/__init__.py index cc6932a..c655646 100644 --- a/colour_demosaicing/bayer/__init__.py +++ b/colour_demosaicing/bayer/__init__.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - from .masks import masks_CFA_Bayer from .mosaicing import mosaicing_CFA_Bayer from .demosaicing import * # noqa @@ -7,9 +5,9 @@ __all__ = [] __all__ += [ - 'masks_CFA_Bayer', + "masks_CFA_Bayer", ] __all__ += [ - 'mosaicing_CFA_Bayer', + "mosaicing_CFA_Bayer", ] __all__ += demosaicing.__all__ diff --git a/colour_demosaicing/bayer/demosaicing/__init__.py b/colour_demosaicing/bayer/demosaicing/__init__.py index e01604e..c04abac 100644 --- a/colour_demosaicing/bayer/demosaicing/__init__.py +++ b/colour_demosaicing/bayer/demosaicing/__init__.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - from .bilinear import demosaicing_CFA_Bayer_bilinear from .malvar2004 import demosaicing_CFA_Bayer_Malvar2004 from .menon2007 import ( @@ -9,12 +7,12 @@ __all__ = [] __all__ += [ - 'demosaicing_CFA_Bayer_bilinear', + "demosaicing_CFA_Bayer_bilinear", ] __all__ += [ - 'demosaicing_CFA_Bayer_Malvar2004', + "demosaicing_CFA_Bayer_Malvar2004", ] __all__ += [ - 'demosaicing_CFA_Bayer_DDFAPD', - 'demosaicing_CFA_Bayer_Menon2007', + "demosaicing_CFA_Bayer_DDFAPD", + "demosaicing_CFA_Bayer_Menon2007", ] diff --git a/colour_demosaicing/bayer/demosaicing/bilinear.py b/colour_demosaicing/bayer/demosaicing/bilinear.py index 35bf3d3..c21e998 100644 --- a/colour_demosaicing/bayer/demosaicing/bilinear.py +++ b/colour_demosaicing/bayer/demosaicing/bilinear.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Bilinear Bayer CFA Demosaicing ============================== @@ -21,21 +20,21 @@ from colour_demosaicing.bayer import masks_CFA_Bayer -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2015-2021 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +__author__ = "Colour Developers" +__copyright__ = "Copyright (C) 2015-2021 - Colour Developers" +__license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" +__maintainer__ = "Colour Developers" +__email__ = "colour-developers@colour-science.org" +__status__ = "Production" __all__ = [ - 'demosaicing_CFA_Bayer_bilinear', + "demosaicing_CFA_Bayer_bilinear", ] def demosaicing_CFA_Bayer_bilinear( - CFA: ArrayLike, - pattern: Union[Literal['RGGB', 'BGGR', 'GRBG', 'GBRG'], str] = 'RGGB' + CFA: ArrayLike, + pattern: Union[Literal["RGGB", "BGGR", "GRBG", "GBRG"], str] = "RGGB", ) -> NDArray: """ Returns the demosaiced *RGB* colourspace array from given *Bayer* CFA using @@ -100,17 +99,27 @@ def demosaicing_CFA_Bayer_bilinear( CFA = as_float_array(CFA) R_m, G_m, B_m = masks_CFA_Bayer(CFA.shape, pattern) - H_G = as_float_array([ - [0, 1, 0], - [1, 4, 1], - [0, 1, 0], - ]) / 4 - - H_RB = as_float_array([ - [1, 2, 1], - [2, 4, 2], - [1, 2, 1], - ]) / 4 + H_G = ( + as_float_array( + [ + [0, 1, 0], + [1, 4, 1], + [0, 1, 0], + ] + ) + / 4 + ) + + H_RB = ( + as_float_array( + [ + [1, 2, 1], + [2, 4, 2], + [1, 2, 1], + ] + ) + / 4 + ) R = convolve(CFA * R_m, H_RB) G = convolve(CFA * G_m, H_G) diff --git a/colour_demosaicing/bayer/demosaicing/malvar2004.py b/colour_demosaicing/bayer/demosaicing/malvar2004.py index 270bbdf..a4cd678 100644 --- a/colour_demosaicing/bayer/demosaicing/malvar2004.py +++ b/colour_demosaicing/bayer/demosaicing/malvar2004.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Malvar (2004) Bayer CFA Demosaicing =================================== @@ -24,21 +23,21 @@ from colour_demosaicing.bayer import masks_CFA_Bayer -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2015-2021 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +__author__ = "Colour Developers" +__copyright__ = "Copyright (C) 2015-2021 - Colour Developers" +__license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" +__maintainer__ = "Colour Developers" +__email__ = "colour-developers@colour-science.org" +__status__ = "Production" __all__ = [ - 'demosaicing_CFA_Bayer_Malvar2004', + "demosaicing_CFA_Bayer_Malvar2004", ] def demosaicing_CFA_Bayer_Malvar2004( - CFA: ArrayLike, - pattern: Union[Literal['RGGB', 'BGGR', 'GRBG', 'GBRG'], str] = 'RGGB' + CFA: ArrayLike, + pattern: Union[Literal["RGGB", "BGGR", "GRBG", "GBRG"], str] = "RGGB", ) -> NDArray: """ Returns the demosaiced *RGB* colourspace array from given *Bayer* CFA using @@ -102,31 +101,46 @@ def demosaicing_CFA_Bayer_Malvar2004( CFA = as_float_array(CFA) R_m, G_m, B_m = masks_CFA_Bayer(CFA.shape, pattern) - GR_GB = as_float_array([ - [0.0, 0.0, -1.0, 0.0, 0.0], - [0.0, 0.0, 2.0, 0.0, 0.0], - [-1.0, 2.0, 4.0, 2.0, -1.0], - [0.0, 0.0, 2.0, 0.0, 0.0], - [0.0, 0.0, -1.0, 0.0, 0.0], - ]) / 8 - - Rg_RB_Bg_BR = as_float_array([ - [0.0, 0.0, 0.5, 0.0, 0.0], - [0.0, -1.0, 0.0, -1.0, 0.0], - [-1.0, 4.0, 5.0, 4.0, -1.0], - [0.0, -1.0, 0.0, -1.0, 0.0], - [0.0, 0.0, 0.5, 0.0, 0.0], - ]) / 8 + GR_GB = ( + as_float_array( + [ + [0.0, 0.0, -1.0, 0.0, 0.0], + [0.0, 0.0, 2.0, 0.0, 0.0], + [-1.0, 2.0, 4.0, 2.0, -1.0], + [0.0, 0.0, 2.0, 0.0, 0.0], + [0.0, 0.0, -1.0, 0.0, 0.0], + ] + ) + / 8 + ) + + Rg_RB_Bg_BR = ( + as_float_array( + [ + [0.0, 0.0, 0.5, 0.0, 0.0], + [0.0, -1.0, 0.0, -1.0, 0.0], + [-1.0, 4.0, 5.0, 4.0, -1.0], + [0.0, -1.0, 0.0, -1.0, 0.0], + [0.0, 0.0, 0.5, 0.0, 0.0], + ] + ) + / 8 + ) Rg_BR_Bg_RB = np.transpose(Rg_RB_Bg_BR) - Rb_BB_Br_RR = as_float_array([ - [0.0, 0.0, -1.5, 0.0, 0.0], - [0.0, 2.0, 0.0, 2.0, 0.0], - [-1.5, 0.0, 6.0, 0.0, -1.5], - [0.0, 2.0, 0.0, 2.0, 0.0], - [0.0, 0.0, -1.5, 0.0, 0.0], - ]) / 8 + Rb_BB_Br_RR = ( + as_float_array( + [ + [0.0, 0.0, -1.5, 0.0, 0.0], + [0.0, 2.0, 0.0, 2.0, 0.0], + [-1.5, 0.0, 6.0, 0.0, -1.5], + [0.0, 2.0, 0.0, 2.0, 0.0], + [0.0, 0.0, -1.5, 0.0, 0.0], + ] + ) + / 8 + ) R = CFA * R_m G = CFA * G_m diff --git a/colour_demosaicing/bayer/demosaicing/menon2007.py b/colour_demosaicing/bayer/demosaicing/menon2007.py index e2dae2f..a9dd4bd 100644 --- a/colour_demosaicing/bayer/demosaicing/menon2007.py +++ b/colour_demosaicing/bayer/demosaicing/menon2007.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ DDFAPD - Menon (2007) Bayer CFA Demosaicing =========================================== @@ -23,17 +22,17 @@ from colour_demosaicing.bayer import masks_CFA_Bayer -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2015-2021 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +__author__ = "Colour Developers" +__copyright__ = "Copyright (C) 2015-2021 - Colour Developers" +__license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" +__maintainer__ = "Colour Developers" +__email__ = "colour-developers@colour-science.org" +__status__ = "Production" __all__ = [ - 'demosaicing_CFA_Bayer_Menon2007', - 'demosaicing_CFA_Bayer_DDFAPD', - 'refining_step_Menon2007', + "demosaicing_CFA_Bayer_Menon2007", + "demosaicing_CFA_Bayer_DDFAPD", + "refining_step_Menon2007", ] @@ -42,7 +41,7 @@ def _cnv_h(x: ArrayLike, y: ArrayLike) -> NDArray: Helper function for horizontal convolution. """ - return convolve1d(x, y, mode='mirror') + return convolve1d(x, y, mode="mirror") def _cnv_v(x: ArrayLike, y: ArrayLike) -> NDArray: @@ -50,13 +49,14 @@ def _cnv_v(x: ArrayLike, y: ArrayLike) -> NDArray: Helper function for vertical convolution. """ - return convolve1d(x, y, mode='mirror', axis=0) + return convolve1d(x, y, mode="mirror", axis=0) def demosaicing_CFA_Bayer_Menon2007( - CFA: ArrayLike, - pattern: Union[Literal['RGGB', 'BGGR', 'GRBG', 'GBRG'], str] = 'RGGB', - refining_step: Boolean = True): + CFA: ArrayLike, + pattern: Union[Literal["RGGB", "BGGR", "GRBG", "GBRG"], str] = "RGGB", + refining_step: Boolean = True, +): """ Returns the demosaiced *RGB* colourspace array from given *Bayer* CFA using DDFAPD - *Menon (2007)* demosaicing algorithm. @@ -142,16 +142,18 @@ def demosaicing_CFA_Bayer_Menon2007( del h_0, h_1, CFA, C_V, C_H - k = as_float_array([ - [0.0, 0.0, 1.0, 0.0, 1.0], - [0.0, 0.0, 0.0, 1.0, 0.0], - [0.0, 0.0, 3.0, 0.0, 3.0], - [0.0, 0.0, 0.0, 1.0, 0.0], - [0.0, 0.0, 1.0, 0.0, 1.0], - ]) + k = as_float_array( + [ + [0.0, 0.0, 1.0, 0.0, 1.0], + [0.0, 0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 3.0, 0.0, 3.0], + [0.0, 0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 1.0, 0.0, 1.0], + ] + ) - d_H = convolve(D_H, k, mode='constant') - d_V = convolve(D_V, np.transpose(k), mode='constant') + d_H = convolve(D_H, k, mode="constant") + d_V = convolve(D_V, np.transpose(k), mode="constant") del D_H, D_V @@ -227,8 +229,9 @@ def demosaicing_CFA_Bayer_Menon2007( demosaicing_CFA_Bayer_DDFAPD = demosaicing_CFA_Bayer_Menon2007 -def refining_step_Menon2007(RGB: ArrayLike, RGB_m: ArrayLike, - M: ArrayLike) -> NDArray: +def refining_step_Menon2007( + RGB: ArrayLike, RGB_m: ArrayLike, M: ArrayLike +) -> NDArray: """ Performs the refining step on given *RGB* colourspace array. diff --git a/colour_demosaicing/bayer/demosaicing/tests/__init__.py b/colour_demosaicing/bayer/demosaicing/tests/__init__.py index 40a96af..e69de29 100644 --- a/colour_demosaicing/bayer/demosaicing/tests/__init__.py +++ b/colour_demosaicing/bayer/demosaicing/tests/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py b/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py index d9fee96..fdb922b 100644 --- a/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py +++ b/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py @@ -1,5 +1,4 @@ # !/usr/bin/env python -# -*- coding: utf-8 -*- """ Defines the unit tests for the :mod:`colour_demosaicing.bayer.demosaicing.bilinear` module. @@ -16,20 +15,21 @@ from colour_demosaicing import TESTS_RESOURCES_DIRECTORY from colour_demosaicing.bayer import demosaicing_CFA_Bayer_bilinear -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2015-2021 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +__author__ = "Colour Developers" +__copyright__ = "Copyright (C) 2015-2021 - Colour Developers" +__license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" +__maintainer__ = "Colour Developers" +__email__ = "colour-developers@colour-science.org" +__status__ = "Production" __all__ = [ - 'BAYER_DIRECTORY', - 'TestDemosaicing_CFA_Bayer_bilinear', + "BAYER_DIRECTORY", + "TestDemosaicing_CFA_Bayer_bilinear", ] -BAYER_DIRECTORY: str = os.path.join(TESTS_RESOURCES_DIRECTORY, - 'colour_demosaicing', 'bayer') +BAYER_DIRECTORY: str = os.path.join( + TESTS_RESOURCES_DIRECTORY, "colour_demosaicing", "bayer" +) class TestDemosaicing_CFA_Bayer_bilinear(unittest.TestCase): @@ -44,16 +44,18 @@ def test_demosaicing_CFA_Bayer_bilinear(self): demosaicing_CFA_Bayer_bilinear` definition. """ - for pattern in ('RGGB', 'BGGR', 'GRBG', 'GBRG'): - CFA = os.path.join(BAYER_DIRECTORY, 'Lighthouse_CFA_{0}.exr') - RGB = os.path.join(BAYER_DIRECTORY, 'Lighthouse_Bilinear_{0}.exr') + for pattern in ("RGGB", "BGGR", "GRBG", "GBRG"): + CFA = os.path.join(BAYER_DIRECTORY, "Lighthouse_CFA_{0}.exr") + RGB = os.path.join(BAYER_DIRECTORY, "Lighthouse_Bilinear_{0}.exr") np.testing.assert_almost_equal( demosaicing_CFA_Bayer_bilinear( - read_image(str(CFA.format(pattern)))[..., 0], pattern), + read_image(str(CFA.format(pattern)))[..., 0], pattern + ), read_image(str(RGB.format(pattern))), - decimal=7) + decimal=7, + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py b/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py index 4bc2a58..c4ef241 100644 --- a/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py +++ b/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py @@ -1,5 +1,4 @@ # !/usr/bin/env python -# -*- coding: utf-8 -*- """ Defines the unit tests for the :mod:`colour_demosaicing.bayer.demosaicing.malvar2004` module. @@ -16,20 +15,21 @@ from colour_demosaicing import TESTS_RESOURCES_DIRECTORY from colour_demosaicing.bayer import demosaicing_CFA_Bayer_Malvar2004 -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2015-2021 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +__author__ = "Colour Developers" +__copyright__ = "Copyright (C) 2015-2021 - Colour Developers" +__license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" +__maintainer__ = "Colour Developers" +__email__ = "colour-developers@colour-science.org" +__status__ = "Production" __all__ = [ - 'BAYER_DIRECTORY', - 'TestDemosaicing_CFA_Bayer_Malvar2004', + "BAYER_DIRECTORY", + "TestDemosaicing_CFA_Bayer_Malvar2004", ] -BAYER_DIRECTORY: str = os.path.join(TESTS_RESOURCES_DIRECTORY, - 'colour_demosaicing', 'bayer') +BAYER_DIRECTORY: str = os.path.join( + TESTS_RESOURCES_DIRECTORY, "colour_demosaicing", "bayer" +) class TestDemosaicing_CFA_Bayer_Malvar2004(unittest.TestCase): @@ -44,17 +44,20 @@ def test_demosaicing_CFA_Bayer_Malvar2004(self): demosaicing_CFA_Bayer_Malvar2004` definition. """ - for pattern in ('RGGB', 'BGGR', 'GRBG', 'GBRG'): - CFA = os.path.join(BAYER_DIRECTORY, 'Lighthouse_CFA_{0}.exr') - RGB = os.path.join(BAYER_DIRECTORY, - 'Lighthouse_Malvar2004_{0}.exr') + for pattern in ("RGGB", "BGGR", "GRBG", "GBRG"): + CFA = os.path.join(BAYER_DIRECTORY, "Lighthouse_CFA_{0}.exr") + RGB = os.path.join( + BAYER_DIRECTORY, "Lighthouse_Malvar2004_{0}.exr" + ) np.testing.assert_almost_equal( demosaicing_CFA_Bayer_Malvar2004( - read_image(str(CFA.format(pattern)))[..., 0], pattern), + read_image(str(CFA.format(pattern)))[..., 0], pattern + ), read_image(str(RGB.format(pattern))), - decimal=7) + decimal=7, + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py b/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py index 8e99080..8339b97 100644 --- a/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py +++ b/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py @@ -1,5 +1,4 @@ # !/usr/bin/env python -# -*- coding: utf-8 -*- """ Defines the unit tests for the :mod:`colour_demosaicing.bayer.demosaicing.menon2007` module. @@ -16,20 +15,21 @@ from colour_demosaicing import TESTS_RESOURCES_DIRECTORY from colour_demosaicing.bayer import demosaicing_CFA_Bayer_Menon2007 -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2015-2021 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +__author__ = "Colour Developers" +__copyright__ = "Copyright (C) 2015-2021 - Colour Developers" +__license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" +__maintainer__ = "Colour Developers" +__email__ = "colour-developers@colour-science.org" +__status__ = "Production" __all__ = [ - 'BAYER_DIRECTORY', - 'TestDemosaicing_CFA_Bayer_Menon2007', + "BAYER_DIRECTORY", + "TestDemosaicing_CFA_Bayer_Menon2007", ] -BAYER_DIRECTORY: str = os.path.join(TESTS_RESOURCES_DIRECTORY, - 'colour_demosaicing', 'bayer') +BAYER_DIRECTORY: str = os.path.join( + TESTS_RESOURCES_DIRECTORY, "colour_demosaicing", "bayer" +) class TestDemosaicing_CFA_Bayer_Menon2007(unittest.TestCase): @@ -44,26 +44,31 @@ def test_demosaicing_CFA_Bayer_Menon2007(self): demosaicing_CFA_Bayer_Menon2007` definition. """ - for pattern in ('RGGB', 'BGGR', 'GRBG', 'GBRG'): - CFA = os.path.join(BAYER_DIRECTORY, 'Lighthouse_CFA_{0}.exr') - RGB = os.path.join(BAYER_DIRECTORY, 'Lighthouse_Menon2007_{0}.exr') + for pattern in ("RGGB", "BGGR", "GRBG", "GBRG"): + CFA = os.path.join(BAYER_DIRECTORY, "Lighthouse_CFA_{0}.exr") + RGB = os.path.join(BAYER_DIRECTORY, "Lighthouse_Menon2007_{0}.exr") np.testing.assert_almost_equal( demosaicing_CFA_Bayer_Menon2007( - read_image(str(CFA.format(pattern)))[..., 0], pattern), + read_image(str(CFA.format(pattern)))[..., 0], pattern + ), read_image(str(RGB.format(pattern))), - decimal=7) + decimal=7, + ) - RGB = os.path.join(BAYER_DIRECTORY, - 'Lighthouse_Menon2007_NR_{0}.exr') + RGB = os.path.join( + BAYER_DIRECTORY, "Lighthouse_Menon2007_NR_{0}.exr" + ) np.testing.assert_almost_equal( demosaicing_CFA_Bayer_Menon2007( read_image(str(CFA.format(pattern)))[..., 0], pattern, - refining_step=False), + refining_step=False, + ), read_image(str(RGB.format(pattern))), - decimal=7) + decimal=7, + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour_demosaicing/bayer/masks.py b/colour_demosaicing/bayer/masks.py index 50d937b..b530ff7 100644 --- a/colour_demosaicing/bayer/masks.py +++ b/colour_demosaicing/bayer/masks.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Bayer CFA Masks =============== @@ -13,21 +12,21 @@ from colour.hints import Literal, NDArray, Tuple, Union from colour.utilities import validate_method -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2015-2021 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +__author__ = "Colour Developers" +__copyright__ = "Copyright (C) 2015-2021 - Colour Developers" +__license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" +__maintainer__ = "Colour Developers" +__email__ = "colour-developers@colour-science.org" +__status__ = "Production" __all__ = [ - 'masks_CFA_Bayer', + "masks_CFA_Bayer", ] def masks_CFA_Bayer( - shape: Union[int, Tuple[int, ...]], - pattern: Union[Literal['RGGB', 'BGGR', 'GRBG', 'GBRG'], str] = 'RGGB' + shape: Union[int, Tuple[int, ...]], + pattern: Union[Literal["RGGB", "BGGR", "GRBG", "GBRG"], str] = "RGGB", ) -> Tuple[NDArray, ...]: """ Returns the *Bayer* CFA red, green and blue masks for given pattern. @@ -71,11 +70,13 @@ def masks_CFA_Bayer( """ pattern = validate_method( - pattern, ['RGGB', 'BGGR', 'GRBG', 'GBRG'], - '"{0}" CFA pattern is invalid, it must be one of {1}!').upper() + pattern, + ["RGGB", "BGGR", "GRBG", "GBRG"], + '"{0}" CFA pattern is invalid, it must be one of {1}!', + ).upper() - channels = dict((channel, np.zeros(shape)) for channel in 'RGB') + channels = {channel: np.zeros(shape) for channel in "RGB"} for channel, (y, x) in zip(pattern, [(0, 0), (0, 1), (1, 0), (1, 1)]): channels[channel][y::2, x::2] = 1 - return tuple(channels[c].astype(bool) for c in 'RGB') + return tuple(channels[c].astype(bool) for c in "RGB") diff --git a/colour_demosaicing/bayer/mosaicing.py b/colour_demosaicing/bayer/mosaicing.py index 8887cf2..7776ea7 100644 --- a/colour_demosaicing/bayer/mosaicing.py +++ b/colour_demosaicing/bayer/mosaicing.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Bayer CFA Mosaicing =================== @@ -13,21 +12,21 @@ from colour_demosaicing.bayer import masks_CFA_Bayer -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2015-2021 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +__author__ = "Colour Developers" +__copyright__ = "Copyright (C) 2015-2021 - Colour Developers" +__license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" +__maintainer__ = "Colour Developers" +__email__ = "colour-developers@colour-science.org" +__status__ = "Production" __all__ = [ - 'mosaicing_CFA_Bayer', + "mosaicing_CFA_Bayer", ] def mosaicing_CFA_Bayer( - RGB: ArrayLike, - pattern: Union[Literal['RGGB', 'BGGR', 'GRBG', 'GBRG'], str] = 'RGGB' + RGB: ArrayLike, + pattern: Union[Literal["RGGB", "BGGR", "GRBG", "GBRG"], str] = "RGGB", ) -> NDArray: """ Returns the *Bayer* CFA mosaic for a given *RGB* colourspace array. diff --git a/colour_demosaicing/bayer/tests/__init__.py b/colour_demosaicing/bayer/tests/__init__.py index 40a96af..e69de29 100644 --- a/colour_demosaicing/bayer/tests/__init__.py +++ b/colour_demosaicing/bayer/tests/__init__.py @@ -1 +0,0 @@ -# -*- coding: utf-8 -*- diff --git a/colour_demosaicing/bayer/tests/test_masks.py b/colour_demosaicing/bayer/tests/test_masks.py index e968356..13644e8 100644 --- a/colour_demosaicing/bayer/tests/test_masks.py +++ b/colour_demosaicing/bayer/tests/test_masks.py @@ -1,5 +1,4 @@ # !/usr/bin/env python -# -*- coding: utf-8 -*- """ Defines the unit tests for the :mod:`colour_demosaicing.bayer.masks` module. """ @@ -16,20 +15,21 @@ from colour_demosaicing import TESTS_RESOURCES_DIRECTORY from colour_demosaicing.bayer import masks_CFA_Bayer -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2015-2021 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +__author__ = "Colour Developers" +__copyright__ = "Copyright (C) 2015-2021 - Colour Developers" +__license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" +__maintainer__ = "Colour Developers" +__email__ = "colour-developers@colour-science.org" +__status__ = "Production" __all__ = [ - 'BAYER_DIRECTORY', - 'TestMasks_CFA_Bayer', + "BAYER_DIRECTORY", + "TestMasks_CFA_Bayer", ] -BAYER_DIRECTORY: str = os.path.join(TESTS_RESOURCES_DIRECTORY, - 'colour_demosaicing', 'bayer') +BAYER_DIRECTORY: str = os.path.join( + TESTS_RESOURCES_DIRECTORY, "colour_demosaicing", "bayer" +) class TestMasks_CFA_Bayer(unittest.TestCase): @@ -44,13 +44,14 @@ def test_masks_CFA_Bayer(self): definition. """ - for pattern in ('RGGB', 'BGGR', 'GRBG', 'GBRG'): - mask = os.path.join(BAYER_DIRECTORY, '{0}_Masks.exr') + for pattern in ("RGGB", "BGGR", "GRBG", "GBRG"): + mask = os.path.join(BAYER_DIRECTORY, "{0}_Masks.exr") np.testing.assert_almost_equal( tstack(masks_CFA_Bayer((8, 8), pattern)), read_image(str(mask.format(pattern))), - decimal=7) + decimal=7, + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/colour_demosaicing/bayer/tests/test_mosaicing.py b/colour_demosaicing/bayer/tests/test_mosaicing.py index a34438b..8b0c9c7 100644 --- a/colour_demosaicing/bayer/tests/test_mosaicing.py +++ b/colour_demosaicing/bayer/tests/test_mosaicing.py @@ -1,5 +1,4 @@ # !/usr/bin/env python -# -*- coding: utf-8 -*- """ Defines the unit tests for the :mod:`colour_demosaicing.bayer.mosaicing` module. @@ -16,20 +15,21 @@ from colour_demosaicing import TESTS_RESOURCES_DIRECTORY from colour_demosaicing.bayer import mosaicing_CFA_Bayer -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2015-2021 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +__author__ = "Colour Developers" +__copyright__ = "Copyright (C) 2015-2021 - Colour Developers" +__license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" +__maintainer__ = "Colour Developers" +__email__ = "colour-developers@colour-science.org" +__status__ = "Production" __all__ = [ - 'BAYER_DIRECTORY', - 'TestMosaicing_CFA_Bayer', + "BAYER_DIRECTORY", + "TestMosaicing_CFA_Bayer", ] -BAYER_DIRECTORY: str = os.path.join(TESTS_RESOURCES_DIRECTORY, - 'colour_demosaicing', 'bayer') +BAYER_DIRECTORY: str = os.path.join( + TESTS_RESOURCES_DIRECTORY, "colour_demosaicing", "bayer" +) class TestMosaicing_CFA_Bayer(unittest.TestCase): @@ -45,15 +45,17 @@ def test_mosaicing_CFA_Bayer(self): """ image = read_image( - str(os.path.join(BAYER_DIRECTORY, 'Lighthouse.exr'))) + str(os.path.join(BAYER_DIRECTORY, "Lighthouse.exr")) + ) - for pattern in ('RGGB', 'BGGR', 'GRBG', 'GBRG'): - CFA = os.path.join(BAYER_DIRECTORY, 'Lighthouse_CFA_{0}.exr') + for pattern in ("RGGB", "BGGR", "GRBG", "GBRG"): + CFA = os.path.join(BAYER_DIRECTORY, "Lighthouse_CFA_{0}.exr") np.testing.assert_almost_equal( mosaicing_CFA_Bayer(image, pattern), read_image(str(CFA.format(pattern)))[..., 0], - decimal=7) + decimal=7, + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/docs/conf.py b/docs/conf.py index 6731452..b48b01f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # colour-demosaicing documentation build configuration file, created by # sphinx-quickstart on Tue Aug 5 14:31:53 2014. @@ -16,8 +15,9 @@ import colour_demosaicing as package -basename = re.sub('_(\\w)', lambda x: x.group(1).upper(), - package.__name__.title()) +basename = re.sub( + "_(\\w)", lambda x: x.group(1).upper(), package.__name__.title() +) # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the @@ -33,82 +33,81 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.autosummary', - 'sphinx.ext.coverage', - 'sphinx.ext.ifconfig', - 'sphinx.ext.inheritance_diagram', - 'sphinx.ext.intersphinx', - 'sphinx.ext.mathjax', - 'sphinx.ext.napoleon', - 'sphinx.ext.todo', - 'sphinx.ext.viewcode', - 'sphinxcontrib.bibtex', + "sphinx.ext.autodoc", + "sphinx.ext.autosummary", + "sphinx.ext.coverage", + "sphinx.ext.ifconfig", + "sphinx.ext.inheritance_diagram", + "sphinx.ext.intersphinx", + "sphinx.ext.mathjax", + "sphinx.ext.napoleon", + "sphinx.ext.todo", + "sphinx.ext.viewcode", + "sphinxcontrib.bibtex", ] -autodoc_member_order = 'bysource' +autodoc_member_order = "bysource" autodoc_mock_imports = [ - 'colour', - 'scipy', - 'scipy.ndimage.filters', + "colour", + "scipy", + "scipy.ndimage.filters", ] -autodoc_typehints = 'both' +autodoc_typehints = "both" autodoc_type_aliases = { - 'ArrayLike': 'ArrayLike', - 'Boolean': 'Boolean', - 'BooleanOrArrayLike': 'BooleanOrArrayLike', - 'BooleanOrNDArray': 'BooleanOrNDArray', - 'DType': 'DType', - 'DTypeBoolean': 'DTypeBoolean', - 'DTypeComplex': 'DTypeComplex', - 'DTypeFloating': 'DTypeFloating', - 'DTypeInteger': 'DTypeInteger', - 'DTypeNumber': 'DTypeNumber', - 'Floating': 'Floating', - 'FloatingOrArrayLike': 'FloatingOrArrayLike', - 'FloatingOrNDArray': 'FloatingOrNDArray', - 'Integer': 'Integer', - 'IntegerOrArrayLike': 'IntegerOrArrayLike', - 'IntegerOrNDArray': 'IntegerOrNDArray', - 'NestedSequence': 'NestedSequence', - 'Number': 'Number', - 'NumberOrArrayLike': 'NumberOrArrayLike', - 'NumberOrNDArray': 'NumberOrNDArray', - 'StrOrArrayLike': 'StrOrArrayLike', - 'StrOrNDArray': 'StrOrNDArray', + "ArrayLike": "ArrayLike", + "Boolean": "Boolean", + "BooleanOrArrayLike": "BooleanOrArrayLike", + "BooleanOrNDArray": "BooleanOrNDArray", + "DType": "DType", + "DTypeBoolean": "DTypeBoolean", + "DTypeComplex": "DTypeComplex", + "DTypeFloating": "DTypeFloating", + "DTypeInteger": "DTypeInteger", + "DTypeNumber": "DTypeNumber", + "Floating": "Floating", + "FloatingOrArrayLike": "FloatingOrArrayLike", + "FloatingOrNDArray": "FloatingOrNDArray", + "Integer": "Integer", + "IntegerOrArrayLike": "IntegerOrArrayLike", + "IntegerOrNDArray": "IntegerOrNDArray", + "NestedSequence": "NestedSequence", + "Number": "Number", + "NumberOrArrayLike": "NumberOrArrayLike", + "NumberOrNDArray": "NumberOrNDArray", + "StrOrArrayLike": "StrOrArrayLike", + "StrOrNDArray": "StrOrNDArray", } autodoc_preserve_defaults = True autosummary_generate = True -bibtex_bibfiles = ['bibliography.bib'] -bibtex_encoding = 'utf8' +bibtex_bibfiles = ["bibliography.bib"] +bibtex_encoding = "utf8" -napoleon_custom_sections = ['Attributes', 'Methods'] +napoleon_custom_sections = ["Attributes", "Methods"] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix of source filenames. -source_suffix = '.rst' +source_suffix = ".rst" # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. project = package.__application_name__ -copyright = package.__copyright__.replace('Copyright (C)', '') +copyright = package.__copyright__.replace("Copyright (C)", "") # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = '{0}.{1}'.format(package.__major_version__, - package.__minor_version__) +version = f"{package.__major_version__}.{package.__minor_version__}" # The full version, including alpha/beta/rc tags. release = package.__version__ @@ -124,7 +123,7 @@ # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build'] +exclude_patterns = ["_build"] # The reST default role (used for this markup: `text`) to use for all # documents. @@ -142,7 +141,7 @@ # show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'lovelace' +pygments_style = "lovelace" # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] @@ -154,7 +153,7 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'sphinx_rtd_theme' +html_theme = "sphinx_rtd_theme" # # html_theme_options = {} @@ -175,7 +174,7 @@ # The name of an image file (relative to this directory) to place at the top # of the sidebar. -html_logo = '_static/Logo_Small_001.png' +html_logo = "_static/Logo_Small_001.png" # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 @@ -185,7 +184,7 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named 'default.css' will overwrite the builtin 'default.css'. -html_static_path = ['_static'] +html_static_path = ["_static"] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied @@ -234,22 +233,17 @@ # html_file_suffix = None # Output file base name for HTML help builder. -htmlhelp_basename = '{0}Doc'.format(basename) +htmlhelp_basename = f"{basename}Doc" # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). - 'papersize': - 'a4paper', - + "papersize": "a4paper", # The font size ('10pt', '11pt' or '12pt'). - 'pointsize': - '10pt', - + "pointsize": "10pt", # Additional stuff for the LaTeX preamble. - 'preamble': - """ + "preamble": """ \\usepackage{charter} \\usepackage[defaultsans]{lato} \\usepackage{inconsolata} @@ -271,13 +265,18 @@ # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - ('index', '{0}.tex'.format(basename), u'{0} Documentation'.format( - package.__application_name__), package.__author__, 'manual'), + ( + "index", + f"{basename}.tex", + f"{package.__application_name__} Documentation", + package.__author__, + "manual", + ), ] # The name of an image file (relative to this directory) to place at the top of # the title page. -latex_logo = '_static/Logo_Medium_001.png' +latex_logo = "_static/Logo_Medium_001.png" # For 'manual' documents, if this is true, then toplevel headings are parts, # not chapters. @@ -299,8 +298,15 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [('index', basename, u'{0} Documentation'.format( - package.__application_name__), [package.__author__], 1)] +man_pages = [ + ( + "index", + basename, + f"{package.__application_name__} Documentation", + [package.__author__], + 1, + ) +] # If true, show URL addresses after external links. # man_show_urls = False @@ -311,9 +317,15 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', basename, u'{0} Documentation'.format( - package.__application_name__), package.__author__, - package.__application_name__, basename, 'Miscellaneous'), + ( + "index", + basename, + f"{package.__application_name__} Documentation", + package.__author__, + package.__application_name__, + basename, + "Miscellaneous", + ), ] # Documents to append as an appendix to all manuals. @@ -334,7 +346,7 @@ epub_title = package.__application_name__ epub_author = package.__author__ epub_publisher = package.__author__ -epub_copyright = package.__copyright__.replace('Copyright (C)', '') +epub_copyright = package.__copyright__.replace("Copyright (C)", "") # The basename for the epub file. It defaults to the project name. # epub_basename = basename @@ -374,7 +386,7 @@ # epub_post_files = [] # A list of files that should not be packed into the epub file. -epub_exclude_files = ['search.html'] +epub_exclude_files = ["search.html"] # The depth of the table of contents in toc.ncx. # epub_tocdepth = 3 @@ -397,14 +409,14 @@ # If false, no index is generated. # epub_use_index = True -autoclass_content = 'both' +autoclass_content = "both" intersphinx_mapping = { - 'python': ('https://docs.python.org/3.7', None), - 'matplotlib': ('http://matplotlib.org/stable', None), - 'numpy': ('http://docs.scipy.org/doc/numpy', None), - 'pandas': ('http://pandas.pydata.org/pandas-docs/dev', None), - 'scipy': ('http://docs.scipy.org/doc/scipy/reference', None) + "python": ("https://docs.python.org/3.7", None), + "matplotlib": ("http://matplotlib.org/stable", None), + "numpy": ("http://docs.scipy.org/doc/numpy", None), + "pandas": ("http://pandas.pydata.org/pandas-docs/dev", None), + "scipy": ("http://docs.scipy.org/doc/scipy/reference", None), } @@ -414,9 +426,9 @@ def _autodoc_process_docstring(app, what, name, obj, options, lines): """ for i, line in enumerate(lines): - lines[i] = line.replace('# noqa', '') + lines[i] = line.replace("# noqa", "") def setup(app): - app.add_css_file('custom.css') - app.connect('autodoc-process-docstring', _autodoc_process_docstring) + app.add_css_file("custom.css") + app.connect("autodoc-process-docstring", _autodoc_process_docstring) diff --git a/pyproject.toml b/pyproject.toml index e6c7ac8..d548ec6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,74 +48,91 @@ exclude = [ ] [tool.poetry.dependencies] -python = "^3.7" +python = ">= 3.8, < 3.11" colour-science = "^0.3.16" biblib-simple = { version = "*", optional = true } # Development dependency. -coverage = { version = "*", optional = true } # Development dependency. +black = { version = "*", optional = true } # Development dependency. +coverage = { version = "!= 6.3", optional = true } # Development dependency. coveralls = { version = "*", optional = true } # Development dependency. flake8 = { version = "*", optional = true } # Development dependency. +flynt = { version = "*", optional = true } # Development dependency. invoke = { version = "*", optional = true } # Development dependency. jupyter = { version = "*", optional = true } # Development dependency. -matplotlib = { version = "*", optional = true } +matplotlib = { version = ">= 3.2, != 3.5.0, != 3.5.1", optional = true } mypy = { version = "*", optional = true } # Development dependency. -nose = { version = "*", optional = true } # Development dependency. -numpy = { version = "*", optional = true } +numpy = { version = ">= 1.19, < 2", optional = true } pre-commit = { version = "*", optional = true } # Development dependency. pytest = { version = "*", optional = true } # Development dependency. +pytest-cov = { version = "*", optional = true } # Development dependency. +pyupgrade = { version = "*", optional = true } # Development dependency. restructuredtext-lint = { version = "*", optional = true } # Development dependency. -sphinx = { version = "<=3.1.2", optional = true } # Development dependency. -sphinx_rtd_theme = { version = "*", optional = true } # Development dependency. -sphinxcontrib-bibtex = { version = "<2.0.0", optional = true } # Development dependency. +sphinx = { version = "*", optional = true } # Development dependency. +sphinx-rtd-theme = { version = "*", optional = true } # Development dependency. +sphinxcontrib-bibtex = { version = "*", optional = true } # Development dependency. toml = { version = "*", optional = true } # Development dependency. twine = { version = "*", optional = true } # Development dependency. -typing-extensions = { version = "*", optional = true } # Development dependency. -yapf = { version = "0.23", optional = true } # Development dependency. [tool.poetry.dev-dependencies] biblib-simple = "*" -coverage = "*" +black = "*" +coverage = "!= 6.3" coveralls = "*" flake8 = "*" +flynt = "*" invoke = "*" jupyter = "*" mypy = "*" -nose = "*" pre-commit = "*" pytest = "*" +pytest-cov = "*" +pyupgrade = "*" restructuredtext-lint = "*" -sphinx = "<=3.1.2" -sphinx_rtd_theme = "*" -sphinxcontrib-bibtex = "<2.0.0" +sphinx = "*" +sphinx-rtd-theme = "*" +sphinxcontrib-bibtex = "*" toml = "*" twine = "*" -typing-extensions = "*" -yapf = "0.23" [tool.poetry.extras] development = [ "biblib-simple", + "black", "coverage", "coveralls", "flake8", + "flynt", "invoke", "jupyter", "mypy", - "nose", "pre-commit", "pytest", + "pytest-cov", + "pyupgrade", "restructuredtext-lint", "sphinx", - "sphinx_rtd_theme", + "sphinx-rtd-theme", "sphinxcontrib-bibtex", "toml", "twine", - "typing-extensions", - "yapf" ] plotting = [ "matplotlib" ] read-the-docs = [ "matplotlib", "numpy", "sphinxcontrib-bibtex" ] +[tool.black] +line-length = 79 +exclude = ''' +/( + \.git + | \.mypy_cache + | build + | dist +)/ +''' + +[tool.flynt] +line_length=999 + [tool.mypy] plugins = "numpy.typing.mypy_plugin" ignore_missing_imports = true diff --git a/tasks.py b/tasks.py index 6ceef7a..8d945f6 100644 --- a/tasks.py +++ b/tasks.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ Invoke - Tasks ============== @@ -18,33 +17,34 @@ import colour_demosaicing from colour.utilities import message_box -__author__ = 'Colour Developers' -__copyright__ = 'Copyright (C) 2015-2021 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +__author__ = "Colour Developers" +__copyright__ = "Copyright (C) 2015-2021 - Colour Developers" +__license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" +__maintainer__ = "Colour Developers" +__email__ = "colour-developers@colour-science.org" +__status__ = "Production" __all__ = [ - 'APPLICATION_NAME', - 'APPLICATION_VERSION', - 'PYTHON_PACKAGE_NAME', - 'PYPI_PACKAGE_NAME', - 'BIBLIOGRAPHY_NAME', - 'clean', - 'formatting', - 'tests', - 'quality', - 'examples', - 'preflight', - 'docs', - 'todo', - 'requirements', - 'build', - 'virtualise', - 'tag', - 'release', - 'sha256', + "APPLICATION_NAME", + "APPLICATION_VERSION", + "PYTHON_PACKAGE_NAME", + "PYPI_PACKAGE_NAME", + "BIBLIOGRAPHY_NAME", + "clean", + "formatting", + "quality", + "precommit", + "tests", + "examples", + "preflight", + "docs", + "todo", + "requirements", + "build", + "virtualise", + "tag", + "release", + "sha256", ] APPLICATION_NAME: str = colour_demosaicing.__application_name__ @@ -53,9 +53,9 @@ PYTHON_PACKAGE_NAME: str = colour_demosaicing.__name__ -PYPI_PACKAGE_NAME: str = 'colour-demosaicing' +PYPI_PACKAGE_NAME: str = "colour-demosaicing" -BIBLIOGRAPHY_NAME: str = 'BIBLIOGRAPHY.bib' +BIBLIOGRAPHY_NAME: str = "BIBLIOGRAPHY.bib" def _patch_invoke_annotations_support(): @@ -75,7 +75,8 @@ def patched_inspect_getargspec(function): def patched_task_argspec(*args, **kwargs): with patch( - target="inspect.getargspec", new=patched_inspect_getargspec): + target="inspect.getargspec", new=patched_inspect_getargspec + ): return org_task_argspec(*args, **kwargs) invoke.tasks.Task.argspec = patched_task_argspec @@ -85,11 +86,13 @@ def patched_task_argspec(*args, **kwargs): @task -def clean(ctx: Context, - docs: Boolean = True, - bytecode: Boolean = False, - mypy: Boolean = True, - pytest: Boolean = True): +def clean( + ctx: Context, + docs: Boolean = True, + bytecode: Boolean = False, + mypy: Boolean = True, + pytest: Boolean = True, +): """ Cleans the project. @@ -107,146 +110,150 @@ def clean(ctx: Context, Whether to clean the *Pytest* cache directory. """ - message_box('Cleaning project...') + message_box("Cleaning project...") - patterns = ['build', '*.egg-info', 'dist'] + patterns = ["build", "*.egg-info", "dist"] if docs: - patterns.append('docs/_build') - patterns.append('docs/generated') + patterns.append("docs/_build") + patterns.append("docs/generated") if bytecode: - patterns.append('**/__pycache__') - patterns.append('**/*.pyc') + patterns.append("**/__pycache__") + patterns.append("**/*.pyc") if mypy: - patterns.append('.mypy_cache') + patterns.append(".mypy_cache") if pytest: - patterns.append('.pytest_cache') + patterns.append(".pytest_cache") for pattern in patterns: - ctx.run("rm -rf {}".format(pattern)) + ctx.run(f"rm -rf {pattern}") @task -def formatting(ctx: Context, - yapf: Boolean = True, - asciify: Boolean = True, - bibtex: Boolean = True): +def formatting( + ctx: Context, + asciify: Boolean = True, + bibtex: Boolean = True, +): """ - Formats the codebase with *Yapf*, converts unicode characters to ASCII and - cleanup the "BibTeX" file. + Converts unicode characters to ASCII and cleanup the *BibTeX* file. Parameters ---------- ctx Context. - yapf - Whether to format the codebase with *Yapf*. asciify Whether to convert unicode characters to ASCII. bibtex Whether to cleanup the *BibTeX* file. """ - if yapf: - message_box('Formatting codebase with "Yapf"...') - ctx.run('yapf -p -i -r --exclude \'.git\' .') - if asciify: - message_box('Converting unicode characters to ASCII...') - with ctx.cd('utilities'): - ctx.run('./unicode_to_ascii.py') + message_box("Converting unicode characters to ASCII...") + with ctx.cd("utilities"): + ctx.run("./unicode_to_ascii.py") if bibtex: message_box('Cleaning up "BibTeX" file...') bibtex_path = BIBLIOGRAPHY_NAME with open(bibtex_path) as bibtex_file: - entries = biblib.bib.Parser().parse( - bibtex_file.read()).get_entries() + entries = ( + biblib.bib.Parser().parse(bibtex_file.read()).get_entries() + ) for entry in sorted(entries.values(), key=lambda x: x.key): try: - del entry['file'] + del entry["file"] except KeyError: pass for key, value in entry.items(): - entry[key] = re.sub('(? requirements.txt') + ctx.run( + "poetry run pip list --format=freeze | " + 'egrep -v "colour-demosaicing=" ' + "> requirements.txt" + ) @task(clean, preflight, docs, todo, requirements) @@ -356,19 +367,16 @@ def build(ctx: Context): Context. """ - message_box('Building...') - ctx.run('poetry build') + message_box("Building...") + ctx.run("poetry build") - with ctx.cd('dist'): - ctx.run('tar -xvf {0}-{1}.tar.gz'.format(PYPI_PACKAGE_NAME, - APPLICATION_VERSION)) - ctx.run('cp {0}-{1}/setup.py ../'.format(PYPI_PACKAGE_NAME, - APPLICATION_VERSION)) + with ctx.cd("dist"): + ctx.run(f"tar -xvf {PYPI_PACKAGE_NAME}-{APPLICATION_VERSION}.tar.gz") + ctx.run(f"cp {PYPI_PACKAGE_NAME}-{APPLICATION_VERSION}/setup.py ../") - ctx.run('rm -rf {0}-{1}'.format(PYPI_PACKAGE_NAME, - APPLICATION_VERSION)) + ctx.run(f"rm -rf {PYPI_PACKAGE_NAME}-{APPLICATION_VERSION}") - with open('setup.py') as setup_file: + with open("setup.py") as setup_file: source = setup_file.read() setup_kwargs = [] @@ -376,34 +384,40 @@ def build(ctx: Context): def sub_callable(match): setup_kwargs.append(match) - return '' + return "" template = """ setup({0} ) """ - source = re.sub('from setuptools import setup', - 'import codecs\nfrom setuptools import setup', source) source = re.sub( - 'setup_kwargs = {(.*)}.*setup\\(\\*\\*setup_kwargs\\)', + "from setuptools import setup", + "import codecs\nfrom setuptools import setup", + source, + ) + source = re.sub( + "setup_kwargs = {(.*)}.*setup\\(\\*\\*setup_kwargs\\)", sub_callable, source, - flags=re.DOTALL)[:-2] + flags=re.DOTALL, + )[:-2] setup_kwargs = setup_kwargs[0].group(1).splitlines() for i, line in enumerate(setup_kwargs): - setup_kwargs[i] = re.sub('^\\s*(\'(\\w+)\':\\s?)', ' \\2=', line) - if setup_kwargs[i].strip().startswith('long_description'): - setup_kwargs[i] = (' long_description=' - 'codecs.open(\'README.rst\', encoding=\'utf8\')' - '.read(),') + setup_kwargs[i] = re.sub("^\\s*('(\\w+)':\\s?)", " \\2=", line) + if setup_kwargs[i].strip().startswith("long_description"): + setup_kwargs[i] = ( + " long_description=" + "codecs.open('README.rst', encoding='utf8')" + ".read()," + ) - source += template.format('\n'.join(setup_kwargs)) + source += template.format("\n".join(setup_kwargs)) - with open('setup.py', 'w') as setup_file: + with open("setup.py", "w") as setup_file: setup_file.write(source) - ctx.run('twine check dist/*') + ctx.run("twine check dist/*") @task @@ -419,25 +433,27 @@ def virtualise(ctx: Context, tests: Boolean = True): Whether to run tests on the virtual environment. """ - unique_name = '{0}-{1}'.format(PYPI_PACKAGE_NAME, uuid.uuid1()) - with ctx.cd('dist'): - ctx.run('tar -xvf {0}-{1}.tar.gz'.format(PYPI_PACKAGE_NAME, - APPLICATION_VERSION)) - ctx.run('mv {0}-{1} {2}'.format(PYPI_PACKAGE_NAME, APPLICATION_VERSION, - unique_name)) - ctx.run('rm -rf {0}/{1}/resources'.format(unique_name, - PYTHON_PACKAGE_NAME)) - ctx.run('ln -s ../../../{0}/resources {1}/{0}'.format( - PYTHON_PACKAGE_NAME, unique_name)) + unique_name = f"{PYPI_PACKAGE_NAME}-{uuid.uuid1()}" + with ctx.cd("dist"): + ctx.run(f"tar -xvf {PYPI_PACKAGE_NAME}-{APPLICATION_VERSION}.tar.gz") + ctx.run(f"mv {PYPI_PACKAGE_NAME}-{APPLICATION_VERSION} {unique_name}") + ctx.run(f"rm -rf {unique_name}/{PYTHON_PACKAGE_NAME}/resources") + ctx.run( + "ln -s ../../../{0}/resources {1}/{0}".format( + PYTHON_PACKAGE_NAME, unique_name + ) + ) with ctx.cd(unique_name): - ctx.run('poetry env use 3') - ctx.run('poetry install') - ctx.run('source $(poetry env info -p)/bin/activate') - ctx.run('python -c "import imageio;' - 'imageio.plugins.freeimage.download()"') + ctx.run("poetry env use 3") + ctx.run("poetry install") + ctx.run("source $(poetry env info -p)/bin/activate") + ctx.run( + 'python -c "import imageio;' + 'imageio.plugins.freeimage.download()"' + ) if tests: - ctx.run('poetry run nosetests', env={'MPLBACKEND': 'AGG'}) + ctx.run("poetry run nosetests", env={"MPLBACKEND": "AGG"}) @task @@ -451,42 +467,48 @@ def tag(ctx: Context): Context. """ - message_box('Tagging...') - result = ctx.run('git rev-parse --abbrev-ref HEAD', hide='both') + message_box("Tagging...") + result = ctx.run("git rev-parse --abbrev-ref HEAD", hide="both") - assert result.stdout.strip() == 'develop', ( - 'Are you still on a feature or master branch?') + assert ( + result.stdout.strip() == "develop" + ), "Are you still on a feature or master branch?" - with open(os.path.join(PYTHON_PACKAGE_NAME, '__init__.py')) as file_handle: + with open(os.path.join(PYTHON_PACKAGE_NAME, "__init__.py")) as file_handle: file_content = file_handle.read() major_version = re.search( - "__major_version__\\s+=\\s+'(.*)'", - file_content).group( # type: ignore[union-attr] - 1) + "__major_version__\\s+=\\s+'(.*)'", file_content + ).group( # type: ignore[union-attr] + 1 + ) minor_version = re.search( - "__minor_version__\\s+=\\s+'(.*)'", - file_content).group( # type: ignore[union-attr] - 1) + "__minor_version__\\s+=\\s+'(.*)'", file_content + ).group( # type: ignore[union-attr] + 1 + ) change_version = re.search( - "__change_version__\\s+=\\s+'(.*)'", - file_content).group( # type: ignore[union-attr] - 1) + "__change_version__\\s+=\\s+'(.*)'", file_content + ).group( # type: ignore[union-attr] + 1 + ) - version = '.'.join((major_version, minor_version, change_version)) + version = ".".join((major_version, minor_version, change_version)) - result = ctx.run('git ls-remote --tags upstream', hide='both') - remote_tags = result.stdout.strip().split('\n') + result = ctx.run("git ls-remote --tags upstream", hide="both") + remote_tags = result.stdout.strip().split("\n") tags = set() for remote_tag in remote_tags: tags.add( - remote_tag.split('refs/tags/')[1].replace('refs/tags/', '^{}')) + remote_tag.split("refs/tags/")[1].replace("refs/tags/", "^{}") + ) version_tags = sorted(list(tags)) - assert 'v{0}'.format(version) not in version_tags, ( - 'A "{0}" "v{1}" tag already exists in remote repository!'.format( - PYTHON_PACKAGE_NAME, version)) + assert f"v{version}" not in version_tags, ( + f'A "{PYTHON_PACKAGE_NAME}" "v{version}" tag already exists in ' + f"remote repository!" + ) - ctx.run('git flow release start v{0}'.format(version)) - ctx.run('git flow release finish v{0}'.format(version)) + ctx.run(f"git flow release start v{version}") + ctx.run(f"git flow release finish v{version}") @task(build) @@ -500,10 +522,10 @@ def release(ctx: Context): Context. """ - message_box('Releasing...') - with ctx.cd('dist'): - ctx.run('twine upload *.tar.gz') - ctx.run('twine upload *.whl') + message_box("Releasing...") + with ctx.cd("dist"): + ctx.run("twine upload *.tar.gz") + ctx.run("twine upload *.whl") @task @@ -518,5 +540,5 @@ def sha256(ctx: Context): """ message_box('Computing "sha256"...') - with ctx.cd('dist'): - ctx.run('openssl sha256 {0}-*.tar.gz'.format(PYPI_PACKAGE_NAME)) + with ctx.cd("dist"): + ctx.run(f"openssl sha256 {PYPI_PACKAGE_NAME}-*.tar.gz") diff --git a/utilities/export_todo.py b/utilities/export_todo.py index 81b7def..529993a 100755 --- a/utilities/export_todo.py +++ b/utilities/export_todo.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Export TODOs ============ @@ -11,16 +10,16 @@ import os from collections import OrderedDict -__copyright__ = 'Copyright (C) 2015-2021 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +__copyright__ = "Copyright (C) 2015-2021 - Colour Developers" +__license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" +__maintainer__ = "Colour Developers" +__email__ = "colour-developers@colour-science.org" +__status__ = "Production" __all__ = [ - 'TODO_FILE_TEMPLATE', - 'extract_todo_items', - 'export_todo_items', + "TODO_FILE_TEMPLATE", + "extract_todo_items", + "export_todo_items", ] TODO_FILE_TEMPLATE = """ @@ -42,7 +41,9 @@ https://opensource.org/licenses/BSD-3-Clause | `https://github.com/colour-science/colour-demosaicing \ `__ -""" [1:] +"""[ + 1: +] def extract_todo_items(root_directory: str) -> OrderedDict: @@ -63,11 +64,11 @@ def extract_todo_items(root_directory: str) -> OrderedDict: todo_items = OrderedDict() for root, dirnames, filenames in os.walk(root_directory): for filename in filenames: - if not filename.endswith('.py'): + if not filename.endswith(".py"): continue filename = os.path.join(root, filename) - with codecs.open(filename, encoding='utf8') as file_handle: + with codecs.open(filename, encoding="utf8") as file_handle: content = file_handle.readlines() in_todo = False @@ -75,20 +76,20 @@ def extract_todo_items(root_directory: str) -> OrderedDict: todo_item = [] for i, line in enumerate(content): line = line.strip() - if line.startswith('# TODO:'): + if line.startswith("# TODO:"): in_todo = True line_number = i todo_item.append(line) continue - if in_todo and line.startswith('#'): - todo_item.append(line.replace('#', '').strip()) + if in_todo and line.startswith("#"): + todo_item.append(line.replace("#", "").strip()) elif len(todo_item): - key = filename.replace('../', '') + key = filename.replace("../", "") if not todo_items.get(key): todo_items[key] = [] - todo_items[key].append((line_number, ' '.join(todo_item))) + todo_items[key].append((line_number, " ".join(todo_item))) in_todo = False line_number todo_item = [] @@ -110,18 +111,18 @@ def export_todo_items(todo_items: OrderedDict, file_path: str): todo_rst = [] for module, todo_items in todo_items.items(): - todo_rst.append('- {0}\n'.format(module)) + todo_rst.append(f"- {module}\n") for line_numer, todo_item in todo_items: - todo_rst.append(' - Line {0} : {1}'.format( - line_numer, todo_item)) + todo_rst.append(f" - Line {line_numer} : {todo_item}") - todo_rst.append('\n') + todo_rst.append("\n") - with codecs.open(file_path, 'w', encoding='utf8') as todo_file: - todo_file.write(TODO_FILE_TEMPLATE.format('\n'.join(todo_rst[:-1]))) + with codecs.open(file_path, "w", encoding="utf8") as todo_file: + todo_file.write(TODO_FILE_TEMPLATE.format("\n".join(todo_rst[:-1]))) -if __name__ == '__main__': +if __name__ == "__main__": export_todo_items( - extract_todo_items(os.path.join('..', 'colour_demosaicing')), - os.path.join('..', 'TODO.rst')) + extract_todo_items(os.path.join("..", "colour_demosaicing")), + os.path.join("..", "TODO.rst"), + ) diff --git a/utilities/unicode_to_ascii.py b/utilities/unicode_to_ascii.py index 0d0789d..00d9b17 100755 --- a/utilities/unicode_to_ascii.py +++ b/utilities/unicode_to_ascii.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Unicode to ASCII Utility ======================== @@ -10,26 +9,25 @@ import codecs import os import unicodedata -from typing import Dict -__copyright__ = 'Copyright (C) 2015-2021 - Colour Developers' -__license__ = 'New BSD License - https://opensource.org/licenses/BSD-3-Clause' -__maintainer__ = 'Colour Developers' -__email__ = 'colour-developers@colour-science.org' -__status__ = 'Production' +__copyright__ = "Copyright (C) 2015-2021 - Colour Developers" +__license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" +__maintainer__ = "Colour Developers" +__email__ = "colour-developers@colour-science.org" +__status__ = "Production" __all__ = [ - 'SUBSTITUTIONS', - 'unicode_to_ascii', + "SUBSTITUTIONS", + "unicode_to_ascii", ] -SUBSTITUTIONS: Dict = { - '–': '-', - '“': '"', - '”': '"', - '‘': "'", - '’': "'", - '′': "'", +SUBSTITUTIONS: dict[str, str] = { + "–": "-", + "“": '"', + "”": '"', + "‘": "'", + "’": "'", + "′": "'", } @@ -46,27 +44,29 @@ def unicode_to_ascii(root_directory: str): for root, dirnames, filenames in os.walk(root_directory): for filename in filenames: - if (not filename.endswith('.tex') and - not filename.endswith('.py') and - not filename.endswith('.bib') and - not filename.endswith('.rst')): + if ( + not filename.endswith(".tex") + and not filename.endswith(".py") + and not filename.endswith(".bib") + and not filename.endswith(".rst") + ): continue - if filename == 'unicode_to_ascii.py': + if filename == "unicode_to_ascii.py": continue filename = os.path.join(root, filename) - with codecs.open(filename, encoding='utf8') as file_handle: + with codecs.open(filename, encoding="utf8") as file_handle: content = file_handle.read() - with codecs.open(filename, 'w', encoding='utf8') as file_handle: + with codecs.open(filename, "w", encoding="utf8") as file_handle: for key, value in SUBSTITUTIONS.items(): content = content.replace(key, value) - content = unicodedata.normalize('NFD', content) + content = unicodedata.normalize("NFD", content) file_handle.write(content) -if __name__ == '__main__': - unicode_to_ascii(os.path.join('..', 'colour_demosaicing')) +if __name__ == "__main__": + unicode_to_ascii(os.path.join("..", "colour_demosaicing")) From 8d89964bfcb63424aaee628d25761a30aa2b7b64 Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Sat, 5 Feb 2022 17:14:56 +1300 Subject: [PATCH 31/51] Use imperative mood in docstrings. --- .../bayer/demosaicing/bilinear.py | 2 +- .../bayer/demosaicing/malvar2004.py | 2 +- .../bayer/demosaicing/menon2007.py | 4 +-- .../bayer/demosaicing/tests/test_bilinear.py | 4 +-- .../demosaicing/tests/test_malvar2004.py | 4 +-- .../bayer/demosaicing/tests/test_menon2007.py | 4 +-- colour_demosaicing/bayer/masks.py | 2 +- colour_demosaicing/bayer/mosaicing.py | 2 +- colour_demosaicing/bayer/tests/test_masks.py | 4 +-- .../bayer/tests/test_mosaicing.py | 4 +-- docs/conf.py | 2 +- tasks.py | 26 +++++++++---------- utilities/export_todo.py | 4 +-- utilities/unicode_to_ascii.py | 2 +- 14 files changed, 33 insertions(+), 33 deletions(-) diff --git a/colour_demosaicing/bayer/demosaicing/bilinear.py b/colour_demosaicing/bayer/demosaicing/bilinear.py index c21e998..b347e97 100644 --- a/colour_demosaicing/bayer/demosaicing/bilinear.py +++ b/colour_demosaicing/bayer/demosaicing/bilinear.py @@ -37,7 +37,7 @@ def demosaicing_CFA_Bayer_bilinear( pattern: Union[Literal["RGGB", "BGGR", "GRBG", "GBRG"], str] = "RGGB", ) -> NDArray: """ - Returns the demosaiced *RGB* colourspace array from given *Bayer* CFA using + Return the demosaiced *RGB* colourspace array from given *Bayer* CFA using bilinear interpolation. Parameters diff --git a/colour_demosaicing/bayer/demosaicing/malvar2004.py b/colour_demosaicing/bayer/demosaicing/malvar2004.py index a4cd678..e7d8fd7 100644 --- a/colour_demosaicing/bayer/demosaicing/malvar2004.py +++ b/colour_demosaicing/bayer/demosaicing/malvar2004.py @@ -40,7 +40,7 @@ def demosaicing_CFA_Bayer_Malvar2004( pattern: Union[Literal["RGGB", "BGGR", "GRBG", "GBRG"], str] = "RGGB", ) -> NDArray: """ - Returns the demosaiced *RGB* colourspace array from given *Bayer* CFA using + Return the demosaiced *RGB* colourspace array from given *Bayer* CFA using *Malvar (2004)* demosaicing algorithm. Parameters diff --git a/colour_demosaicing/bayer/demosaicing/menon2007.py b/colour_demosaicing/bayer/demosaicing/menon2007.py index a9dd4bd..11fa8f7 100644 --- a/colour_demosaicing/bayer/demosaicing/menon2007.py +++ b/colour_demosaicing/bayer/demosaicing/menon2007.py @@ -58,7 +58,7 @@ def demosaicing_CFA_Bayer_Menon2007( refining_step: Boolean = True, ): """ - Returns the demosaiced *RGB* colourspace array from given *Bayer* CFA using + Return the demosaiced *RGB* colourspace array from given *Bayer* CFA using DDFAPD - *Menon (2007)* demosaicing algorithm. Parameters @@ -233,7 +233,7 @@ def refining_step_Menon2007( RGB: ArrayLike, RGB_m: ArrayLike, M: ArrayLike ) -> NDArray: """ - Performs the refining step on given *RGB* colourspace array. + Perform the refining step on given *RGB* colourspace array. Parameters ---------- diff --git a/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py b/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py index fdb922b..815c5fc 100644 --- a/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py +++ b/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py @@ -34,13 +34,13 @@ class TestDemosaicing_CFA_Bayer_bilinear(unittest.TestCase): """ - Defines :func:`colour_demosaicing.bayer.demosaicing.bilinear.\ + Define :func:`colour_demosaicing.bayer.demosaicing.bilinear.\ demosaicing_CFA_Bayer_bilinear` definition unit tests methods. """ def test_demosaicing_CFA_Bayer_bilinear(self): """ - Tests :func:`colour_demosaicing.bayer.demosaicing.bilinear.\ + Test :func:`colour_demosaicing.bayer.demosaicing.bilinear.\ demosaicing_CFA_Bayer_bilinear` definition. """ diff --git a/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py b/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py index c4ef241..8b39666 100644 --- a/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py +++ b/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py @@ -34,13 +34,13 @@ class TestDemosaicing_CFA_Bayer_Malvar2004(unittest.TestCase): """ - Defines :func:`colour_demosaicing.bayer.demosaicing.malvar2004.\ + Define :func:`colour_demosaicing.bayer.demosaicing.malvar2004.\ demosaicing_CFA_Bayer_Malvar2004` definition unit tests methods. """ def test_demosaicing_CFA_Bayer_Malvar2004(self): """ - Tests :func:`colour_demosaicing.bayer.demosaicing.malvar2004.\ + Test :func:`colour_demosaicing.bayer.demosaicing.malvar2004.\ demosaicing_CFA_Bayer_Malvar2004` definition. """ diff --git a/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py b/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py index 8339b97..6ba1fd7 100644 --- a/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py +++ b/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py @@ -34,13 +34,13 @@ class TestDemosaicing_CFA_Bayer_Menon2007(unittest.TestCase): """ - Defines :func:`colour_demosaicing.bayer.demosaicing.menon2007.\ + Define :func:`colour_demosaicing.bayer.demosaicing.menon2007.\ demosaicing_CFA_Bayer_Menon2007` definition unit tests methods. """ def test_demosaicing_CFA_Bayer_Menon2007(self): """ - Tests :func:`colour_demosaicing.bayer.demosaicing.menon2007.\ + Test :func:`colour_demosaicing.bayer.demosaicing.menon2007.\ demosaicing_CFA_Bayer_Menon2007` definition. """ diff --git a/colour_demosaicing/bayer/masks.py b/colour_demosaicing/bayer/masks.py index b530ff7..05c8a9c 100644 --- a/colour_demosaicing/bayer/masks.py +++ b/colour_demosaicing/bayer/masks.py @@ -29,7 +29,7 @@ def masks_CFA_Bayer( pattern: Union[Literal["RGGB", "BGGR", "GRBG", "GBRG"], str] = "RGGB", ) -> Tuple[NDArray, ...]: """ - Returns the *Bayer* CFA red, green and blue masks for given pattern. + Return the *Bayer* CFA red, green and blue masks for given pattern. Parameters ---------- diff --git a/colour_demosaicing/bayer/mosaicing.py b/colour_demosaicing/bayer/mosaicing.py index 7776ea7..34595ca 100644 --- a/colour_demosaicing/bayer/mosaicing.py +++ b/colour_demosaicing/bayer/mosaicing.py @@ -29,7 +29,7 @@ def mosaicing_CFA_Bayer( pattern: Union[Literal["RGGB", "BGGR", "GRBG", "GBRG"], str] = "RGGB", ) -> NDArray: """ - Returns the *Bayer* CFA mosaic for a given *RGB* colourspace array. + Return the *Bayer* CFA mosaic for a given *RGB* colourspace array. Parameters ---------- diff --git a/colour_demosaicing/bayer/tests/test_masks.py b/colour_demosaicing/bayer/tests/test_masks.py index 13644e8..a9e2995 100644 --- a/colour_demosaicing/bayer/tests/test_masks.py +++ b/colour_demosaicing/bayer/tests/test_masks.py @@ -34,13 +34,13 @@ class TestMasks_CFA_Bayer(unittest.TestCase): """ - Defines :func:`colour_demosaicing.bayer.masks.masks_CFA_Bayer` definition + Define :func:`colour_demosaicing.bayer.masks.masks_CFA_Bayer` definition unit tests methods. """ def test_masks_CFA_Bayer(self): """ - Tests :func:`colour_demosaicing.bayer.masks.masks_CFA_Bayer` + Test :func:`colour_demosaicing.bayer.masks.masks_CFA_Bayer` definition. """ diff --git a/colour_demosaicing/bayer/tests/test_mosaicing.py b/colour_demosaicing/bayer/tests/test_mosaicing.py index 8b0c9c7..1682f9b 100644 --- a/colour_demosaicing/bayer/tests/test_mosaicing.py +++ b/colour_demosaicing/bayer/tests/test_mosaicing.py @@ -34,13 +34,13 @@ class TestMosaicing_CFA_Bayer(unittest.TestCase): """ - Defines :func:`colour_demosaicing.bayer.mosaicing.mosaicing_CFA_Bayer` + Define :func:`colour_demosaicing.bayer.mosaicing.mosaicing_CFA_Bayer` definition unit tests methods. """ def test_mosaicing_CFA_Bayer(self): """ - Tests :func:`colour_demosaicing.bayer.mosaicing.mosaicing_CFA_Bayer` + Test :func:`colour_demosaicing.bayer.mosaicing.mosaicing_CFA_Bayer` definition. """ diff --git a/docs/conf.py b/docs/conf.py index b48b01f..b8f3dda 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -422,7 +422,7 @@ def _autodoc_process_docstring(app, what, name, obj, options, lines): """ - Process the docstrings to remove the *# noqa* *flake8* pragma. + Proces the docstrings to remove the *# noqa* *flake8* pragma. """ for i, line in enumerate(lines): diff --git a/tasks.py b/tasks.py index 8d945f6..8293bfe 100644 --- a/tasks.py +++ b/tasks.py @@ -94,7 +94,7 @@ def clean( pytest: Boolean = True, ): """ - Cleans the project. + Clean the project. Parameters ---------- @@ -139,7 +139,7 @@ def formatting( bibtex: Boolean = True, ): """ - Converts unicode characters to ASCII and cleanup the *BibTeX* file. + Convert unicode characters to ASCII and cleanup the *BibTeX* file. Parameters ---------- @@ -186,7 +186,7 @@ def quality( rstlint: Boolean = True, ): """ - Checks the codebase with *Mypy* and lints various *restructuredText* + Check the codebase with *Mypy* and lints various *restructuredText* files with *rst-lint*. Parameters @@ -222,7 +222,7 @@ def quality( @task def precommit(ctx: Context): """ - Runs the "pre-commit" hooks on the codebase. + Run the "pre-commit" hooks on the codebase. Parameters ---------- @@ -237,7 +237,7 @@ def precommit(ctx: Context): @task def tests(ctx: Context): """ - Runs the unit tests with *Pytest*. + Run the unit tests with *Pytest*. Parameters ---------- @@ -259,7 +259,7 @@ def tests(ctx: Context): @task def examples(ctx: Context): """ - Runs the examples. + Run the examples. Parameters ---------- @@ -279,7 +279,7 @@ def examples(ctx: Context): @task(formatting, quality, precommit, tests, examples) def preflight(ctx: Context): """ - Performs the preflight tasks, i.e. *formatting*, *tests*, *quality*, and + Perform the preflight tasks, i.e. *formatting*, *tests*, *quality*, and *examples*. Parameters @@ -294,7 +294,7 @@ def preflight(ctx: Context): @task def docs(ctx: Context, html: Boolean = True, pdf: Boolean = True): """ - Builds the documentation. + Build the documentation. Parameters ---------- @@ -339,7 +339,7 @@ def todo(ctx: Context): @task def requirements(ctx: Context): """ - Exports the *requirements.txt* file. + Export the *requirements.txt* file. Parameters ---------- @@ -358,7 +358,7 @@ def requirements(ctx: Context): @task(clean, preflight, docs, todo, requirements) def build(ctx: Context): """ - Builds the project and runs dependency tasks, i.e. *docs*, *todo*, and + Build the project and runs dependency tasks, i.e. *docs*, *todo*, and *preflight*. Parameters @@ -459,7 +459,7 @@ def virtualise(ctx: Context, tests: Boolean = True): @task def tag(ctx: Context): """ - Tags the repository according to defined version using *git-flow*. + Tag the repository according to defined version using *git-flow*. Parameters ---------- @@ -514,7 +514,7 @@ def tag(ctx: Context): @task(build) def release(ctx: Context): """ - Releases the project to *Pypi* with *Twine*. + Release the project to *Pypi* with *Twine*. Parameters ---------- @@ -531,7 +531,7 @@ def release(ctx: Context): @task def sha256(ctx: Context): """ - Computes the project *Pypi* package *sha256* with *OpenSSL*. + Compute the project *Pypi* package *sha256* with *OpenSSL*. Parameters ---------- diff --git a/utilities/export_todo.py b/utilities/export_todo.py index 529993a..9d40a9d 100755 --- a/utilities/export_todo.py +++ b/utilities/export_todo.py @@ -48,7 +48,7 @@ def extract_todo_items(root_directory: str) -> OrderedDict: """ - Extracts the TODO items from given directory. + Extract the TODO items from given directory. Parameters ---------- @@ -99,7 +99,7 @@ def extract_todo_items(root_directory: str) -> OrderedDict: def export_todo_items(todo_items: OrderedDict, file_path: str): """ - Exports TODO items to given file. + Export TODO items to given file. Parameters ---------- diff --git a/utilities/unicode_to_ascii.py b/utilities/unicode_to_ascii.py index 00d9b17..f0d4b51 100755 --- a/utilities/unicode_to_ascii.py +++ b/utilities/unicode_to_ascii.py @@ -33,7 +33,7 @@ def unicode_to_ascii(root_directory: str): """ - Recursively converts from unicode to ASCII *.py*, *.bib* and *.rst* files + Recursively convert from unicode to ASCII *.py*, *.bib* and *.rst* files in given directory. Parameters From a922723aac0b0d9ce022d268a535e868fc6862b3 Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Sun, 6 Feb 2022 20:40:41 +1300 Subject: [PATCH 32/51] Set "black" version. --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 340cfa3..14bdc43 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: hooks: - id: flynt - repo: https://github.com/psf/black - rev: stable + rev: 22.1.0 hooks: - id: black language_version: python3.8 From 97f274d8231cca739343dd6aceace04b67e5922b Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Sat, 12 Feb 2022 18:02:34 +1300 Subject: [PATCH 33/51] Use "PyData" "Sphinx" theme. --- README.rst | 45 ++-- docs/_static/Logo_Dark_001.svg | 71 +++++++ docs/_static/Logo_Light_001.svg | 71 +++++++ docs/_static/Logo_Medium_001.png | Bin 50501 -> 53455 bytes docs/_static/Logo_Small_001.png | Bin 19080 -> 13481 bytes docs/_static/custom.css | 26 --- docs/colour_demosaicing.bayer.rst | 2 - docs/conf.py | 340 +++++------------------------- docs/index.rst | 81 ++----- docs/installation.rst | 30 +++ docs/manual.rst | 8 - docs/reference.rst | 4 +- docs/user-guide.rst | 14 ++ pyproject.toml | 17 +- tasks.py | 4 +- 15 files changed, 291 insertions(+), 422 deletions(-) create mode 100644 docs/_static/Logo_Dark_001.svg create mode 100644 docs/_static/Logo_Light_001.svg delete mode 100644 docs/_static/custom.css create mode 100644 docs/installation.rst delete mode 100644 docs/manual.rst create mode 100644 docs/user-guide.rst diff --git a/README.rst b/README.rst index 5a80cb7..539284f 100644 --- a/README.rst +++ b/README.rst @@ -30,7 +30,7 @@ It is open source and freely available under the .. contents:: **Table of Contents** :backlinks: none - :depth: 3 + :depth: 2 .. sectnum:: @@ -43,8 +43,17 @@ The following CFA (Colour Filter Array) demosaicing algorithms are implemented: - Malvar (2004) - DDFAPD - Menon (2007) +Examples +^^^^^^^^ + +Various usage examples are available from the +`examples directory `__. + +User Guide +---------- + Installation ------------- +^^^^^^^^^^^^ Because of their size, the resources dependencies needed to run the various examples and unit tests are not provided within the Pypi package. They are @@ -54,15 +63,15 @@ when cloning the `repository `__. Primary Dependencies -^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~ **Colour - Demosaicing** requires various dependencies in order to run: -- `python>=3.5 `__ +- `python >= 3.8, < 4 `__ - `colour-science `__ Pypi -^^^^ +~~~~ Once the dependencies are satisfied, **Colour - Demosaicing** can be installed from the `Python Package Index `__ by @@ -74,35 +83,26 @@ The overall development dependencies are installed as follows:: pip install --user 'colour-demosaicing[development]' -Usage ------ - -API -^^^ - -The main reference for `Colour - Demosaicing `__ -is the `Colour - Demosaicing Manual `__. - -Examples -^^^^^^^^ - -Various usage examples are available from the -`examples directory `__. - Contributing ------------- +^^^^^^^^^^^^ If you would like to contribute to `Colour - Demosaicing `__, please refer to the following `Contributing `__ guide for `Colour `__. Bibliography ------------- +^^^^^^^^^^^^ The bibliography is available in the repository in `BibTeX `__ format. +API Reference +------------- + +The main technical reference for `Colour - Demosaicing `__ +is the `API Reference `__. + Code of Conduct --------------- @@ -115,7 +115,6 @@ Contact & Social The *Colour Developers* can be reached via different means: - `Email `__ -- `Discourse `__ - `Facebook `__ - `Github Discussions `__ - `Gitter `__ diff --git a/docs/_static/Logo_Dark_001.svg b/docs/_static/Logo_Dark_001.svg new file mode 100644 index 0000000..eb0ff27 --- /dev/null +++ b/docs/_static/Logo_Dark_001.svg @@ -0,0 +1,71 @@ + + + + + + + + + diff --git a/docs/_static/Logo_Light_001.svg b/docs/_static/Logo_Light_001.svg new file mode 100644 index 0000000..3d7da27 --- /dev/null +++ b/docs/_static/Logo_Light_001.svg @@ -0,0 +1,71 @@ + + + + + + + + + diff --git a/docs/_static/Logo_Medium_001.png b/docs/_static/Logo_Medium_001.png index 1cb61385c476bc18ccbdf85d18a21f44d4d0be48..ca330432edbdbaf5d18369dfc7fcebd43cdddbcc 100644 GIT binary patch literal 53455 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%0AR#w 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 H+sOY17e^!= literal 50501 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 diff --git a/docs/_static/custom.css b/docs/_static/custom.css deleted file mode 100644 index e39ccc7..0000000 --- a/docs/_static/custom.css +++ /dev/null @@ -1,26 +0,0 @@ -p { - margin-bottom: 8px !important; -} - -.wy-table-responsive table td { - background-color: rgb(252, 252, 252) !important; - border: 0 !important; - padding: 2px 4px 2px 0px !important; - vertical-align: top !important; - white-space: normal !important; -} - -.wy-table-responsive { - overflow: visible !important; -} - -.wy-table-bordered-all, .rst-content table.docutils { - border: 0 !important; -} - -div.highlight-text { - background-color: rgb(252, 252, 252) !important; - font-family: monospace; - font-style: italic; - margin: -24px 0 24px 0; -} \ No newline at end of file diff --git a/docs/colour_demosaicing.bayer.rst b/docs/colour_demosaicing.bayer.rst index bcc4cca..4c76589 100644 --- a/docs/colour_demosaicing.bayer.rst +++ b/docs/colour_demosaicing.bayer.rst @@ -1,8 +1,6 @@ Bayer CFA Demosaicing and Mosaicing =================================== -.. contents:: :local: - Demosaicing ----------- diff --git a/docs/conf.py b/docs/conf.py index b8f3dda..4bfacef 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,15 +1,7 @@ -# -# colour-demosaicing documentation build configuration file, created by -# sphinx-quickstart on Tue Aug 5 14:31:53 2014. -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. +""" +Colour - Demosaicing - Documentation Configuration +================================================== +""" import re @@ -19,19 +11,7 @@ "_(\\w)", lambda x: x.group(1).upper(), package.__name__.title() ) -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# sys.path.insert(0, os.path.abspath('.')) - # -- General configuration ------------------------------------------------ - -# If your documentation needs a minimal Sphinx version, state it here. -# needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. extensions = [ "sphinx.ext.autodoc", "sphinx.ext.autosummary", @@ -46,6 +26,14 @@ "sphinxcontrib.bibtex", ] +intersphinx_mapping = { + "python": ("https://docs.python.org/3.7", None), + "matplotlib": ("https://matplotlib.org/stable", None), + "numpy": ("https://numpy.org/doc/stable", None), + "pandas": ("https://pandas.pydata.org/pandas-docs/dev", None), + "scipy": ("https://docs.scipy.org/doc/scipy-1.8.0/html-scipyorg", None), +} + autodoc_member_order = "bysource" autodoc_mock_imports = [ "colour", @@ -79,6 +67,8 @@ } autodoc_preserve_defaults = True +autoclass_content = "both" + autosummary_generate = True bibtex_bibfiles = ["bibliography.bib"] @@ -86,163 +76,62 @@ napoleon_custom_sections = ["Attributes", "Methods"] -# Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] - -# The suffix of source filenames. source_suffix = ".rst" - -# The encoding of source files. -# source_encoding = 'utf-8-sig' - -# The master toctree document. master_doc = "index" -# General information about the project. project = package.__application_name__ copyright = package.__copyright__.replace("Copyright (C)", "") - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. version = f"{package.__major_version__}.{package.__minor_version__}" -# The full version, including alpha/beta/rc tags. release = package.__version__ -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -# today = '' -# Else, today_fmt is used as the format for a strftime call. -# today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. exclude_patterns = ["_build"] -# The reST default role (used for this markup: `text`) to use for all -# documents. -# default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -# add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -# add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -# show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. pygments_style = "lovelace" -# A list of ignored prefixes for module index sorting. -# modindex_common_prefix = [] - -# If true, keep warnings as 'system message' paragraphs in the built documents. -# keep_warnings = False - # -- Options for HTML output ---------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -html_theme = "sphinx_rtd_theme" -# -# html_theme_options = {} - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -# html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# ' v documentation'. -# html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -# html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -html_logo = "_static/Logo_Small_001.png" - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -# html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named 'default.css' will overwrite the builtin 'default.css'. +html_theme = "pydata_sphinx_theme" +html_theme_options = { + "show_nav_level": 2, + "icon_links": [ + { + "name": "Email", + "url": "mailto:colour-developers@colour-science.org", + "icon": "fas fa-envelope", + }, + { + "name": "GitHub", + "url": ( + f"https://github.com/colour-science/" + f"{package.__name__.replace('_', '-')}" + ), + "icon": "fab fa-github", + }, + { + "name": "Facebook", + "url": "https://www.facebook.com/python.colour.science", + "icon": "fab fa-facebook", + }, + { + "name": "Gitter", + "url": "https://gitter.im/colour-science/colour", + "icon": "fab fa-gitter", + }, + { + "name": "Twitter", + "url": "https://twitter.com/colour_science", + "icon": "fab fa-twitter", + }, + ], +} +html_logo = "_static/Logo_Light_001.svg" html_static_path = ["_static"] - -# Add any extra paths that contain custom files (such as robots.txt or -# .htaccess) here, relative to this directory. These files are copied -# directly to the root of the documentation. -# html_extra_path = [] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -# html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -# html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -# html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -# html_additional_pages = {} - -# If false, no module index is generated. -# html_domain_indices = True - -# If false, no index is generated. -# html_use_index = True - -# If true, the index is split into individual pages for each letter. -# html_split_index = False - -# If true, links to the reST sources are added to the pages. -# html_show_sourcelink = True - -# If true, 'Created using Sphinx' is shown in the HTML footer. Default is True. -# html_show_sphinx = True - -# If true, '(C) Copyright ...' is shown in the HTML footer. Default is True. -# html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -# html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. '.xhtml'). -# html_file_suffix = None - -# Output file base name for HTML help builder. htmlhelp_basename = f"{basename}Doc" # -- Options for LaTeX output --------------------------------------------- - latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). "papersize": "a4paper", - # The font size ('10pt', '11pt' or '12pt'). "pointsize": "10pt", - # Additional stuff for the LaTeX preamble. "preamble": """ \\usepackage{charter} \\usepackage[defaultsans]{lato} @@ -260,10 +149,6 @@ \\makeatother """, } - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). latex_documents = [ ( "index", @@ -273,31 +158,9 @@ "manual", ), ] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. latex_logo = "_static/Logo_Medium_001.png" -# For 'manual' documents, if this is true, then toplevel headings are parts, -# not chapters. -# latex_use_parts = False - -# If true, show page references after internal links. -# latex_show_pagerefs = False - -# If true, show URL addresses after external links. -# latex_show_urls = False - -# Documents to append as an appendix to all manuals. -# latex_appendices = [] - -# If false, no module index is generated. -# latex_domain_indices = True - # -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). man_pages = [ ( "index", @@ -308,14 +171,7 @@ ) ] -# If true, show URL addresses after external links. -# man_show_urls = False - # -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) texinfo_documents = [ ( "index", @@ -328,107 +184,25 @@ ), ] -# Documents to append as an appendix to all manuals. -# texinfo_appendices = [] - -# If false, no module index is generated. -# texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -# texinfo_show_urls = 'footnote' - -# If true, do not generate a @detailmenu in the 'Top' node's menu. -# texinfo_no_detailmenu = False - # -- Options for Epub output ---------------------------------------------- - -# Bibliographic Dublin Core info. epub_title = package.__application_name__ epub_author = package.__author__ epub_publisher = package.__author__ epub_copyright = package.__copyright__.replace("Copyright (C)", "") - -# The basename for the epub file. It defaults to the project name. -# epub_basename = basename - -# The HTML theme for the epub output. Since the default themes are not -# optimized for small screen space, using the same theme for HTML and epub -# output is usually not wise. This defaults to 'epub', a theme designed to save -# visual space. -# epub_theme = 'epub' - -# The language of the text. It defaults to the language option -# or en if the language is not set. -# epub_language = '' - -# The scheme of the identifier. Typical schemes are ISBN or URL. -# epub_scheme = '' - -# The unique identifier of the text. This can be a ISBN number -# or the project homepage. -# epub_identifier = '' - -# A unique identification for the text. -# epub_uid = '' - -# A tuple containing the cover image and cover page html template filenames. -# epub_cover = () - -# A sequence of (type, uri, title) tuples for the guide element of content.opf. -# epub_guide = () - -# HTML files that should be inserted before the pages created by sphinx. -# The format is a list of tuples containing the path and title. -# epub_pre_files = [] - -# HTML files shat should be inserted after the pages created by sphinx. -# The format is a list of tuples containing the path and title. -# epub_post_files = [] - -# A list of files that should not be packed into the epub file. epub_exclude_files = ["search.html"] -# The depth of the table of contents in toc.ncx. -# epub_tocdepth = 3 - -# Allow duplicate toc entries. -# epub_tocdup = True - -# Choose between 'default' and 'includehidden'. -# epub_tocscope = 'default' - -# Fix unsupported image types using the PIL. -# epub_fix_images = False - -# Scale large images. -# epub_max_image_width = 0 - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -# epub_show_urls = 'inline' - -# If false, no index is generated. -# epub_use_index = True - -autoclass_content = "both" - -intersphinx_mapping = { - "python": ("https://docs.python.org/3.7", None), - "matplotlib": ("http://matplotlib.org/stable", None), - "numpy": ("http://docs.scipy.org/doc/numpy", None), - "pandas": ("http://pandas.pydata.org/pandas-docs/dev", None), - "scipy": ("http://docs.scipy.org/doc/scipy/reference", None), -} - -def _autodoc_process_docstring(app, what, name, obj, options, lines): - """ - Proces the docstrings to remove the *# noqa* *flake8* pragma. - """ +def autodoc_process_docstring(app, what, name, obj, options, lines): + """Process the docstrings to remove the *# noqa* *flake8* pragma.""" for i, line in enumerate(lines): lines[i] = line.replace("# noqa", "") def setup(app): - app.add_css_file("custom.css") - app.connect("autodoc-process-docstring", _autodoc_process_docstring) + """ + Prepare the extension and linking resources that Sphinx uses in the + build process. + """ + + app.connect("autodoc-process-docstring", autodoc_process_docstring) diff --git a/docs/index.rst b/docs/index.rst index 99f2b81..a9e126d 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -9,10 +9,6 @@ It is open source and freely available under the .. image:: https://raw.githubusercontent.com/colour-science/colour-demosaicing/master/docs/_static/Demosaicing_001.png -.. contents:: **Table of Contents** - :backlinks: none - :depth: 3 - .. sectnum:: Features @@ -24,79 +20,27 @@ The following CFA (Colour Filter Array) demosaicing algorithms are implemented: - Malvar (2004) - DDFAPD - Menon (2007) -Installation ------------- - -Because of their size, the resources dependencies needed to run the various -examples and unit tests are not provided within the Pypi package. They are -separately available as -`Git Submodules `__ -when cloning the -`repository `__. - -Primary Dependencies -^^^^^^^^^^^^^^^^^^^^ - -**Colour - Demosaicing** requires various dependencies in order to run: - -- `python>=3.5 `__ -- `colour-science `__ - -Pypi -^^^^ - -Once the dependencies are satisfied, **Colour - Demosaicing** can be installed from -the `Python Package Index `__ by -issuing this command in a shell:: - - pip install --user colour-demosaicing - -The tests suite dependencies are installed as follows:: - - pip install --user 'colour-demosaicing[tests]' - -The documentation building dependencies are installed as follows:: - - pip install --user 'colour-demosaicing[docs]' - -The overall development dependencies are installed as follows:: - - pip install --user 'colour-demosaicing[development]' - -Usage ------ - -API -^^^ - -The main reference for -`Colour - Demosaicing `__ -is the manual: - -.. toctree:: - :maxdepth: 4 - - manual - Examples ^^^^^^^^ Various usage examples are available from the `examples directory `__. -Contributing ------------- +User Guide +---------- + +.. toctree:: + :maxdepth: 2 + + user-guide -If you would like to contribute to `Colour - Demosaicing `__, -please refer to the following `Contributing `__ -guide for `Colour `__. +API Reference +------------- -Bibliography ------------- +.. toctree:: + :maxdepth: 2 -The bibliography is available in the repository in -`BibTeX `__ -format. + reference Code of Conduct --------------- @@ -110,7 +54,6 @@ Contact & Social The *Colour Developers* can be reached via different means: - `Email `__ -- `Discourse `__ - `Facebook `__ - `Github Discussions `__ - `Gitter `__ diff --git a/docs/installation.rst b/docs/installation.rst new file mode 100644 index 0000000..d9e1e5c --- /dev/null +++ b/docs/installation.rst @@ -0,0 +1,30 @@ +Installation Guide +================== + +Because of their size, the resources dependencies needed to run the various +examples and unit tests are not provided within the Pypi package. They are +separately available as +`Git Submodules `__ +when cloning the +`repository `__. + +Primary Dependencies +-------------------- + +**Colour - Demosaicing** requires various dependencies in order to run: + +- `python >= 3.8, < 4 `__ +- `colour-science `__ + +Pypi +---- + +Once the dependencies are satisfied, **Colour - Demosaicing** can be installed from +the `Python Package Index `__ by +issuing this command in a shell:: + + pip install --user colour-demosaicing + +The overall development dependencies are installed as follows:: + + pip install --user 'colour-demosaicing[development]' diff --git a/docs/manual.rst b/docs/manual.rst deleted file mode 100644 index 5223ce7..0000000 --- a/docs/manual.rst +++ /dev/null @@ -1,8 +0,0 @@ -Colour - Demosaicing Manual -=========================== - -.. toctree:: - :maxdepth: 3 - - reference - bibliography \ No newline at end of file diff --git a/docs/reference.rst b/docs/reference.rst index 62fe644..453c32a 100644 --- a/docs/reference.rst +++ b/docs/reference.rst @@ -1,5 +1,5 @@ -Reference -========= +API Reference +============= .. toctree:: :titlesonly: diff --git a/docs/user-guide.rst b/docs/user-guide.rst new file mode 100644 index 0000000..a72a033 --- /dev/null +++ b/docs/user-guide.rst @@ -0,0 +1,14 @@ +User Guide +========== + +The user guide provides an overview of **Colour - Demosaicing** and +explains important concepts and features, details can be found in the +`API Reference `__. + +.. toctree:: + :maxdepth: 1 + + Installation + Contributing + Changes + bibliography diff --git a/pyproject.toml b/pyproject.toml index d548ec6..d8ca634 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,12 +63,12 @@ matplotlib = { version = ">= 3.2, != 3.5.0, != 3.5.1", optional = true } mypy = { version = "*", optional = true } # Development dependency. numpy = { version = ">= 1.19, < 2", optional = true } pre-commit = { version = "*", optional = true } # Development dependency. +pydata-sphinx-theme = { version = "*", optional = true } # Development dependency. pytest = { version = "*", optional = true } # Development dependency. pytest-cov = { version = "*", optional = true } # Development dependency. pyupgrade = { version = "*", optional = true } # Development dependency. restructuredtext-lint = { version = "*", optional = true } # Development dependency. -sphinx = { version = "*", optional = true } # Development dependency. -sphinx-rtd-theme = { version = "*", optional = true } # Development dependency. +sphinx = { version = ">= 4, < 5", optional = true } # Development dependency. sphinxcontrib-bibtex = { version = "*", optional = true } # Development dependency. toml = { version = "*", optional = true } # Development dependency. twine = { version = "*", optional = true } # Development dependency. @@ -84,12 +84,12 @@ invoke = "*" jupyter = "*" mypy = "*" pre-commit = "*" +pydata-sphinx-theme = "*" pytest = "*" pytest-cov = "*" pyupgrade = "*" restructuredtext-lint = "*" -sphinx = "*" -sphinx-rtd-theme = "*" +sphinx = ">= 4, < 5" sphinxcontrib-bibtex = "*" toml = "*" twine = "*" @@ -106,18 +106,23 @@ development = [ "jupyter", "mypy", "pre-commit", + "pydata-sphinx-theme", "pytest", "pytest-cov", "pyupgrade", "restructuredtext-lint", "sphinx", - "sphinx-rtd-theme", "sphinxcontrib-bibtex", "toml", "twine", ] plotting = [ "matplotlib" ] -read-the-docs = [ "matplotlib", "numpy", "sphinxcontrib-bibtex" ] +read-the-docs = [ + "matplotlib", + "numpy", + "pydata-sphinx-theme", + "sphinxcontrib-bibtex" +] [tool.black] line-length = 79 diff --git a/tasks.py b/tasks.py index 8293bfe..ce9d4c6 100644 --- a/tasks.py +++ b/tasks.py @@ -306,9 +306,7 @@ def docs(ctx: Context, html: Boolean = True, pdf: Boolean = True): Whether to build the *PDF* documentation. """ - with ctx.prefix( - "export COLOUR_SCIENCE__DOCUMENTATION_BUILD: Boolean=True" - ): + with ctx.prefix("export COLOUR_SCIENCE__DOCUMENTATION_BUILD=True"): with ctx.cd("docs"): if html: message_box('Building "HTML" documentation...') From 429465ec1e9f69b8f761a9ecdf6795c26d9c378b Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Sat, 12 Feb 2022 17:46:03 +1300 Subject: [PATCH 34/51] Update copyright statements. --- CONTRIBUTORS.rst | 2 +- LICENSE | 2 +- README.rst | 2 +- TODO.rst | 2 +- colour_demosaicing/__init__.py | 2 +- colour_demosaicing/bayer/demosaicing/bilinear.py | 2 +- colour_demosaicing/bayer/demosaicing/malvar2004.py | 2 +- colour_demosaicing/bayer/demosaicing/menon2007.py | 2 +- colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py | 2 +- colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py | 2 +- colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py | 2 +- colour_demosaicing/bayer/masks.py | 2 +- colour_demosaicing/bayer/mosaicing.py | 2 +- colour_demosaicing/bayer/tests/test_masks.py | 2 +- colour_demosaicing/bayer/tests/test_mosaicing.py | 2 +- docs/index.rst | 2 +- tasks.py | 2 +- utilities/export_todo.py | 4 ++-- utilities/unicode_to_ascii.py | 2 +- 19 files changed, 20 insertions(+), 20 deletions(-) diff --git a/CONTRIBUTORS.rst b/CONTRIBUTORS.rst index d0a6a1c..995761a 100644 --- a/CONTRIBUTORS.rst +++ b/CONTRIBUTORS.rst @@ -30,6 +30,6 @@ About ----- | **Colour - Demosaicing** by Colour Developers -| Copyright © 2015-2021 – Colour Developers – `colour-developers@colour-science.org `__ +| Copyright 2015 Colour Developers – `colour-developers@colour-science.org `__ | This software is released under terms of New BSD License: https://opensource.org/licenses/BSD-3-Clause | `https://github.com/colour-science/colour-demosaicing `__ diff --git a/LICENSE b/LICENSE index 3a083c8..f63ef1d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2015-2021, Colour Developers +Copyright 2015 Colour Developers All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/README.rst b/README.rst index 539284f..5f98465 100644 --- a/README.rst +++ b/README.rst @@ -124,6 +124,6 @@ About ----- | **Colour - Demosaicing** by Colour Developers -| Copyright © 2015-2021 – Colour Developers – `colour-developers@colour-science.org `__ +| Copyright 2015 Colour Developers – `colour-developers@colour-science.org `__ | This software is released under terms of New BSD License: https://opensource.org/licenses/BSD-3-Clause | `https://github.com/colour-science/colour-demosaicing `__ diff --git a/TODO.rst b/TODO.rst index b27fcab..b1cc7ff 100644 --- a/TODO.rst +++ b/TODO.rst @@ -12,6 +12,6 @@ About ----- | **Colour - Demosaicing** by Colour Developers -| Copyright © 2015-2021 – Colour Developers – `colour-developers@colour-science.org `__ +| Copyright 2015 Colour Developers – `colour-developers@colour-science.org `__ | This software is released under terms of New BSD License: https://opensource.org/licenses/BSD-3-Clause | `https://github.com/colour-science/colour-demosaicing `__ diff --git a/colour_demosaicing/__init__.py b/colour_demosaicing/__init__.py index 8376566..4713111 100644 --- a/colour_demosaicing/__init__.py +++ b/colour_demosaicing/__init__.py @@ -27,7 +27,7 @@ ) __author__ = "Colour Developers" -__copyright__ = "Copyright (C) 2015-2021 - Colour Developers" +__copyright__ = "Copyright 2015 Colour Developers" __license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" __maintainer__ = "Colour Developers" __email__ = "colour-developers@colour-science.org" diff --git a/colour_demosaicing/bayer/demosaicing/bilinear.py b/colour_demosaicing/bayer/demosaicing/bilinear.py index b347e97..56e921b 100644 --- a/colour_demosaicing/bayer/demosaicing/bilinear.py +++ b/colour_demosaicing/bayer/demosaicing/bilinear.py @@ -21,7 +21,7 @@ from colour_demosaicing.bayer import masks_CFA_Bayer __author__ = "Colour Developers" -__copyright__ = "Copyright (C) 2015-2021 - Colour Developers" +__copyright__ = "Copyright 2015 Colour Developers" __license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" __maintainer__ = "Colour Developers" __email__ = "colour-developers@colour-science.org" diff --git a/colour_demosaicing/bayer/demosaicing/malvar2004.py b/colour_demosaicing/bayer/demosaicing/malvar2004.py index e7d8fd7..de40cb3 100644 --- a/colour_demosaicing/bayer/demosaicing/malvar2004.py +++ b/colour_demosaicing/bayer/demosaicing/malvar2004.py @@ -24,7 +24,7 @@ from colour_demosaicing.bayer import masks_CFA_Bayer __author__ = "Colour Developers" -__copyright__ = "Copyright (C) 2015-2021 - Colour Developers" +__copyright__ = "Copyright 2015 Colour Developers" __license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" __maintainer__ = "Colour Developers" __email__ = "colour-developers@colour-science.org" diff --git a/colour_demosaicing/bayer/demosaicing/menon2007.py b/colour_demosaicing/bayer/demosaicing/menon2007.py index 11fa8f7..bad5503 100644 --- a/colour_demosaicing/bayer/demosaicing/menon2007.py +++ b/colour_demosaicing/bayer/demosaicing/menon2007.py @@ -23,7 +23,7 @@ from colour_demosaicing.bayer import masks_CFA_Bayer __author__ = "Colour Developers" -__copyright__ = "Copyright (C) 2015-2021 - Colour Developers" +__copyright__ = "Copyright 2015 Colour Developers" __license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" __maintainer__ = "Colour Developers" __email__ = "colour-developers@colour-science.org" diff --git a/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py b/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py index 815c5fc..ebfeea6 100644 --- a/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py +++ b/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py @@ -16,7 +16,7 @@ from colour_demosaicing.bayer import demosaicing_CFA_Bayer_bilinear __author__ = "Colour Developers" -__copyright__ = "Copyright (C) 2015-2021 - Colour Developers" +__copyright__ = "Copyright 2015 Colour Developers" __license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" __maintainer__ = "Colour Developers" __email__ = "colour-developers@colour-science.org" diff --git a/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py b/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py index 8b39666..7efdd7a 100644 --- a/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py +++ b/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py @@ -16,7 +16,7 @@ from colour_demosaicing.bayer import demosaicing_CFA_Bayer_Malvar2004 __author__ = "Colour Developers" -__copyright__ = "Copyright (C) 2015-2021 - Colour Developers" +__copyright__ = "Copyright 2015 Colour Developers" __license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" __maintainer__ = "Colour Developers" __email__ = "colour-developers@colour-science.org" diff --git a/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py b/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py index 6ba1fd7..e426dee 100644 --- a/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py +++ b/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py @@ -16,7 +16,7 @@ from colour_demosaicing.bayer import demosaicing_CFA_Bayer_Menon2007 __author__ = "Colour Developers" -__copyright__ = "Copyright (C) 2015-2021 - Colour Developers" +__copyright__ = "Copyright 2015 Colour Developers" __license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" __maintainer__ = "Colour Developers" __email__ = "colour-developers@colour-science.org" diff --git a/colour_demosaicing/bayer/masks.py b/colour_demosaicing/bayer/masks.py index 05c8a9c..5ac7a18 100644 --- a/colour_demosaicing/bayer/masks.py +++ b/colour_demosaicing/bayer/masks.py @@ -13,7 +13,7 @@ from colour.utilities import validate_method __author__ = "Colour Developers" -__copyright__ = "Copyright (C) 2015-2021 - Colour Developers" +__copyright__ = "Copyright 2015 Colour Developers" __license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" __maintainer__ = "Colour Developers" __email__ = "colour-developers@colour-science.org" diff --git a/colour_demosaicing/bayer/mosaicing.py b/colour_demosaicing/bayer/mosaicing.py index 34595ca..be36517 100644 --- a/colour_demosaicing/bayer/mosaicing.py +++ b/colour_demosaicing/bayer/mosaicing.py @@ -13,7 +13,7 @@ from colour_demosaicing.bayer import masks_CFA_Bayer __author__ = "Colour Developers" -__copyright__ = "Copyright (C) 2015-2021 - Colour Developers" +__copyright__ = "Copyright 2015 Colour Developers" __license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" __maintainer__ = "Colour Developers" __email__ = "colour-developers@colour-science.org" diff --git a/colour_demosaicing/bayer/tests/test_masks.py b/colour_demosaicing/bayer/tests/test_masks.py index a9e2995..6f40de7 100644 --- a/colour_demosaicing/bayer/tests/test_masks.py +++ b/colour_demosaicing/bayer/tests/test_masks.py @@ -16,7 +16,7 @@ from colour_demosaicing.bayer import masks_CFA_Bayer __author__ = "Colour Developers" -__copyright__ = "Copyright (C) 2015-2021 - Colour Developers" +__copyright__ = "Copyright 2015 Colour Developers" __license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" __maintainer__ = "Colour Developers" __email__ = "colour-developers@colour-science.org" diff --git a/colour_demosaicing/bayer/tests/test_mosaicing.py b/colour_demosaicing/bayer/tests/test_mosaicing.py index 1682f9b..dfa3edf 100644 --- a/colour_demosaicing/bayer/tests/test_mosaicing.py +++ b/colour_demosaicing/bayer/tests/test_mosaicing.py @@ -16,7 +16,7 @@ from colour_demosaicing.bayer import mosaicing_CFA_Bayer __author__ = "Colour Developers" -__copyright__ = "Copyright (C) 2015-2021 - Colour Developers" +__copyright__ = "Copyright 2015 Colour Developers" __license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" __maintainer__ = "Colour Developers" __email__ = "colour-developers@colour-science.org" diff --git a/docs/index.rst b/docs/index.rst index a9e126d..33f9407 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -63,6 +63,6 @@ About ----- | **Colour - Demosaicing** by Colour Developers -| Copyright © 2015-2021 – Colour Developers – `colour-developers@colour-science.org `__ +| Copyright 2015 Colour Developers – `colour-developers@colour-science.org `__ | This software is released under terms of New BSD License: https://opensource.org/licenses/BSD-3-Clause | `https://github.com/colour-science/colour-demosaicing `__ diff --git a/tasks.py b/tasks.py index ce9d4c6..9c766cc 100644 --- a/tasks.py +++ b/tasks.py @@ -18,7 +18,7 @@ from colour.utilities import message_box __author__ = "Colour Developers" -__copyright__ = "Copyright (C) 2015-2021 - Colour Developers" +__copyright__ = "Copyright 2015 Colour Developers" __license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" __maintainer__ = "Colour Developers" __email__ = "colour-developers@colour-science.org" diff --git a/utilities/export_todo.py b/utilities/export_todo.py index 9d40a9d..24c7421 100755 --- a/utilities/export_todo.py +++ b/utilities/export_todo.py @@ -10,7 +10,7 @@ import os from collections import OrderedDict -__copyright__ = "Copyright (C) 2015-2021 - Colour Developers" +__copyright__ = "Copyright 2015 Colour Developers" __license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" __maintainer__ = "Colour Developers" __email__ = "colour-developers@colour-science.org" @@ -35,7 +35,7 @@ ----- | **Colour - Demosaicing** by Colour Developers -| Copyright © 2015-2021 – Colour Developers – \ +| Copyright 2015 Colour Developers – \ `colour-developers@colour-science.org `__ | This software is released under terms of New BSD License: \ https://opensource.org/licenses/BSD-3-Clause diff --git a/utilities/unicode_to_ascii.py b/utilities/unicode_to_ascii.py index f0d4b51..b308293 100755 --- a/utilities/unicode_to_ascii.py +++ b/utilities/unicode_to_ascii.py @@ -10,7 +10,7 @@ import os import unicodedata -__copyright__ = "Copyright (C) 2015-2021 - Colour Developers" +__copyright__ = "Copyright 2015 Colour Developers" __license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" __maintainer__ = "Colour Developers" __email__ = "colour-developers@colour-science.org" From d48abe82456d2e0241d3da81f6daf9ac01490178 Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Sat, 12 Feb 2022 19:01:41 +1300 Subject: [PATCH 35/51] Improve docstrings compliance with PEP 257. --- .pre-commit-config.yaml | 7 ++ .../bayer/demosaicing/menon2007.py | 8 +- colour_demosaicing/bayer/tests/test_masks.py | 4 +- pyproject.toml | 7 ++ setup.py | 98 ++++++++++--------- tasks.py | 4 +- 6 files changed, 72 insertions(+), 56 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 14bdc43..54291aa 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,3 +17,10 @@ repos: rev: 4.0.1 hooks: - id: flake8 +- repo: https://github.com/pycqa/pydocstyle + rev: 6.1.1 + hooks: + - id: pydocstyle + args: + - --convention=numpy + - --add-ignore=D104,D200,D202,D205,D301,D400 diff --git a/colour_demosaicing/bayer/demosaicing/menon2007.py b/colour_demosaicing/bayer/demosaicing/menon2007.py index bad5503..4a131df 100644 --- a/colour_demosaicing/bayer/demosaicing/menon2007.py +++ b/colour_demosaicing/bayer/demosaicing/menon2007.py @@ -37,17 +37,13 @@ def _cnv_h(x: ArrayLike, y: ArrayLike) -> NDArray: - """ - Helper function for horizontal convolution. - """ + """Perform horizontal convolution.""" return convolve1d(x, y, mode="mirror") def _cnv_v(x: ArrayLike, y: ArrayLike) -> NDArray: - """ - Helper function for vertical convolution. - """ + """Perform vertical convolution.""" return convolve1d(x, y, mode="mirror", axis=0) diff --git a/colour_demosaicing/bayer/tests/test_masks.py b/colour_demosaicing/bayer/tests/test_masks.py index 6f40de7..d6743dd 100644 --- a/colour_demosaicing/bayer/tests/test_masks.py +++ b/colour_demosaicing/bayer/tests/test_masks.py @@ -1,7 +1,5 @@ # !/usr/bin/env python -""" -Defines the unit tests for the :mod:`colour_demosaicing.bayer.masks` module. -""" +"""Defines the unit tests for the :mod:`colour_demosaicing.bayer.masks` module.""" from __future__ import annotations diff --git a/pyproject.toml b/pyproject.toml index d8ca634..5f6792f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -64,6 +64,7 @@ mypy = { version = "*", optional = true } # Development dependency. numpy = { version = ">= 1.19, < 2", optional = true } pre-commit = { version = "*", optional = true } # Development dependency. pydata-sphinx-theme = { version = "*", optional = true } # Development dependency. +pydocstyle = { version = "*", optional = true } # Development dependency. pytest = { version = "*", optional = true } # Development dependency. pytest-cov = { version = "*", optional = true } # Development dependency. pyupgrade = { version = "*", optional = true } # Development dependency. @@ -85,6 +86,7 @@ jupyter = "*" mypy = "*" pre-commit = "*" pydata-sphinx-theme = "*" +pydocstyle = "*" pytest = "*" pytest-cov = "*" pyupgrade = "*" @@ -107,6 +109,7 @@ development = [ "mypy", "pre-commit", "pydata-sphinx-theme", + "pydocstyle", "pytest", "pytest-cov", "pyupgrade", @@ -142,6 +145,10 @@ line_length=999 plugins = "numpy.typing.mypy_plugin" ignore_missing_imports = true +[tool.pydocstyle] +convention = "numpy" +add-ignore = "D104,D200,D202,D205,D301,D400" + [build-system] requires = [ "poetry>=0.12" ] build-backend = "poetry.masonry.api" diff --git a/setup.py b/setup.py index c6bb811..9467183 100644 --- a/setup.py +++ b/setup.py @@ -1,57 +1,67 @@ -# -*- coding: utf-8 -*- +""" +Colour - Demosaicing - Setup +============================ +""" + import codecs from setuptools import setup -packages = \ -['colour_demosaicing', - 'colour_demosaicing.bayer', - 'colour_demosaicing.bayer.demosaicing', - 'colour_demosaicing.bayer.demosaicing.tests', - 'colour_demosaicing.bayer.tests'] +packages = [ + "colour_demosaicing", + "colour_demosaicing.bayer", + "colour_demosaicing.bayer.demosaicing", + "colour_demosaicing.bayer.demosaicing.tests", + "colour_demosaicing.bayer.tests", +] -package_data = \ -{'': ['*'], - 'colour_demosaicing': ['examples/*', - 'resources/colour-demosaicing-examples-datasets/*', - 'resources/colour-demosaicing-tests-datasets/*']} +package_data = { + "": ["*"], + "colour_demosaicing": [ + "examples/*", + "resources/colour-demosaicing-examples-datasets/*", + "resources/colour-demosaicing-tests-datasets/*", + ], +} -install_requires = \ -['colour-science>=0.3.16,<0.4.0'] +install_requires = ["colour-science>=0.3.16,<0.4.0"] -extras_require = \ -{'development': ['biblib-simple', - 'coverage', - 'coveralls', - 'flake8', - 'invoke', - 'jupyter', - 'mock', - 'nose', - 'pre-commit', - 'pytest', - 'restructuredtext-lint', - 'sphinx<=3.1.2', - 'sphinx_rtd_theme', - 'sphinxcontrib-bibtex', - 'toml', - 'twine', - 'yapf==0.23'], - 'plotting': ['matplotlib'], - 'read-the-docs': ['mock', 'numpy', 'sphinxcontrib-bibtex']} +extras_require = { + "development": [ + "biblib-simple", + "coverage", + "coveralls", + "flake8", + "invoke", + "jupyter", + "mock", + "nose", + "pre-commit", + "pytest", + "restructuredtext-lint", + "sphinx<=3.1.2", + "sphinx_rtd_theme", + "sphinxcontrib-bibtex", + "toml", + "twine", + "yapf==0.23", + ], + "plotting": ["matplotlib"], + "read-the-docs": ["mock", "numpy", "sphinxcontrib-bibtex"], +} setup( - name='colour-demosaicing', - version='0.1.6', - description='CFA (Colour Filter Array) Demosaicing Algorithms for Python', - long_description=codecs.open('README.rst', encoding='utf8').read(), - author='Colour Developers', - author_email='colour-developers@colour-science.org', - maintainer='Colour Developers', - maintainer_email='colour-developers@colour-science.org', - url='https://www.colour-science.org/', + name="colour-demosaicing", + version="0.1.6", + description="CFA (Colour Filter Array) Demosaicing Algorithms for Python", + long_description=codecs.open("README.rst", encoding="utf8").read(), + author="Colour Developers", + author_email="colour-developers@colour-science.org", + maintainer="Colour Developers", + maintainer_email="colour-developers@colour-science.org", + url="https://www.colour-science.org/", packages=packages, package_data=package_data, install_requires=install_requires, extras_require=extras_require, - python_requires='>=3.6,<4.0', + python_requires=">=3.6,<4.0", ) diff --git a/tasks.py b/tasks.py index 9c766cc..40dc6b7 100644 --- a/tasks.py +++ b/tasks.py @@ -59,9 +59,7 @@ def _patch_invoke_annotations_support(): - """ - See https://github.com/pyinvoke/invoke/issues/357 - """ + """See https://github.com/pyinvoke/invoke/issues/357.""" import invoke from unittest.mock import patch From 65a340d84e94ad6cc75cf0923b02ffbf23fe753a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 14 Feb 2022 22:43:23 +0000 Subject: [PATCH 36/51] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - https://gitlab.com/pycqa/flake8 → https://github.com/PyCQA/flake8 --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 54291aa..f2cc0b9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,7 +13,7 @@ repos: hooks: - id: black language_version: python3.8 -- repo: https://gitlab.com/pycqa/flake8 +- repo: https://github.com/PyCQA/flake8 rev: 4.0.1 hooks: - id: flake8 From 41c53815fcd169363ccc8d78dcacde7741f98253 Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Sat, 12 Feb 2022 19:18:38 +1300 Subject: [PATCH 37/51] Use Python 3.8 for Inter-Sphinx. --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 4bfacef..9e0fb0e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -27,7 +27,7 @@ ] intersphinx_mapping = { - "python": ("https://docs.python.org/3.7", None), + "python": ("https://docs.python.org/3.8", None), "matplotlib": ("https://matplotlib.org/stable", None), "numpy": ("https://numpy.org/doc/stable", None), "pandas": ("https://pandas.pydata.org/pandas-docs/dev", None), From da2be1cf51505e68520302a534fd55b28c897fa5 Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Sun, 13 Feb 2022 09:16:26 +1300 Subject: [PATCH 38/51] Drop remaining "OrderedDict" usages. --- utilities/export_todo.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/utilities/export_todo.py b/utilities/export_todo.py index 24c7421..ae06853 100755 --- a/utilities/export_todo.py +++ b/utilities/export_todo.py @@ -8,7 +8,6 @@ import codecs import os -from collections import OrderedDict __copyright__ = "Copyright 2015 Colour Developers" __license__ = "New BSD License - https://opensource.org/licenses/BSD-3-Clause" @@ -46,7 +45,7 @@ ] -def extract_todo_items(root_directory: str) -> OrderedDict: +def extract_todo_items(root_directory: str) -> dict: """ Extract the TODO items from given directory. @@ -57,11 +56,11 @@ def extract_todo_items(root_directory: str) -> OrderedDict: Returns ------- - :class:`collections.OrderedDict` + :class:`dict` TODO items. """ - todo_items = OrderedDict() + todo_items = {} for root, dirnames, filenames in os.walk(root_directory): for filename in filenames: if not filename.endswith(".py"): @@ -97,7 +96,7 @@ def extract_todo_items(root_directory: str) -> OrderedDict: return todo_items -def export_todo_items(todo_items: OrderedDict, file_path: str): +def export_todo_items(todo_items: dict, file_path: str): """ Export TODO items to given file. From 98559701fc729211990a59d37a4365eedf3b4de8 Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Sun, 13 Feb 2022 14:21:25 +1300 Subject: [PATCH 39/51] Replace "Weta Digital" with "WetaFX". --- CONTRIBUTORS.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTORS.rst b/CONTRIBUTORS.rst index 995761a..00ca971 100644 --- a/CONTRIBUTORS.rst +++ b/CONTRIBUTORS.rst @@ -4,7 +4,7 @@ Contributors Development & Technical Support ------------------------------- -- **Thomas Mansencal**, *Visual Effects Artist @ Weta Digital* +- **Thomas Mansencal**, *Lead Pipeline Developer @ WetaFX* Project coordination, overall development. From 9d757fd850487ece1059d649cdfe1ea81ba4f7e0 Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Tue, 15 Feb 2022 21:39:58 +1300 Subject: [PATCH 40/51] Run "pre-commit" on all files in CI. --- .../workflows/continuous-integration-quality-unit-tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/continuous-integration-quality-unit-tests.yml b/.github/workflows/continuous-integration-quality-unit-tests.yml index 5a1c72e..7fa942e 100644 --- a/.github/workflows/continuous-integration-quality-unit-tests.yml +++ b/.github/workflows/continuous-integration-quality-unit-tests.yml @@ -44,9 +44,9 @@ jobs: poetry install poetry run python -c "import imageio;imageio.plugins.freeimage.download()" shell: bash - - name: Lint with flake8 + - name: Pre-Commit (All Files) run: | - poetry run flake8 $CI_PACKAGE --count --show-source --statistics + poetry run pre-commit run --all-files shell: bash - name: Test Optimised Python Execution run: | From 14b1e862ccc0dc24186943135feb3f1ee738a483 Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Sat, 19 Feb 2022 18:26:16 +1300 Subject: [PATCH 41/51] Udpate various utilities. --- utilities/export_todo.py | 7 +++---- utilities/unicode_to_ascii.py | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/utilities/export_todo.py b/utilities/export_todo.py index ae06853..03f420d 100755 --- a/utilities/export_todo.py +++ b/utilities/export_todo.py @@ -61,7 +61,7 @@ def extract_todo_items(root_directory: str) -> dict: """ todo_items = {} - for root, dirnames, filenames in os.walk(root_directory): + for root, _dirnames, filenames in os.walk(root_directory): for filename in filenames: if not filename.endswith(".py"): continue @@ -71,13 +71,13 @@ def extract_todo_items(root_directory: str) -> dict: content = file_handle.readlines() in_todo = False - line_number = -1 + line_number = 1 todo_item = [] for i, line in enumerate(content): line = line.strip() if line.startswith("# TODO:"): in_todo = True - line_number = i + line_number = i + 1 todo_item.append(line) continue @@ -90,7 +90,6 @@ def extract_todo_items(root_directory: str) -> dict: todo_items[key].append((line_number, " ".join(todo_item))) in_todo = False - line_number todo_item = [] return todo_items diff --git a/utilities/unicode_to_ascii.py b/utilities/unicode_to_ascii.py index b308293..c9a6fed 100755 --- a/utilities/unicode_to_ascii.py +++ b/utilities/unicode_to_ascii.py @@ -42,7 +42,7 @@ def unicode_to_ascii(root_directory: str): Directory to convert the files from unicode to ASCII. """ - for root, dirnames, filenames in os.walk(root_directory): + for root, _dirnames, filenames in os.walk(root_directory): for filename in filenames: if ( not filename.endswith(".tex") From 0be2739ebf8879493ebd36cfefb77dec2ffe706c Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Sat, 19 Feb 2022 22:36:18 +1300 Subject: [PATCH 42/51] Use "Poetry" 1.1.12 for CI. --- .github/workflows/continuous-integration-quality-unit-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/continuous-integration-quality-unit-tests.yml b/.github/workflows/continuous-integration-quality-unit-tests.yml index 7fa942e..b26cdb5 100644 --- a/.github/workflows/continuous-integration-quality-unit-tests.yml +++ b/.github/workflows/continuous-integration-quality-unit-tests.yml @@ -35,7 +35,7 @@ jobs: - name: Install Poetry run: | curl -L https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py -o get-poetry.py - python get-poetry.py + python get-poetry.py --version 1.1.12 echo "$HOME/.poetry/bin" >> $GITHUB_PATH shell: bash - name: Install Package Dependencies From 7acfbb4638433e067b21098a8f491d7f0007e1d1 Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Sat, 19 Feb 2022 22:40:13 +1300 Subject: [PATCH 43/51] Remove unneeded ".format" usages. --- .../bayer/demosaicing/tests/test_bilinear.py | 12 ++++++++---- .../bayer/demosaicing/tests/test_malvar2004.py | 10 ++++++---- .../bayer/demosaicing/tests/test_menon2007.py | 16 ++++++++++------ colour_demosaicing/bayer/tests/test_masks.py | 4 ++-- colour_demosaicing/bayer/tests/test_mosaicing.py | 6 ++++-- 5 files changed, 30 insertions(+), 18 deletions(-) diff --git a/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py b/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py index ebfeea6..9f43171 100644 --- a/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py +++ b/colour_demosaicing/bayer/demosaicing/tests/test_bilinear.py @@ -45,14 +45,18 @@ def test_demosaicing_CFA_Bayer_bilinear(self): """ for pattern in ("RGGB", "BGGR", "GRBG", "GBRG"): - CFA = os.path.join(BAYER_DIRECTORY, "Lighthouse_CFA_{0}.exr") - RGB = os.path.join(BAYER_DIRECTORY, "Lighthouse_Bilinear_{0}.exr") + CFA = os.path.join( + BAYER_DIRECTORY, f"Lighthouse_CFA_{pattern}.exr" + ) + RGB = os.path.join( + BAYER_DIRECTORY, f"Lighthouse_Bilinear_{pattern}.exr" + ) np.testing.assert_almost_equal( demosaicing_CFA_Bayer_bilinear( - read_image(str(CFA.format(pattern)))[..., 0], pattern + read_image(str(CFA))[..., 0], pattern ), - read_image(str(RGB.format(pattern))), + read_image(str(RGB)), decimal=7, ) diff --git a/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py b/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py index 7efdd7a..8a3d6e3 100644 --- a/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py +++ b/colour_demosaicing/bayer/demosaicing/tests/test_malvar2004.py @@ -45,16 +45,18 @@ def test_demosaicing_CFA_Bayer_Malvar2004(self): """ for pattern in ("RGGB", "BGGR", "GRBG", "GBRG"): - CFA = os.path.join(BAYER_DIRECTORY, "Lighthouse_CFA_{0}.exr") + CFA = os.path.join( + BAYER_DIRECTORY, f"Lighthouse_CFA_{pattern}.exr" + ) RGB = os.path.join( - BAYER_DIRECTORY, "Lighthouse_Malvar2004_{0}.exr" + BAYER_DIRECTORY, f"Lighthouse_Malvar2004_{pattern}.exr" ) np.testing.assert_almost_equal( demosaicing_CFA_Bayer_Malvar2004( - read_image(str(CFA.format(pattern)))[..., 0], pattern + read_image(str(CFA))[..., 0], pattern ), - read_image(str(RGB.format(pattern))), + read_image(str(RGB)), decimal=7, ) diff --git a/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py b/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py index e426dee..60f4eef 100644 --- a/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py +++ b/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py @@ -45,14 +45,18 @@ def test_demosaicing_CFA_Bayer_Menon2007(self): """ for pattern in ("RGGB", "BGGR", "GRBG", "GBRG"): - CFA = os.path.join(BAYER_DIRECTORY, "Lighthouse_CFA_{0}.exr") - RGB = os.path.join(BAYER_DIRECTORY, "Lighthouse_Menon2007_{0}.exr") + CFA = os.path.join( + BAYER_DIRECTORY, f"Lighthouse_CFA_{pattern}.exr" + ) + RGB = os.path.join( + BAYER_DIRECTORY, f"Lighthouse_Menon2007_{pattern}.exr" + ) np.testing.assert_almost_equal( demosaicing_CFA_Bayer_Menon2007( - read_image(str(CFA.format(pattern)))[..., 0], pattern + read_image(str(CFA))[..., 0], pattern ), - read_image(str(RGB.format(pattern))), + read_image(str(RGB)), decimal=7, ) @@ -61,11 +65,11 @@ def test_demosaicing_CFA_Bayer_Menon2007(self): ) np.testing.assert_almost_equal( demosaicing_CFA_Bayer_Menon2007( - read_image(str(CFA.format(pattern)))[..., 0], + read_image(str(CFA))[..., 0], pattern, refining_step=False, ), - read_image(str(RGB.format(pattern))), + read_image(str(RGB)), decimal=7, ) diff --git a/colour_demosaicing/bayer/tests/test_masks.py b/colour_demosaicing/bayer/tests/test_masks.py index d6743dd..7fe0bf9 100644 --- a/colour_demosaicing/bayer/tests/test_masks.py +++ b/colour_demosaicing/bayer/tests/test_masks.py @@ -43,10 +43,10 @@ def test_masks_CFA_Bayer(self): """ for pattern in ("RGGB", "BGGR", "GRBG", "GBRG"): - mask = os.path.join(BAYER_DIRECTORY, "{0}_Masks.exr") + mask = os.path.join(BAYER_DIRECTORY, f"{pattern}_Masks.exr") np.testing.assert_almost_equal( tstack(masks_CFA_Bayer((8, 8), pattern)), - read_image(str(mask.format(pattern))), + read_image(str(mask)), decimal=7, ) diff --git a/colour_demosaicing/bayer/tests/test_mosaicing.py b/colour_demosaicing/bayer/tests/test_mosaicing.py index dfa3edf..62cfb91 100644 --- a/colour_demosaicing/bayer/tests/test_mosaicing.py +++ b/colour_demosaicing/bayer/tests/test_mosaicing.py @@ -49,10 +49,12 @@ def test_mosaicing_CFA_Bayer(self): ) for pattern in ("RGGB", "BGGR", "GRBG", "GBRG"): - CFA = os.path.join(BAYER_DIRECTORY, "Lighthouse_CFA_{0}.exr") + CFA = os.path.join( + BAYER_DIRECTORY, f"Lighthouse_CFA_{pattern}.exr" + ) np.testing.assert_almost_equal( mosaicing_CFA_Bayer(image, pattern), - read_image(str(CFA.format(pattern)))[..., 0], + read_image(str(CFA))[..., 0], decimal=7, ) From e8928e1b130a690ecbbd47f72badff80f2116ddc Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Sun, 20 Feb 2022 12:30:16 +1300 Subject: [PATCH 44/51] Update ".gitignore" file. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 9a04916..008a4fa 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ .dmypy.json .idea .ipynb_checkpoints/ +.mypy_cache __pycache__ build colour_demosaicing.egg-info From c073609517475e3828ed4a691ec6ba3a6f2ee7cc Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Sun, 20 Feb 2022 22:44:52 +1300 Subject: [PATCH 45/51] Set minimal "colour-science" version. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 5f6792f..666c811 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,7 +49,7 @@ exclude = [ [tool.poetry.dependencies] python = ">= 3.8, < 3.11" -colour-science = "^0.3.16" +colour-science = ">= 0.4.0" biblib-simple = { version = "*", optional = true } # Development dependency. black = { version = "*", optional = true } # Development dependency. From df2fb5b9a284b23f8aaba010bd01320568425208 Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Sun, 20 Feb 2022 23:23:36 +1300 Subject: [PATCH 46/51] Fix broken unit tests. --- colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py b/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py index 60f4eef..db1a964 100644 --- a/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py +++ b/colour_demosaicing/bayer/demosaicing/tests/test_menon2007.py @@ -61,7 +61,7 @@ def test_demosaicing_CFA_Bayer_Menon2007(self): ) RGB = os.path.join( - BAYER_DIRECTORY, "Lighthouse_Menon2007_NR_{0}.exr" + BAYER_DIRECTORY, f"Lighthouse_Menon2007_NR_{pattern}.exr" ) np.testing.assert_almost_equal( demosaicing_CFA_Bayer_Menon2007( From 50d717da4e58ea32391077652e7f47a3a295aa97 Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Sun, 20 Feb 2022 23:34:24 +1300 Subject: [PATCH 47/51] Update "requirements.txt" file. --- requirements.txt | 148 ++++++++++++++++++++++++++--------------------- 1 file changed, 82 insertions(+), 66 deletions(-) diff --git a/requirements.txt b/requirements.txt index f165aa4..29f2b73 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,129 +1,145 @@ alabaster==0.7.12 appnope==0.1.2 -argon2-cffi==21.1.0 -attrs==21.2.0 +argon2-cffi==21.3.0 +argon2-cffi-bindings==21.2.0 +astor==0.8.1 +asttokens==2.0.5 +attrs==21.4.0 Babel==2.9.1 backcall==0.2.0 -backports.entry-points-selectable==1.1.1 +beautifulsoup4==4.10.0 biblib-simple==0.1.1 +black==22.1.0 bleach==4.1.0 certifi==2021.10.8 cffi==1.15.0 cfgv==3.3.1 -charset-normalizer==2.0.7 +charset-normalizer==2.0.12 +click==8.0.4 colorama==0.4.4 -colour-science==0.3.16 -coverage==6.1.1 -coveralls==3.3.0 -cycler==0.10.0 +colour-science==0.4.0 +coverage==6.3.1 +coveralls==3.3.1 +cycler==0.11.0 debugpy==1.5.1 -decorator==5.1.0 +decorator==5.1.1 defusedxml==0.7.1 -distlib==0.3.3 +distlib==0.3.4 docopt==0.6.2 docutils==0.17.1 -entrypoints==0.3 -filelock==3.3.2 +entrypoints==0.4 +executing==0.8.2 +filelock==3.6.0 flake8==4.0.1 -identify==2.3.5 +flynt==0.76 +identify==2.4.10 idna==3.3 -imageio==2.10.3 -imagesize==1.2.0 -importlib-metadata==4.2.0 -importlib-resources==5.4.0 +imageio==2.16.0 +imagesize==1.3.0 +importlib-metadata==4.11.1 iniconfig==1.1.1 invoke==1.6.0 -ipykernel==6.5.0 -ipython==7.29.0 +ipykernel==6.9.1 +ipython==8.0.1 ipython-genutils==0.2.0 ipywidgets==7.6.5 -jedi==0.18.0 -Jinja2==3.0.2 -jsonschema==4.2.1 +jedi==0.18.1 +Jinja2==3.0.3 +jsonschema==4.4.0 jupyter==1.0.0 -jupyter-client==7.0.6 +jupyter-client==7.1.2 jupyter-console==6.4.0 -jupyter-core==4.9.1 +jupyter-core==4.9.2 jupyterlab-pygments==0.1.2 jupyterlab-widgets==1.0.2 -keyring==23.2.1 -kiwisolver==1.3.1 +keyring==23.5.0 +kiwisolver==1.3.2 latexcodec==2.0.1 -MarkupSafe==2.0.1 -matplotlib==3.3.3 +MarkupSafe==2.1.0 +matplotlib==3.4.3 matplotlib-inline==0.1.3 mccabe==0.6.1 mistune==0.8.4 -nbclient==0.5.5 -nbconvert==6.2.0 +mypy==0.931 +mypy-extensions==0.4.3 +nbclient==0.5.11 +nbconvert==6.4.2 nbformat==5.1.3 -nest-asyncio==1.5.1 +nest-asyncio==1.5.4 nodeenv==1.6.0 -nose==1.3.7 -notebook==6.4.5 -numpy==1.21.1 -oset==0.1.3 -packaging==21.2 +notebook==6.4.8 +numpy==1.22.2 +packaging==21.3 pandocfilters==1.5.0 -parso==0.8.2 +parso==0.8.3 +pathspec==0.9.0 pexpect==4.8.0 pickleshare==0.7.5 -Pillow==8.4.0 -pip==20.2.2 -pkginfo==1.7.1 -platformdirs==2.4.0 +Pillow==9.0.1 +pip==21.3.1 +pkginfo==1.8.2 +platformdirs==2.5.1 pluggy==1.0.0 -pre-commit==2.15.0 -prometheus-client==0.12.0 -prompt-toolkit==3.0.22 +pre-commit==2.17.0 +prometheus-client==0.13.1 +prompt-toolkit==3.0.28 ptyprocess==0.7.0 +pure-eval==0.2.2 py==1.11.0 pybtex==0.24.0 pybtex-docutils==1.0.1 pycodestyle==2.8.0 pycparser==2.21 +pydata-sphinx-theme==0.8.0 +pydocstyle==6.1.1 pyflakes==2.4.0 -Pygments==2.10.0 -pyparsing==2.4.7 -pyrsistent==0.18.0 -pytest==6.2.5 +Pygments==2.11.2 +pyparsing==3.0.7 +pyrsistent==0.18.1 +pytest==7.0.1 +pytest-cov==3.0.0 python-dateutil==2.8.2 pytz==2021.3 +pyupgrade==2.31.0 PyYAML==6.0 pyzmq==22.3.0 -qtconsole==5.2.0 -QtPy==1.11.2 -readme-renderer==30.0 -requests==2.26.0 +qtconsole==5.2.2 +QtPy==2.0.1 +readme-renderer==32.0 +requests==2.27.1 requests-toolbelt==0.9.1 restructuredtext-lint==1.3.2 -rfc3986==1.5.0 -scipy==1.6.1 +rfc3986==2.0.0 +scipy==1.8.0 Send2Trash==1.8.0 -setuptools==49.6.0 +setuptools==59.6.0 six==1.16.0 -snowballstemmer==2.1.0 -Sphinx==3.1.2 -sphinx-rtd-theme==1.0.0 +snowballstemmer==2.2.0 +soupsieve==2.3.1 +Sphinx==4.4.0 sphinxcontrib-applehelp==1.0.2 -sphinxcontrib-bibtex==1.0.0 +sphinxcontrib-bibtex==2.4.1 sphinxcontrib-devhelp==1.0.2 sphinxcontrib-htmlhelp==2.0.0 sphinxcontrib-jsmath==1.0.1 sphinxcontrib-qthelp==1.0.3 sphinxcontrib-serializinghtml==1.1.5 -terminado==0.12.1 +stack-data==0.2.0 +terminado==0.13.1 testpath==0.5.0 +tokenize-rt==4.2.1 toml==0.10.2 +tomli==2.0.1 tornado==6.1 tqdm==4.62.3 traitlets==5.1.1 -twine==3.5.0 -urllib3==1.26.7 -virtualenv==20.10.0 +twine==3.8.0 +types-setuptools==57.4.9 +typing_extensions==4.1.1 +urllib3==1.26.8 +virtualenv==20.13.1 wcwidth==0.2.5 webencodings==0.5.1 -wheel==0.35.1 +wheel==0.37.0 widgetsnbextension==3.5.2 -yapf==0.23.0 -zipp==3.6.0 +zipp==3.7.0 From e2402da9a8989b3342d782611fa4d48163d2791c Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Sun, 20 Feb 2022 23:34:47 +1300 Subject: [PATCH 48/51] Update "TODO.rst" file. --- TODO.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO.rst b/TODO.rst index b1cc7ff..449cd38 100644 --- a/TODO.rst +++ b/TODO.rst @@ -6,7 +6,7 @@ TODO - colour_demosaicing/__init__.py - - Line 64 : # TODO: Remove legacy printing support when deemed appropriate. + - Line 81 : # TODO: Remove legacy printing support when deemed appropriate. About ----- From fc4ae4589bc850d0df52576147aea529e9a6083b Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Sun, 20 Feb 2022 23:35:22 +1300 Subject: [PATCH 49/51] Update "tasks.py" file. --- tasks.py | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/tasks.py b/tasks.py index 40dc6b7..74dd9b0 100644 --- a/tasks.py +++ b/tasks.py @@ -389,7 +389,14 @@ def sub_callable(match): source = re.sub( "from setuptools import setup", - "import codecs\nfrom setuptools import setup", + ( + '"""\n' + "Colour - Demosaicing - Setup\n" + "============================\n" + '"""\n\n' + "import codecs\n" + "from setuptools import setup" + ), source, ) source = re.sub( @@ -413,6 +420,8 @@ def sub_callable(match): with open("setup.py", "w") as setup_file: setup_file.write(source) + ctx.run("poetry run pre-commit run --files setup.py || true") + ctx.run("twine check dist/*") @@ -441,15 +450,21 @@ def virtualise(ctx: Context, tests: Boolean = True): ) with ctx.cd(unique_name): - ctx.run("poetry env use 3") - ctx.run("poetry install") + ctx.run('poetry install --extras "plotting"') ctx.run("source $(poetry env info -p)/bin/activate") ctx.run( 'python -c "import imageio;' 'imageio.plugins.freeimage.download()"' ) if tests: - ctx.run("poetry run nosetests", env={"MPLBACKEND": "AGG"}) + ctx.run( + "poetry run py.test " + "--disable-warnings " + "--doctest-modules " + f"--ignore={PYTHON_PACKAGE_NAME}/examples " + f"{PYTHON_PACKAGE_NAME}", + env={"MPLBACKEND": "AGG"}, + ) @task @@ -473,17 +488,17 @@ def tag(ctx: Context): with open(os.path.join(PYTHON_PACKAGE_NAME, "__init__.py")) as file_handle: file_content = file_handle.read() major_version = re.search( - "__major_version__\\s+=\\s+'(.*)'", file_content + '__major_version__\\s+=\\s+"(.*)"', file_content ).group( # type: ignore[union-attr] 1 ) minor_version = re.search( - "__minor_version__\\s+=\\s+'(.*)'", file_content + '__minor_version__\\s+=\\s+"(.*)"', file_content ).group( # type: ignore[union-attr] 1 ) change_version = re.search( - "__change_version__\\s+=\\s+'(.*)'", file_content + '__change_version__\\s+=\\s+"(.*)"', file_content ).group( # type: ignore[union-attr] 1 ) From 259d7655ab8d7ed184470da74ea2c9f3aa8c093f Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Sun, 20 Feb 2022 23:35:59 +1300 Subject: [PATCH 50/51] Update "setup.py" file. --- setup.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/setup.py b/setup.py index 9467183..b256744 100644 --- a/setup.py +++ b/setup.py @@ -23,30 +23,38 @@ ], } -install_requires = ["colour-science>=0.3.16,<0.4.0"] +install_requires = ["colour-science>=0.4.0"] extras_require = { "development": [ "biblib-simple", - "coverage", + "black", + "coverage!=6.3", "coveralls", "flake8", + "flynt", "invoke", "jupyter", - "mock", - "nose", + "mypy", "pre-commit", + "pydata-sphinx-theme", + "pydocstyle", "pytest", + "pytest-cov", + "pyupgrade", "restructuredtext-lint", - "sphinx<=3.1.2", - "sphinx_rtd_theme", + "sphinx>=4,<5", "sphinxcontrib-bibtex", "toml", "twine", - "yapf==0.23", ], - "plotting": ["matplotlib"], - "read-the-docs": ["mock", "numpy", "sphinxcontrib-bibtex"], + "plotting": ["matplotlib>=3.2,!=3.5.0,!=3.5.1"], + "read-the-docs": [ + "matplotlib>=3.2,!=3.5.0,!=3.5.1", + "numpy>=1.19,<2", + "pydata-sphinx-theme", + "sphinxcontrib-bibtex", + ], } setup( @@ -63,5 +71,5 @@ package_data=package_data, install_requires=install_requires, extras_require=extras_require, - python_requires=">=3.6,<4.0", + python_requires=">=3.8,<3.11", ) From 0fc20784b2ad63177fea84cb55b068838828e1e3 Mon Sep 17 00:00:00 2001 From: Thomas Mansencal Date: Mon, 21 Feb 2022 22:10:01 +1300 Subject: [PATCH 51/51] Raise package version to 0.2.0. --- colour_demosaicing/__init__.py | 4 ++-- pyproject.toml | 2 +- setup.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/colour_demosaicing/__init__.py b/colour_demosaicing/__init__.py index 4713111..614f0fa 100644 --- a/colour_demosaicing/__init__.py +++ b/colour_demosaicing/__init__.py @@ -53,8 +53,8 @@ __application_name__ = "Colour - Demosaicing" __major_version__ = "0" -__minor_version__ = "1" -__change_version__ = "6" +__minor_version__ = "2" +__change_version__ = "0" __version__ = ".".join( (__major_version__, __minor_version__, __change_version__) ) diff --git a/pyproject.toml b/pyproject.toml index 666c811..33c56d2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "colour-demosaicing" -version = "0.1.6" +version = "0.2.0" description = "CFA (Colour Filter Array) Demosaicing Algorithms for Python" license = "BSD-3-Clause" authors = [ "Colour Developers " ] diff --git a/setup.py b/setup.py index b256744..5c1b9bb 100644 --- a/setup.py +++ b/setup.py @@ -59,7 +59,7 @@ setup( name="colour-demosaicing", - version="0.1.6", + version="0.2.0", description="CFA (Colour Filter Array) Demosaicing Algorithms for Python", long_description=codecs.open("README.rst", encoding="utf8").read(), author="Colour Developers",